UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

79 lines • 3.04 kB
/** * hosting/signin/source — the lifetimes, applied in ONE place. * * `signInSource` turns a {@link SignInStore} (which only keeps records) into a * {@link SignInSource} (which answers "who is behind this key, right now?"). * A sign-in is live while BOTH clocks allow it: * * - the absolute lifetime (`SignIn.expiresAt`, fixed when it was created — * 8 hours by default at the sign-in door), and * - the idle limit (`idleMinutes` since `SignIn.lastSeenAt`) — 30 minutes * under an SSO strategy, where signing in again is silent, and 60 under a * password strategy, where it means retyping. * * A sign-in found past either clock is DELETED and reported ended, so the next * request does not find it either and an open socket carrying it closes. The * honest costs, stated rather than implied: a person disabled at the IdP keeps * access until their sign-in ends, and their roles are frozen at sign-in for * the same time — the library does not poll the IdP. */ export function signInSource(options) { const { store } = options; const idleMs = checkIdle(options.idleMinutes) * 60_000; const now = options.now ?? Date.now; const listeners = new Set(); const tell = (key) => { for (const listener of [...listeners]) { try { listener(key); } catch { // A listener that throws is told nothing more; the sign-in is over regardless. } } }; // Every end the store reports is announced once — including the ends a // store performs itself (a sweep, a per-account cap). const storeAnnounces = typeof store.onDelete === 'function'; if (storeAnnounces) store.onDelete?.(tell); const end = async (key) => { await store.delete(key); if (!storeAnnounces) tell(key); }; return { async identify(key) { const signIn = await store.find(key); if (signIn === undefined) return undefined; const at = now(); if (at >= signIn.expiresAt || at - signIn.lastSeenAt >= idleMs) { await end(key); try { options.onExpired?.(signIn, at >= signIn.expiresAt ? 'lifetime' : 'idle'); } catch { // An observer that throws changes nothing: the sign-in is over regardless. } return undefined; } await store.touch(key, at); return signIn.identity; }, onEnd(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; }, end, }; } function checkIdle(minutes) { if (!Number.isFinite(minutes) || minutes <= 0) { throw new TypeError(`[hosting] signInSource needs idleMinutes, a positive number of minutes (got ${String(minutes)}).`); } return minutes; } //# sourceMappingURL=source.js.map