@spfn/core 0.3.0-beta.26 → 0.3.0-beta.28

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":["../../src/state/client-store.ts","../../src/state/client.ts"],"names":["notify"],"mappings":"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","file":"client.js","sourcesContent":["/**\n * State Client Store\n *\n * One frozen entry per `(id, key)` and the ordering rule that decides whether\n * an arrival — a snapshot entry, a frame, a read or a write answer — replaces\n * it. An entry is replaced only on an applied change, so `current()` returns\n * the same object until then: `useSyncExternalStore` compares by reference\n * and would re-render forever on a fresh object per call.\n */\n\nimport type { StateEntry, StateFrame, StateListener } from './client-types';\n\n/** Key fields, flat strings by construction (`.key()` refuses other fields). */\nexport type StateKeyFields = Record<string, string>;\n\n/** A key as text with its fields sorted, so equal keys written in any order name one entry. */\nexport function stableKey(key: StateKeyFields | undefined): string\n{\n if (!key)\n {\n return '';\n }\n\n return JSON.stringify(Object.keys(key).sort().map(field => [field, key[field]]));\n}\n\n/** The inverse of `stableKey`. */\nexport function parseKey(text: string): StateKeyFields | undefined\n{\n return text ? Object.fromEntries(JSON.parse(text) as [string, string][]) : undefined;\n}\n\n/** Deep equality of JSON values, which is all a frame carries. */\nfunction sameJson(a: unknown, b: unknown): boolean\n{\n if (a === b)\n {\n return true;\n }\n\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null || Array.isArray(a) !== Array.isArray(b))\n {\n return false;\n }\n\n const aKeys = Object.keys(a);\n const bRecord = b as Record<string, unknown>;\n\n return aKeys.length === Object.keys(b).length\n && aKeys.every(field => field in bRecord && sameJson((a as Record<string, unknown>)[field], bRecord[field]));\n}\n\n/**\n * The ordering rule for the value. A newer `at` replaces; an older one is\n * dropped; an equal one is by design the same value (one read stamps one\n * `at`), so it replaces only when it says something different. An entry\n * marked revoked before any value arrived has no `at`: anything replaces it.\n */\nfunction supersedes(current: StateEntry | undefined, arrival: StateFrame): boolean\n{\n if (current?.at === undefined || arrival.at > current.at)\n {\n return true;\n }\n\n if (arrival.at < current.at)\n {\n return false;\n }\n\n return !sameJson(current.value, arrival.value) || !sameJson(current.data, arrival.data);\n}\n\nfunction entryOf(frame: StateFrame, status: StateStatus): StateEntry\n{\n const { at, value, data } = frame;\n\n return Object.freeze(data === undefined ? { at, value, status } : { at, value, data, status });\n}\n\ntype StateStatus = StateEntry['status'];\n\nexport class StateStore\n{\n private readonly entries = new Map<string, StateEntry>();\n private readonly listeners = new Map<string, Set<StateListener>>();\n\n /**\n * The slots the server stopped serving, with the generation of the\n * connection that said so. Kept apart from the entries: a revoke can\n * arrive before the slot has a value, and an arrival afterwards supplies\n * the value without clearing the mark.\n */\n private readonly revoked = new Map<string, number>();\n\n /** The store slot of `(id, key)`. */\n static slot(id: string, keyText: string): string\n {\n return `${id}\\u0000${keyText}`;\n }\n\n current(slot: string): StateEntry | undefined\n {\n return this.entries.get(slot);\n }\n\n /**\n * Apply an arrival: the value under the ordering rule, the status from the\n * revoked mark. Listeners hear only an applied change.\n */\n apply(slot: string, frame: StateFrame): void\n {\n const current = this.entries.get(slot);\n const status: StateStatus = this.revoked.has(slot) ? 'revoked' : 'ready';\n\n if (supersedes(current, frame))\n {\n this.replace(slot, entryOf(frame, status));\n }\n else if (current!.status !== status)\n {\n // Not superseded, so the current entry has a value: only its status changes.\n this.replace(slot, entryOf(current as StateFrame, status));\n }\n }\n\n /** Mark `slot` revoked by the connection of `generation`; its value, if any, is kept. */\n revoke(slot: string, generation: number): void\n {\n this.revoked.set(slot, generation);\n const entry = this.entries.get(slot);\n\n if (entry?.status !== 'revoked')\n {\n this.replace(slot, Object.freeze(entry ? { ...entry, status: 'revoked' as const } : { status: 'revoked' as const }));\n }\n }\n\n /** The generation of the connection that revoked `slot`, or `undefined` while it is served. */\n revokedBy(slot: string): number | undefined\n {\n return this.revoked.get(slot);\n }\n\n /** Clear the revoked mark; the next arrival for `slot` is `'ready'`. */\n allow(slot: string): void\n {\n this.revoked.delete(slot);\n }\n\n listen(slot: string, listener: StateListener): void\n {\n const set = this.listeners.get(slot) ?? new Set();\n set.add(listener);\n this.listeners.set(slot, set);\n }\n\n unlisten(slot: string, listener?: StateListener): void\n {\n const set = this.listeners.get(slot);\n\n if (listener)\n {\n set?.delete(listener);\n }\n\n if (!listener || set?.size === 0)\n {\n this.listeners.delete(slot);\n }\n }\n\n clearListeners(): void\n {\n this.listeners.clear();\n }\n\n /** Replace the entry and notify; a throwing listener is logged and does not stop the others. */\n private replace(slot: string, entry: StateEntry): void\n {\n this.entries.set(slot, entry);\n\n for (const listener of [...this.listeners.get(slot) ?? []])\n {\n notify(slot, listener);\n }\n }\n}\n\nfunction notify(slot: string, listener: StateListener): void\n{\n try\n {\n listener();\n }\n catch (error)\n {\n // The state id only: the key is the app's data.\n console.warn(`[state] A listener of state \"${slot.split('\\u0000')[0]}\" threw:`, error);\n }\n}\n","/**\n * State Client\n *\n * The browser side of a state router (`@spfn/core/state`): it calls the derived\n * read, write and snapshot routes, keeps one SSE stream open for every state of\n * the router, and orders snapshots against frames per `(id, key)`. It knows the\n * router by type — `typeof states` — and at runtime by the manifest every\n * snapshot carries; it imports no server code.\n *\n * @example\n * ```typescript\n * import { createStateClient } from '@spfn/core/state/client';\n * import { createAuthSSEClient } from '@spfn/core/event/sse/client';\n * import type { states } from '@/server/states';\n *\n * export const state = createStateClient<typeof states>({ sse: createAuthSSEClient() });\n *\n * const stop = state.subscribe('runtime', { workspaceId }, () => render(state.current('runtime', { workspaceId })));\n * ```\n */\n\nimport type { SSESubscriptionChange } from '@spfn/core/event/sse';\nimport { createApi } from '@spfn/core/nextjs';\nimport { stableKey, StateStore, type StateKeyFields } from './client-store';\nimport type {\n AnyStateRouter,\n StateClient,\n StateClientConfig,\n StateEntry,\n StateFrame,\n StateListener,\n} from './client-types';\n\nexport type {\n AnyStateRouter,\n InferStateData,\n StateCatalog,\n StateFrameOf,\n InferStateInput,\n InferStateKey,\n InferStateValue,\n StateClient,\n StateClientConfig,\n StateEntry,\n StateFrame,\n StateId,\n StateKeyArgs,\n StateListener,\n WritableStateId,\n} from './client-types';\n\n/** A frame as the SSE event carries it. */\ntype WireFrame = StateFrame & { key?: StateKeyFields; subject?: string };\n\n/** Every state of the router, in declaration order: its key fields and whether it has a write. */\ntype Manifest = Record<string, { key: string[]; write: boolean }>;\n\ninterface Snapshot\n{\n at: string;\n states: Record<string, { value: unknown; data?: unknown }>;\n manifest: Manifest;\n}\n\ntype RouteCalls = Record<string, { call(input: object): Promise<unknown> }>;\n\n/** One `(id, key)` someone watches, and how many subscriptions hold it. */\ninterface Watch\n{\n id: string;\n key: StateKeyFields | undefined;\n count: number;\n}\n\n/** The stream as one `sse.subscribe` opened it; `generation` names its current connection. */\ninterface Stream\n{\n query: StateKeyFields;\n text: string;\n generation: number;\n close: () => void;\n}\n\n/** When an automatic read was started: the connection generation and the client's clock. */\ninterface ReadTag\n{\n generation: number;\n tick: number;\n}\n\n/** `get<Id>State` / `put<Id>State`, the names the server router derives. */\nfunction routeName(method: 'get' | 'put', id: string): string\n{\n return `${method}${id[0].toUpperCase()}${id.slice(1)}State`;\n}\n\n/** Whether `query` carries every field of `key` with its value. */\nfunction carries(query: StateKeyFields, key: StateKeyFields): boolean\n{\n return Object.entries(key).every(([field, value]) => query[field] === value);\n}\n\nfunction sortedFields(fields: string[]): string\n{\n return [...fields].sort().join(', ');\n}\n\n/**\n * Build the client for `typeof states`. The first subscription (or call)\n * reads the snapshot, whose manifest names every state; the stream then\n * requests every state event — the server's `grant` can only activate an\n * event the connection requested.\n */\nexport function createStateClient<TStates extends AnyStateRouter>(config: StateClientConfig): StateClient<TStates>\n{\n return new BrowserStateClient(config) as unknown as StateClient<TStates>;\n}\n\nclass BrowserStateClient\n{\n private readonly store = new StateStore();\n private readonly api: RouteCalls;\n private readonly watched = new Map<string, Watch>();\n private readonly reportError: (error: unknown) => void;\n private manifest: Manifest | undefined;\n private manifestRead: Promise<void> | undefined;\n private stream: Stream | undefined;\n private syncQueued = false;\n\n /** The lifetime whose subscriptions wait on the first snapshot — its read in flight, or its retry timer set. */\n private waitingLifetime: number | undefined;\n private retryTimer: ReturnType<typeof setTimeout> | undefined;\n\n /** Bumped by `close()`: a snapshot started before it is discarded. */\n private lifetime = 0;\n\n /** Bumped on every connection open; tags the snapshot read for it. */\n private generation = 0;\n\n /** Bumped on every automatic read and every grant, to tell which came first. */\n private clock = 0;\n\n /** The clock at the latest grant of each slot. */\n private readonly grants = new Map<string, number>();\n\n constructor(private readonly config: StateClientConfig)\n {\n this.api = createApi<any>(config.api) as unknown as RouteCalls;\n this.reportError = config.onError ?? (error => console.error('[state] A refresh failed:', error));\n }\n\n async get(id: string, key?: StateKeyFields): Promise<StateFrame>\n {\n await this.requireManifest();\n const frame = await this.api[routeName('get', id)].call(key ? { query: key } : {}) as StateFrame;\n this.store.apply(this.slot(id, key), frame);\n\n return frame;\n }\n\n async write(id: string, input: unknown, key?: StateKeyFields): Promise<StateFrame>\n {\n if (!(await this.requireManifest())[id]?.write)\n {\n throw new Error(`State client: state \"${id}\" declares no .write(), so it has no write route.`);\n }\n\n const frame = await this.api[routeName('put', id)].call({ body: input, ...(key ? { query: key } : {}) }) as StateFrame;\n this.store.apply(this.slot(id, key), frame);\n\n return frame;\n }\n\n snapshot(query: StateKeyFields = this.streamQuery()): Promise<Snapshot>\n {\n return this.readSnapshot(query, this.tag());\n }\n\n subscribe(id: string, ...args: [StateListener] | [StateKeyFields, StateListener]): () => void\n {\n const listener = args[args.length - 1] as StateListener;\n const key = args.length === 2 ? args[0] as StateKeyFields : undefined;\n const refusal = this.manifest && this.refusal(id, key);\n\n if (refusal)\n {\n this.reportError(refusal);\n\n return () => undefined;\n }\n\n // One wrapper per subscription: the same listener subscribed twice is two subscriptions.\n const notify = () => listener();\n const slot = this.slot(id, key);\n this.store.listen(slot, notify);\n this.watch(slot, id, key, 1);\n let live = true;\n\n return () =>\n {\n if (live)\n {\n live = false;\n this.store.unlisten(slot, notify);\n this.watch(slot, id, key, -1);\n }\n };\n }\n\n current(id: string, key?: StateKeyFields): StateEntry | undefined\n {\n return this.store.current(this.slot(id, key));\n }\n\n close(): void\n {\n this.lifetime++;\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n this.closeStream();\n this.watched.clear();\n this.store.clearListeners();\n }\n\n private slot(id: string, key: StateKeyFields | undefined): string\n {\n return StateStore.slot(id, stableKey(key));\n }\n\n private tag(): ReadTag\n {\n return { generation: this.generation, tick: ++this.clock };\n }\n\n /** The key of state `id` the query names; `undefined` for a keyless state. */\n private keyIn(id: string, query: StateKeyFields): StateKeyFields | undefined\n {\n const fields = this.manifest![id].key;\n\n return fields.length ? Object.fromEntries(fields.map(field => [field, query[field]])) : undefined;\n }\n\n /** Why the manifest refuses `subscribe(id, key)` — an unknown id, or key fields not the state's. */\n private refusal(id: string, key: StateKeyFields | undefined): Error | undefined\n {\n const state = this.manifest![id];\n\n if (!state)\n {\n return new Error(`State client: the server's manifest has no state \"${id}\".`);\n }\n\n const expected = sortedFields(state.key);\n const given = sortedFields(Object.keys(key ?? {}));\n\n return expected === given\n ? undefined\n : new Error(`State client: state \"${id}\" takes the key fields [${expected}], given [${given}].`);\n }\n\n /** The first snapshot, read once however many callers wait for it. */\n private ensureManifest(): Promise<void>\n {\n if (this.manifest)\n {\n return Promise.resolve();\n }\n\n this.manifestRead ??= this.snapshot()\n .then(() => undefined)\n .finally(() =>\n {\n this.manifestRead = undefined;\n });\n\n return this.manifestRead;\n }\n\n /** The manifest for a call the app made; one whose wait outlived `close()` is refused. */\n private async requireManifest(): Promise<Manifest>\n {\n await this.ensureManifest();\n\n if (!this.manifest)\n {\n throw new Error('State client: the client was closed while the first snapshot was in flight.');\n }\n\n return this.manifest;\n }\n\n /**\n * Read the snapshot and apply it: the manifest first — `keyIn` and the\n * key checks need it — then each state present, then each watched state\n * it omits. A snapshot that resolves after `close()` is discarded.\n */\n private async readSnapshot(query: StateKeyFields, tag: ReadTag): Promise<Snapshot>\n {\n const lifetime = this.lifetime;\n const snapshot = await this.api.getStates.call({ query }) as Snapshot;\n\n if (lifetime === this.lifetime)\n {\n this.installManifest(snapshot.manifest);\n this.applyPresent(snapshot, query, tag);\n this.applyOmitted(snapshot, query, tag);\n }\n\n return snapshot;\n }\n\n /**\n * The manifest is static; the first one stays. Subscriptions made before\n * it are checked now, and the stream opens for those it accepts —\n * whichever call (`subscribe`, `get`, `write`, `snapshot`) brought it.\n */\n private installManifest(manifest: Manifest): void\n {\n if (this.manifest)\n {\n return;\n }\n\n this.manifest = manifest;\n\n for (const [slot, watch] of this.watched)\n {\n const refusal = this.refusal(watch.id, watch.key);\n\n if (refusal)\n {\n this.watched.delete(slot);\n this.store.unlisten(slot);\n this.reportError(refusal);\n }\n }\n\n if (this.watched.size > 0)\n {\n this.queueSync();\n }\n }\n\n /**\n * An arrival from a read started for the connection of `tag`: a\n * connection newer than the one that revoked the slot lifts the mark, an\n * older or the same connection leaves `'revoked'`. The lift is by\n * generation alone — a frame or a write answer of the new connection may\n * land first with a newer `at` — and the ordering rule decides only the value.\n */\n private serve(slot: string, frame: StateFrame, tag: ReadTag): void\n {\n if ((this.store.revokedBy(slot) ?? Infinity) < tag.generation)\n {\n this.store.allow(slot);\n }\n\n this.store.apply(slot, frame);\n }\n\n /** A state present is served, under the generation rule of `serve`. */\n private applyPresent(snapshot: Snapshot, query: StateKeyFields, tag: ReadTag): void\n {\n for (const [id, result] of Object.entries(snapshot.states))\n {\n this.serve(this.slot(id, this.keyIn(id, query)), { at: snapshot.at, ...result }, tag);\n }\n }\n\n /**\n * A watched state whose key the query carried (or a keyless one) and that\n * the snapshot omits is not served to this subject: `'revoked'`. Not when\n * a newer connection exists, nor when a grant came after the read started.\n */\n private applyOmitted(snapshot: Snapshot, query: StateKeyFields, tag: ReadTag): void\n {\n if (tag.generation !== this.generation)\n {\n return;\n }\n\n for (const [slot, watch] of this.watched)\n {\n const asked = !watch.key || carries(query, watch.key);\n const grantedSince = (this.grants.get(slot) ?? 0) > tag.tick;\n\n if (asked && !(watch.id in snapshot.states) && !grantedSince)\n {\n this.store.revoke(slot, tag.generation);\n }\n }\n }\n\n private watch(slot: string, id: string, key: StateKeyFields | undefined, delta: 1 | -1): void\n {\n const count = (this.watched.get(slot)?.count ?? 0) + delta;\n\n if (count > 0)\n {\n this.watched.set(slot, { id, key, count });\n }\n else\n {\n this.watched.delete(slot);\n }\n\n this.queueSync();\n }\n\n /**\n * One stream for the client, its query the union of the subscribed keys.\n * Batched to the end of the task, so components mounting together (or a\n * StrictMode unmount-remount) open the stream once.\n */\n private queueSync(): void\n {\n if (!this.syncQueued)\n {\n this.syncQueued = true;\n queueMicrotask(() =>\n {\n this.syncQueued = false;\n this.sync();\n });\n }\n }\n\n /**\n * Until the manifest is known, subscriptions wait for the first snapshot;\n * the stream opens after it, never before — its `events=` comes from it.\n */\n private sync(): void\n {\n if (this.watched.size === 0)\n {\n this.closeStream();\n\n return;\n }\n\n if (!this.manifest)\n {\n this.awaitManifest();\n\n return;\n }\n\n const query = this.streamQuery();\n\n if (this.stream?.text !== stableKey(query))\n {\n this.openStream(query);\n }\n }\n\n /**\n * One waiter per lifetime on the first snapshot. A failure goes to\n * `onError`, and the read is retried after `retryDelay` — the stream that\n * would reconnect on its own does not exist yet. A retry stops on\n * `close()` or when nothing is watched (`sync` closes instead).\n */\n private awaitManifest(): void\n {\n const lifetime = this.lifetime;\n\n if (this.waitingLifetime === lifetime)\n {\n return;\n }\n\n this.waitingLifetime = lifetime;\n this.ensureManifest().then(\n () => lifetime === this.lifetime && this.resync(),\n error =>\n {\n this.reportError(error);\n\n if (lifetime === this.lifetime)\n {\n this.retryLater();\n }\n },\n );\n }\n\n /** The wait is over; `sync` opens the stream, or waits again when a closed lifetime's read was discarded. */\n private resync(): void\n {\n this.waitingLifetime = undefined;\n this.sync();\n }\n\n /** Read the first snapshot again after `retryDelay`, as the SSE client reconnects a dropped stream. */\n private retryLater(): void\n {\n this.retryTimer = setTimeout(() =>\n {\n this.retryTimer = undefined;\n this.resync();\n }, this.config.retryDelay ?? 3000);\n // A timer in Node has `unref`: a pending retry does not keep the process alive.\n (this.retryTimer as { unref?: () => void }).unref?.();\n }\n\n /**\n * Every subscribed key's fields; one connection carries one value per\n * field, so on a conflict the latest subscribed key wins. The keys it\n * shadows are read once when the stream opens and get no frames.\n */\n private streamQuery(): StateKeyFields\n {\n return Object.assign({}, ...[...this.watched.values()].map(watch => watch.key ?? {}));\n }\n\n /**\n * Open (or reopen) the stream. `events` is every state of the manifest, in\n * its order, never only the subscribed ones: a state subscribed later would\n * otherwise be unrequested, and the server's `grant` cannot activate it.\n */\n private openStream(query: StateKeyFields): void\n {\n this.closeStream();\n\n const ids = Object.keys(this.manifest!);\n const stream: Stream = { query, text: stableKey(query), generation: 0, close: () => undefined };\n stream.close = this.config.sse.subscribe({\n events: ids.map(id => `state.${id}.changed`),\n handlers: Object.fromEntries(ids.map(id => [`state.${id}.changed`, (frame: WireFrame) => this.onFrame(id, frame)])),\n query,\n // Every open — the first, a reconnect, a reopen on a key change — re-reads the\n // snapshot. A frame sent while no stream was open is lost, whether its key\n // changed or not; the snapshot read after the open covers that gap.\n onOpen: () =>\n {\n stream.generation = ++this.generation;\n this.refresh(query);\n },\n onSubscriptionChange: change => this.onSubscriptionChange(change, stream),\n });\n\n this.stream = stream;\n }\n\n private closeStream(): void\n {\n this.stream?.close();\n this.stream = undefined;\n }\n\n /** The snapshot for the stream's query, and one read for each subscribed key the query shadows. */\n private refresh(query: StateKeyFields): void\n {\n this.readSnapshot(query, this.tag()).catch(this.reportError);\n\n for (const watch of this.watched.values())\n {\n if (watch.key && !carries(query, watch.key))\n {\n this.readShadowed(watch.id, watch.key, this.tag()).catch(this.reportError);\n }\n }\n }\n\n /**\n * A shadowed key gets no frames, no grant and no snapshot entry; the\n * server answering its own read is the evidence of access instead.\n */\n private async readShadowed(id: string, key: StateKeyFields, tag: ReadTag): Promise<void>\n {\n const frame = await this.api[routeName('get', id)].call({ query: key }) as StateFrame;\n this.serve(this.slot(id, key), frame, tag);\n }\n\n /** A frame for a key nobody here subscribed is dropped — the server's filter is the first guard, this the second. */\n private onFrame(id: string, frame: WireFrame): void\n {\n const slot = this.slot(id, frame.key);\n\n if (this.watched.has(slot))\n {\n this.store.apply(slot, frame);\n }\n }\n\n /**\n * A revoke or a grant concerns the key the connection carries — the\n * server scopes both by the connect query — and so does the read after a\n * grant. Revoked: the entry keeps its value, marked `'revoked'`, until a\n * grant. Granted: the mark is cleared and the key read once; the server\n * sends a frame too, in either order, and both arrive `'ready'`.\n */\n private onSubscriptionChange(change: SSESubscriptionChange<any>, stream: Stream): void\n {\n const id = /^state\\.(.+)\\.changed$/.exec(change.event as string)?.[1];\n\n if (id === undefined || !this.manifest![id])\n {\n return;\n }\n\n const key = this.keyIn(id, stream.query);\n const slot = this.slot(id, key);\n\n if (change.status === 'revoked')\n {\n this.store.revoke(slot, stream.generation);\n\n return;\n }\n\n this.grants.set(slot, ++this.clock);\n this.store.allow(slot);\n\n if (this.watched.has(slot))\n {\n this.get(id, key).catch(this.reportError);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/state/client-store.ts","../../src/state/client.ts"],"names":["notify"],"mappings":"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","file":"client.js","sourcesContent":["/**\n * State Client Store\n *\n * One frozen entry per `(id, key)` and the ordering rule that decides whether\n * an arrival — a snapshot entry, a frame, a read or a write answer — replaces\n * it. An entry is replaced only when its status, value or data changes, so\n * `current()` returns the same object until then: `useSyncExternalStore`\n * compares by reference and would re-render forever on a fresh object per\n * call. A newer `at` alone advances the slot's ordering stamp, not the entry.\n */\n\nimport type { StateEntry, StateFrame, StateListener } from './client-types';\n\n/** Key fields, flat strings by construction (`.key()` refuses other fields). */\nexport type StateKeyFields = Record<string, string>;\n\n/** A key as text with its fields sorted, so equal keys written in any order name one entry. */\nexport function stableKey(key: StateKeyFields | undefined): string\n{\n if (!key)\n {\n return '';\n }\n\n return JSON.stringify(Object.keys(key).sort().map(field => [field, key[field]]));\n}\n\n/** The inverse of `stableKey`. */\nexport function parseKey(text: string): StateKeyFields | undefined\n{\n return text ? Object.fromEntries(JSON.parse(text) as [string, string][]) : undefined;\n}\n\n/** Deep equality of JSON values, which is all a frame carries. */\nfunction sameJson(a: unknown, b: unknown): boolean\n{\n if (a === b)\n {\n return true;\n }\n\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null || Array.isArray(a) !== Array.isArray(b))\n {\n return false;\n }\n\n const aKeys = Object.keys(a);\n const bRecord = b as Record<string, unknown>;\n\n return aKeys.length === Object.keys(b).length\n && aKeys.every(field => field in bRecord && sameJson((a as Record<string, unknown>)[field], bRecord[field]));\n}\n\n/**\n * The ordering rule for the value: an arrival older than the slot's stamp —\n * the newest `at` seen — is dropped. An equal `at` is by design the same\n * value (one read stamps one `at`), so it is not older and `changes` decides.\n */\nfunction isOlder(stamp: string | undefined, arrival: StateFrame): boolean\n{\n return stamp !== undefined && arrival.at < stamp;\n}\n\n/**\n * Whether an arrival says something the entry does not. An entry marked\n * revoked before any value arrived has no `at`: anything changes it.\n */\nfunction changes(current: StateEntry | undefined, arrival: StateFrame): boolean\n{\n return current?.at === undefined || !sameJson(current.value, arrival.value) || !sameJson(current.data, arrival.data);\n}\n\nfunction entryOf(frame: StateFrame, status: StateStatus): StateEntry\n{\n const { at, value, data } = frame;\n\n return Object.freeze(data === undefined ? { at, value, status } : { at, value, data, status });\n}\n\ntype StateStatus = StateEntry['status'];\n\nexport class StateStore\n{\n private readonly entries = new Map<string, StateEntry>();\n\n /**\n * The newest `at` seen per slot, kept apart from the entry: an arrival\n * that changes nothing still orders the ones after it, while the entry's\n * `at` stays the `at` of the read that last changed the value.\n */\n private readonly stamps = new Map<string, string>();\n\n private readonly listeners = new Map<string, Set<StateListener>>();\n\n /**\n * The slots the server stopped serving, with the generation of the\n * connection that said so. Kept apart from the entries: a revoke can\n * arrive before the slot has a value, and an arrival afterwards supplies\n * the value without clearing the mark.\n */\n private readonly revoked = new Map<string, number>();\n\n /** The store slot of `(id, key)`. */\n static slot(id: string, keyText: string): string\n {\n return `${id}\\u0000${keyText}`;\n }\n\n current(slot: string): StateEntry | undefined\n {\n return this.entries.get(slot);\n }\n\n /**\n * Apply an arrival: the value under the ordering rule, the status from the\n * revoked mark. Listeners hear only a changed status, value or data.\n */\n apply(slot: string, frame: StateFrame): void\n {\n const current = this.entries.get(slot);\n const status: StateStatus = this.revoked.has(slot) ? 'revoked' : 'ready';\n const ordered = !isOlder(this.stamps.get(slot), frame);\n\n if (ordered)\n {\n this.stamps.set(slot, frame.at);\n }\n\n if (ordered && changes(current, frame))\n {\n this.replace(slot, entryOf(frame, status));\n }\n else if (current!.status !== status)\n {\n // Not applied, so the current entry has a value: only its status changes.\n this.replace(slot, entryOf(current as StateFrame, status));\n }\n }\n\n /** Mark `slot` revoked by the connection of `generation`; its value, if any, is kept. */\n revoke(slot: string, generation: number): void\n {\n this.revoked.set(slot, generation);\n const entry = this.entries.get(slot);\n\n if (entry?.status !== 'revoked')\n {\n this.replace(slot, Object.freeze(entry ? { ...entry, status: 'revoked' as const } : { status: 'revoked' as const }));\n }\n }\n\n /** The generation of the connection that revoked `slot`, or `undefined` while it is served. */\n revokedBy(slot: string): number | undefined\n {\n return this.revoked.get(slot);\n }\n\n /** Clear the revoked mark; the next arrival for `slot` is `'ready'`. */\n allow(slot: string): void\n {\n this.revoked.delete(slot);\n }\n\n listen(slot: string, listener: StateListener): void\n {\n const set = this.listeners.get(slot) ?? new Set();\n set.add(listener);\n this.listeners.set(slot, set);\n }\n\n unlisten(slot: string, listener?: StateListener): void\n {\n const set = this.listeners.get(slot);\n\n if (listener)\n {\n set?.delete(listener);\n }\n\n if (!listener || set?.size === 0)\n {\n this.listeners.delete(slot);\n }\n }\n\n clearListeners(): void\n {\n this.listeners.clear();\n }\n\n /** Replace the entry and notify; a throwing listener is logged and does not stop the others. */\n private replace(slot: string, entry: StateEntry): void\n {\n this.entries.set(slot, entry);\n\n for (const listener of [...this.listeners.get(slot) ?? []])\n {\n notify(slot, listener);\n }\n }\n}\n\nfunction notify(slot: string, listener: StateListener): void\n{\n try\n {\n listener();\n }\n catch (error)\n {\n // The state id only: the key is the app's data.\n console.warn(`[state] A listener of state \"${slot.split('\\u0000')[0]}\" threw:`, error);\n }\n}\n","/**\n * State Client\n *\n * The browser side of a state router (`@spfn/core/state`): it calls the derived\n * read, write and snapshot routes, keeps one SSE stream open for every state of\n * the router, and orders snapshots against frames per `(id, key)`. It knows the\n * router by type — `typeof states` — and at runtime by the manifest every\n * snapshot carries; it imports no server code.\n *\n * @example\n * ```typescript\n * import { createStateClient } from '@spfn/core/state/client';\n * import { createAuthSSEClient } from '@spfn/core/event/sse/client';\n * import type { states } from '@/server/states';\n *\n * export const state = createStateClient<typeof states>({ sse: createAuthSSEClient() });\n *\n * const stop = state.subscribe('runtime', { workspaceId }, () => render(state.current('runtime', { workspaceId })));\n * ```\n */\n\nimport type { ConnectedPayload, SSESubscriptionChange } from '@spfn/core/event/sse';\nimport { createApi } from '@spfn/core/nextjs';\nimport { stableKey, StateStore, type StateKeyFields } from './client-store';\nimport type {\n AnyStateRouter,\n ConnectionListener,\n ConnectionStatus,\n StateClient,\n StateClientConfig,\n StateClientInitial,\n StateEntry,\n StateFrame,\n StateListener,\n} from './client-types';\n\nexport type {\n AnyStateRouter,\n ConnectionListener,\n ConnectionStatus,\n InferStateData,\n StateCatalog,\n StateFrameOf,\n InferStateInput,\n InferStateKey,\n InferStateValue,\n StateClient,\n StateClientConfig,\n StateClientInitial,\n StateEntry,\n StateFrame,\n StateId,\n StateKeyArgs,\n StateListener,\n WritableStateId,\n} from './client-types';\n\n/** A frame as the SSE event carries it. */\ntype WireFrame = StateFrame & { key?: StateKeyFields; subject?: string };\n\n/** Every state of the router, in declaration order: its key fields and whether it has a write. */\ntype Manifest = Record<string, { key: string[]; write: boolean }>;\n\ninterface Snapshot\n{\n at: string;\n states: Record<string, { value: unknown; data?: unknown }>;\n manifest: Manifest;\n}\n\ntype RouteCalls = Record<string, { call(input: object): Promise<unknown> }>;\n\n/** One `(id, key)` someone watches, and how many subscriptions hold it. */\ninterface Watch\n{\n id: string;\n key: StateKeyFields | undefined;\n count: number;\n}\n\n/**\n * The stream as one `sse.subscribe` opened it; `generation` names its current\n * connection. `closedByClient`: the client closed it itself, so its `onClose`\n * is no failure; `failed`: the SSE client gave up on it.\n */\ninterface Stream\n{\n query: StateKeyFields;\n text: string;\n generation: number;\n closedByClient: boolean;\n failed: boolean;\n close: () => void;\n}\n\n/** When an automatic read was started: the connection generation and the client's clock. */\ninterface ReadTag\n{\n generation: number;\n tick: number;\n}\n\nconst CONNECTED: ConnectionStatus = Object.freeze({ status: 'connected' });\nconst FAILED: ConnectionStatus = Object.freeze({ status: 'failed' });\nconst STARTING: ConnectionStatus = Object.freeze({ status: 'reconnecting', attempt: 0 });\n\nfunction reconnecting(attempt: number): ConnectionStatus\n{\n return attempt === 0 ? STARTING : Object.freeze({ status: 'reconnecting', attempt });\n}\n\nfunction sameStatus(a: ConnectionStatus, b: ConnectionStatus): boolean\n{\n return a.status === b.status && (a as { attempt?: number }).attempt === (b as { attempt?: number }).attempt;\n}\n\n/** `get<Id>State` / `put<Id>State`, the names the server router derives. */\nfunction routeName(method: 'get' | 'put', id: string): string\n{\n return `${method}${id[0].toUpperCase()}${id.slice(1)}State`;\n}\n\n/** The wire name of a state's SSE event, as the server derives it. */\nfunction stateEventName(id: string): string\n{\n return `state.${id}.changed`;\n}\n\n/** Whether `query` carries every field of `key` with its value. */\nfunction carries(query: StateKeyFields, key: StateKeyFields): boolean\n{\n return Object.entries(key).every(([field, value]) => query[field] === value);\n}\n\nfunction sortedFields(fields: string[]): string\n{\n return [...fields].sort().join(', ');\n}\n\n/**\n * Build the client for `typeof states`. The first subscription (or call)\n * reads the snapshot, whose manifest names every state — or takes it from\n * `initial`, the snapshot the server rendered with; the stream then\n * requests every state event — the server's `grant` can only activate an\n * event the connection requested. The router type parameter is what types\n * `initial.snapshot`; without it the client is untyped, as before.\n */\nexport function createStateClient<TStates extends AnyStateRouter>(config: StateClientConfig<TStates>): StateClient<TStates>\n{\n return new BrowserStateClient(config) as unknown as StateClient<TStates>;\n}\n\nclass BrowserStateClient\n{\n private readonly store = new StateStore();\n private readonly api: RouteCalls;\n private readonly watched = new Map<string, Watch>();\n private readonly reportError: (error: unknown) => void;\n private manifest: Manifest | undefined;\n private manifestRead: Promise<void> | undefined;\n private stream: Stream | undefined;\n private syncQueued = false;\n\n private status = STARTING;\n private readonly connectionListeners = new Set<ConnectionListener>();\n private readonly onOnline = (): void => this.retry();\n\n /** The retries of the first snapshot since its last start, against `maxRetries`. */\n private retries = 0;\n\n /** The lifetime whose subscriptions wait on the first snapshot — its read in flight, or its retry timer set. */\n private waitingLifetime: number | undefined;\n private retryTimer: ReturnType<typeof setTimeout> | undefined;\n\n /** Bumped by `close()`: a snapshot started before it is discarded. */\n private lifetime = 0;\n\n /** Bumped on every connection open; tags the snapshot read for it. */\n private generation = 0;\n\n /** Bumped on every automatic read and every grant, to tell which came first. */\n private clock = 0;\n\n /** The clock at the latest grant of each slot. */\n private readonly grants = new Map<string, number>();\n\n constructor(private readonly config: StateClientConfig)\n {\n this.api = createApi<any>(config.api) as unknown as RouteCalls;\n this.reportError = config.onError ?? (error => console.error('[state] A refresh failed:', error));\n\n if (config.initial)\n {\n this.hydrate(config.initial);\n }\n\n // Absent in Node and in a server render.\n if (typeof globalThis.addEventListener === 'function')\n {\n globalThis.addEventListener('online', this.onOnline);\n }\n }\n\n async get(id: string, key?: StateKeyFields): Promise<StateFrame>\n {\n await this.requireManifest();\n const frame = await this.api[routeName('get', id)].call(key ? { query: key } : {}) as StateFrame;\n this.store.apply(this.slot(id, key), frame);\n\n return frame;\n }\n\n async write(id: string, input: unknown, key?: StateKeyFields): Promise<StateFrame>\n {\n if (!(await this.requireManifest())[id]?.write)\n {\n throw new Error(`State client: state \"${id}\" declares no .write(), so it has no write route.`);\n }\n\n const frame = await this.api[routeName('put', id)].call({ body: input, ...(key ? { query: key } : {}) }) as StateFrame;\n this.store.apply(this.slot(id, key), frame);\n\n return frame;\n }\n\n snapshot(query: StateKeyFields = this.streamQuery()): Promise<Snapshot>\n {\n return this.readSnapshot(query, this.tag());\n }\n\n subscribe(id: string, ...args: [StateListener] | [StateKeyFields, StateListener]): () => void\n {\n const listener = args[args.length - 1] as StateListener;\n const key = args.length === 2 ? args[0] as StateKeyFields : undefined;\n const refusal = this.manifest && this.refusal(id, key);\n\n if (refusal)\n {\n this.reportError(refusal);\n\n return () => undefined;\n }\n\n // One wrapper per subscription: the same listener subscribed twice is two subscriptions.\n const notify = () => listener();\n const slot = this.slot(id, key);\n this.store.listen(slot, notify);\n this.watch(slot, id, key, 1);\n let live = true;\n\n return () =>\n {\n if (live)\n {\n live = false;\n this.store.unlisten(slot, notify);\n this.watch(slot, id, key, -1);\n }\n };\n }\n\n current(id: string, key?: StateKeyFields): StateEntry | undefined\n {\n return this.store.current(this.slot(id, key));\n }\n\n connection(): ConnectionStatus\n {\n return this.status;\n }\n\n subscribeConnection(listener: ConnectionListener): () => void\n {\n // One wrapper per subscription: the same listener subscribed twice is two subscriptions.\n const notify: ConnectionListener = status => listener(status);\n this.connectionListeners.add(notify);\n\n return () =>\n {\n this.connectionListeners.delete(notify);\n };\n }\n\n /**\n * Restart what gave up or is still retrying, now: the first snapshot\n * while the manifest is unknown, else the stream — a new `sse.subscribe`,\n * whose attempt count starts at 0. Nothing to do when connected or with\n * nothing watched — so after `close()`, until a new `subscribe`.\n */\n retry(): void\n {\n if (this.status === CONNECTED || this.watched.size === 0)\n {\n return;\n }\n\n if (this.manifest)\n {\n this.openStream(this.streamQuery());\n }\n else\n {\n this.rereadManifest();\n }\n }\n\n close(): void\n {\n this.lifetime++;\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n this.retries = 0;\n this.closeStream();\n this.watched.clear();\n this.store.clearListeners();\n this.connectionListeners.clear();\n this.status = STARTING;\n\n if (typeof globalThis.removeEventListener === 'function')\n {\n globalThis.removeEventListener('online', this.onOnline);\n }\n }\n\n /** The status is `next` from now; each connection listener hears it, a throwing one logged. */\n private setConnection(next: ConnectionStatus): void\n {\n if (sameStatus(next, this.status))\n {\n return;\n }\n\n this.status = next;\n\n for (const listener of [...this.connectionListeners])\n {\n try\n {\n listener(next);\n }\n catch (error)\n {\n console.warn('[state] A connection listener threw:', error);\n }\n }\n }\n\n /**\n * Start from the snapshot the server rendered with: its manifest, then\n * each state present, as a read snapshot applies them, under generation 0\n * — no connection yet. Nothing is marked `'revoked'`: nothing is watched,\n * and an omission in the render's snapshot is not a denial by the stream.\n */\n private hydrate(initial: StateClientInitial): void\n {\n const snapshot = initial.snapshot as Snapshot;\n this.installManifest(snapshot.manifest);\n this.applyPresent(snapshot, initial.query ?? {}, this.tag());\n }\n\n private slot(id: string, key: StateKeyFields | undefined): string\n {\n return StateStore.slot(id, stableKey(key));\n }\n\n private tag(): ReadTag\n {\n return { generation: this.generation, tick: ++this.clock };\n }\n\n /** The key of state `id` the query names; `undefined` for a keyless state. */\n private keyIn(id: string, query: StateKeyFields): StateKeyFields | undefined\n {\n const fields = this.manifest![id].key;\n\n return fields.length ? Object.fromEntries(fields.map(field => [field, query[field]])) : undefined;\n }\n\n /** Why the manifest refuses `subscribe(id, key)` — an unknown id, or key fields not the state's. */\n private refusal(id: string, key: StateKeyFields | undefined): Error | undefined\n {\n const state = this.manifest![id];\n\n if (!state)\n {\n return new Error(`State client: the server's manifest has no state \"${id}\".`);\n }\n\n const expected = sortedFields(state.key);\n const given = sortedFields(Object.keys(key ?? {}));\n\n return expected === given\n ? undefined\n : new Error(`State client: state \"${id}\" takes the key fields [${expected}], given [${given}].`);\n }\n\n /** The first snapshot, read once however many callers wait for it. */\n private ensureManifest(): Promise<void>\n {\n if (this.manifest)\n {\n return Promise.resolve();\n }\n\n this.manifestRead ??= this.snapshot()\n .then(() => undefined)\n .finally(() =>\n {\n this.manifestRead = undefined;\n });\n\n return this.manifestRead;\n }\n\n /** The manifest for a call the app made; one whose wait outlived `close()` is refused. */\n private async requireManifest(): Promise<Manifest>\n {\n await this.ensureManifest();\n\n if (!this.manifest)\n {\n throw new Error('State client: the client was closed while the first snapshot was in flight.');\n }\n\n return this.manifest;\n }\n\n /**\n * Read the snapshot and apply it: the manifest first — `keyIn` and the\n * key checks need it — then each state present, then each watched state\n * it omits. A snapshot that resolves after `close()` is discarded.\n */\n private async readSnapshot(query: StateKeyFields, tag: ReadTag): Promise<Snapshot>\n {\n const lifetime = this.lifetime;\n const snapshot = await this.api.getStates.call({ query }) as Snapshot;\n\n if (lifetime === this.lifetime)\n {\n this.installManifest(snapshot.manifest);\n this.applyPresent(snapshot, query, tag);\n this.applyOmitted(snapshot, query, tag);\n }\n\n return snapshot;\n }\n\n /**\n * The manifest is static; the first one stays. Subscriptions made before\n * it are checked now, and the stream opens for those it accepts —\n * whichever call (`subscribe`, `get`, `write`, `snapshot`) brought it.\n */\n private installManifest(manifest: Manifest): void\n {\n if (this.manifest)\n {\n return;\n }\n\n this.manifest = manifest;\n\n for (const [slot, watch] of this.watched)\n {\n const refusal = this.refusal(watch.id, watch.key);\n\n if (refusal)\n {\n this.watched.delete(slot);\n this.store.unlisten(slot);\n this.reportError(refusal);\n }\n }\n\n if (this.watched.size > 0)\n {\n this.queueSync();\n }\n }\n\n /**\n * An arrival from a read started for the connection of `tag`: a\n * connection newer than the one that revoked the slot lifts the mark, an\n * older or the same connection leaves `'revoked'`. The lift is by\n * generation alone — a frame or a write answer of the new connection may\n * land first with a newer `at` — and the ordering rule decides only the value.\n */\n private serve(slot: string, frame: StateFrame, generation: number): void\n {\n if ((this.store.revokedBy(slot) ?? Infinity) < generation)\n {\n this.store.allow(slot);\n }\n\n this.store.apply(slot, frame);\n }\n\n /**\n * A state present is served, under the generation rule of `serve`. A state\n * the manifest does not describe, or a keyed one whose key fields the query\n * lacks, is skipped: the server never serves either, but a hand-built\n * `initial` might.\n */\n private applyPresent(snapshot: Snapshot, query: StateKeyFields, tag: ReadTag): void\n {\n for (const [id, result] of Object.entries(snapshot.states))\n {\n if (this.manifest![id]?.key.every(field => field in query))\n {\n this.serve(this.slot(id, this.keyIn(id, query)), { at: snapshot.at, ...result }, tag.generation);\n }\n }\n }\n\n /**\n * A watched state whose key the query carried (or a keyless one) and that\n * the snapshot omits is not served to this subject: `'revoked'`. Not when\n * a newer connection exists, nor when a grant came after the read started.\n */\n private applyOmitted(snapshot: Snapshot, query: StateKeyFields, tag: ReadTag): void\n {\n if (tag.generation !== this.generation)\n {\n return;\n }\n\n for (const [slot, watch] of this.watched)\n {\n const asked = !watch.key || carries(query, watch.key);\n const grantedSince = (this.grants.get(slot) ?? 0) > tag.tick;\n\n if (asked && !(watch.id in snapshot.states) && !grantedSince)\n {\n this.store.revoke(slot, tag.generation);\n }\n }\n }\n\n private watch(slot: string, id: string, key: StateKeyFields | undefined, delta: 1 | -1): void\n {\n const count = (this.watched.get(slot)?.count ?? 0) + delta;\n\n if (count > 0)\n {\n this.watched.set(slot, { id, key, count });\n }\n else\n {\n this.watched.delete(slot);\n }\n\n // A removal that leaves subscribers keeps a failed client failed; the last removal closes and resets.\n if (delta === 1 || this.watched.size === 0 || this.status !== FAILED)\n {\n this.queueSync();\n }\n }\n\n /**\n * One stream for the client, its query the union of the subscribed keys.\n * Batched to the end of the task, so components mounting together (or a\n * StrictMode unmount-remount) open the stream once.\n */\n private queueSync(): void\n {\n if (!this.syncQueued)\n {\n this.syncQueued = true;\n queueMicrotask(() =>\n {\n this.syncQueued = false;\n this.sync();\n });\n }\n }\n\n /**\n * Until the manifest is known, subscriptions wait for the first snapshot;\n * the stream opens after it, never before — its `events=` comes from it.\n * A client built with `initial` knows it from the start. With nothing\n * watched the stream closes, the retry count starts over for the next\n * wait, and the status is the start value: no stream, nothing answered.\n */\n private sync(): void\n {\n if (this.watched.size === 0)\n {\n this.retries = 0;\n this.closeStream();\n this.setConnection(STARTING);\n\n return;\n }\n\n if (!this.manifest)\n {\n this.awaitManifest();\n\n return;\n }\n\n // A stream the SSE client gave up on counts as absent.\n const query = this.streamQuery();\n\n if (this.stream?.failed || this.stream?.text !== stableKey(query))\n {\n this.openStream(query);\n }\n }\n\n /**\n * One waiter per lifetime on the first snapshot. A failure goes to\n * `onError`, and the read is retried after `retryDelay` — the stream that\n * would reconnect on its own does not exist yet. A retry stops on\n * `close()`, when nothing is watched (`sync` closes instead), or after\n * `maxRetries`; a wait started after that last one restarts the count.\n */\n private awaitManifest(): void\n {\n const lifetime = this.lifetime;\n\n if (this.waitingLifetime === lifetime)\n {\n return;\n }\n\n if (this.status === FAILED)\n {\n this.retries = 0;\n this.setConnection(STARTING);\n }\n\n this.waitingLifetime = lifetime;\n this.ensureManifest().then(\n () => lifetime === this.lifetime && this.resync(),\n error =>\n {\n this.reportError(error);\n\n if (lifetime === this.lifetime)\n {\n this.retryLater();\n }\n },\n );\n }\n\n /** The wait is over; `sync` opens the stream, or waits again when a closed lifetime's read was discarded. */\n private resync(): void\n {\n this.waitingLifetime = undefined;\n this.sync();\n }\n\n /**\n * Read the first snapshot again after `retryDelay`, as the SSE client\n * reconnects a dropped stream — or, with `maxRetries` spent, give up:\n * `'failed'`, no timer, no wait.\n */\n private retryLater(): void\n {\n const maxRetries = this.config.maxRetries ?? 0;\n\n if (maxRetries > 0 && this.retries >= maxRetries)\n {\n this.waitingLifetime = undefined;\n this.setConnection(FAILED);\n\n return;\n }\n\n this.retries++;\n this.retryTimer = setTimeout(() =>\n {\n this.retryTimer = undefined;\n this.resync();\n }, this.config.retryDelay ?? 3000);\n // A timer in Node has `unref`: a pending retry does not keep the process alive.\n (this.retryTimer as { unref?: () => void }).unref?.();\n }\n\n /**\n * `retry()` before the manifest: the count starts over and the first\n * snapshot is read now, its pending timer cleared. A read in flight is\n * already the retry; it is not doubled.\n */\n private rereadManifest(): void\n {\n this.retries = 0;\n\n if (this.waitingLifetime === this.lifetime && this.retryTimer === undefined)\n {\n return;\n }\n\n clearTimeout(this.retryTimer);\n this.retryTimer = undefined;\n this.setConnection(STARTING);\n this.resync();\n }\n\n /**\n * Every subscribed key's fields; one connection carries one value per\n * field, so on a conflict the latest subscribed key wins. The keys it\n * shadows are read once when the stream opens and get no frames.\n */\n private streamQuery(): StateKeyFields\n {\n return Object.assign({}, ...[...this.watched.values()].map(watch => watch.key ?? {}));\n }\n\n /**\n * Open (or reopen) the stream. `events` is every state of the manifest, in\n * its order, never only the subscribed ones: a state subscribed later would\n * otherwise be unrequested, and the server's `grant` cannot activate it.\n * Unless connected, the status is `'reconnecting'` 0 until it opens: a\n * new subscription counts its attempts from 0. A reopen on a key change\n * while connected stays `'connected'`.\n */\n private openStream(query: StateKeyFields): void\n {\n this.closeStream();\n\n if (this.status !== CONNECTED)\n {\n this.setConnection(STARTING);\n }\n\n const ids = Object.keys(this.manifest!);\n const stream: Stream = { query, text: stableKey(query), generation: 0, closedByClient: false, failed: false, close: () => undefined };\n stream.close = this.config.sse.subscribe({\n events: ids.map(stateEventName),\n handlers: Object.fromEntries(ids.map(id => [stateEventName(id), (frame: WireFrame) => this.onFrame(id, frame, stream)])),\n query,\n onOpen: () =>\n {\n stream.generation = ++this.generation;\n this.streamStatus(stream, CONNECTED);\n },\n onReconnect: attempt => this.streamStatus(stream, reconnecting(attempt)),\n onClose: () => this.onStreamClose(stream),\n onConnected: connected => this.onConnected(connected, stream),\n onSubscriptionChange: change => this.onSubscriptionChange(change, stream),\n });\n\n this.stream = stream;\n }\n\n /** Flagged first: the SSE client fires `onClose` on this teardown too, and it is no failure. */\n private closeStream(): void\n {\n if (this.stream)\n {\n this.stream.closedByClient = true;\n this.stream.close();\n this.stream = undefined;\n }\n }\n\n /** The status of a callback of `stream`, when no other stream replaced it. */\n private streamStatus(stream: Stream, status: ConnectionStatus): void\n {\n if (stream === this.stream)\n {\n this.setConnection(status);\n }\n }\n\n /**\n * The SSE client fires `onClose` on every teardown; one the client did\n * not start is the reconnect attempts exhausted: `'failed'`, and the\n * stream counts as absent for the next `sync`.\n */\n private onStreamClose(stream: Stream): void\n {\n if (!stream.closedByClient && stream === this.stream)\n {\n stream.failed = true;\n this.setConnection(FAILED);\n }\n }\n\n /**\n * Every connection — the first, a reconnect, a reopen on a key change —\n * must cover the frames sent while no stream was open. The server says in\n * `connected` which states it served their current frame right after it;\n * the snapshot is read only when one it did not serve is neither denied\n * nor served — or when the server predates `initial`. A denied state the\n * client watches is `'revoked'`, as an omission from that snapshot would\n * be. A connection already replaced is ignored.\n */\n private onConnected(connected: ConnectedPayload, stream: Stream): void\n {\n if (stream !== this.stream || stream.generation !== this.generation)\n {\n return;\n }\n\n const denied = new Set(connected.deniedEvents);\n const served = new Set(connected.initial ?? []);\n const unserved = Object.keys(this.manifest!).map(stateEventName).filter(name => !denied.has(name) && !served.has(name));\n\n this.revokeDenied(denied, stream);\n\n if (connected.initial && unserved.length === 0)\n {\n this.readShadowedKeys(stream.query);\n }\n else\n {\n this.refresh(stream.query);\n }\n }\n\n /** Each watched state the connection denies, on the key it carries, marked `'revoked'` by it. */\n private revokeDenied(denied: Set<string>, stream: Stream): void\n {\n for (const id of Object.keys(this.manifest!))\n {\n const slot = this.slot(id, this.keyIn(id, stream.query));\n\n if (denied.has(stateEventName(id)) && this.watched.has(slot))\n {\n this.store.revoke(slot, stream.generation);\n }\n }\n }\n\n /** The snapshot for the stream's query, and one read for each subscribed key the query shadows. */\n private refresh(query: StateKeyFields): void\n {\n this.readSnapshot(query, this.tag()).catch(this.reportError);\n this.readShadowedKeys(query);\n }\n\n /** One read for each subscribed key the query shadows: the stream serves one key per field. */\n private readShadowedKeys(query: StateKeyFields): void\n {\n for (const watch of this.watched.values())\n {\n if (watch.key && !carries(query, watch.key))\n {\n this.readShadowed(watch.id, watch.key, this.tag()).catch(this.reportError);\n }\n }\n }\n\n /**\n * A shadowed key gets no frames, no grant and no snapshot entry; the\n * server answering its own read is the evidence of access instead.\n */\n private async readShadowed(id: string, key: StateKeyFields, tag: ReadTag): Promise<void>\n {\n const frame = await this.api[routeName('get', id)].call({ query: key }) as StateFrame;\n this.serve(this.slot(id, key), frame, tag.generation);\n }\n\n /**\n * A frame for a key nobody here subscribed is dropped — the server's filter\n * is the first guard, this the second. A frame is served under its\n * connection's generation: the server sends one only on a connection that\n * allows the state, so a newer connection's frame lifts an older revoke.\n */\n private onFrame(id: string, frame: WireFrame, stream: Stream): void\n {\n const slot = this.slot(id, frame.key);\n\n if (this.watched.has(slot))\n {\n this.serve(slot, frame, stream.generation);\n }\n }\n\n /**\n * A revoke or a grant concerns the key the connection carries — the\n * server scopes both by the connect query — and so does the read after a\n * grant. Revoked: the entry keeps its value, marked `'revoked'`, until a\n * grant. Granted: the mark is cleared and the key read once; the server\n * sends a frame too, in either order, and both arrive `'ready'`.\n */\n private onSubscriptionChange(change: SSESubscriptionChange<any>, stream: Stream): void\n {\n const id = /^state\\.(.+)\\.changed$/.exec(change.event as string)?.[1];\n\n if (id === undefined || !this.manifest![id])\n {\n return;\n }\n\n const key = this.keyIn(id, stream.query);\n const slot = this.slot(id, key);\n\n if (change.status === 'revoked')\n {\n this.store.revoke(slot, stream.generation);\n\n return;\n }\n\n this.grants.set(slot, ++this.clock);\n this.store.allow(slot);\n\n if (this.watched.has(slot))\n {\n this.get(id, key).catch(this.reportError);\n }\n }\n}\n"]}
@@ -21,6 +21,8 @@ interface StateShape {
21
21
  data: TSchema | undefined;
22
22
  input: TSchema | undefined;
23
23
  read: boolean;
24
+ /** Who the callbacks' `principal` is, from `.principal<P>()`; `unknown` until declared. */
25
+ principal: unknown;
24
26
  }
25
27
  /** The shape of `defineState(id)` before any call. */
26
28
  interface EmptyStateShape extends StateShape {
@@ -30,6 +32,7 @@ interface EmptyStateShape extends StateShape {
30
32
  data: undefined;
31
33
  input: undefined;
32
34
  read: false;
35
+ principal: unknown;
33
36
  }
34
37
  /** `T` with the fields of `U` replaced. */
35
38
  type WithShape<T extends StateShape, U extends Partial<StateShape>> = {
@@ -57,22 +60,38 @@ type StateResult<T extends StateShape> = T['data'] extends TSchema ? {
57
60
  type StateSnapshot<T extends StateShape> = Simplify<{
58
61
  at: string;
59
62
  } & StateResult<T>>;
60
- /** What `.canRead()` / `.canWrite()` decide on. */
63
+ /**
64
+ * What `.canRead()` decides on. `principal` may be `undefined`: at SSE connect
65
+ * it is what `resolvePrincipal` returned.
66
+ */
61
67
  type PermissionContext<T extends StateShape> = T['key'] extends TObject ? {
62
68
  subject: string;
63
- principal: unknown;
69
+ principal: T['principal'] | undefined;
64
70
  key: Static<T['key']>;
65
71
  } : {
66
72
  subject: string;
67
- principal: unknown;
73
+ principal: T['principal'] | undefined;
68
74
  };
69
- /** What `read()` receives. A shared state's value does not depend on the subject, so it gets none. */
75
+ /**
76
+ * What `.canWrite()` decides on. Only the write route asks it, and with
77
+ * `.principal()` declared that route refuses a request whose `auth` is
78
+ * `undefined` or `null` (401).
79
+ */
80
+ type WritePermissionContext<T extends StateShape> = Simplify<Omit<PermissionContext<T>, 'principal'> & {
81
+ principal: T['principal'];
82
+ }>;
83
+ /**
84
+ * What `read()` receives. A shared state's value does not depend on the
85
+ * subject, so it gets none. `principal` may be `undefined`: the change
86
+ * signal's re-read has no request.
87
+ */
70
88
  type ReadContext<T extends StateShape> = T['key'] extends TObject ? T['shared'] extends true ? {
71
- principal: unknown;
89
+ principal: T['principal'] | undefined;
72
90
  key: Static<T['key']>;
73
91
  } : PermissionContext<T> : PermissionContext<T>;
74
- /** What `write()` receives: the read context plus the validated input. */
75
- type WriteContext<T extends StateShape, I extends TSchema> = Simplify<ReadContext<T> & {
92
+ /** What `write()` receives: the read context, the write route's principal and the validated input. */
93
+ type WriteContext<T extends StateShape, I extends TSchema> = Simplify<Omit<ReadContext<T>, 'principal'> & {
94
+ principal: T['principal'];
76
95
  input: Static<I>;
77
96
  }>;
78
97
  /** The payload of `state.changed.emit()`: exactly what names the value to re-read. */
@@ -137,6 +156,8 @@ interface StateDefinition {
137
156
  id: string;
138
157
  key?: TObject;
139
158
  shared: boolean;
159
+ /** Whether `.principal()` was declared: the write route then refuses a request whose `auth` is `undefined` or `null`. */
160
+ principal: boolean;
140
161
  value?: TSchema;
141
162
  data?: TSchema;
142
163
  middlewares: (MiddlewareHandler | NamedMiddleware<string>)[];
@@ -204,6 +225,15 @@ declare class StateBuilder<TId extends string, T extends StateShape> {
204
225
  data<D extends TSchema>(schema: D): StateBuilder<TId, WithShape<T, {
205
226
  data: D;
206
227
  }>>;
228
+ /**
229
+ * Declare who the callbacks' `principal` is — what the app's auth
230
+ * middleware sets as `c.get('auth')`. Type-only, but for one rule: the write
231
+ * route refuses a request without a principal (401), so `.canWrite()` and
232
+ * `.write()` receive `P`; `.read()` and `.canRead()` receive `P | undefined`.
233
+ */
234
+ principal<P>(): StateBuilder<TId, WithShape<T, {
235
+ principal: P;
236
+ }>>;
207
237
  /**
208
238
  * Middleware for the two per-state routes, `get<Id>State` / `put<Id>State`.
209
239
  * Not authorization: the snapshot and the SSE stream cannot run it, so a
@@ -217,7 +247,7 @@ declare class StateBuilder<TId extends string, T extends StateShape> {
217
247
  /** Who may read. Required on a keyed state; checked on HTTP and at SSE connect. */
218
248
  canRead(predicate: (ctx: PermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>;
219
249
  /** Who may write. Absent → `canRead` decides. */
220
- canWrite(predicate: (ctx: PermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>;
250
+ canWrite(predicate: (ctx: WritePermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>;
221
251
  /** The only source of a value. Required. */
222
252
  read(fn: (ctx: ReadContext<T>) => StateResult<T> | Promise<StateResult<T>>): StateBuilder<TId, WithShape<T, {
223
253
  read: true;
@@ -294,6 +324,19 @@ declare function defineState<TId extends string>(id: TId): StateBuilder<TId, Emp
294
324
 
295
325
  /** Who a request is for. `null` answers 401. */
296
326
  type SubjectResolver = (c: Context) => string | null;
327
+ /**
328
+ * What the router gives the snapshot route `GET <path>` alone. A per-state
329
+ * route keeps its own state's `.use()` / `.skip()` / `.contract()`, and the
330
+ * SSE stream runs none of them.
331
+ */
332
+ interface SnapshotRouteOptions {
333
+ /** Middleware for the snapshot route only. Not authorization: the stream never runs it (see README). */
334
+ use?: (MiddlewareHandler | NamedMiddleware<string>)[];
335
+ /** Server-level named middlewares the snapshot route skips; `'*'` skips every one. */
336
+ skip?: string[] | '*';
337
+ /** Publishes the snapshot route in the contract; the response schema is derived. */
338
+ contract?: StateContract;
339
+ }
297
340
  /** What the browser cannot read from a type: every state, in declaration order, its key fields and whether it has a write. */
298
341
  type StatesManifest = Record<string, {
299
342
  key: string[];
@@ -308,7 +351,12 @@ type StatesManifest = Record<string, {
308
351
  * subscribes each state's change signal — once, here.
309
352
  */
310
353
 
311
- interface StateRouterOptions {
354
+ /**
355
+ * Where the router mounts, whom a request is for, and the snapshot route's own
356
+ * `use` / `skip` / `contract` (`SnapshotRouteOptions`) — those reach `GET <path>`
357
+ * only, never a per-state route or the SSE stream.
358
+ */
359
+ interface StateRouterOptions extends SnapshotRouteOptions {
312
360
  /**
313
361
  * Where the routes are mounted: `GET <path>`, `GET|PUT <path>/<id>`.
314
362
  * @default '/state'
@@ -392,4 +440,4 @@ type InferStateInput<R extends StateRouter<any>, TId extends string> = Shape<Sta
392
440
  */
393
441
  declare function defineStateRouter<S extends Record<string, RoutableState>>(states: S, options?: StateRouterOptions): StateRouter<S>;
394
442
 
395
- export { type ChangedPayload, type GrantTarget, type InferStateInput, type InferStateKey, type InferStateValue, type PermissionContext, type ReadContext, type RevokeTarget, type RoutableState, StateBuilder, type StateContract, type StateEvents, type StateFrame, type StateKey, type StateResult, type StateRouter, type StateRouterOptions, type StateRoutes, type StateShape, type StateSnapshot, type StateValue, type StateWriteOptions, type StatesManifest, type StatesSnapshot, type SubjectResolver, type WriteContext, defineState, defineStateRouter };
443
+ export { type ChangedPayload, type GrantTarget, type InferStateInput, type InferStateKey, type InferStateValue, type PermissionContext, type ReadContext, type RevokeTarget, type RoutableState, type SnapshotRouteOptions, StateBuilder, type StateContract, type StateEvents, type StateFrame, type StateKey, type StateResult, type StateRouter, type StateRouterOptions, type StateRoutes, type StateShape, type StateSnapshot, type StateValue, type StateWriteOptions, type StatesManifest, type StatesSnapshot, type SubjectResolver, type WriteContext, type WritePermissionContext, defineState, defineStateRouter };
@@ -1,5 +1,6 @@
1
1
  import { Type, Kind } from '@sinclair/typebox';
2
2
  import { Value } from '@sinclair/typebox/value';
3
+ import { getTransactionContext, onAfterCommit } from '@spfn/core/db';
3
4
  import { defineEvent } from '@spfn/core/event';
4
5
  import { logger } from '@spfn/core/logger';
5
6
  import { defineRouter, route } from '@spfn/core/route';
@@ -83,7 +84,12 @@ function toFrame(def, snapshot, subject, key) {
83
84
  }
84
85
  async function readAndPublish(def, event, subject, key, principal) {
85
86
  const snapshot = await readSnapshot(def, readContext(def, subject, principal, key));
86
- await event.emit(toFrame(def, snapshot, subject, key));
87
+ const frame = toFrame(def, snapshot, subject, key);
88
+ if (getTransactionContext()) {
89
+ onAfterCommit(() => event.emit(frame));
90
+ } else {
91
+ await event.emit(frame);
92
+ }
87
93
  return snapshot;
88
94
  }
89
95
  function framePayloadSchema(def) {
@@ -108,8 +114,18 @@ function framePredicate(def) {
108
114
  const fields = def.key ? Object.keys(def.key.properties) : [];
109
115
  return (payload, { subject, request }) => subject !== void 0 && fields.every((field) => payload.key?.[field] === request.query[field]) && (payload.subject === void 0 || payload.subject === subject);
110
116
  }
117
+ function initialProducer(def) {
118
+ return async ({ subject, principal, request }) => {
119
+ const key = def.key ? pickKey(def.key, request.query) : void 0;
120
+ if (def.key && !key) {
121
+ return [];
122
+ }
123
+ const snapshot = await readSnapshot(def, readContext(def, subject, principal, key));
124
+ return [toFrame(def, snapshot, subject, key)];
125
+ };
126
+ }
111
127
  function createStateEvent(def) {
112
- const event = defineEvent(stateEventName(def.id), framePayloadSchema(def)).canSubscribe(subscribePredicate(def)).filter(framePredicate(def));
128
+ const event = defineEvent(stateEventName(def.id), framePayloadSchema(def)).canSubscribe(subscribePredicate(def)).filter(framePredicate(def)).initial(initialProducer(def));
113
129
  if (def.contract) {
114
130
  const { since, deprecatedIn, removedIn } = def.contract;
115
131
  event.contract({
@@ -154,7 +170,7 @@ async function onSignal(state, payload) {
154
170
  try {
155
171
  await readAndPublish(state._definition, state._event, subject, key, void 0);
156
172
  } catch (error) {
157
- stateLogger.warn(`State "${state.id}" could not be re-read on its change signal; no frame was sent`, {
173
+ stateLogger.warn(`State "${state.id}" could not be re-read or published on its change signal; no frame was sent`, {
158
174
  state: state.id,
159
175
  error: error instanceof Error ? error.message : String(error)
160
176
  });
@@ -207,6 +223,16 @@ var StateBuilder = class _StateBuilder {
207
223
  this.assertOnce("data", !!this._definition.data);
208
224
  return this.with({ data: schema });
209
225
  }
226
+ /**
227
+ * Declare who the callbacks' `principal` is — what the app's auth
228
+ * middleware sets as `c.get('auth')`. Type-only, but for one rule: the write
229
+ * route refuses a request without a principal (401), so `.canWrite()` and
230
+ * `.write()` receive `P`; `.read()` and `.canRead()` receive `P | undefined`.
231
+ */
232
+ principal() {
233
+ this.assertOnce("principal", this._definition.principal);
234
+ return this.with({ principal: true });
235
+ }
210
236
  /**
211
237
  * Middleware for the two per-state routes, `get<Id>State` / `put<Id>State`.
212
238
  * Not authorization: the snapshot and the SSE stream cannot run it, so a
@@ -371,7 +397,7 @@ function defineState(id) {
371
397
  `defineState("${id}"): a state id must match ${STATE_ID}. It becomes part of the route names get<Id>State / put<Id>State and of the event name state.<id>.changed. Use a lowerCamel identifier, e.g. "myState".`
372
398
  );
373
399
  }
374
- return new StateBuilder(id, { id, shared: false, middlewares: [] });
400
+ return new StateBuilder(id, { id, shared: false, principal: false, middlewares: [] });
375
401
  }
376
402
  var defaultSubject = (c) => c.get("auth")?.userId ?? null;
377
403
  function stateRouteName(method, id) {
@@ -384,19 +410,30 @@ function requireSubject(c, resolveSubject) {
384
410
  }
385
411
  return subject;
386
412
  }
413
+ function writePrincipal(def, c) {
414
+ const principal = c.get("auth");
415
+ if (def.principal && principal == null) {
416
+ throw new UnauthorizedError({
417
+ message: `State "${def.id}" declares .principal(), and this request carries none`
418
+ });
419
+ }
420
+ return principal;
421
+ }
387
422
  async function assertPermitted(def, can, ctx) {
388
423
  const predicate = can === "canWrite" ? def.canWrite ?? def.canRead : def.canRead;
389
424
  if (predicate && !await predicate(ctx)) {
390
425
  throw new ForbiddenError({ message: `${can === "canWrite" ? "Writing" : "Reading"} state "${def.id}" is not permitted` });
391
426
  }
392
427
  }
393
- function responseSchema(def) {
428
+ function resultSchema2(def) {
394
429
  return Type.Object({
395
- at: Type.String(),
396
430
  value: def.value,
397
431
  ...def.data ? { data: def.data } : {}
398
432
  });
399
433
  }
434
+ function responseSchema(def) {
435
+ return Type.Object({ at: Type.String(), ...resultSchema2(def).properties });
436
+ }
400
437
  function routeContract(def, write) {
401
438
  if (!def.contract) {
402
439
  return void 0;
@@ -431,7 +468,7 @@ function writeRoute(state, path, resolveSubject) {
431
468
  const write = def.write;
432
469
  return stateRoute(def, "put", `${path}/${def.id}`, { body: write.input, ...keyInput(def) }).handler(async (c) => {
433
470
  const subject = requireSubject(c.raw, resolveSubject);
434
- const principal = c.raw.get("auth");
471
+ const principal = writePrincipal(def, c.raw);
435
472
  const { body, query } = await c.data();
436
473
  const key = def.key ? pickKey(def.key, query) : void 0;
437
474
  await assertPermitted(def, "canWrite", permissionContext(def, subject, principal, key));
@@ -465,8 +502,22 @@ function stateManifest(states) {
465
502
  write: state._definition.write !== void 0
466
503
  }]));
467
504
  }
468
- function snapshotRoute(states, manifest, path, resolveSubject) {
469
- return route.get(path).input({ query: snapshotQuery(states) }).handler(async (c) => {
505
+ function snapshotResponseSchema(states) {
506
+ const manifestEntry = Type.Object({ key: Type.Array(Type.String()), write: Type.Boolean() });
507
+ return Type.Object({
508
+ at: Type.String(),
509
+ states: Type.Object(Object.fromEntries(states.map((state) => [state.id, Type.Optional(resultSchema2(state._definition))]))),
510
+ manifest: Type.Object(Object.fromEntries(states.map((state) => [state.id, manifestEntry])))
511
+ });
512
+ }
513
+ function snapshotBuilder(states, path, options) {
514
+ const builder = route.get(path).input({ query: snapshotQuery(states) });
515
+ const used = options.use?.length ? builder.use(options.use) : builder;
516
+ const skipped = options.skip ? used.skip(options.skip) : used;
517
+ return options.contract ? skipped.contract(stripUndefined({ ...options.contract, response: snapshotResponseSchema(states) })) : skipped;
518
+ }
519
+ function snapshotRoute(states, manifest, path, resolveSubject, options = {}) {
520
+ return snapshotBuilder(states, path, options).handler(async (c) => {
470
521
  const subject = requireSubject(c.raw, resolveSubject);
471
522
  const principal = c.raw.get("auth");
472
523
  const { query } = await c.data();
@@ -486,17 +537,19 @@ function defineStateRouter(states, options = {}) {
486
537
  const list = Object.values(states);
487
538
  assertPath(path);
488
539
  list.forEach(assertRoutable);
540
+ assertSnapshotMiddlewareIsNotAuthorization(list, options);
489
541
  assertDistinctIds(list);
490
542
  list.forEach((state) => assertNotRouted(state, path));
491
543
  list.forEach((state) => bindState(state._definition, path));
492
544
  list.forEach(subscribeChangeSignals);
493
545
  return {
494
- router: defineRouter(deriveRoutes(list, path, options.subject ?? defaultSubject)),
546
+ router: defineRouter(deriveRoutes(list, path, options)),
495
547
  events: Object.fromEntries(list.map((state) => [stateEventName(state.id), state._event])),
496
548
  states
497
549
  };
498
550
  }
499
- function deriveRoutes(states, path, resolveSubject) {
551
+ function deriveRoutes(states, path, options) {
552
+ const resolveSubject = options.subject ?? defaultSubject;
500
553
  const routes = {};
501
554
  for (const state of states) {
502
555
  routes[stateRouteName("get", state.id)] = readRoute(state, path, resolveSubject);
@@ -504,7 +557,7 @@ function deriveRoutes(states, path, resolveSubject) {
504
557
  routes[stateRouteName("put", state.id)] = writeRoute(state, path, resolveSubject);
505
558
  }
506
559
  }
507
- routes.getStates = snapshotRoute(states, stateManifest(states), path, resolveSubject);
560
+ routes.getStates = snapshotRoute(states, stateManifest(states), path, resolveSubject, options);
508
561
  return routes;
509
562
  }
510
563
  function assertPath(path) {
@@ -543,6 +596,14 @@ function assertMiddlewareIsNotAuthorization(state) {
543
596
  );
544
597
  }
545
598
  }
599
+ function assertSnapshotMiddlewareIsNotAuthorization(states, options) {
600
+ const unguarded = states.find((state) => !state._definition.canRead);
601
+ if (((options.use?.length ?? 0) > 0 || options.skip) && unguarded) {
602
+ throw new Error(
603
+ `defineStateRouter: the router gives the snapshot route use or skip, but state "${unguarded.id}" declares no .canRead(). The snapshot's middleware never reaches the SSE stream, which serves the same state, so it cannot authorize. Authorization belongs in .canRead(); a state readable by every subject declares .canRead(() => true) to say so.`
604
+ );
605
+ }
606
+ }
546
607
  function assertChangedBy(state, event) {
547
608
  const payload = event.schema;
548
609
  const signal = changedPayloadSchema(state._definition);