import { type HttpLink } from "../link/http-link.js"; import { PortRendezvous } from "../link/rendezvous.js"; import type { WebSocketConnection } from "../ws/client.js"; import { type HandlerInfo, HandlerTable, type RouteFunction } from "./handler-table.js"; export interface SwHttpDispatcherOptions { self: ServiceWorkerGlobalScope; log?: (...args: unknown[]) => void; /** * Picks the handler of a request among the live handlers of its key. * Default `leastInflightRoute`: the fewest requests in flight, the oldest on * a tie — with one tab per key, the first live handler, as before. */ route?: RouteFunction; /** * How long a request waits for a handler while its key is handed over (the * handlers it has are all draining, or `route` picked none), in ms. Past * it, 503. Default 15 s. */ attachTimeoutMs?: number; /** * How long a request for a key with no handler waits for the pages to * attach their links again (after a ServiceWorker restart), in ms. Past it, * 404 for a key this worker has served before. Default 3 s. */ relinkTimeoutMs?: number; /** How long a replaced link may finish its requests, in ms. Default 5 s. */ handoverMs?: number; /** How long a socket waits for its handler's answer, in ms. Default 15 s. */ wsConnectTimeoutMs?: number; /** Per-stream inactivity timeout of the links (webrun-rpc's). Default none. */ streamTimeout?: number; /** * Period of the backup liveness check, in ms: handlers whose owner lock is * no longer held are dropped. Default 5 s; 0 turns it off. */ livenessCheckMs?: number; } /** One attached handler link, as the ServiceWorker holds it. */ declare class HandlerEntry implements HandlerInfo { readonly key: string; readonly id: string; readonly owner: string; readonly meta: unknown; readonly link: HttpLink; readonly ctl: MessagePort | undefined; readonly clientId: string | undefined; readonly ownerLock: string | undefined; draining: boolean; readonly attachedAt: number; readonly sockets: PortRendezvous; stopWatch: () => void; closeUp?: () => Promise; constructor(key: string, id: string, owner: string, meta: unknown, link: HttpLink, ctl: MessagePort | undefined, clientId: string | undefined, ownerLock: string | undefined); get inflight(): number; } /** The reason requests fail with when their handler dies under them. */ export declare const HANDLER_LOST_MESSAGE = "The handler was lost while this request was in flight"; /** * The same-origin ServiceWorker: routes `fetch` events under its scope to * handler links that pages attach — handlers in the pages themselves * (`SwHttpAdapter.register`) or in Dedicated Workers * (`SwHttpAdapter.attachWorker` + `serveHttpInWorker`). * * A url `//...` belongs to the handlers of `key`. Of those, the * `route` option picks one per request. Requests and responses stream with * backpressure in both directions, and an aborted fetch reaches the handler * as a cancellation (webrun-rpc + webrun-http-streams). * * Urls of keys this worker never served go to the network untouched: the * worker does not call `respondWith` for them. * * RESTARTS. The browser stops an idle ServiceWorker, and every port it held * dies with it. Nothing is persisted but the set of keys ever served: the * first request after a restart asks every window to attach its links again * (`NEED_LINKS`) and waits for them. * * LIVENESS. Each handler holds an owner lock; when it is granted to this * worker, the handler is gone and its requests in flight fail at once (502). * This worker holds a router lock that handlers watch the same way. */ export declare class SwHttpDispatcher { readonly self: ServiceWorkerGlobalScope; readonly log: (...args: unknown[]) => void; readonly options: SwHttpDispatcherOptions; readonly table: HandlerTable; /** * Keys that were served at some point on this worker (sticky, kept in * IndexedDB). Lets a fetch tell "a site url whose handler is gone → 404" * from "an unknown url under the scope → the network". */ readonly claimedKeys: Set; /** The Web Lock this worker holds while it lives; handlers watch it. */ readonly routerLock?: string; private _routerHeld; private _restored; private _restoreSettled; private _cleanup; private _livenessTimer?; private readonly _relinkRounds; /** Keys whose last handler was drained: until the time stored, requests wait for the next. */ private readonly _vacant; constructor(options: SwHttpDispatcherOptions); get scope(): string; /** The key `url` belongs to: its first path segment under the scope. */ keyOf(url: string | URL): string | undefined; /** Whether `key` has ever been served on this worker. */ isClaimedKey(key: string): boolean; /** Whether requests for `key` are this worker's to answer. */ serves(key: string): boolean; start(): void; stop(): Promise; /** * Answers `request` as a fetch under the scope would be: through the * handler its key routes to. A url outside the scope, or of a key this * worker never served, goes to the network. */ dispatch(request: Request): Promise; /** * Opens a socket to the handler `url` routes to: the handler accepts or * refuses it like an HTTP upgrade, and the socket's port comes back. After * that the socket runs between the caller and the handler directly; this * worker is not on its data path. */ connectWebSocket(url: string, protocols?: string[], headers?: [string, string][]): Promise; private _onFetch; private _attach; private _add; /** * The backup of the owner-lock watches: every `livenessCheckMs`, drops the * handlers whose owner lock is no longer held. Runs only while some handler * has an owner lock, and keeps nothing awake: a timer is not an event. */ private _watchLiveness; /** The entry a drain or detach names, if its owner token still matches. */ private _find; /** * Stops routing to `entry`, lets its requests in flight run for up to `ms`, * then closes it. Requests that arrive meanwhile wait for the next owner. */ private _retire; private _close; /** * The handler for `request`: picked by `route` among the live handlers of * its key, waiting for one when there is none. */ private _acquire; private _pick; /** * Asks every window to attach its links again, and waits until a handler * for `key` is back, every window answered, or `relinkTimeoutMs` passed. * At most one round per key every 10 s: a key whose tab is simply gone * should not cost every request the wait. */ private _askForLinks; private _linkRound; /** Asks a version-1 page for a fresh port, as version 1's worker did. */ private _askLegacyPage; private _onControl; private _isOwnClient; private _claim; private _restoreClaimedKeys; } /** * Starts the same-origin dispatcher in a ServiceWorker. Returns a function * that stops it. */ export declare function startHttpDispatcher(options: SwHttpDispatcherOptions): () => void; export {}; //# sourceMappingURL=http-sw-dispatcher.d.ts.map