@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
39 lines • 1.75 kB
JavaScript
import { useEffect, useRef, useState } from 'react';
/** What {@link useApprovalCountdown} reads for `expiresAt` at `now`, without the ticking. */
export function approvalCountdown(expiresAt, now = Date.now()) {
const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
if (!Number.isFinite(deadline)) {
return { remainingMs: null, isExpired: false };
}
const remainingMs = Math.max(0, deadline - now);
return { remainingMs, isExpired: remainingMs === 0 };
}
/**
* Time left on an approval request (`call.approval.expiresAt`), ticking until it lapses and then
* holding at zero. Headless: pair `remainingMs` with `formatElapsed` or any label of your own, and
* use `isExpired` to stop offering buttons the server would refuse (`410 Gone`). The server stays
* the authority — a lapse is final only once the call settles `expired`.
*/
export function useApprovalCountdown(expiresAt, options = {}) {
const { intervalMs = 1000 } = options;
const nowRef = useRef(options.now ?? Date.now);
nowRef.current = options.now ?? Date.now;
const [state, setState] = useState(() => approvalCountdown(expiresAt, nowRef.current()));
useEffect(() => {
const read = () => approvalCountdown(expiresAt, nowRef.current());
const first = read();
setState(first);
if (first.remainingMs === null || first.isExpired) {
return;
}
const timer = setInterval(() => {
const next = read();
setState(next);
if (next.isExpired)
clearInterval(timer);
}, intervalMs);
return () => clearInterval(timer);
}, [expiresAt, intervalMs]);
return state;
}
//# sourceMappingURL=countdown.js.map