Initial Commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { raisePrincipalExpired } from "../error-codes/error-codes.js";
|
||||
|
||||
/** Clock abstraction used by the principal-expiry watcher. */
|
||||
export interface PrincipalExpiryClock {
|
||||
now(): number;
|
||||
}
|
||||
|
||||
/** Listener signature invoked when the principal's token expires. */
|
||||
export type PrincipalExpiryListener = (event: {
|
||||
readonly connectionId: string;
|
||||
readonly principalId: string;
|
||||
}) => void;
|
||||
|
||||
/** Options for {@link PrincipalExpiryWatcher}. */
|
||||
export interface PrincipalExpiryWatcherOptions {
|
||||
readonly connectionId: string;
|
||||
readonly principalId: string;
|
||||
/**
|
||||
* Wall-clock expiry timestamp (ms since epoch). The handshake
|
||||
* pipeline reads this from the resolved token; once `now()` reaches
|
||||
* this value the watcher fires.
|
||||
*/
|
||||
readonly expiresAt: number;
|
||||
readonly clock: PrincipalExpiryClock;
|
||||
readonly onExpired: PrincipalExpiryListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the frozen principal's underlying token expiry. The engine
|
||||
* polls `check()` from a timer (production) or from a test loop driven
|
||||
* by `FakeClock`. Once the expiry passes, the watcher fires the
|
||||
* `onExpired` listener exactly once and throws
|
||||
* `WS_BUN.PRINCIPAL_EXPIRED` so the engine can close the connection.
|
||||
*
|
||||
* The principal itself is NOT mutated by this watcher — the contract
|
||||
* requires it to stay frozen for the connection's lifetime. The watcher
|
||||
* is the engine's signal that it is time to drop the connection.
|
||||
*/
|
||||
export class PrincipalExpiryWatcher {
|
||||
private fired = false;
|
||||
|
||||
public constructor(
|
||||
private readonly options: PrincipalExpiryWatcherOptions
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Run one expiry check. If `now() >= expiresAt` and the watcher has
|
||||
* not yet fired, invoke `onExpired` and throw `WS_BUN.PRINCIPAL_EXPIRED`.
|
||||
*/
|
||||
public check(): void {
|
||||
if (this.fired) return;
|
||||
if (this.options.clock.now() < this.options.expiresAt) return;
|
||||
this.fired = true;
|
||||
this.options.onExpired({
|
||||
connectionId: this.options.connectionId,
|
||||
principalId: this.options.principalId,
|
||||
});
|
||||
raisePrincipalExpired(this.options.connectionId);
|
||||
}
|
||||
|
||||
public get hasFired(): boolean {
|
||||
return this.fired;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user