dynamodb-reactive 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../client/src/cluster.ts","../../client/src/hub.ts","../../client/src/cloudflare.ts"],"names":["isRecord","send","body","principal"],"mappings":"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","file":"cloudflare.cjs","sourcesContent":["import { ReactiveClient } from './client.js';\nimport type { ReactiveClientConfig } from './types.js';\n\n/**\n * Cluster workers run on machines with no inbound access. They authenticate\n * outbound with a long-lived runner token, exchange it for a short-lived ticket,\n * and hold a reactive WebSocket that pushes work to them. These helpers use only\n * Web standards (fetch, WebSocket, Web Crypto), so the same code runs in Node and\n * in Workers runtimes, whichever backend serves the reactive protocol.\n */\n\nexport const DEFAULT_CLUSTER_TICKET_PATH = '/api/cluster/token';\nexport const DEFAULT_CLUSTER_HTTP_PATH = '/api/reactive';\nconst DEFAULT_RETRY_DELAY_MS = 10_000;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nconst DEFAULT_TICKET_LIFETIME_SECONDS = 60;\n// Workers are few and long-lived, and the server needs their pings to know they are alive.\nconst DEFAULT_CLUSTER_HEARTBEAT_MS = 30_000;\n\n/** Short-lived credential returned by the ticket endpoint. */\nexport interface ClusterTicket {\n /** Sent as the WebSocket `token` query parameter and the HTTP bearer token. */\n token: string;\n /** Expiry in epoch seconds. */\n expiresAt: number;\n /** `wss:` URL of the reactive WebSocket. */\n url: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Validates a ticket endpoint response; the socket URL must use `wss:`. */\nexport function parseClusterTicket(value: unknown): ClusterTicket {\n if (\n !isRecord(value) ||\n typeof value.token !== 'string' ||\n value.token.length === 0 ||\n typeof value.expiresAt !== 'number' ||\n !Number.isFinite(value.expiresAt) ||\n typeof value.url !== 'string'\n ) {\n throw new Error('Cluster ticket response is invalid');\n }\n const url = new URL(value.url);\n if (url.protocol !== 'wss:') throw new Error('Cluster ticket URL must use WSS');\n return { token: value.token, expiresAt: value.expiresAt, url: url.toString() };\n}\n\nexport interface ClusterWorkerConfig {\n /** Origin of the application that serves the ticket and reactive HTTP endpoints. */\n baseUrl: string;\n /** Long-lived runner credential exchanged for tickets. */\n runnerToken: string | (() => string | Promise<string>);\n /** Ticket endpoint path. Defaults to `/api/cluster/token`. */\n ticketPath?: string;\n /** Reactive HTTP endpoint path. Defaults to `/api/reactive`. */\n httpPath?: string;\n /** Delay between ticket, connect, and reconnect attempts. Defaults to 10 seconds. */\n retryDelay?: number;\n /** Ticket request timeout. Defaults to 10 seconds. */\n requestTimeout?: number;\n /** Stops retrying when aborted. */\n signal?: AbortSignal;\n /** Called with each failed ticket or connect attempt before the next retry. */\n onRetry?: (error: Error) => void;\n /** Extra client options; the URL, HTTP URL, and auth are managed here. */\n client?: Omit<ReactiveClientConfig, 'url' | 'httpUrl' | 'auth'>;\n /** Fetch implementation for ticket requests. Defaults to the global fetch. */\n fetch?: typeof fetch;\n}\n\nfunction toError(cause: unknown): Error {\n return cause instanceof Error ? cause : new Error(String(cause));\n}\n\nfunction abortError(signal: AbortSignal): Error {\n return toError(signal.reason ?? new Error('Cluster worker connection aborted'));\n}\n\nfunction wait(milliseconds: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortError(signal));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortError(signal!));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, milliseconds);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Requests a ticket from the application using the runner token. */\nexport async function fetchClusterTicket(\n config: Pick<\n ClusterWorkerConfig,\n 'baseUrl' | 'runnerToken' | 'ticketPath' | 'requestTimeout' | 'fetch'\n >,\n): Promise<ClusterTicket> {\n const runnerToken =\n typeof config.runnerToken === 'function' ? await config.runnerToken() : config.runnerToken;\n const endpoint = new URL(config.ticketPath ?? DEFAULT_CLUSTER_TICKET_PATH, config.baseUrl);\n const response = await (config.fetch ?? fetch)(endpoint, {\n method: 'POST',\n headers: { authorization: `Bearer ${runnerToken}` },\n signal: AbortSignal.timeout(config.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS),\n });\n if (!response.ok) {\n throw new Error(`Cluster ticket request failed: HTTP ${response.status}`);\n }\n return parseClusterTicket(await response.json());\n}\n\n/**\n * Connects a cluster worker, retrying ticket and connect failures until it\n * succeeds or `signal` aborts. Each (re)connect fetches a fresh ticket. The\n * returned client keeps reconnecting on its own; subscribe to work with\n * `client.subscribe(...)` and stop with `client.disconnect()`.\n */\nexport async function connectClusterWorker(config: ClusterWorkerConfig): Promise<ReactiveClient> {\n const retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;\n const httpUrl = new URL(config.httpPath ?? DEFAULT_CLUSTER_HTTP_PATH, config.baseUrl).toString();\n for (;;) {\n if (config.signal?.aborted) throw abortError(config.signal);\n let client: ReactiveClient | undefined;\n try {\n // The first ticket only supplies the socket URL; the client fetches its own\n // ticket for every connection so reconnects never reuse an expired one.\n // oxlint-disable-next-line no-await-in-loop -- Attempts retry serially.\n const ticket = await fetchClusterTicket(config);\n client = new ReactiveClient({\n reconnectDelay: retryDelay,\n maxReconnectAttempts: Number.MAX_SAFE_INTEGER,\n heartbeatInterval: DEFAULT_CLUSTER_HEARTBEAT_MS,\n ...config.client,\n url: ticket.url,\n httpUrl,\n auth: async () => (await fetchClusterTicket(config)).token,\n });\n // oxlint-disable-next-line no-await-in-loop -- Attempts retry serially.\n await client.connect();\n return client;\n } catch (cause) {\n // connect() keeps reconnecting after it rejects; stop it before retrying.\n client?.disconnect();\n if (config.signal?.aborted) throw abortError(config.signal);\n config.onRetry?.(toError(cause));\n // oxlint-disable-next-line no-await-in-loop -- Backoff belongs to the retry loop.\n await wait(retryDelay, config.signal);\n }\n }\n}\n\nconst encoder = new TextEncoder();\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/u, '');\n}\n\nfunction base64UrlDecode(value: string): Uint8Array<ArrayBuffer> {\n const padded = value.replaceAll('-', '+').replaceAll('_', '/');\n const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4));\n const bytes = new Uint8Array(new ArrayBuffer(binary.length));\n for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);\n return bytes;\n}\n\nfunction hmacKey(secret: string, usage: 'sign' | 'verify'): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n [usage],\n );\n}\n\nexport interface SignedClusterTicketOptions {\n /** HMAC secret shared only by the servers that mint and verify tickets. */\n secret: string;\n /** Service principal the ticket authenticates as. */\n principal: string;\n /** `wss:` URL returned to the worker. */\n url: string;\n /** Ticket lifetime. Defaults to 60 seconds. */\n lifetimeSeconds?: number;\n /** Current time in milliseconds, for tests. */\n now?: number;\n}\n\n/**\n * Mints a stateless HMAC-signed ticket. Verifying it needs no storage read,\n * which keeps reconnect storms off the database.\n */\nexport async function createSignedClusterTicket(\n options: SignedClusterTicketOptions,\n): Promise<ClusterTicket> {\n const expiresAt =\n Math.floor((options.now ?? Date.now()) / 1000) +\n (options.lifetimeSeconds ?? DEFAULT_TICKET_LIFETIME_SECONDS);\n const payload = base64UrlEncode(\n encoder.encode(JSON.stringify({ p: options.principal, exp: expiresAt })),\n );\n const signature = new Uint8Array(\n await crypto.subtle.sign(\n 'HMAC',\n await hmacKey(options.secret, 'sign'),\n encoder.encode(payload),\n ),\n );\n return parseClusterTicket({\n token: `${payload}.${base64UrlEncode(signature)}`,\n expiresAt,\n url: options.url,\n });\n}\n\n/** Returns the ticket's principal, or `undefined` when it is forged, malformed, or expired. */\nexport async function verifySignedClusterTicket(\n token: string,\n secret: string,\n now = Date.now(),\n): Promise<string | undefined> {\n const parts = token.split('.');\n if (parts.length !== 2) return undefined;\n const [payload, signature] = parts as [string, string];\n if (!payload || !signature) return undefined;\n try {\n // crypto.subtle.verify compares in constant time.\n const valid = await crypto.subtle.verify(\n 'HMAC',\n await hmacKey(secret, 'verify'),\n base64UrlDecode(signature),\n encoder.encode(payload),\n );\n if (!valid) return undefined;\n const claims: unknown = JSON.parse(new TextDecoder().decode(base64UrlDecode(payload)));\n if (\n !isRecord(claims) ||\n typeof claims.p !== 'string' ||\n typeof claims.exp !== 'number' ||\n claims.exp <= Math.floor(now / 1000)\n ) {\n return undefined;\n }\n return claims.p;\n } catch {\n return undefined;\n }\n}\n","import type {\n JsonPatch,\n SnapshotResponse,\n SubscribeRequest,\n UnsubscribeRequest,\n WebSocketEvent,\n} from './protocol.js';\n\n/**\n * Protocol core for a reactive hub: the server half of the wire protocol for\n * backends that hold WebSockets themselves (for example a Cloudflare Durable\n * Object). It keeps every piece of per-socket state on the socket's attachment so\n * the state survives hibernation, and it has no runtime imports, so it runs and\n * tests in Node.\n */\n\n/** Hibernation limits a socket attachment to 2,048 bytes of serialized JSON. */\nexport const DEFAULT_MAX_ATTACHMENT_BYTES = 2_048;\n\n/** One subscription held by a socket. */\nexport interface HubSubscription {\n subscriptionId: string;\n generation: number;\n path: string;\n}\n\n/** State kept on each socket's attachment. */\nexport interface HubSocketState {\n connectionId: string;\n principal: string;\n subscriptions: HubSubscription[];\n /** Epoch ms the socket was accepted; the liveness baseline before its first ping. */\n connectedAt?: number;\n /** Application data kept with the socket, such as what a listener is playing. */\n data?: unknown;\n}\n\n/** The parts of a hibernatable WebSocket the hub uses; faked in tests. */\nexport interface HubSocket {\n send(message: string): void;\n deserializeAttachment(): unknown;\n serializeAttachment(value: unknown): void;\n}\n\nexport interface HubSockets {\n all(): HubSocket[];\n byConnectionId(connectionId: string): HubSocket | undefined;\n}\n\n/** Monotonic per-path versions. Versions only need to grow while sockets stay open. */\nexport interface HubVersions {\n get(path: string): Promise<number>;\n set(path: string, version: number): Promise<void>;\n}\n\nexport interface HubPathContext {\n path: string;\n principal: string;\n input: unknown;\n}\n\nexport interface HubPath {\n /**\n * Runs before the subscription is stored. Return false to reject it. Every\n * subscriber of a path receives its pushes, so authorise here, not in `snapshot`.\n */\n authorize?(context: HubPathContext): boolean | Promise<boolean>;\n /** Current data for the path, sent as the subscription snapshot. */\n snapshot(context: HubPathContext): unknown;\n}\n\n/** Fixed paths by name, or a resolver for parameterised paths such as `drafts.<id>`. */\nexport type HubPaths = Record<string, HubPath> | ((path: string) => HubPath | undefined);\n\nexport interface ReactiveHubOptions {\n sockets: HubSockets;\n versions: HubVersions;\n paths: HubPaths;\n /** Rejects subscribes that would grow an attachment past this size. */\n maxAttachmentBytes?: number;\n}\n\n/** Thrown for requests the caller is not allowed to make. */\nexport class ReactiveHubError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReactiveHubError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Validates a reactive HTTP body for the subscribe and unsubscribe requests a hub serves. */\nexport function isHubRequest(value: unknown): value is SubscribeRequest | UnsubscribeRequest {\n if (!isRecord(value)) return false;\n return (\n (value.type === 'subscribe' || value.type === 'unsubscribe') &&\n typeof value.connectionId === 'string' &&\n typeof value.subscriptionId === 'string' &&\n typeof value.generation === 'number' &&\n (value.type === 'unsubscribe' || typeof value.path === 'string')\n );\n}\n\n/** Returns the bearer token from an `Authorization` header, or an empty string. */\nexport function bearerToken(request: Request): string {\n const match = /^Bearer ([^\\s]+)$/u.exec(request.headers.get('authorization') ?? '');\n return match?.[1] ?? '';\n}\n\n/** Initial attachment for a newly accepted socket. */\nexport function createHubSocketState(\n connectionId: string,\n principal: string,\n connectedAt?: number,\n): HubSocketState {\n return {\n connectionId,\n principal,\n subscriptions: [],\n ...(connectedAt === undefined ? {} : { connectedAt }),\n };\n}\n\nfunction socketState(socket: HubSocket): HubSocketState {\n const state = socket.deserializeAttachment() as HubSocketState;\n // Attachments written before subscriptions carried a path have no path; they\n // are dropped rather than fanned out to every notification.\n return { ...state, subscriptions: state.subscriptions.filter((entry) => entry.path) };\n}\n\nfunction send(socket: HubSocket, event: WebSocketEvent): void {\n try {\n socket.send(JSON.stringify(event));\n } catch {\n // A socket closing mid-broadcast is cleaned up by its close handler.\n }\n}\n\n/** Creates the protocol core for one hub instance. */\nexport function createReactiveHub(options: ReactiveHubOptions) {\n const { sockets, versions } = options;\n const maxAttachmentBytes = options.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES;\n const resolve = (path: string): HubPath | undefined =>\n typeof options.paths === 'function'\n ? options.paths(path)\n : Object.hasOwn(options.paths, path)\n ? options.paths[path]\n : undefined;\n\n const ownedSocket = (connectionId: string, principal: string): HubSocket | undefined => {\n const socket = sockets.byConnectionId(connectionId);\n // The HTTP caller must own the WebSocket it registers against.\n return socket && socketState(socket).principal === principal ? socket : undefined;\n };\n\n const subscribersOf = (path: string) =>\n sockets.all().flatMap((socket) =>\n socketState(socket)\n .subscriptions.filter((subscription) => subscription.path === path)\n .map((subscription) => ({ socket, subscription })),\n );\n\n const bump = async (path: string) => {\n const fromVersion = await versions.get(path);\n const toVersion = fromVersion + 1;\n await versions.set(path, toVersion);\n return { fromVersion, toVersion };\n };\n\n return {\n /**\n * Answers the client's `init` and `ping` frames. Any other frame with a\n * string `type` is returned for the application to handle.\n */\n message(\n socket: HubSocket,\n raw: string,\n ): (Record<string, unknown> & { type: string }) | undefined {\n let frame: unknown;\n try {\n frame = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isRecord(frame) || typeof frame.type !== 'string') return undefined;\n if (frame.type === 'init') {\n send(socket, { type: 'connected', connectionId: socketState(socket).connectionId });\n } else if (frame.type === 'ping') {\n send(socket, { type: 'pong' });\n } else {\n return frame as Record<string, unknown> & { type: string };\n }\n return undefined;\n },\n\n /** The application data stored with a socket. */\n socketData<T = unknown>(socket: HubSocket): T | undefined {\n return socketState(socket).data as T | undefined;\n },\n\n /** Stores application data with a socket; it shares the attachment size limit. */\n setSocketData(socket: HubSocket, data: unknown): void {\n const next: HubSocketState = { ...socketState(socket), data };\n if (new TextEncoder().encode(JSON.stringify(next)).length > maxAttachmentBytes) {\n throw new ReactiveHubError('Socket data is too large');\n }\n socket.serializeAttachment(next);\n },\n\n /** The principal a socket authenticated as. */\n principalOf(socket: HubSocket): string {\n return socketState(socket).principal;\n },\n\n async subscribe(request: SubscribeRequest, principal: string): Promise<SnapshotResponse> {\n const handler = resolve(request.path);\n if (!handler) throw new ReactiveHubError('Unknown subscription path');\n if (!ownedSocket(request.connectionId, principal)) {\n throw new ReactiveHubError('Unknown connection');\n }\n const context: HubPathContext = { path: request.path, principal, input: request.input };\n if (handler.authorize && !(await handler.authorize(context))) {\n throw new ReactiveHubError('Subscription not allowed');\n }\n // Re-read after the awaits so a concurrent subscribe on the same socket is kept.\n const socket = ownedSocket(request.connectionId, principal);\n if (!socket) throw new ReactiveHubError('Unknown connection');\n const state = socketState(socket);\n const next: HubSocketState = {\n ...state,\n subscriptions: [\n ...state.subscriptions.filter((entry) => entry.subscriptionId !== request.subscriptionId),\n {\n subscriptionId: request.subscriptionId,\n generation: request.generation,\n path: request.path,\n },\n ],\n };\n if (new TextEncoder().encode(JSON.stringify(next)).length > maxAttachmentBytes) {\n throw new ReactiveHubError('Too many subscriptions on this connection');\n }\n socket.serializeAttachment(next);\n // Read the version before the data so a concurrent notify can only make the\n // client re-fetch, never miss a change.\n const version = await versions.get(request.path);\n const data = await handler.snapshot(context);\n return {\n type: 'snapshot',\n subscriptionId: request.subscriptionId,\n generation: request.generation,\n version,\n data,\n };\n },\n\n unsubscribe(request: UnsubscribeRequest, principal: string): void {\n const socket = ownedSocket(request.connectionId, principal);\n if (!socket) return;\n const state = socketState(socket);\n socket.serializeAttachment({\n ...state,\n subscriptions: state.subscriptions.filter(\n (entry) => entry.subscriptionId !== request.subscriptionId,\n ),\n } satisfies HubSocketState);\n },\n\n /** Bumps the path's version and asks each of its subscribers to re-fetch. */\n async notify(path: string): Promise<void> {\n const { fromVersion, toVersion } = await bump(path);\n for (const { socket, subscription } of subscribersOf(path)) {\n send(socket, {\n type: 'resync',\n subscriptionId: subscription.subscriptionId,\n generation: subscription.generation,\n fromVersion,\n toVersion,\n messageId: crypto.randomUUID(),\n });\n }\n },\n\n /**\n * Bumps the path's version and sends the same JSON patches to each subscriber.\n * Use it only when every subscriber of the path sees the same data.\n */\n async publish(path: string, patches: JsonPatch[]): Promise<void> {\n const { fromVersion, toVersion } = await bump(path);\n for (const { socket, subscription } of subscribersOf(path)) {\n send(socket, {\n type: 'patch',\n subscriptionId: subscription.subscriptionId,\n generation: subscription.generation,\n fromVersion,\n toVersion,\n messageId: crypto.randomUUID(),\n patches,\n });\n }\n },\n };\n}\n\nexport type ReactiveHub = ReturnType<typeof createReactiveHub>;\n","import { createSignedClusterTicket, verifySignedClusterTicket } from './cluster.js';\nimport {\n bearerToken,\n createHubSocketState,\n createReactiveHub,\n type HubPaths,\n type HubSocket,\n type HubSocketState,\n isHubRequest,\n ReactiveHubError,\n} from './hub.js';\nimport type { JsonPatch, SubscribeRequest, UnsubscribeRequest } from './protocol.js';\n\n/**\n * Cloudflare Durable Object shell for the reactive hub. It is structural: it\n * takes the Durable Object's `ctx` instead of extending `DurableObject`, so this\n * entry imports nothing from `cloudflare:workers` and builds like every other\n * entry. Your Durable Object class owns its name and migrations and delegates\n * `fetch`, `webSocketMessage`, `webSocketClose` and `webSocketError` to it, plus\n * `alarm` when it tracks presence.\n */\n\n/** Set by the route handler on requests it forwards to the hub; never trusted from clients. */\nexport const HUB_PRINCIPAL_HEADER = 'x-reactive-principal';\nconst HUB_ACTION_URL = 'https://reactive-hub/';\nconst PRESENCE_TAG = 'presence';\nconst PRESENCE_TAG_PREFIX = 'presence:';\nconst PRESENCE_KEY_PREFIX = 'presence:';\nconst DEFAULT_PRESENCE_TIMEOUT_MS = 90_000;\n/** Close code for a socket the presence sweep found silent. */\nexport const HEARTBEAT_TIMEOUT_CLOSE_CODE = 4001;\n\n/** A hibernatable Workers WebSocket, as far as the hub needs it. */\nexport interface DurableObjectWebSocket extends HubSocket {\n close(code?: number, reason?: string): void;\n}\n\n/** The parts of `DurableObjectState` the hub uses. */\nexport interface DurableObjectStateLike {\n acceptWebSocket(socket: DurableObjectWebSocket, tags?: string[]): void;\n getWebSockets(tag?: string): DurableObjectWebSocket[];\n setWebSocketAutoResponse?(pair?: unknown): void;\n /** When the runtime last answered this socket's `ping` itself; needed for presence. */\n getWebSocketAutoResponseTimestamp?(socket: DurableObjectWebSocket): Date | null;\n storage: {\n get<T>(key: string): Promise<T | undefined>;\n put<T>(key: string, value: T): Promise<void>;\n /** Lets presence forget principals that left instead of storing `false`. */\n delete?(key: string): Promise<boolean>;\n /** Needed for presence. */\n getAlarm?(): Promise<number | null>;\n /** Needed for presence. */\n setAlarm?(scheduledTime: number): Promise<void>;\n };\n}\n\n/** Anything with a Durable Object stub's `fetch`. */\nexport interface HubStub {\n fetch(request: Request): Promise<Response>;\n}\n\ntype WebSocketPairConstructor = new () => {\n 0: DurableObjectWebSocket;\n 1: DurableObjectWebSocket;\n};\ntype RequestResponsePairConstructor = new (request: string, response: string) => unknown;\n\n/** A frame from the client that is not part of the protocol, such as `{ type: 'listening' }`. */\nexport type HubClientFrame = Record<string, unknown> & { type: string };\n\nexport interface DurableObjectHubOptions {\n paths: HubPaths;\n /** Storage key for a path's version. Defaults to `version:<path>`. */\n versionKey?: (path: string) => string;\n maxAttachmentBytes?: number;\n /**\n * Answers the client's `ping` with `pong` without waking the Durable Object.\n * Defaults to true.\n */\n autoPong?: boolean;\n /**\n * Tracks whether principals hold a live socket, so the hub can report a\n * worker or listener as online without it polling over HTTP. Requires `autoPong`.\n */\n presence?: HubPresenceOptions;\n /** Runs after a socket is accepted. */\n onOpen?: (socket: DurableObjectWebSocket, principal: string) => void | Promise<void>;\n /**\n * Runs when a socket closes or errors. The socket may still be listed by\n * `sockets()` during this call, so exclude it when counting.\n */\n onClose?: (socket: DurableObjectWebSocket, principal: string) => void | Promise<void>;\n /** Handles frames the protocol does not, such as application presence updates. */\n onMessage?: (\n socket: DurableObjectWebSocket,\n principal: string,\n frame: HubClientFrame,\n ) => void | Promise<void>;\n /** Workers' `WebSocketPair`; injectable for tests. */\n WebSocketPair?: WebSocketPairConstructor;\n /** Creates a connection id; injectable for tests. */\n randomId?: () => string;\n}\n\nexport interface HubPresenceOptions {\n /** The principals to track, or a predicate for open-ended ones such as anonymous listeners. */\n principals: readonly string[] | ((principal: string) => boolean);\n /**\n * A socket whose last `ping` is older than this is closed as dead, which is how\n * a peer that vanished without a close frame is noticed. Clients must ping more\n * often than this. @default 90000\n */\n timeoutMs?: number;\n /**\n * Called when a principal gains its first live socket or loses its last one,\n * for example to `notify` a status path. The last reported state is kept in\n * storage under `presence:<principal>`, so each change is reported once.\n */\n onChange?: (principal: string, present: boolean) => void | Promise<void>;\n}\n\ntype HubAction =\n | { action: 'subscribe'; principal: string; body: SubscribeRequest }\n | { action: 'unsubscribe'; principal: string; body: UnsubscribeRequest }\n | { action: 'notify'; path: string }\n | { action: 'publish'; path: string; patches: JsonPatch[] };\n\nfunction json(value: unknown, status = 200): Response {\n return Response.json(value, { status, headers: { 'cache-control': 'no-store' } });\n}\n\nfunction error(message: string, status: number): Response {\n return json({ type: 'error', message }, status);\n}\n\nfunction workersGlobal<T>(name: string): T | undefined {\n return (globalThis as Record<string, unknown>)[name] as T | undefined;\n}\n\n/** Wires the hub core to a Durable Object's hibernatable WebSockets and storage. */\nexport function createDurableObjectHub(\n ctx: DurableObjectStateLike,\n options: DurableObjectHubOptions,\n) {\n const versionKey = options.versionKey ?? ((path: string) => `version:${path}`);\n const randomId = options.randomId ?? (() => crypto.randomUUID());\n const hub = createReactiveHub({\n sockets: {\n all: () => ctx.getWebSockets(),\n // Tagging by connection id lets HTTP subscribes find the socket after hibernation.\n byConnectionId: (connectionId) => ctx.getWebSockets(connectionId)[0],\n },\n versions: {\n get: async (path) => (await ctx.storage.get<number>(versionKey(path))) ?? 0,\n set: (path, version) => ctx.storage.put(versionKey(path), version),\n },\n paths: options.paths,\n ...(options.maxAttachmentBytes === undefined\n ? {}\n : { maxAttachmentBytes: options.maxAttachmentBytes }),\n });\n\n if (options.autoPong !== false) {\n const Pair = workersGlobal<RequestResponsePairConstructor>('WebSocketRequestResponsePair');\n if (Pair && ctx.setWebSocketAutoResponse) {\n ctx.setWebSocketAutoResponse(\n new Pair(JSON.stringify({ type: 'ping' }), JSON.stringify({ type: 'pong' })),\n );\n }\n }\n\n const presence = options.presence;\n const timeoutMs = presence?.timeoutMs ?? DEFAULT_PRESENCE_TIMEOUT_MS;\n const isTracked = (principal: string): boolean => {\n if (!presence) return false;\n const { principals } = presence;\n return typeof principals === 'function'\n ? principals(principal)\n : principals.includes(principal);\n };\n const principalOf = (socket: DurableObjectWebSocket): string =>\n (socket.deserializeAttachment() as HubSocketState).principal;\n\n // The runtime records each auto-answered ping without waking the object;\n // before the first ping the accept time stands in.\n const lastSeen = (socket: DurableObjectWebSocket): number =>\n Math.max(\n ctx.getWebSocketAutoResponseTimestamp?.(socket)?.getTime() ?? 0,\n (socket.deserializeAttachment() as HubSocketState).connectedAt ?? 0,\n );\n\n const liveSockets = (principal: string, closing?: DurableObjectWebSocket) => {\n const now = Date.now();\n return ctx\n .getWebSockets(PRESENCE_TAG_PREFIX + principal)\n .filter((socket) => socket !== closing && now - lastSeen(socket) <= timeoutMs);\n };\n\n const reconcile = async (principal: string, closing?: DurableObjectWebSocket) => {\n const present = liveSockets(principal, closing).length > 0;\n const key = PRESENCE_KEY_PREFIX + principal;\n if (((await ctx.storage.get<boolean>(key)) ?? false) === present) return;\n if (present || !ctx.storage.delete) await ctx.storage.put(key, present);\n else await ctx.storage.delete(key);\n await presence?.onChange?.(principal, present);\n };\n\n // Wakes when the stalest tracked socket would time out; no tracked sockets, no\n // alarm. An earlier alarm is kept, so applications sharing the alarm should\n // schedule the same way and call `alarm()` from theirs.\n const scheduleSweep = async () => {\n const { storage } = ctx;\n if (!storage.getAlarm || !storage.setAlarm) {\n throw new Error('Hub presence needs Durable Object alarms');\n }\n const seen = ctx.getWebSockets(PRESENCE_TAG).map((socket) => lastSeen(socket));\n if (seen.length === 0) return;\n const due = Math.min(...seen) + timeoutMs + 1;\n const current = await storage.getAlarm();\n if (current === null || current > due) await storage.setAlarm(due);\n };\n\n const closed = async (socket: DurableObjectWebSocket) => {\n const principal = principalOf(socket);\n if (isTracked(principal)) await reconcile(principal, socket);\n await options.onClose?.(socket, principal);\n };\n\n const accept = async (request: Request): Promise<Response> => {\n const principal = request.headers.get(HUB_PRINCIPAL_HEADER);\n if (!principal) return error('Unauthorized', 401);\n const Pair = options.WebSocketPair ?? workersGlobal<WebSocketPairConstructor>('WebSocketPair');\n if (!Pair) throw new Error('WebSocketPair is not available in this runtime');\n const pair = new Pair();\n const connectionId = randomId();\n const tracked = isTracked(principal);\n ctx.acceptWebSocket(\n pair[1],\n tracked ? [connectionId, PRESENCE_TAG, PRESENCE_TAG_PREFIX + principal] : [connectionId],\n );\n pair[1].serializeAttachment(\n createHubSocketState(connectionId, principal, tracked ? Date.now() : undefined),\n );\n if (tracked) {\n await reconcile(principal);\n await scheduleSweep();\n }\n await options.onOpen?.(pair[1], principal);\n return new Response(null, { status: 101, webSocket: pair[0] } as ResponseInit);\n };\n\n const act = async (action: HubAction): Promise<Response> => {\n try {\n switch (action.action) {\n case 'subscribe':\n return json(await hub.subscribe(action.body, action.principal));\n case 'unsubscribe':\n hub.unsubscribe(action.body, action.principal);\n return json({ type: 'ok' });\n case 'notify':\n await hub.notify(action.path);\n return json({ type: 'ok' });\n case 'publish':\n await hub.publish(action.path, action.patches);\n return json({ type: 'ok' });\n default:\n return error('Unknown hub action', 400);\n }\n } catch (cause) {\n if (cause instanceof ReactiveHubError) return error(cause.message, 400);\n throw cause;\n }\n };\n\n return {\n hub,\n\n /** The Durable Object's `fetch`: WebSocket upgrades and actions from `hubClient`. */\n async fetch(request: Request): Promise<Response> {\n if (request.headers.get('upgrade') === 'websocket') return accept(request);\n return act((await request.json()) as HubAction);\n },\n\n async webSocketMessage(\n socket: DurableObjectWebSocket,\n message: string | ArrayBuffer,\n ): Promise<void> {\n if (typeof message !== 'string') return;\n const frame = hub.message(socket, message);\n if (frame && options.onMessage) await options.onMessage(socket, principalOf(socket), frame);\n },\n\n async webSocketClose(socket: DurableObjectWebSocket, code: number): Promise<void> {\n try {\n socket.close(code === 1005 ? 1000 : code, 'closed');\n } catch {\n // Already closed, or the runtime replied to the close frame itself.\n }\n await closed(socket);\n },\n\n async webSocketError(socket: DurableObjectWebSocket): Promise<void> {\n try {\n socket.close(1011, 'error');\n } catch {\n // Already closed.\n }\n await closed(socket);\n },\n\n /** Every accepted socket, including one whose close is being handled. */\n sockets(): DurableObjectWebSocket[] {\n return ctx.getWebSockets();\n },\n\n /** The principal a socket authenticated as. */\n principalOf,\n\n /** Application data stored with a socket by `setSocketData`. */\n socketData: <T = unknown>(socket: DurableObjectWebSocket): T | undefined =>\n hub.socketData<T>(socket),\n\n /** Stores application data with a socket; it survives hibernation and shares the 2 KB limit. */\n setSocketData: (socket: DurableObjectWebSocket, data: unknown): void =>\n hub.setSocketData(socket, data),\n\n /** Whether `principal` holds a socket that has pinged within the presence timeout. */\n present(principal: string): boolean {\n return liveSockets(principal).length > 0;\n },\n\n /**\n * The Durable Object's `alarm` when presence is tracked: closes silent\n * sockets, reports principals that lost their last one, and reschedules.\n */\n async alarm(): Promise<void> {\n const now = Date.now();\n const principals = new Set<string>();\n for (const socket of ctx.getWebSockets(PRESENCE_TAG)) {\n const principal = principalOf(socket);\n principals.add(principal);\n if (now - lastSeen(socket) <= timeoutMs) continue;\n try {\n socket.close(HEARTBEAT_TIMEOUT_CLOSE_CODE, 'Heartbeat timeout');\n } catch {\n // Already closed.\n }\n // oxlint-disable-next-line no-await-in-loop -- Close hooks run in socket order.\n await options.onClose?.(socket, principal);\n }\n for (const principal of principals) {\n // oxlint-disable-next-line no-await-in-loop -- Storage writes stay ordered.\n await reconcile(principal);\n }\n await scheduleSweep();\n },\n };\n}\n\nexport type DurableObjectHub = ReturnType<typeof createDurableObjectHub>;\n\n/** Calls a hub Durable Object from the Worker. */\nexport function hubClient(stub: HubStub) {\n const post = (action: HubAction) =>\n stub.fetch(new Request(HUB_ACTION_URL, { method: 'POST', body: JSON.stringify(action) }));\n const ok = async (response: Response) => {\n if (!response.ok) throw new Error(`Hub request failed: HTTP ${response.status}`);\n };\n return {\n /** Forwards a reactive HTTP request and returns the hub's response unchanged. */\n request: (body: SubscribeRequest | UnsubscribeRequest, principal: string) =>\n post(\n body.type === 'subscribe'\n ? { action: 'subscribe', principal, body }\n : { action: 'unsubscribe', principal, body },\n ),\n notify: async (path: string) => ok(await post({ action: 'notify', path })),\n publish: async (path: string, patches: JsonPatch[]) =>\n ok(await post({ action: 'publish', path, patches })),\n };\n}\n\nexport interface RemoteHubOptions {\n /** Absolute URL of the hub Worker's `routes.notify`. */\n url: string;\n /** The Worker's `notifySecret`. Keep it server-side. */\n secret: string;\n /** Injectable for tests. */\n fetch?: typeof fetch;\n}\n\n/**\n * Notifies a hub from another backend, such as an AWS Lambda or a Vercel route,\n * after the data behind a path changes.\n */\nexport function remoteHub(options: RemoteHubOptions) {\n const send = async (body: { path: string; patches?: JsonPatch[] }) => {\n const response = await (options.fetch ?? fetch)(options.url, {\n method: 'POST',\n headers: { authorization: `Bearer ${options.secret}`, 'content-type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!response.ok) throw new Error(`Hub notify failed: HTTP ${response.status}`);\n };\n return {\n notify: (path: string) => send({ path }),\n publish: (path: string, patches: JsonPatch[]) => send({ path, patches }),\n };\n}\n\nexport interface ReactiveRoutes {\n /** `POST` exchanges a credential for a signed ticket. */\n ticket: string;\n /** WebSocket upgrade, authenticated by the ticket in the `token` query parameter. */\n socket: string;\n /** Reactive HTTP subscribe and unsubscribe, authenticated by the ticket as a bearer token. */\n reactive: string;\n /** `POST { path, patches? }` from other backends, authenticated by `notifySecret`. */\n notify?: string;\n}\n\nexport interface ReactiveRoutesOptions {\n routes: ReactiveRoutes;\n /** Ticket signing secret. Requests get 503 when it is not configured. */\n secret: string | undefined;\n /** Bearer secret for `routes.notify`; the route answers 503 while it is unset. */\n notifySecret?: string | undefined;\n /**\n * Browser origins allowed to call the ticket and reactive routes cross-origin,\n * for a hub on a different host from the app.\n */\n allowedOrigins?: readonly string[];\n /** The hub Durable Object stub that serves these routes. */\n hub: () => HubStub;\n /** Returns the principal for a ticket request, or undefined to answer 401. */\n authenticate(request: Request): string | undefined | Promise<string | undefined>;\n ticketLifetimeSeconds?: number;\n}\n\nfunction sameSecret(given: string, expected: string): boolean {\n const left = new TextEncoder().encode(given);\n const right = new TextEncoder().encode(expected);\n let difference = left.length ^ right.length;\n for (let index = 0; index < right.length; index++) {\n difference |= (left[index] ?? 0) ^ right[index]!;\n }\n return difference === 0;\n}\n\nfunction withCors(response: Response, origin: string | null): Response {\n if (!origin) return response;\n const headers = new Headers(response.headers);\n headers.set('access-control-allow-origin', origin);\n headers.append('vary', 'origin');\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\n/** Serves the ticket, socket, reactive and notify routes; `undefined` for any other path. */\nexport async function handleReactiveRoutes(\n request: Request,\n options: ReactiveRoutesOptions,\n): Promise<Response | undefined> {\n const url = new URL(request.url);\n const { routes, secret } = options;\n const known = [routes.ticket, routes.socket, routes.reactive, routes.notify];\n if (!known.includes(url.pathname)) return undefined;\n\n const requestOrigin = request.headers.get('origin');\n const corsOrigin =\n requestOrigin && options.allowedOrigins?.includes(requestOrigin) ? requestOrigin : null;\n if (request.method === 'OPTIONS' && url.pathname !== routes.socket) {\n if (!corsOrigin) return new Response(null, { status: 403 });\n return new Response(null, {\n status: 204,\n headers: {\n 'access-control-allow-origin': corsOrigin,\n 'access-control-allow-methods': 'POST, OPTIONS',\n 'access-control-allow-headers': 'authorization, content-type',\n 'access-control-max-age': '86400',\n vary: 'origin',\n },\n });\n }\n\n if (url.pathname === routes.notify) {\n if (request.method !== 'POST') return error('Method not allowed', 405);\n if (!options.notifySecret) return error('Hub notify is not configured', 503);\n if (!sameSecret(bearerToken(request), options.notifySecret)) {\n return error('Unauthorized', 401);\n }\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return error('Invalid JSON', 400);\n }\n const { path, patches } = (body ?? {}) as { path?: unknown; patches?: unknown };\n if (typeof path !== 'string' || (patches !== undefined && !Array.isArray(patches))) {\n return error('Expected { path, patches? }', 400);\n }\n const client = hubClient(options.hub());\n if (patches) await client.publish(path, patches as JsonPatch[]);\n else await client.notify(path);\n return json({ type: 'ok' });\n }\n\n if (!secret) return withCors(error('Reactive hub is not configured', 503), corsOrigin);\n\n if (url.pathname === routes.ticket) {\n if (request.method !== 'POST') return withCors(error('Method not allowed', 405), corsOrigin);\n const principal = await options.authenticate(request);\n if (!principal) return withCors(error('Unauthorized', 401), corsOrigin);\n const socketUrl = new URL(routes.socket, url);\n socketUrl.protocol = 'wss:';\n return withCors(\n json(\n await createSignedClusterTicket({\n secret,\n principal,\n url: socketUrl.toString(),\n ...(options.ticketLifetimeSeconds === undefined\n ? {}\n : { lifetimeSeconds: options.ticketLifetimeSeconds }),\n }),\n ),\n corsOrigin,\n );\n }\n\n if (url.pathname === routes.socket) {\n if (request.headers.get('upgrade') !== 'websocket') {\n return error('Expected a WebSocket upgrade', 426);\n }\n const principal = await verifySignedClusterTicket(url.searchParams.get('token') ?? '', secret);\n if (!principal) return new Response('Unauthorized', { status: 401 });\n const forwarded = new Request(request);\n forwarded.headers.set(HUB_PRINCIPAL_HEADER, principal);\n return options.hub().fetch(forwarded);\n }\n\n if (request.method !== 'POST') return withCors(error('Method not allowed', 405), corsOrigin);\n const principal = await verifySignedClusterTicket(bearerToken(request), secret);\n if (!principal) return withCors(error('Unauthorized', 401), corsOrigin);\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return withCors(error('Invalid JSON', 400), corsOrigin);\n }\n if (!isHubRequest(body)) return withCors(error('Unsupported reactive request', 400), corsOrigin);\n return withCors(await hubClient(options.hub()).request(body, principal), corsOrigin);\n}\n"]}
1
+ {"version":3,"sources":["../../client/src/log.ts","../../client/src/cluster.ts","../../client/src/hub.ts","../../client/src/cloudflare.ts","../../client/src/cloudflare-hub.ts"],"names":["isRecord","error","send","body","principal","sameSecret","encoder"],"mappings":"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","file":"cloudflare.cjs","sourcesContent":["/* eslint-disable no-console */\ntype DebugGlobal = typeof globalThis & {\n __DYNAMODB_REACTIVE_DEBUG__?: boolean;\n};\n\nexport function debug(...values: unknown[]): void {\n if ((globalThis as DebugGlobal).__DYNAMODB_REACTIVE_DEBUG__) {\n console.debug(...values);\n }\n}\n\nexport function warn(...values: unknown[]): void {\n console.warn(...values);\n}\n\nexport function error(...values: unknown[]): void {\n console.error(...values);\n}\n","import { ReactiveClient } from './client.js';\nimport type { ReactiveClientConfig } from './types.js';\n\n/**\n * Cluster workers run on machines with no inbound access. They authenticate\n * outbound with a long-lived runner token, exchange it for a short-lived ticket,\n * and hold a reactive WebSocket that pushes work to them. These helpers use only\n * Web standards (fetch, WebSocket, Web Crypto), so the same code runs in Node and\n * in Workers runtimes, whichever backend serves the reactive protocol.\n */\n\nexport const DEFAULT_CLUSTER_TICKET_PATH = '/api/cluster/token';\nexport const DEFAULT_CLUSTER_HTTP_PATH = '/api/reactive';\nconst DEFAULT_RETRY_DELAY_MS = 10_000;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nconst DEFAULT_TICKET_LIFETIME_SECONDS = 60;\n// Workers are few and long-lived, and the server needs their pings to know they are alive.\nconst DEFAULT_CLUSTER_HEARTBEAT_MS = 30_000;\n\n/** Short-lived credential returned by the ticket endpoint. */\nexport interface ClusterTicket {\n /** Sent as the WebSocket `token` query parameter and the HTTP bearer token. */\n token: string;\n /** Expiry in epoch seconds. */\n expiresAt: number;\n /** `wss:` URL of the reactive WebSocket. */\n url: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Validates a ticket endpoint response; the socket URL must use `wss:`. */\nexport function parseClusterTicket(value: unknown): ClusterTicket {\n if (\n !isRecord(value) ||\n typeof value.token !== 'string' ||\n value.token.length === 0 ||\n typeof value.expiresAt !== 'number' ||\n !Number.isFinite(value.expiresAt) ||\n typeof value.url !== 'string'\n ) {\n throw new Error('Cluster ticket response is invalid');\n }\n const url = new URL(value.url);\n if (url.protocol !== 'wss:') throw new Error('Cluster ticket URL must use WSS');\n return { token: value.token, expiresAt: value.expiresAt, url: url.toString() };\n}\n\nexport interface ClusterWorkerConfig {\n /** Origin of the application that serves the ticket and reactive HTTP endpoints. */\n baseUrl: string;\n /** Long-lived runner credential exchanged for tickets. */\n runnerToken: string | (() => string | Promise<string>);\n /** Ticket endpoint path. Defaults to `/api/cluster/token`. */\n ticketPath?: string;\n /** Reactive HTTP endpoint path. Defaults to `/api/reactive`. */\n httpPath?: string;\n /** Delay between ticket, connect, and reconnect attempts. Defaults to 10 seconds. */\n retryDelay?: number;\n /** Ticket request timeout. Defaults to 10 seconds. */\n requestTimeout?: number;\n /** Stops retrying when aborted. */\n signal?: AbortSignal;\n /** Called with each failed ticket or connect attempt before the next retry. */\n onRetry?: (error: Error) => void;\n /** Extra client options; the URL, HTTP URL, and auth are managed here. */\n client?: Omit<ReactiveClientConfig, 'url' | 'httpUrl' | 'auth'>;\n /** Fetch implementation for ticket requests. Defaults to the global fetch. */\n fetch?: typeof fetch;\n}\n\nfunction toError(cause: unknown): Error {\n return cause instanceof Error ? cause : new Error(String(cause));\n}\n\nfunction abortError(signal: AbortSignal): Error {\n return toError(signal.reason ?? new Error('Cluster worker connection aborted'));\n}\n\nfunction wait(milliseconds: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortError(signal));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortError(signal!));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, milliseconds);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Requests a ticket from the application using the runner token. */\nexport async function fetchClusterTicket(\n config: Pick<\n ClusterWorkerConfig,\n 'baseUrl' | 'runnerToken' | 'ticketPath' | 'requestTimeout' | 'fetch'\n >,\n): Promise<ClusterTicket> {\n const runnerToken =\n typeof config.runnerToken === 'function' ? await config.runnerToken() : config.runnerToken;\n const endpoint = new URL(config.ticketPath ?? DEFAULT_CLUSTER_TICKET_PATH, config.baseUrl);\n const response = await (config.fetch ?? fetch)(endpoint, {\n method: 'POST',\n headers: { authorization: `Bearer ${runnerToken}` },\n signal: AbortSignal.timeout(config.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS),\n });\n if (!response.ok) {\n throw new Error(`Cluster ticket request failed: HTTP ${response.status}`);\n }\n return parseClusterTicket(await response.json());\n}\n\n/**\n * Connects a cluster worker, retrying ticket and connect failures until it\n * succeeds or `signal` aborts. Each (re)connect fetches a fresh ticket. The\n * returned client keeps reconnecting on its own; subscribe to work with\n * `client.subscribe(...)` and stop with `client.disconnect()`.\n */\nexport async function connectClusterWorker(config: ClusterWorkerConfig): Promise<ReactiveClient> {\n const retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;\n const httpUrl = new URL(config.httpPath ?? DEFAULT_CLUSTER_HTTP_PATH, config.baseUrl).toString();\n for (;;) {\n if (config.signal?.aborted) throw abortError(config.signal);\n let client: ReactiveClient | undefined;\n try {\n // The first ticket only supplies the socket URL; the client fetches its own\n // ticket for every connection so reconnects never reuse an expired one.\n // oxlint-disable-next-line no-await-in-loop -- Attempts retry serially.\n const ticket = await fetchClusterTicket(config);\n client = new ReactiveClient({\n reconnectDelay: retryDelay,\n maxReconnectAttempts: Number.MAX_SAFE_INTEGER,\n heartbeatInterval: DEFAULT_CLUSTER_HEARTBEAT_MS,\n ...config.client,\n url: ticket.url,\n httpUrl,\n auth: async () => (await fetchClusterTicket(config)).token,\n });\n // oxlint-disable-next-line no-await-in-loop -- Attempts retry serially.\n await client.connect();\n return client;\n } catch (cause) {\n // connect() keeps reconnecting after it rejects; stop it before retrying.\n client?.disconnect();\n if (config.signal?.aborted) throw abortError(config.signal);\n config.onRetry?.(toError(cause));\n // oxlint-disable-next-line no-await-in-loop -- Backoff belongs to the retry loop.\n await wait(retryDelay, config.signal);\n }\n }\n}\n\nconst encoder = new TextEncoder();\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/u, '');\n}\n\nfunction base64UrlDecode(value: string): Uint8Array<ArrayBuffer> {\n const padded = value.replaceAll('-', '+').replaceAll('_', '/');\n const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4));\n const bytes = new Uint8Array(new ArrayBuffer(binary.length));\n for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);\n return bytes;\n}\n\nfunction hmacKey(secret: string, usage: 'sign' | 'verify'): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n [usage],\n );\n}\n\nexport interface SignedClusterTicketOptions {\n /** HMAC secret shared only by the servers that mint and verify tickets. */\n secret: string;\n /** Service principal the ticket authenticates as. */\n principal: string;\n /** `wss:` URL returned to the worker. */\n url: string;\n /** Ticket lifetime. Defaults to 60 seconds. */\n lifetimeSeconds?: number;\n /** Current time in milliseconds, for tests. */\n now?: number;\n}\n\n/**\n * Mints a stateless HMAC-signed ticket. Verifying it needs no storage read,\n * which keeps reconnect storms off the database.\n */\nexport async function createSignedClusterTicket(\n options: SignedClusterTicketOptions,\n): Promise<ClusterTicket> {\n const expiresAt =\n Math.floor((options.now ?? Date.now()) / 1000) +\n (options.lifetimeSeconds ?? DEFAULT_TICKET_LIFETIME_SECONDS);\n const payload = base64UrlEncode(\n encoder.encode(JSON.stringify({ p: options.principal, exp: expiresAt })),\n );\n const signature = new Uint8Array(\n await crypto.subtle.sign(\n 'HMAC',\n await hmacKey(options.secret, 'sign'),\n encoder.encode(payload),\n ),\n );\n return parseClusterTicket({\n token: `${payload}.${base64UrlEncode(signature)}`,\n expiresAt,\n url: options.url,\n });\n}\n\n/** Returns the ticket's principal, or `undefined` when it is forged, malformed, or expired. */\nexport async function verifySignedClusterTicket(\n token: string,\n secret: string,\n now = Date.now(),\n): Promise<string | undefined> {\n const parts = token.split('.');\n if (parts.length !== 2) return undefined;\n const [payload, signature] = parts as [string, string];\n if (!payload || !signature) return undefined;\n try {\n // crypto.subtle.verify compares in constant time.\n const valid = await crypto.subtle.verify(\n 'HMAC',\n await hmacKey(secret, 'verify'),\n base64UrlDecode(signature),\n encoder.encode(payload),\n );\n if (!valid) return undefined;\n const claims: unknown = JSON.parse(new TextDecoder().decode(base64UrlDecode(payload)));\n if (\n !isRecord(claims) ||\n typeof claims.p !== 'string' ||\n typeof claims.exp !== 'number' ||\n claims.exp <= Math.floor(now / 1000)\n ) {\n return undefined;\n }\n return claims.p;\n } catch {\n return undefined;\n }\n}\n","import type {\n JsonPatch,\n SnapshotResponse,\n SubscribeRequest,\n UnsubscribeRequest,\n WebSocketEvent,\n} from './protocol.js';\n\n/**\n * Protocol core for a reactive hub: the server half of the wire protocol for\n * backends that hold WebSockets themselves (for example a Cloudflare Durable\n * Object). It keeps every piece of per-socket state on the socket's attachment so\n * the state survives hibernation, and it has no runtime imports, so it runs and\n * tests in Node.\n */\n\n/** Hibernation limits a socket attachment to 2,048 bytes of serialized JSON. */\nexport const DEFAULT_MAX_ATTACHMENT_BYTES = 2_048;\n\n/** One subscription held by a socket. */\nexport interface HubSubscription {\n subscriptionId: string;\n generation: number;\n path: string;\n}\n\n/** State kept on each socket's attachment. */\nexport interface HubSocketState {\n connectionId: string;\n principal: string;\n subscriptions: HubSubscription[];\n /** Epoch ms the socket was accepted; the liveness baseline before its first ping. */\n connectedAt?: number;\n /** Application data kept with the socket, such as what a listener is playing. */\n data?: unknown;\n}\n\n/** The parts of a hibernatable WebSocket the hub uses; faked in tests. */\nexport interface HubSocket {\n send(message: string): void;\n deserializeAttachment(): unknown;\n serializeAttachment(value: unknown): void;\n}\n\nexport interface HubSockets {\n all(): HubSocket[];\n byConnectionId(connectionId: string): HubSocket | undefined;\n}\n\n/** Monotonic per-path versions. Versions only need to grow while sockets stay open. */\nexport interface HubVersions {\n get(path: string): Promise<number>;\n set(path: string, version: number): Promise<void>;\n}\n\nexport interface HubPathContext {\n path: string;\n principal: string;\n input: unknown;\n}\n\nexport interface HubPath {\n /**\n * Runs before the subscription is stored. Return false to reject it. Every\n * subscriber of a path receives its pushes, so authorise here, not in `snapshot`.\n */\n authorize?(context: HubPathContext): boolean | Promise<boolean>;\n /** Current data for the path, sent as the subscription snapshot. */\n snapshot(context: HubPathContext): unknown;\n}\n\n/** Fixed paths by name, or a resolver for parameterised paths such as `drafts.<id>`. */\nexport type HubPaths = Record<string, HubPath> | ((path: string) => HubPath | undefined);\n\nexport interface ReactiveHubOptions {\n sockets: HubSockets;\n versions: HubVersions;\n paths: HubPaths;\n /** Rejects subscribes that would grow an attachment past this size. */\n maxAttachmentBytes?: number;\n}\n\n/** Thrown for requests the caller is not allowed to make. */\nexport class ReactiveHubError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReactiveHubError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Validates a reactive HTTP body for the subscribe and unsubscribe requests a hub serves. */\nexport function isHubRequest(value: unknown): value is SubscribeRequest | UnsubscribeRequest {\n if (!isRecord(value)) return false;\n return (\n (value.type === 'subscribe' || value.type === 'unsubscribe') &&\n typeof value.connectionId === 'string' &&\n typeof value.subscriptionId === 'string' &&\n typeof value.generation === 'number' &&\n (value.type === 'unsubscribe' || typeof value.path === 'string')\n );\n}\n\n/** Returns the bearer token from an `Authorization` header, or an empty string. */\nexport function bearerToken(request: Request): string {\n const match = /^Bearer ([^\\s]+)$/u.exec(request.headers.get('authorization') ?? '');\n return match?.[1] ?? '';\n}\n\n/** Initial attachment for a newly accepted socket. */\nexport function createHubSocketState(\n connectionId: string,\n principal: string,\n connectedAt?: number,\n): HubSocketState {\n return {\n connectionId,\n principal,\n subscriptions: [],\n ...(connectedAt === undefined ? {} : { connectedAt }),\n };\n}\n\nfunction socketState(socket: HubSocket): HubSocketState {\n const state = socket.deserializeAttachment() as HubSocketState;\n // Attachments written before subscriptions carried a path have no path; they\n // are dropped rather than fanned out to every notification.\n return { ...state, subscriptions: state.subscriptions.filter((entry) => entry.path) };\n}\n\nfunction send(socket: HubSocket, event: WebSocketEvent): void {\n try {\n socket.send(JSON.stringify(event));\n } catch {\n // A socket closing mid-broadcast is cleaned up by its close handler.\n }\n}\n\n/** Creates the protocol core for one hub instance. */\nexport function createReactiveHub(options: ReactiveHubOptions) {\n const { sockets, versions } = options;\n const maxAttachmentBytes = options.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES;\n const resolve = (path: string): HubPath | undefined =>\n typeof options.paths === 'function'\n ? options.paths(path)\n : Object.hasOwn(options.paths, path)\n ? options.paths[path]\n : undefined;\n\n const ownedSocket = (connectionId: string, principal: string): HubSocket | undefined => {\n const socket = sockets.byConnectionId(connectionId);\n // The HTTP caller must own the WebSocket it registers against.\n return socket && socketState(socket).principal === principal ? socket : undefined;\n };\n\n const subscribersOf = (path: string) =>\n sockets.all().flatMap((socket) =>\n socketState(socket)\n .subscriptions.filter((subscription) => subscription.path === path)\n .map((subscription) => ({ socket, subscription })),\n );\n\n const bump = async (path: string) => {\n const fromVersion = await versions.get(path);\n const toVersion = fromVersion + 1;\n await versions.set(path, toVersion);\n return { fromVersion, toVersion };\n };\n\n return {\n /**\n * Answers the client's `init` and `ping` frames. Any other frame with a\n * string `type` is returned for the application to handle.\n */\n message(\n socket: HubSocket,\n raw: string,\n ): (Record<string, unknown> & { type: string }) | undefined {\n let frame: unknown;\n try {\n frame = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isRecord(frame) || typeof frame.type !== 'string') return undefined;\n if (frame.type === 'init') {\n send(socket, { type: 'connected', connectionId: socketState(socket).connectionId });\n } else if (frame.type === 'ping') {\n send(socket, { type: 'pong' });\n } else {\n return frame as Record<string, unknown> & { type: string };\n }\n return undefined;\n },\n\n /** The application data stored with a socket. */\n socketData<T = unknown>(socket: HubSocket): T | undefined {\n return socketState(socket).data as T | undefined;\n },\n\n /** Stores application data with a socket; it shares the attachment size limit. */\n setSocketData(socket: HubSocket, data: unknown): void {\n const next: HubSocketState = { ...socketState(socket), data };\n if (new TextEncoder().encode(JSON.stringify(next)).length > maxAttachmentBytes) {\n throw new ReactiveHubError('Socket data is too large');\n }\n socket.serializeAttachment(next);\n },\n\n /** The principal a socket authenticated as. */\n principalOf(socket: HubSocket): string {\n return socketState(socket).principal;\n },\n\n async subscribe(request: SubscribeRequest, principal: string): Promise<SnapshotResponse> {\n const handler = resolve(request.path);\n if (!handler) throw new ReactiveHubError('Unknown subscription path');\n if (!ownedSocket(request.connectionId, principal)) {\n throw new ReactiveHubError('Unknown connection');\n }\n const context: HubPathContext = { path: request.path, principal, input: request.input };\n if (handler.authorize && !(await handler.authorize(context))) {\n throw new ReactiveHubError('Subscription not allowed');\n }\n // Re-read after the awaits so a concurrent subscribe on the same socket is kept.\n const socket = ownedSocket(request.connectionId, principal);\n if (!socket) throw new ReactiveHubError('Unknown connection');\n const state = socketState(socket);\n const next: HubSocketState = {\n ...state,\n subscriptions: [\n ...state.subscriptions.filter((entry) => entry.subscriptionId !== request.subscriptionId),\n {\n subscriptionId: request.subscriptionId,\n generation: request.generation,\n path: request.path,\n },\n ],\n };\n if (new TextEncoder().encode(JSON.stringify(next)).length > maxAttachmentBytes) {\n throw new ReactiveHubError('Too many subscriptions on this connection');\n }\n socket.serializeAttachment(next);\n // Read the version before the data so a concurrent notify can only make the\n // client re-fetch, never miss a change.\n const version = await versions.get(request.path);\n const data = await handler.snapshot(context);\n return {\n type: 'snapshot',\n subscriptionId: request.subscriptionId,\n generation: request.generation,\n version,\n data,\n };\n },\n\n unsubscribe(request: UnsubscribeRequest, principal: string): void {\n const socket = ownedSocket(request.connectionId, principal);\n if (!socket) return;\n const state = socketState(socket);\n socket.serializeAttachment({\n ...state,\n subscriptions: state.subscriptions.filter(\n (entry) => entry.subscriptionId !== request.subscriptionId,\n ),\n } satisfies HubSocketState);\n },\n\n /** Bumps the path's version and asks each of its subscribers to re-fetch. */\n async notify(path: string): Promise<void> {\n const { fromVersion, toVersion } = await bump(path);\n for (const { socket, subscription } of subscribersOf(path)) {\n send(socket, {\n type: 'resync',\n subscriptionId: subscription.subscriptionId,\n generation: subscription.generation,\n fromVersion,\n toVersion,\n messageId: crypto.randomUUID(),\n });\n }\n },\n\n /**\n * Bumps the path's version and sends the same JSON patches to each subscriber.\n * Use it only when every subscriber of the path sees the same data.\n */\n async publish(path: string, patches: JsonPatch[]): Promise<void> {\n const { fromVersion, toVersion } = await bump(path);\n for (const { socket, subscription } of subscribersOf(path)) {\n send(socket, {\n type: 'patch',\n subscriptionId: subscription.subscriptionId,\n generation: subscription.generation,\n fromVersion,\n toVersion,\n messageId: crypto.randomUUID(),\n patches,\n });\n }\n },\n };\n}\n\nexport type ReactiveHub = ReturnType<typeof createReactiveHub>;\n","import { createSignedClusterTicket, verifySignedClusterTicket } from './cluster.js';\nimport {\n bearerToken,\n createHubSocketState,\n createReactiveHub,\n type HubPaths,\n type HubSocket,\n type HubSocketState,\n isHubRequest,\n ReactiveHubError,\n} from './hub.js';\nimport type { JsonPatch, SubscribeRequest, UnsubscribeRequest } from './protocol.js';\n\n/**\n * Cloudflare Durable Object shell for the reactive hub. It is structural: it\n * takes the Durable Object's `ctx` instead of extending `DurableObject`, so this\n * entry imports nothing from `cloudflare:workers` and builds like every other\n * entry. Your Durable Object class owns its name and migrations and delegates\n * `fetch`, `webSocketMessage`, `webSocketClose` and `webSocketError` to it, plus\n * `alarm` when it tracks presence.\n */\n\n/** Set by the route handler on requests it forwards to the hub; never trusted from clients. */\nexport const HUB_PRINCIPAL_HEADER = 'x-reactive-principal';\nconst HUB_ACTION_URL = 'https://reactive-hub/';\nconst PRESENCE_TAG = 'presence';\nconst PRESENCE_TAG_PREFIX = 'presence:';\nconst PRESENCE_KEY_PREFIX = 'presence:';\nconst DEFAULT_PRESENCE_TIMEOUT_MS = 90_000;\n/** Close code for a socket the presence sweep found silent. */\nexport const HEARTBEAT_TIMEOUT_CLOSE_CODE = 4001;\n\n/** A hibernatable Workers WebSocket, as far as the hub needs it. */\nexport interface DurableObjectWebSocket extends HubSocket {\n close(code?: number, reason?: string): void;\n}\n\n/** The parts of `DurableObjectState` the hub uses. */\nexport interface DurableObjectStateLike {\n acceptWebSocket(socket: DurableObjectWebSocket, tags?: string[]): void;\n getWebSockets(tag?: string): DurableObjectWebSocket[];\n setWebSocketAutoResponse?(pair?: unknown): void;\n /** When the runtime last answered this socket's `ping` itself; needed for presence. */\n getWebSocketAutoResponseTimestamp?(socket: DurableObjectWebSocket): Date | null;\n storage: {\n get<T>(key: string): Promise<T | undefined>;\n put<T>(key: string, value: T): Promise<void>;\n /** Lets presence forget principals that left instead of storing `false`. */\n delete?(key: string): Promise<boolean>;\n /** Needed for presence. */\n getAlarm?(): Promise<number | null>;\n /** Needed for presence. */\n setAlarm?(scheduledTime: number): Promise<void>;\n };\n}\n\n/** Anything with a Durable Object stub's `fetch`. */\nexport interface HubStub {\n fetch(request: Request): Promise<Response>;\n}\n\ntype WebSocketPairConstructor = new () => {\n 0: DurableObjectWebSocket;\n 1: DurableObjectWebSocket;\n};\ntype RequestResponsePairConstructor = new (request: string, response: string) => unknown;\n\n/** A frame from the client that is not part of the protocol, such as `{ type: 'listening' }`. */\nexport type HubClientFrame = Record<string, unknown> & { type: string };\n\nexport interface DurableObjectHubOptions {\n paths: HubPaths;\n /** Storage key for a path's version. Defaults to `version:<path>`. */\n versionKey?: (path: string) => string;\n maxAttachmentBytes?: number;\n /**\n * Answers the client's `ping` with `pong` without waking the Durable Object.\n * Defaults to true.\n */\n autoPong?: boolean;\n /**\n * Tracks whether principals hold a live socket, so the hub can report a\n * worker or listener as online without it polling over HTTP. Requires `autoPong`.\n */\n presence?: HubPresenceOptions;\n /** Runs after a socket is accepted. */\n onOpen?: (socket: DurableObjectWebSocket, principal: string) => void | Promise<void>;\n /**\n * Runs when a socket closes or errors. The socket may still be listed by\n * `sockets()` during this call, so exclude it when counting.\n */\n onClose?: (socket: DurableObjectWebSocket, principal: string) => void | Promise<void>;\n /** Handles frames the protocol does not, such as application presence updates. */\n onMessage?: (\n socket: DurableObjectWebSocket,\n principal: string,\n frame: HubClientFrame,\n ) => void | Promise<void>;\n /** Workers' `WebSocketPair`; injectable for tests. */\n WebSocketPair?: WebSocketPairConstructor;\n /** Creates a connection id; injectable for tests. */\n randomId?: () => string;\n}\n\nexport interface HubPresenceOptions {\n /** The principals to track, or a predicate for open-ended ones such as anonymous listeners. */\n principals: readonly string[] | ((principal: string) => boolean);\n /**\n * A socket whose last `ping` is older than this is closed as dead, which is how\n * a peer that vanished without a close frame is noticed. Clients must ping more\n * often than this. @default 90000\n */\n timeoutMs?: number;\n /**\n * Called when a principal gains its first live socket or loses its last one,\n * for example to `notify` a status path. The last reported state is kept in\n * storage under `presence:<principal>`, so each change is reported once.\n */\n onChange?: (principal: string, present: boolean) => void | Promise<void>;\n}\n\ntype HubAction =\n | { action: 'subscribe'; principal: string; body: SubscribeRequest }\n | { action: 'unsubscribe'; principal: string; body: UnsubscribeRequest }\n | { action: 'notify'; path: string }\n | { action: 'publish'; path: string; patches: JsonPatch[] };\n\nfunction json(value: unknown, status = 200): Response {\n return Response.json(value, { status, headers: { 'cache-control': 'no-store' } });\n}\n\nfunction error(message: string, status: number): Response {\n return json({ type: 'error', message }, status);\n}\n\nfunction workersGlobal<T>(name: string): T | undefined {\n return (globalThis as Record<string, unknown>)[name] as T | undefined;\n}\n\n/** Wires the hub core to a Durable Object's hibernatable WebSockets and storage. */\nexport function createDurableObjectHub(\n ctx: DurableObjectStateLike,\n options: DurableObjectHubOptions,\n) {\n const versionKey = options.versionKey ?? ((path: string) => `version:${path}`);\n const randomId = options.randomId ?? (() => crypto.randomUUID());\n const hub = createReactiveHub({\n sockets: {\n all: () => ctx.getWebSockets(),\n // Tagging by connection id lets HTTP subscribes find the socket after hibernation.\n byConnectionId: (connectionId) => ctx.getWebSockets(connectionId)[0],\n },\n versions: {\n get: async (path) => (await ctx.storage.get<number>(versionKey(path))) ?? 0,\n set: (path, version) => ctx.storage.put(versionKey(path), version),\n },\n paths: options.paths,\n ...(options.maxAttachmentBytes === undefined\n ? {}\n : { maxAttachmentBytes: options.maxAttachmentBytes }),\n });\n\n if (options.autoPong !== false) {\n const Pair = workersGlobal<RequestResponsePairConstructor>('WebSocketRequestResponsePair');\n if (Pair && ctx.setWebSocketAutoResponse) {\n ctx.setWebSocketAutoResponse(\n new Pair(JSON.stringify({ type: 'ping' }), JSON.stringify({ type: 'pong' })),\n );\n }\n }\n\n const presence = options.presence;\n const timeoutMs = presence?.timeoutMs ?? DEFAULT_PRESENCE_TIMEOUT_MS;\n const isTracked = (principal: string): boolean => {\n if (!presence) return false;\n const { principals } = presence;\n return typeof principals === 'function'\n ? principals(principal)\n : principals.includes(principal);\n };\n const principalOf = (socket: DurableObjectWebSocket): string =>\n (socket.deserializeAttachment() as HubSocketState).principal;\n\n // The runtime records each auto-answered ping without waking the object;\n // before the first ping the accept time stands in.\n const lastSeen = (socket: DurableObjectWebSocket): number =>\n Math.max(\n ctx.getWebSocketAutoResponseTimestamp?.(socket)?.getTime() ?? 0,\n (socket.deserializeAttachment() as HubSocketState).connectedAt ?? 0,\n );\n\n const liveSockets = (principal: string, closing?: DurableObjectWebSocket) => {\n const now = Date.now();\n return ctx\n .getWebSockets(PRESENCE_TAG_PREFIX + principal)\n .filter((socket) => socket !== closing && now - lastSeen(socket) <= timeoutMs);\n };\n\n const reconcile = async (principal: string, closing?: DurableObjectWebSocket) => {\n const present = liveSockets(principal, closing).length > 0;\n const key = PRESENCE_KEY_PREFIX + principal;\n if (((await ctx.storage.get<boolean>(key)) ?? false) === present) return;\n if (present || !ctx.storage.delete) await ctx.storage.put(key, present);\n else await ctx.storage.delete(key);\n await presence?.onChange?.(principal, present);\n };\n\n // Wakes when the stalest tracked socket would time out; no tracked sockets, no\n // alarm. An earlier alarm is kept, so applications sharing the alarm should\n // schedule the same way and call `alarm()` from theirs.\n const scheduleSweep = async () => {\n const { storage } = ctx;\n if (!storage.getAlarm || !storage.setAlarm) {\n throw new Error('Hub presence needs Durable Object alarms');\n }\n const seen = ctx.getWebSockets(PRESENCE_TAG).map((socket) => lastSeen(socket));\n if (seen.length === 0) return;\n const due = Math.min(...seen) + timeoutMs + 1;\n const current = await storage.getAlarm();\n if (current === null || current > due) await storage.setAlarm(due);\n };\n\n const closed = async (socket: DurableObjectWebSocket) => {\n const principal = principalOf(socket);\n if (isTracked(principal)) await reconcile(principal, socket);\n await options.onClose?.(socket, principal);\n };\n\n const accept = async (request: Request): Promise<Response> => {\n const principal = request.headers.get(HUB_PRINCIPAL_HEADER);\n if (!principal) return error('Unauthorized', 401);\n const Pair = options.WebSocketPair ?? workersGlobal<WebSocketPairConstructor>('WebSocketPair');\n if (!Pair) throw new Error('WebSocketPair is not available in this runtime');\n const pair = new Pair();\n const connectionId = randomId();\n const tracked = isTracked(principal);\n ctx.acceptWebSocket(\n pair[1],\n tracked ? [connectionId, PRESENCE_TAG, PRESENCE_TAG_PREFIX + principal] : [connectionId],\n );\n pair[1].serializeAttachment(\n createHubSocketState(connectionId, principal, tracked ? Date.now() : undefined),\n );\n if (tracked) {\n await reconcile(principal);\n await scheduleSweep();\n }\n await options.onOpen?.(pair[1], principal);\n return new Response(null, { status: 101, webSocket: pair[0] } as ResponseInit);\n };\n\n const act = async (action: HubAction): Promise<Response> => {\n try {\n switch (action.action) {\n case 'subscribe':\n return json(await hub.subscribe(action.body, action.principal));\n case 'unsubscribe':\n hub.unsubscribe(action.body, action.principal);\n return json({ type: 'ok' });\n case 'notify':\n await hub.notify(action.path);\n return json({ type: 'ok' });\n case 'publish':\n await hub.publish(action.path, action.patches);\n return json({ type: 'ok' });\n default:\n return error('Unknown hub action', 400);\n }\n } catch (cause) {\n if (cause instanceof ReactiveHubError) return error(cause.message, 400);\n throw cause;\n }\n };\n\n return {\n hub,\n\n /** The Durable Object's `fetch`: WebSocket upgrades and actions from `hubClient`. */\n async fetch(request: Request): Promise<Response> {\n if (request.headers.get('upgrade') === 'websocket') return accept(request);\n return act((await request.json()) as HubAction);\n },\n\n async webSocketMessage(\n socket: DurableObjectWebSocket,\n message: string | ArrayBuffer,\n ): Promise<void> {\n if (typeof message !== 'string') return;\n const frame = hub.message(socket, message);\n if (frame && options.onMessage) await options.onMessage(socket, principalOf(socket), frame);\n },\n\n async webSocketClose(socket: DurableObjectWebSocket, code: number): Promise<void> {\n try {\n socket.close(code === 1005 ? 1000 : code, 'closed');\n } catch {\n // Already closed, or the runtime replied to the close frame itself.\n }\n await closed(socket);\n },\n\n async webSocketError(socket: DurableObjectWebSocket): Promise<void> {\n try {\n socket.close(1011, 'error');\n } catch {\n // Already closed.\n }\n await closed(socket);\n },\n\n /** Every accepted socket, including one whose close is being handled. */\n sockets(): DurableObjectWebSocket[] {\n return ctx.getWebSockets();\n },\n\n /** The principal a socket authenticated as. */\n principalOf,\n\n /** Application data stored with a socket by `setSocketData`. */\n socketData: <T = unknown>(socket: DurableObjectWebSocket): T | undefined =>\n hub.socketData<T>(socket),\n\n /** Stores application data with a socket; it survives hibernation and shares the 2 KB limit. */\n setSocketData: (socket: DurableObjectWebSocket, data: unknown): void =>\n hub.setSocketData(socket, data),\n\n /** Whether `principal` holds a socket that has pinged within the presence timeout. */\n present(principal: string): boolean {\n return liveSockets(principal).length > 0;\n },\n\n /**\n * The Durable Object's `alarm` when presence is tracked: closes silent\n * sockets, reports principals that lost their last one, and reschedules.\n */\n async alarm(): Promise<void> {\n const now = Date.now();\n const principals = new Set<string>();\n for (const socket of ctx.getWebSockets(PRESENCE_TAG)) {\n const principal = principalOf(socket);\n principals.add(principal);\n if (now - lastSeen(socket) <= timeoutMs) continue;\n try {\n socket.close(HEARTBEAT_TIMEOUT_CLOSE_CODE, 'Heartbeat timeout');\n } catch {\n // Already closed.\n }\n // oxlint-disable-next-line no-await-in-loop -- Close hooks run in socket order.\n await options.onClose?.(socket, principal);\n }\n for (const principal of principals) {\n // oxlint-disable-next-line no-await-in-loop -- Storage writes stay ordered.\n await reconcile(principal);\n }\n await scheduleSweep();\n },\n };\n}\n\nexport type DurableObjectHub = ReturnType<typeof createDurableObjectHub>;\n\n/** Calls a hub Durable Object from the Worker. */\nexport function hubClient(stub: HubStub) {\n const post = (action: HubAction) =>\n stub.fetch(new Request(HUB_ACTION_URL, { method: 'POST', body: JSON.stringify(action) }));\n const ok = async (response: Response) => {\n if (!response.ok) throw new Error(`Hub request failed: HTTP ${response.status}`);\n };\n return {\n /** Forwards a reactive HTTP request and returns the hub's response unchanged. */\n request: (body: SubscribeRequest | UnsubscribeRequest, principal: string) =>\n post(\n body.type === 'subscribe'\n ? { action: 'subscribe', principal, body }\n : { action: 'unsubscribe', principal, body },\n ),\n notify: async (path: string) => ok(await post({ action: 'notify', path })),\n publish: async (path: string, patches: JsonPatch[]) =>\n ok(await post({ action: 'publish', path, patches })),\n };\n}\n\nexport interface RemoteHubOptions {\n /** Absolute URL of the hub Worker's `routes.notify`. */\n url: string;\n /** The Worker's `notifySecret`. Keep it server-side. */\n secret: string;\n /** Aborts a notify that takes longer than this. Default 3000 ms. */\n timeoutMs?: number;\n /** Injectable for tests. */\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_REMOTE_HUB_TIMEOUT_MS = 3_000;\n\n/**\n * Notifies a hub from another backend, such as an AWS Lambda or a Vercel route,\n * after the data behind a path changes.\n */\nexport function remoteHub(options: RemoteHubOptions) {\n const send = async (body: { path: string; patches?: JsonPatch[] }) => {\n const response = await (options.fetch ?? fetch)(options.url, {\n method: 'POST',\n headers: { authorization: `Bearer ${options.secret}`, 'content-type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_REMOTE_HUB_TIMEOUT_MS),\n });\n if (!response.ok) throw new Error(`Hub notify failed: HTTP ${response.status}`);\n };\n return {\n notify: (path: string) => send({ path }),\n publish: (path: string, patches: JsonPatch[]) => send({ path, patches }),\n };\n}\n\nexport interface ReactiveRoutes {\n /** `POST` exchanges a credential for a signed ticket. */\n ticket: string;\n /** WebSocket upgrade, authenticated by the ticket in the `token` query parameter. */\n socket: string;\n /** Reactive HTTP subscribe and unsubscribe, authenticated by the ticket as a bearer token. */\n reactive: string;\n /** `POST { path, patches? }` from other backends, authenticated by `notifySecret`. */\n notify?: string;\n}\n\nexport interface ReactiveRoutesOptions {\n routes: ReactiveRoutes;\n /** Ticket signing secret. Requests get 503 when it is not configured. */\n secret: string | undefined;\n /** Bearer secret for `routes.notify`; the route answers 503 while it is unset. */\n notifySecret?: string | undefined;\n /**\n * Browser origins allowed to call the ticket and reactive routes cross-origin,\n * for a hub on a different host from the app.\n */\n allowedOrigins?: readonly string[];\n /** The hub Durable Object stub that serves these routes. */\n hub: () => HubStub;\n /** Returns the principal for a ticket request, or undefined to answer 401. */\n authenticate(request: Request): string | undefined | Promise<string | undefined>;\n ticketLifetimeSeconds?: number;\n}\n\nfunction sameSecret(given: string, expected: string): boolean {\n const left = new TextEncoder().encode(given);\n const right = new TextEncoder().encode(expected);\n let difference = left.length ^ right.length;\n for (let index = 0; index < right.length; index++) {\n difference |= (left[index] ?? 0) ^ right[index]!;\n }\n return difference === 0;\n}\n\nfunction withCors(response: Response, origin: string | null): Response {\n if (!origin) return response;\n const headers = new Headers(response.headers);\n headers.set('access-control-allow-origin', origin);\n headers.append('vary', 'origin');\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\n/** Serves the ticket, socket, reactive and notify routes; `undefined` for any other path. */\nexport async function handleReactiveRoutes(\n request: Request,\n options: ReactiveRoutesOptions,\n): Promise<Response | undefined> {\n const url = new URL(request.url);\n const { routes, secret } = options;\n const known = [routes.ticket, routes.socket, routes.reactive, routes.notify];\n if (!known.includes(url.pathname)) return undefined;\n\n const requestOrigin = request.headers.get('origin');\n const corsOrigin =\n requestOrigin && options.allowedOrigins?.includes(requestOrigin) ? requestOrigin : null;\n if (request.method === 'OPTIONS' && url.pathname !== routes.socket) {\n if (!corsOrigin) return new Response(null, { status: 403 });\n return new Response(null, {\n status: 204,\n headers: {\n 'access-control-allow-origin': corsOrigin,\n 'access-control-allow-methods': 'POST, OPTIONS',\n 'access-control-allow-headers': 'authorization, content-type',\n 'access-control-max-age': '86400',\n vary: 'origin',\n },\n });\n }\n\n if (url.pathname === routes.notify) {\n if (request.method !== 'POST') return error('Method not allowed', 405);\n if (!options.notifySecret) return error('Hub notify is not configured', 503);\n if (!sameSecret(bearerToken(request), options.notifySecret)) {\n return error('Unauthorized', 401);\n }\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return error('Invalid JSON', 400);\n }\n const { path, patches } = (body ?? {}) as { path?: unknown; patches?: unknown };\n if (typeof path !== 'string' || (patches !== undefined && !Array.isArray(patches))) {\n return error('Expected { path, patches? }', 400);\n }\n const client = hubClient(options.hub());\n if (patches) await client.publish(path, patches as JsonPatch[]);\n else await client.notify(path);\n return json({ type: 'ok' });\n }\n\n if (!secret) return withCors(error('Reactive hub is not configured', 503), corsOrigin);\n\n if (url.pathname === routes.ticket) {\n if (request.method !== 'POST') return withCors(error('Method not allowed', 405), corsOrigin);\n const principal = await options.authenticate(request);\n if (!principal) return withCors(error('Unauthorized', 401), corsOrigin);\n const socketUrl = new URL(routes.socket, url);\n socketUrl.protocol = 'wss:';\n return withCors(\n json(\n await createSignedClusterTicket({\n secret,\n principal,\n url: socketUrl.toString(),\n ...(options.ticketLifetimeSeconds === undefined\n ? {}\n : { lifetimeSeconds: options.ticketLifetimeSeconds }),\n }),\n ),\n corsOrigin,\n );\n }\n\n if (url.pathname === routes.socket) {\n if (request.headers.get('upgrade') !== 'websocket') {\n return error('Expected a WebSocket upgrade', 426);\n }\n const principal = await verifySignedClusterTicket(url.searchParams.get('token') ?? '', secret);\n if (!principal) return new Response('Unauthorized', { status: 401 });\n const forwarded = new Request(request);\n forwarded.headers.set(HUB_PRINCIPAL_HEADER, principal);\n return options.hub().fetch(forwarded);\n }\n\n if (request.method !== 'POST') return withCors(error('Method not allowed', 405), corsOrigin);\n const principal = await verifySignedClusterTicket(bearerToken(request), secret);\n if (!principal) return withCors(error('Unauthorized', 401), corsOrigin);\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return withCors(error('Invalid JSON', 400), corsOrigin);\n }\n if (!isHubRequest(body)) return withCors(error('Unsupported reactive request', 400), corsOrigin);\n return withCors(await hubClient(options.hub()).request(body, principal), corsOrigin);\n}\n","import {\n createDurableObjectHub,\n handleReactiveRoutes,\n type DurableObjectHub,\n type DurableObjectStateLike,\n type DurableObjectWebSocket,\n type HubStub,\n} from './cloudflare.js';\nimport { createSignedClusterTicket, type ClusterTicket } from './cluster.js';\nimport { bearerToken, type HubPaths } from './hub.js';\nimport { error as logError } from './log.js';\n\n/** Routes a `defineHub` Worker serves. */\nexport const HUB_ROUTES = {\n /** `POST` with the notify secret and `?principal=`: a ticket for a machine client. */\n ticket: '/api/hub/token',\n /** WebSocket upgrade with a signed ticket in `?token=`. */\n socket: '/api/hub/socket',\n /** Reactive HTTP subscribe and unsubscribe, with a signed ticket as the bearer. */\n reactive: '/api/hub/reactive',\n /** `POST { path, patches? }` with the notify secret, after the data behind a path changed. */\n notify: '/api/hub/notify',\n /** `GET` with the notify secret: `{ principals }`, the tracked principals holding a live socket. */\n presence: '/api/hub/presence',\n} as const;\n\n/** Names of the Worker secrets and variables a `defineHub` Worker reads. */\nexport const HUB_ENV = {\n TICKET_SECRET: 'REACTIVE_TICKET_SECRET',\n NOTIFY_SECRET: 'HUB_NOTIFY_SECRET',\n /** Comma-separated browser origins allowed to call the hub cross-origin. */\n ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',\n} as const;\n\n/** The Durable Object class name and binding a `defineHub` Worker uses. */\nexport const HUB_DURABLE_OBJECT = { className: 'ReactiveHub', binding: 'REACTIVE_HUB' } as const;\n\n/** The secrets and variables a `defineHub` Worker reads. */\nexport interface HubWorkerEnv {\n REACTIVE_TICKET_SECRET?: string;\n HUB_NOTIFY_SECRET?: string;\n ALLOWED_ORIGINS?: string;\n REACTIVE_HUB: { idFromName(name: string): unknown; get(id: unknown): HubStub };\n}\n\n/** What a hub's path rules can read from the Durable Object. */\nexport interface HubContext {\n /** Whether `principal` holds a live socket. Needs `presence`. */\n present(principal: string): boolean;\n /** Every tracked principal holding a live socket, sorted. Needs `presence`. */\n presentPrincipals(): string[];\n}\n\nexport interface HubConfig {\n /** Path rules: who may watch each path and its snapshot. */\n paths: (hub: HubContext) => HubPaths;\n /**\n * Principals whose live socket counts as presence, such as machine workers.\n * They must ping more often than `timeoutMs`; `connectClusterWorker` does.\n */\n presence?: {\n principals: readonly string[] | ((principal: string) => boolean);\n /** @default 90000 */\n timeoutMs?: number;\n /** Paths notified when a principal comes online or goes offline, such as a status page. */\n notify?: readonly string[];\n };\n /**\n * Machine principals that may get a ticket from `HUB_ROUTES.ticket` by presenting\n * the notify secret. Browsers get tickets from the app instead (`mintHubTicket`).\n */\n machinePrincipal?: (principal: string) => boolean;\n}\n\n/** The Durable Object a `defineHub` Worker exports as `ReactiveHub`. */\nexport interface HubDurableObject {\n fetch(request: Request): Promise<Response>;\n webSocketMessage(socket: DurableObjectWebSocket, message: string | ArrayBuffer): Promise<void>;\n webSocketClose(socket: DurableObjectWebSocket, code: number): Promise<void>;\n webSocketError(socket: DurableObjectWebSocket): Promise<void>;\n alarm(): Promise<void>;\n}\n\n/** What `defineHub` returns: export `ReactiveHub` by that name and `worker` as the default. */\nexport interface DefinedHub {\n ReactiveHub: new (ctx: DurableObjectStateLike, env: unknown) => HubDurableObject;\n worker: { fetch(request: Request, env: HubWorkerEnv): Promise<Response> };\n}\n\nconst HUB_INSTANCE = 'hub';\nconst PRESENCE_ACTION_URL = 'https://reactive-hub/presence';\n\nfunction sameSecret(given: string, expected: string): boolean {\n const left = new TextEncoder().encode(given);\n const right = new TextEncoder().encode(expected);\n let difference = left.length ^ right.length;\n for (let index = 0; index < right.length; index++) {\n difference |= (left[index] ?? 0) ^ right[index]!;\n }\n return difference === 0;\n}\n\nfunction parseOrigins(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((origin) => origin.trim().replace(/\\/+$/u, ''))\n .filter(Boolean);\n}\n\n/**\n * A complete hub Worker: the Durable Object class and the Worker handler, with\n * the ticket, socket, reactive, notify and presence routes. The application\n * supplies only its path rules.\n *\n * ```ts\n * const hub = defineHub({ paths: () => ({ 'status': { authorize: () => true, snapshot: () => ({}) } }) });\n * export const ReactiveHub = hub.ReactiveHub;\n * export default hub.worker;\n * ```\n *\n * Deploy it with `dynamodb-reactive-hub deploy`, which writes the matching\n * Wrangler config and puts the secrets.\n */\nexport function defineHub(config: HubConfig): DefinedHub {\n class ReactiveHub implements HubDurableObject {\n readonly #ctx: DurableObjectStateLike;\n readonly #reactive: DurableObjectHub;\n\n constructor(ctx: DurableObjectStateLike, _env: unknown) {\n this.#ctx = ctx;\n const context: HubContext = {\n present: (principal) => this.#reactive.present(principal),\n presentPrincipals: () => this.#presentPrincipals(),\n };\n const presence = config.presence;\n this.#reactive = createDurableObjectHub(ctx, {\n paths: config.paths(context),\n ...(presence\n ? {\n presence: {\n principals: presence.principals,\n ...(presence.timeoutMs === undefined ? {} : { timeoutMs: presence.timeoutMs }),\n onChange: async () => {\n for (const path of presence.notify ?? []) {\n // oxlint-disable-next-line no-await-in-loop -- Notifications stay ordered.\n await this.#reactive.hub.notify(path);\n }\n },\n },\n }\n : {}),\n });\n }\n\n #presentPrincipals(): string[] {\n const principals = new Set<string>();\n for (const socket of this.#ctx.getWebSockets()) {\n const principal = this.#reactive.principalOf(socket);\n if (this.#reactive.present(principal)) principals.add(principal);\n }\n return [...principals].sort();\n }\n\n async fetch(request: Request): Promise<Response> {\n if (request.url === PRESENCE_ACTION_URL) {\n return Response.json(\n { principals: this.#presentPrincipals() },\n { headers: { 'cache-control': 'no-store' } },\n );\n }\n return this.#reactive.fetch(request);\n }\n\n webSocketMessage(socket: DurableObjectWebSocket, message: string | ArrayBuffer) {\n return this.#reactive.webSocketMessage(socket, message);\n }\n\n webSocketClose(socket: DurableObjectWebSocket, code: number) {\n return this.#reactive.webSocketClose(socket, code);\n }\n\n webSocketError(socket: DurableObjectWebSocket) {\n return this.#reactive.webSocketError(socket);\n }\n\n alarm() {\n return this.#reactive.alarm();\n }\n }\n\n const worker = {\n async fetch(request: Request, env: HubWorkerEnv): Promise<Response> {\n const stub = () => env.REACTIVE_HUB.get(env.REACTIVE_HUB.idFromName(HUB_INSTANCE));\n const notifySecret = env.HUB_NOTIFY_SECRET;\n const authorized = () =>\n Boolean(notifySecret) && sameSecret(bearerToken(request), notifySecret!);\n try {\n if (new URL(request.url).pathname === HUB_ROUTES.presence) {\n if (request.method !== 'GET') return new Response(null, { status: 405 });\n if (!notifySecret) return new Response(null, { status: 503 });\n if (!authorized()) return new Response(null, { status: 401 });\n return await stub().fetch(new Request(PRESENCE_ACTION_URL));\n }\n const response = await handleReactiveRoutes(request, {\n routes: HUB_ROUTES,\n secret: env.REACTIVE_TICKET_SECRET,\n notifySecret,\n allowedOrigins: parseOrigins(env.ALLOWED_ORIGINS),\n hub: stub,\n authenticate: (incoming) => {\n const principal = new URL(incoming.url).searchParams.get('principal') ?? '';\n return authorized() && config.machinePrincipal?.(principal) ? principal : undefined;\n },\n });\n return response ?? new Response('Not found', { status: 404 });\n } catch (cause) {\n // Surface unexpected failures to `wrangler tail` instead of a bare 5xx.\n logError('hub request failed', request.method, new URL(request.url).pathname, cause);\n return Response.json({ error: 'Hub error' }, { status: 500 });\n }\n },\n };\n\n return { ReactiveHub, worker };\n}\n\n/** The hub's two secrets, derived from one root secret the application already holds. */\nexport interface HubSecrets {\n /** `REACTIVE_TICKET_SECRET`: signs and verifies tickets. */\n ticket: string;\n /** `HUB_NOTIFY_SECRET`: notify, presence and machine ticket routes. */\n notify: string;\n}\n\nasync function hmacHex(key: string, label: string): Promise<string> {\n const encoder = new TextEncoder();\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(key),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = new Uint8Array(\n await crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(label)),\n );\n return Array.from(signature, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Derives the hub secrets as HMAC-SHA256 hex of `root` under `<name>-ticket` and\n * `<name>-notify`, where `name` is the hub Worker's name. Everything that already\n * holds `root` derives them itself; `dynamodb-reactive-hub deploy` puts the same\n * values on the Worker, so no hub secret has to be distributed.\n */\nexport async function deriveHubSecrets(root: string, name: string): Promise<HubSecrets> {\n const [ticket, notify] = await Promise.all([\n hmacHex(root, `${name}-ticket`),\n hmacHex(root, `${name}-notify`),\n ]);\n return { ticket, notify };\n}\n\nexport interface MintHubTicketOptions {\n /** The hub's origin, such as `https://my-hub.example.workers.dev`. */\n origin: string;\n /** The hub's ticket secret. */\n secret: string;\n principal: string;\n /** @default 60 */\n lifetimeSeconds?: number;\n}\n\n/** A browser ticket for the hub, minted by the app that knows the signed-in user. */\nexport async function mintHubTicket(\n options: MintHubTicketOptions,\n): Promise<ClusterTicket & { httpUrl: string }> {\n const socketUrl = new URL(HUB_ROUTES.socket, options.origin);\n socketUrl.protocol = 'wss:';\n const ticket = await createSignedClusterTicket({\n secret: options.secret,\n principal: options.principal,\n url: socketUrl.toString(),\n ...(options.lifetimeSeconds === undefined ? {} : { lifetimeSeconds: options.lifetimeSeconds }),\n });\n return { ...ticket, httpUrl: new URL(HUB_ROUTES.reactive, options.origin).toString() };\n}\n\n/**\n * The tracked principals holding a live socket, from `HUB_ROUTES.presence`, or\n * undefined when the hub cannot be reached.\n */\nexport async function fetchHubPresence(options: {\n origin: string;\n secret: string;\n /** @default 3000 */\n timeoutMs?: number;\n fetch?: typeof fetch;\n}): Promise<string[] | undefined> {\n try {\n const response = await (options.fetch ?? fetch)(new URL(HUB_ROUTES.presence, options.origin), {\n headers: { authorization: `Bearer ${options.secret}` },\n signal: AbortSignal.timeout(options.timeoutMs ?? 3_000),\n });\n if (!response.ok) return undefined;\n const { principals } = (await response.json()) as { principals?: unknown };\n return Array.isArray(principals) && principals.every((value) => typeof value === 'string')\n ? principals\n : undefined;\n } catch {\n return undefined;\n }\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  import { SubscribeRequest, SnapshotResponse, UnsubscribeRequest, JsonPatch } from './cluster.cjs';
2
2
  import { HubSocket, HubPaths } from './hub.cjs';
3
+ import { ClusterTicket } from './cluster.cjs';
4
+ import './client-BPAct-Dz.js';
3
5
 
4
6
  /**
5
7
  * Cloudflare Durable Object shell for the reactive hub. It is structural: it
@@ -140,6 +142,8 @@ interface RemoteHubOptions {
140
142
  url: string;
141
143
  /** The Worker's `notifySecret`. Keep it server-side. */
142
144
  secret: string;
145
+ /** Aborts a notify that takes longer than this. Default 3000 ms. */
146
+ timeoutMs?: number;
143
147
  /** Injectable for tests. */
144
148
  fetch?: typeof fetch;
145
149
  }
@@ -181,5 +185,136 @@ interface ReactiveRoutesOptions {
181
185
  /** Serves the ticket, socket, reactive and notify routes; `undefined` for any other path. */
182
186
  declare function handleReactiveRoutes(request: Request, options: ReactiveRoutesOptions): Promise<Response | undefined>;
183
187
 
184
- export { HEARTBEAT_TIMEOUT_CLOSE_CODE, HUB_PRINCIPAL_HEADER, createDurableObjectHub, handleReactiveRoutes, hubClient, remoteHub };
185
- export type { DurableObjectHub, DurableObjectHubOptions, DurableObjectStateLike, DurableObjectWebSocket, HubClientFrame, HubPresenceOptions, HubStub, ReactiveRoutes, ReactiveRoutesOptions, RemoteHubOptions };
188
+ /** Routes a `defineHub` Worker serves. */
189
+ declare const HUB_ROUTES: {
190
+ /** `POST` with the notify secret and `?principal=`: a ticket for a machine client. */
191
+ readonly ticket: "/api/hub/token";
192
+ /** WebSocket upgrade with a signed ticket in `?token=`. */
193
+ readonly socket: "/api/hub/socket";
194
+ /** Reactive HTTP subscribe and unsubscribe, with a signed ticket as the bearer. */
195
+ readonly reactive: "/api/hub/reactive";
196
+ /** `POST { path, patches? }` with the notify secret, after the data behind a path changed. */
197
+ readonly notify: "/api/hub/notify";
198
+ /** `GET` with the notify secret: `{ principals }`, the tracked principals holding a live socket. */
199
+ readonly presence: "/api/hub/presence";
200
+ };
201
+ /** Names of the Worker secrets and variables a `defineHub` Worker reads. */
202
+ declare const HUB_ENV: {
203
+ readonly TICKET_SECRET: "REACTIVE_TICKET_SECRET";
204
+ readonly NOTIFY_SECRET: "HUB_NOTIFY_SECRET";
205
+ /** Comma-separated browser origins allowed to call the hub cross-origin. */
206
+ readonly ALLOWED_ORIGINS: "ALLOWED_ORIGINS";
207
+ };
208
+ /** The Durable Object class name and binding a `defineHub` Worker uses. */
209
+ declare const HUB_DURABLE_OBJECT: {
210
+ readonly className: "ReactiveHub";
211
+ readonly binding: "REACTIVE_HUB";
212
+ };
213
+ /** The secrets and variables a `defineHub` Worker reads. */
214
+ interface HubWorkerEnv {
215
+ REACTIVE_TICKET_SECRET?: string;
216
+ HUB_NOTIFY_SECRET?: string;
217
+ ALLOWED_ORIGINS?: string;
218
+ REACTIVE_HUB: {
219
+ idFromName(name: string): unknown;
220
+ get(id: unknown): HubStub;
221
+ };
222
+ }
223
+ /** What a hub's path rules can read from the Durable Object. */
224
+ interface HubContext {
225
+ /** Whether `principal` holds a live socket. Needs `presence`. */
226
+ present(principal: string): boolean;
227
+ /** Every tracked principal holding a live socket, sorted. Needs `presence`. */
228
+ presentPrincipals(): string[];
229
+ }
230
+ interface HubConfig {
231
+ /** Path rules: who may watch each path and its snapshot. */
232
+ paths: (hub: HubContext) => HubPaths;
233
+ /**
234
+ * Principals whose live socket counts as presence, such as machine workers.
235
+ * They must ping more often than `timeoutMs`; `connectClusterWorker` does.
236
+ */
237
+ presence?: {
238
+ principals: readonly string[] | ((principal: string) => boolean);
239
+ /** @default 90000 */
240
+ timeoutMs?: number;
241
+ /** Paths notified when a principal comes online or goes offline, such as a status page. */
242
+ notify?: readonly string[];
243
+ };
244
+ /**
245
+ * Machine principals that may get a ticket from `HUB_ROUTES.ticket` by presenting
246
+ * the notify secret. Browsers get tickets from the app instead (`mintHubTicket`).
247
+ */
248
+ machinePrincipal?: (principal: string) => boolean;
249
+ }
250
+ /** The Durable Object a `defineHub` Worker exports as `ReactiveHub`. */
251
+ interface HubDurableObject {
252
+ fetch(request: Request): Promise<Response>;
253
+ webSocketMessage(socket: DurableObjectWebSocket, message: string | ArrayBuffer): Promise<void>;
254
+ webSocketClose(socket: DurableObjectWebSocket, code: number): Promise<void>;
255
+ webSocketError(socket: DurableObjectWebSocket): Promise<void>;
256
+ alarm(): Promise<void>;
257
+ }
258
+ /** What `defineHub` returns: export `ReactiveHub` by that name and `worker` as the default. */
259
+ interface DefinedHub {
260
+ ReactiveHub: new (ctx: DurableObjectStateLike, env: unknown) => HubDurableObject;
261
+ worker: {
262
+ fetch(request: Request, env: HubWorkerEnv): Promise<Response>;
263
+ };
264
+ }
265
+ /**
266
+ * A complete hub Worker: the Durable Object class and the Worker handler, with
267
+ * the ticket, socket, reactive, notify and presence routes. The application
268
+ * supplies only its path rules.
269
+ *
270
+ * ```ts
271
+ * const hub = defineHub({ paths: () => ({ 'status': { authorize: () => true, snapshot: () => ({}) } }) });
272
+ * export const ReactiveHub = hub.ReactiveHub;
273
+ * export default hub.worker;
274
+ * ```
275
+ *
276
+ * Deploy it with `dynamodb-reactive-hub deploy`, which writes the matching
277
+ * Wrangler config and puts the secrets.
278
+ */
279
+ declare function defineHub(config: HubConfig): DefinedHub;
280
+ /** The hub's two secrets, derived from one root secret the application already holds. */
281
+ interface HubSecrets {
282
+ /** `REACTIVE_TICKET_SECRET`: signs and verifies tickets. */
283
+ ticket: string;
284
+ /** `HUB_NOTIFY_SECRET`: notify, presence and machine ticket routes. */
285
+ notify: string;
286
+ }
287
+ /**
288
+ * Derives the hub secrets as HMAC-SHA256 hex of `root` under `<name>-ticket` and
289
+ * `<name>-notify`, where `name` is the hub Worker's name. Everything that already
290
+ * holds `root` derives them itself; `dynamodb-reactive-hub deploy` puts the same
291
+ * values on the Worker, so no hub secret has to be distributed.
292
+ */
293
+ declare function deriveHubSecrets(root: string, name: string): Promise<HubSecrets>;
294
+ interface MintHubTicketOptions {
295
+ /** The hub's origin, such as `https://my-hub.example.workers.dev`. */
296
+ origin: string;
297
+ /** The hub's ticket secret. */
298
+ secret: string;
299
+ principal: string;
300
+ /** @default 60 */
301
+ lifetimeSeconds?: number;
302
+ }
303
+ /** A browser ticket for the hub, minted by the app that knows the signed-in user. */
304
+ declare function mintHubTicket(options: MintHubTicketOptions): Promise<ClusterTicket & {
305
+ httpUrl: string;
306
+ }>;
307
+ /**
308
+ * The tracked principals holding a live socket, from `HUB_ROUTES.presence`, or
309
+ * undefined when the hub cannot be reached.
310
+ */
311
+ declare function fetchHubPresence(options: {
312
+ origin: string;
313
+ secret: string;
314
+ /** @default 3000 */
315
+ timeoutMs?: number;
316
+ fetch?: typeof fetch;
317
+ }): Promise<string[] | undefined>;
318
+
319
+ export { HEARTBEAT_TIMEOUT_CLOSE_CODE, HUB_DURABLE_OBJECT, HUB_ENV, HUB_PRINCIPAL_HEADER, HUB_ROUTES, createDurableObjectHub, defineHub, deriveHubSecrets, fetchHubPresence, handleReactiveRoutes, hubClient, mintHubTicket, remoteHub };
320
+ export type { DefinedHub, DurableObjectHub, DurableObjectHubOptions, DurableObjectStateLike, DurableObjectWebSocket, HubClientFrame, HubConfig, HubContext, HubDurableObject, HubPresenceOptions, HubSecrets, HubStub, HubWorkerEnv, MintHubTicketOptions, ReactiveRoutes, ReactiveRoutesOptions, RemoteHubOptions };