@spfn/core 0.3.0-beta.27 → 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.
- package/dist/contract/index.d.ts +1 -1
- package/dist/db/index.d.ts +39 -11
- package/dist/db/index.js +34 -14
- package/dist/db/index.js.map +1 -1
- package/dist/event/index.d.ts +3 -3
- package/dist/event/index.js +10 -0
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +2 -2
- package/dist/event/sse/client.js +27 -17
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +3 -3
- package/dist/event/sse/index.js +115 -34
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +2 -2
- package/dist/event/ws/index.d.ts +3 -3
- package/dist/{router-D5B0cpVN.d.ts → router-CX5uVjmU.d.ts} +29 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +115 -34
- package/dist/server/index.js.map +1 -1
- package/dist/state/client.d.ts +73 -11
- package/dist/state/client.js +231 -31
- package/dist/state/client.js.map +1 -1
- package/dist/state/index.js +11 -1
- package/dist/state/index.js.map +1 -1
- package/dist/state/react.d.ts +18 -3
- package/dist/state/react.js +11 -5
- package/dist/state/react.js.map +1 -1
- package/dist/{types-04gu3uMj.d.ts → types-CurMab2Q.d.ts} +19 -2
- package/dist/{types-CqTsDETV.d.ts → types-DncqXfWN.d.ts} +1 -1
- package/package.json +1 -1
- package/src/db/transaction/README.md +58 -35
- package/src/event/README.md +61 -14
- package/src/state/README.md +161 -35
package/dist/state/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/state/state-binding.ts","../../src/state/state-event.ts","../../src/state/state-builder.ts","../../src/state/state-routes.ts","../../src/state/state-router.ts"],"names":["Type","Value","resultSchema"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * State Binding\n *\n * What a state router fixes for each state it groups, keyed by the builder's\n * definition — one per builder instance, so an intermediate builder is never\n * bound: the router's path (a state belongs to one router) and the schema\n * every `read()` result is checked against, built once here rather than per\n * request.\n */\n\nimport { Type, type TObject } from '@sinclair/typebox';\nimport type { StateDefinition } from './types';\n\nexport interface StateBinding\n{\n path: string;\n resultSchema: TObject;\n}\n\nconst bindings = new WeakMap<Readonly<StateDefinition>, StateBinding>();\n\n/** `{ value, data }` exactly as declared: `data` when `.data()` is, and nothing else. */\nfunction resultSchema(def: StateDefinition): TObject\n{\n return Type.Object(\n {\n value: def.value!,\n ...(def.data ? { data: def.data } : {}),\n },\n { additionalProperties: false },\n );\n}\n\n/** Bind a state to the router at `path`. */\nexport function bindState(def: Readonly<StateDefinition>, path: string): void\n{\n bindings.set(def, { path, resultSchema: resultSchema(def) });\n}\n\n/** The binding of a state, `undefined` until a router groups that builder. */\nexport function stateBinding(def: Readonly<StateDefinition>): StateBinding | undefined\n{\n return bindings.get(def);\n}\n","/**\n * State Event\n *\n * The two events a state derives: the change signal the app emits\n * (`state.changed`, in-process) and the SSE event the browser receives\n * (`state.<id>.changed`). Between them sits the one rule of the module — a\n * value is only ever what `read()` returned, re-read here, never computed from\n * the signal.\n */\n\nimport { Type, type TObject, type TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { getTransactionContext, onAfterCommit } from '@spfn/core/db';\nimport { defineEvent } from '@spfn/core/event';\nimport type { EventDef, FramePredicate, SubscribePredicate } from '@spfn/core/event';\nimport { logger } from '@spfn/core/logger';\nimport { stateBinding, type StateBinding } from './state-binding';\nimport type { RoutableState, StateCallContext, StateDefinition } from './types';\n\nconst stateLogger = logger.child('@spfn/core:state');\n\n/** `{ at, value, data? }` as a response and a frame carry it. */\nexport interface Snapshot\n{\n at: string;\n value: unknown;\n data?: unknown;\n}\n\ntype Key = Record<string, string>;\n\ntype FramePayload = Snapshot & { key?: Key; subject?: string };\n\n/** The wire name of a state's SSE event. */\nexport function stateEventName(id: string): string\n{\n return `state.${id}.changed`;\n}\n\n/**\n * The key fields of `keySchema` taken from `source`, or `undefined` when any\n * is absent. Key fields are strings by construction (`.key()` refuses others),\n * so a query value and a payload field compare as they are.\n */\nexport function pickKey(keySchema: TObject, source: Record<string, unknown>): Key | undefined\n{\n const key: Key = {};\n\n for (const field of Object.keys(keySchema.properties))\n {\n const value = source[field];\n if (typeof value !== 'string')\n {\n return undefined;\n }\n key[field] = value;\n }\n\n return key;\n}\n\n/** What `read()` receives: a shared state's value does not depend on the subject, so it gets none. */\nexport function readContext(def: StateDefinition, subject: string | undefined, principal: unknown, key?: Key): StateCallContext\n{\n if (!def.key)\n {\n return { subject, principal };\n }\n\n return def.shared ? { principal, key } : { subject, principal, key };\n}\n\n/** What `.canRead()` / `.canWrite()` receive. */\nexport function permissionContext(def: StateDefinition, subject: string, principal: unknown, key?: Key): StateCallContext\n{\n return def.key ? { subject, principal, key } : { subject, principal };\n}\n\n/**\n * The router binding of a state, or a refusal: an unrouted builder has no\n * subscriber for its signal and no schema to check a read against.\n */\nexport function requireBinding(def: StateDefinition, what: string): StateBinding\n{\n const binding = stateBinding(def);\n\n if (!binding)\n {\n throw new Error(\n `State \"${def.id}\" is in no defineStateRouter, so ${what} reaches nothing. `\n + 'The router subscribes the change signal and checks every read, and every builder call returns a new '\n + 'builder — an earlier one is never routed. Group the state with defineStateRouter and export the builder '\n + 'instance you grouped.',\n );\n }\n\n return binding;\n}\n\n/** Refuse a `read()` result that is not what the state declares, naming the state, never the value. */\nfunction assertResult(def: StateDefinition, binding: StateBinding, result: unknown): asserts result is Omit<Snapshot, 'at'>\n{\n const error = Value.Errors(binding.resultSchema, result).First();\n\n if (error)\n {\n throw new Error(\n `State \"${def.id}\" read() returned a result its declaration does not allow: ${error.path || '(root)'} ${error.message}. `\n + 'Every response and frame is checked against .value() and .data(), so a client never receives a value the '\n + 'contract does not describe. Return { value'\n + `${def.data ? ', data' : ''} } as declared.`,\n );\n }\n}\n\n/**\n * Read the current value and check it against the declaration. `at` is taken\n * before `read()` starts, so a frame is never stamped later than the data it\n * carries.\n */\nexport async function readSnapshot(def: StateDefinition, ctx: StateCallContext, at = new Date().toISOString()): Promise<Snapshot>\n{\n const binding = requireBinding(def, 'a read');\n const result = await def.read!(ctx);\n assertResult(def, binding, result);\n\n return { at, ...result };\n}\n\n/**\n * The frame for a snapshot. A copy: the response keeps the snapshot itself, so\n * neither a handler mutating what it returned nor a subscriber mutating the\n * payload changes the other.\n */\nfunction toFrame(def: StateDefinition, snapshot: Snapshot, subject: string | undefined, key: Key | undefined): FramePayload\n{\n const frame: FramePayload = structuredClone(snapshot);\n\n if (def.key)\n {\n frame.key = { ...key };\n }\n\n if (!(def.key && def.shared))\n {\n frame.subject = subject;\n }\n\n return frame;\n}\n\n/**\n * Read the value for one subject and key, send it as one frame, and return the\n * snapshot. Inside a transaction the frame is published after the root\n * transaction commits, and never when the root rolls back; it still carries the\n * value read inside it. A nested transaction (a savepoint) that rolls back\n * while the root commits still publishes the frame read inside it, because\n * after-commit callbacks belong to the root: emit the signal from the outer\n * transaction, or after it, when an inner one may roll back. Outside a\n * transaction the frame is sent here and awaited, so an emit that throws fails\n * the caller — `onAfterCommit` would only log it.\n */\nexport async function readAndPublish(\n def: StateDefinition,\n event: EventDef<any>,\n subject: string | undefined,\n key: Key | undefined,\n principal: unknown,\n): Promise<Snapshot>\n{\n const snapshot = await readSnapshot(def, readContext(def, subject, principal, key));\n const frame = toFrame(def, snapshot, subject, key);\n\n if (getTransactionContext())\n {\n onAfterCommit(() => event.emit(frame));\n }\n else\n {\n await event.emit(frame);\n }\n\n return snapshot;\n}\n\n// ============================================================================\n// The derived SSE event\n// ============================================================================\n\nfunction framePayloadSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n at: Type.String(),\n value: def.value ?? Type.Unknown(),\n ...(def.data ? { data: def.data } : {}),\n ...(def.key ? { key: def.key } : {}),\n ...(def.key && def.shared ? {} : { subject: Type.String() }),\n });\n}\n\n/**\n * `canRead` translated to the connect: the connection declares its key in the\n * connect query. A key field absent from it denies, without asking `canRead` —\n * there is no key to ask about.\n */\nfunction subscribePredicate(def: StateDefinition): SubscribePredicate\n{\n return async ({ subject, principal, request }) =>\n {\n if (!def.key)\n {\n return def.canRead ? def.canRead({ subject, principal }) : true;\n }\n\n const key = pickKey(def.key, request.query);\n\n return key !== undefined && def.canRead !== undefined && def.canRead({ subject, principal, key });\n };\n}\n\n/**\n * A frame reaches a connection on the key it declared, and only its own\n * subject's value unless shared. Never a connection without a subject: on a\n * stream without auth `canSubscribe` is never asked, so `canRead` would be\n * skipped.\n */\nfunction framePredicate(def: StateDefinition): FramePredicate<FramePayload>\n{\n const fields = def.key ? Object.keys(def.key.properties) : [];\n\n return (payload, { subject, request }) =>\n subject !== undefined\n && fields.every(field => payload.key?.[field] === request.query[field])\n && (payload.subject === undefined || payload.subject === subject);\n}\n\n/** The SSE event `state.<id>.changed`, its permissions and contract set once, here. */\nexport function createStateEvent(def: StateDefinition): EventDef<any>\n{\n const event = defineEvent(stateEventName(def.id), framePayloadSchema(def))\n .canSubscribe(subscribePredicate(def))\n .filter(framePredicate(def) as FramePredicate<unknown>);\n\n if (def.contract)\n {\n const { since, deprecatedIn, removedIn } = def.contract;\n event.contract({\n since,\n ...(deprecatedIn ? { deprecatedIn } : {}),\n ...(removedIn ? { removedIn } : {}),\n });\n }\n\n return event;\n}\n\n// ============================================================================\n// The change signal\n// ============================================================================\n\n/**\n * What a change signal must name: keyless `subject`; keyed the key fields, and\n * `subject` unless shared. Always a fresh object, never `def.key` itself: a key\n * declared `additionalProperties: false` would otherwise refuse every\n * `.changedBy()` payload carrying one more field. Extra fields are ignored.\n */\nexport function changedPayloadSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n ...def.key?.properties,\n ...(def.key && def.shared ? {} : { subject: Type.String() }),\n });\n}\n\nfunction assertChangedPayload(def: StateDefinition, schema: TSchema, payload: unknown): void\n{\n const error = Value.Errors(schema, payload).First();\n\n if (error)\n {\n throw new Error(\n `${def.id}.changed.emit() refused its payload: ${error.path || '(root)'} ${error.message}. `\n + 'The module re-reads exactly the subject and key the payload names, so a missing field cannot be guessed. '\n + `Pass ${def.key ? 'every key field' : 'the subject'}${def.key && !def.shared ? ' and the subject' : ''}.`,\n );\n }\n}\n\n/**\n * The change signal `state.changed`: a plain in-process event whose `emit`\n * refuses a builder no router grouped, and validates its payload, since the\n * module reads the state the payload names.\n */\nexport function createChangedSignal(def: StateDefinition): EventDef<any>\n{\n const schema = changedPayloadSchema(def);\n const event = defineEvent(`state.${def.id}.signal`, schema);\n const emit = event.emit;\n\n event.emit = (async (payload: unknown) =>\n {\n requireBinding(def, `${def.id}.changed.emit()`);\n assertChangedPayload(def, schema, payload);\n await emit(payload as Record<string, unknown>);\n }) as EventDef<any>['emit'];\n\n return event;\n}\n\n/** Subject and key a signal payload names; a shared state's names no subject. */\nfunction signalTarget(def: StateDefinition, payload: Record<string, unknown>): { subject?: string; key?: Key }\n{\n const subject = def.key && def.shared ? undefined : payload.subject as string;\n\n return { subject, key: def.key ? pickKey(def.key, payload) : undefined };\n}\n\n/**\n * Re-read and publish for one signal. A `read()` that throws or returns an\n * undeclared result sends nothing and is logged — the signal came after a\n * commit the app already made, so there is nobody to throw to.\n */\nasync function onSignal(state: RoutableState, payload: Record<string, unknown>): Promise<void>\n{\n const { subject, key } = signalTarget(state._definition, payload);\n\n try\n {\n await readAndPublish(state._definition, state._event, subject, key, undefined);\n }\n catch (error)\n {\n stateLogger.warn(`State \"${state.id}\" could not be re-read or published on its change signal; no frame was sent`, {\n state: state.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/**\n * A `.changedBy()` event's payload, checked as the state's own `emit` checks\n * its signal. The app emitted it, so a payload missing a field is logged and\n * skipped rather than thrown.\n */\nasync function onChangedBy(state: RoutableState, event: EventDef<any>, schema: TObject, payload: unknown): Promise<void>\n{\n const error = Value.Errors(schema, payload).First();\n\n if (error)\n {\n stateLogger.warn(`State \"${state.id}\" skipped an \"${event.name}\" payload that names no value to re-read`, {\n state: state.id,\n event: event.name,\n error: `${error.path || '(root)'} ${error.message}`,\n });\n\n return;\n }\n\n await onSignal(state, payload as Record<string, unknown>);\n}\n\n/**\n * Re-read and publish on every signal of `state` and every event it lists in\n * `.changedBy()`, on the pod that emitted it only. A `.changedBy()` event in an\n * event router is cached, so a plain subscriber would re-read on every pod and\n * each would send its own frame; the derived state event is cached too, so the\n * emitting pod's one frame already reaches every pod. The `changed` signal\n * needs no such option: it is in no event router and never cached.\n */\nexport function subscribeChangeSignals(state: RoutableState): void\n{\n const schema = changedPayloadSchema(state._definition);\n\n state.changed.subscribe(payload => onSignal(state, payload));\n state._definition.changedBy?.forEach(event =>\n event.subscribe(payload => onChangedBy(state, event, schema, payload), { localOnly: true }));\n}\n","/**\n * State Builder\n *\n * `defineState(id)` starts an immutable builder: every call returns a new one,\n * order is free and there is no terminal method. `defineStateRouter` reads\n * what a builder declared and derives its routes and SSE event from it.\n */\n\nimport { Kind, Type, type TObject, type TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport type { MiddlewareHandler } from 'hono';\nimport type { EventDef } from '@spfn/core/event';\nimport type { NamedMiddleware } from '@spfn/core/route';\nimport { createChangedSignal, createStateEvent, pickKey, readAndPublish, requireBinding } from './state-event';\nimport type {\n ChangedPayload,\n EmptyStateShape,\n GrantTarget,\n PermissionContext,\n ReadContext,\n RevokeTarget,\n StateContract,\n StateDefinition,\n StateFrame,\n StatePredicate,\n StateReader,\n StateResult,\n StateShape,\n StateWriteOptions,\n StateWriter,\n WithShape,\n WriteContext,\n WritePermissionContext,\n} from './types';\n\n/** A state id becomes part of a route name (`get<Id>State`) and of an event name (`state.<id>.changed`). */\nconst STATE_ID = /^[a-z][a-zA-Z0-9]*$/;\n\n/**\n * Query names the SSE stream reads itself. Key fields share the connect query\n * with them, so a key field under one of these names would be read as both.\n */\nconst RESERVED_KEY_FIELDS = ['events', 'token'];\n\n/**\n * A server-owned value declared once. See the module README.\n */\nexport class StateBuilder<TId extends string, T extends StateShape>\n{\n /** Type inference helper: what this builder declared. Never set at runtime. */\n declare readonly _types: T;\n\n private changedSignal?: EventDef<any>;\n private stateEvent?: EventDef<any>;\n\n constructor(\n readonly id: TId,\n readonly _definition: Readonly<StateDefinition>,\n )\n {\n }\n\n /**\n * Declare the key: one value per key (and per subject, unless `shared`).\n * Fields are required strings: they travel as query parameters.\n */\n key<K extends TObject, S extends boolean = false>(\n schema: K,\n options: { shared?: S } = {},\n ): StateBuilder<TId, WithShape<T, { key: K; shared: S }>>\n {\n this.assertOnce('key', !!this._definition.key);\n assertKeySchema(this.id, schema);\n\n return this.with({ key: schema, shared: options.shared === true });\n }\n\n /** Declare the value schema. Required. */\n value<V extends TSchema>(schema: V): StateBuilder<TId, WithShape<T, { value: V }>>\n {\n this.assertOnce('value', !!this._definition.value);\n\n return this.with({ value: schema });\n }\n\n /** Declare data attached to the value. Optional. */\n data<D extends TSchema>(schema: D): StateBuilder<TId, WithShape<T, { data: D }>>\n {\n this.assertOnce('data', !!this._definition.data);\n\n return this.with({ data: schema });\n }\n\n /**\n * Declare who the callbacks' `principal` is — what the app's auth\n * middleware sets as `c.get('auth')`. Type-only, but for one rule: the write\n * route refuses a request without a principal (401), so `.canWrite()` and\n * `.write()` receive `P`; `.read()` and `.canRead()` receive `P | undefined`.\n */\n principal<P>(): StateBuilder<TId, WithShape<T, { principal: P }>>\n {\n this.assertOnce('principal', this._definition.principal);\n\n return this.with({ principal: true });\n }\n\n /**\n * Middleware for the two per-state routes, `get<Id>State` / `put<Id>State`.\n * Not authorization: the snapshot and the SSE stream cannot run it, so a\n * state with `.use()` must declare `.canRead()`. Calls add up.\n */\n use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): StateBuilder<TId, T>\n {\n return this.with({ middlewares: [...this._definition.middlewares, ...middlewares] });\n }\n\n /** Server-level named middlewares the two per-state routes skip. Calls add up; `'*'` wins. Needs `.canRead()`, as `.use()`. */\n skip(middlewareNames: string[] | '*'): StateBuilder<TId, T>\n {\n const current = this._definition.skipMiddlewares ?? [];\n const skipMiddlewares = current === '*' || middlewareNames === '*'\n ? '*'\n : [...current, ...middlewareNames];\n\n return this.with({ skipMiddlewares });\n }\n\n /** Publish the read route, the write route and the event in the contract. */\n contract(contract: StateContract): StateBuilder<TId, T>\n {\n this.assertOnce('contract', !!this._definition.contract);\n\n return this.with({ contract });\n }\n\n /** Who may read. Required on a keyed state; checked on HTTP and at SSE connect. */\n canRead(predicate: (ctx: PermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>\n {\n this.assertOnce('canRead', !!this._definition.canRead);\n\n return this.with({ canRead: predicate as StatePredicate });\n }\n\n /** Who may write. Absent → `canRead` decides. */\n canWrite(predicate: (ctx: WritePermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>\n {\n this.assertOnce('canWrite', !!this._definition.canWrite);\n\n return this.with({ canWrite: predicate as StatePredicate });\n }\n\n /** The only source of a value. Required. */\n read(\n fn: (ctx: ReadContext<T>) => StateResult<T> | Promise<StateResult<T>>,\n ): StateBuilder<TId, WithShape<T, { read: true }>>\n {\n this.assertOnce('read', !!this._definition.read);\n\n return this.with({ read: fn as StateReader });\n }\n\n /**\n * Derive `PUT <path>/<id>`. `fn` runs the command; the value the route\n * answers and broadcasts is re-read afterwards, never taken from `input`.\n */\n write<I extends TSchema>(\n input: I,\n fn: (ctx: WriteContext<T, I>) => void | Promise<void>,\n options: StateWriteOptions = {},\n ): StateBuilder<TId, WithShape<T, { input: I }>>\n {\n this.assertOnce('write', !!this._definition.write);\n\n return this.with({ write: { input, fn: fn as StateWriter, options } });\n }\n\n /**\n * Domain events whose payload names a changed value of this state, treated\n * like `changed.emit` with that payload: keyless `subject`, keyed the key\n * fields and `subject`, shared the key fields. The router subscribes them\n * and refuses one whose payload schema lacks a field.\n */\n changedBy(events: EventDef<any>[]): StateBuilder<TId, T>\n {\n this.assertOnce('changedBy', !!this._definition.changedBy);\n\n return this.with({ changedBy: [...events] });\n }\n\n /**\n * The change signal. Emit it after the change is committed; the module\n * re-reads the value and sends the frame. Refused on a builder no router\n * grouped — export the instance you passed to `defineStateRouter`.\n */\n get changed(): EventDef<ChangedPayload<T>>\n {\n this.changedSignal ??= createChangedSignal(this._definition);\n\n return this.changedSignal;\n }\n\n /** Internal: the SSE event `state.<id>.changed` (`defineStateRouter(...).events`). */\n get _event(): EventDef<StateFrame<T>>\n {\n this.stateEvent ??= createStateEvent(this._definition);\n\n return this.stateEvent;\n }\n\n /**\n * Activate the state's event on `subject`'s open connections and send it\n * the current value at once. On a keyed state only the connections that\n * declared `key` are opened. Does not consult `canRead`.\n */\n async grant(target: GrantTarget<T>): Promise<void>\n {\n const { subject, key } = target as { subject: string; key?: Record<string, string> };\n requireBinding(this._definition, `${this.id}.grant()`);\n this.assertGrantKey(key);\n const declared = this.declaredKey('grant', key);\n await this._event.grant(subject, declared && { query: declared });\n await readAndPublish(this._definition, this._event, subject, declared, undefined);\n }\n\n /**\n * Deactivate the state's event on `subject`'s open connections: on a keyed\n * state those that declared `key`, or every one when no key is given.\n */\n async revoke(target: RevokeTarget<T>): Promise<void>\n {\n const { subject, key } = target as { subject: string; key?: Record<string, string> };\n const declared = this.declaredKey('revoke', key);\n await this._event.revoke(subject, declared && { query: declared });\n }\n\n /** A keyed grant opens one key: without it there is no key to open and no value to send. */\n private assertGrantKey(key: Record<string, string> | undefined): void\n {\n if (this._definition.key && !key)\n {\n throw new Error(\n `State \"${this.id}\" .grant() was called without a key. `\n + 'A keyed grant opens exactly one key — every connection of the subject on any key would otherwise be '\n + 'opened, including keys canRead refused. Pass { subject, key }.',\n );\n }\n }\n\n /**\n * The declared key fields of `key`, checked against `.key()`: an empty or\n * partial key would match every connection of the subject. A field the\n * declaration does not name is dropped, never matched on — so the check\n * runs on a fresh object of the declared fields, never `.key()` itself: a\n * key declared `additionalProperties: false` would otherwise refuse it.\n * The fresh object keeps `.key()`'s `required`, and a key that still picks\n * to nothing is refused: `undefined` here would mean every connection.\n */\n private declaredKey(method: 'grant' | 'revoke', key: Record<string, string> | undefined): Record<string, string> | undefined\n {\n const schema = this._definition.key;\n if (!schema || key === undefined)\n {\n return undefined;\n }\n\n const error = Value.Errors({ ...Type.Object(schema.properties), required: schema.required }, key).First();\n if (error)\n {\n this.refuseKey(method, `${error.path || '(root)'} ${error.message}`);\n }\n\n return pickKey(schema, key) ?? this.refuseKey(method, '(root) a declared key field is missing or not a string');\n }\n\n private refuseKey(method: 'grant' | 'revoke', reason: string): never\n {\n throw new Error(\n `State \"${this.id}\" .${method}() refused its key: ${reason}. `\n + `A keyed ${method} reaches exactly the connections on one key, so every key field must be named.`,\n );\n }\n\n private with(changes: Partial<StateDefinition>): StateBuilder<TId, any>\n {\n return new StateBuilder(this.id, { ...this._definition, ...changes });\n }\n\n private assertOnce(method: string, declared: boolean): void\n {\n if (declared)\n {\n throw new Error(\n `State \"${this.id}\" already declares .${method}(). `\n + 'A state is declared once, so a second declaration would silently replace the first. '\n + `Declare .${method}() once.`,\n );\n }\n }\n}\n\n/**\n * Refuse a key the module could not carry: it must be an object of required\n * string fields, none named like a query parameter the SSE stream reads.\n */\nfunction assertKeySchema(id: string, schema: TSchema): void\n{\n if (schema[Kind] !== 'Object')\n {\n throw new Error(\n `State \"${id}\" .key() takes an object schema, got ${String(schema[Kind])}. `\n + 'Each key field becomes a named query parameter and a field of the frame\\'s key. '\n + 'Wrap the field: .key(Type.Object({ workspaceId: Type.String() })).',\n );\n }\n\n const fields = Object.entries((schema as TObject).properties);\n\n if (fields.length === 0)\n {\n throw new Error(\n `State \"${id}\" .key() declares no field. A key with no field names nothing to read by. `\n + 'Drop .key() for one value per subject.',\n );\n }\n\n fields.forEach(([field, fieldSchema]) => assertKeyField(id, schema as TObject, field, fieldSchema));\n}\n\nfunction assertKeyField(id: string, schema: TObject, field: string, fieldSchema: TSchema): void\n{\n if (RESERVED_KEY_FIELDS.includes(field))\n {\n throw new Error(\n `State \"${id}\" key field \"${field}\" is reserved. `\n + `The SSE stream reads ${RESERVED_KEY_FIELDS.map(name => `\"${name}\"`).join(' and ')} from the connect query `\n + 'that also carries the key, so the field would be read as both. Rename the field.',\n );\n }\n\n if (fieldSchema.type !== 'string' || !(schema.required ?? []).includes(field))\n {\n throw new Error(\n `State \"${id}\" key field \"${field}\" is not a required string. `\n + 'A key travels as a query parameter, which is always a string, and the SSE filter compares it with the '\n + 'connect query as one — a number would never match, and a missing field names no value. '\n + `Declare it ${field}: Type.String().`,\n );\n }\n}\n\n/**\n * Declare a server-owned state.\n *\n * @example\n * ```typescript\n * export const consent = defineState('consent')\n * .value(Type.Union([Type.Literal('pending'), Type.Literal('agreed')]))\n * .read(({ subject }) => consents.current(subject));\n * ```\n */\nexport function defineState<TId extends string>(id: TId): StateBuilder<TId, EmptyStateShape>\n{\n if (!STATE_ID.test(id))\n {\n throw new Error(\n `defineState(\"${id}\"): a state id must match ${STATE_ID}. `\n + 'It becomes part of the route names get<Id>State / put<Id>State and of the event name state.<id>.changed. '\n + 'Use a lowerCamel identifier, e.g. \"myState\".',\n );\n }\n\n return new StateBuilder(id, { id, shared: false, principal: false, middlewares: [] });\n}\n","/**\n * State Routes\n *\n * The read, write and snapshot routes a state router derives. They are\n * ordinary `RouteDef`s registered through `registerRoutes`, so a state's\n * `.use()` middleware runs inside the route's own chain after the app's\n * global middleware.\n */\n\nimport { Type, type TObject, type TSchema } from '@sinclair/typebox';\nimport type { Context, MiddlewareHandler } from 'hono';\nimport { ForbiddenError, UnauthorizedError } from '@spfn/core/errors';\nimport { route } from '@spfn/core/route';\nimport type { NamedMiddleware, RouteContract, RouteDef, RouteInput } from '@spfn/core/route';\nimport {\n permissionContext,\n pickKey,\n readAndPublish,\n readContext,\n readSnapshot,\n} from './state-event';\nimport type { RoutableState, StateCallContext, StateContract, StateDefinition } from './types';\n\n/** Who a request is for. `null` answers 401. */\nexport type SubjectResolver = (c: Context) => string | null;\n\n/** The SSE `getSubject` default: the `userId` the app's auth middleware set. */\nexport const defaultSubject: SubjectResolver = c =>\n (c.get('auth') as Record<string, string> | undefined)?.userId ?? null;\n\ntype Key = Record<string, string>;\n\n/** `get<Id>State` / `put<Id>State`. */\nexport function stateRouteName(method: 'get' | 'put', id: string): string\n{\n return `${method}${id[0].toUpperCase()}${id.slice(1)}State`;\n}\n\nfunction requireSubject(c: Context, resolveSubject: SubjectResolver): string\n{\n const subject = resolveSubject(c);\n\n if (!subject)\n {\n throw new UnauthorizedError({ message: 'A state is read for a subject, and this request carries none' });\n }\n\n return subject;\n}\n\n/**\n * The write route's principal, `c.get('auth')`. A state that declared\n * `.principal()` types `canWrite` / `write` without `undefined`, so a request\n * whose `auth` is `undefined` or `null` is refused here, before either runs.\n */\nfunction writePrincipal(def: StateDefinition, c: Context): unknown\n{\n const principal = c.get('auth');\n\n if (def.principal && principal == null)\n {\n throw new UnauthorizedError({\n message: `State \"${def.id}\" declares .principal(), and this request carries none`,\n });\n }\n\n return principal;\n}\n\nasync function assertPermitted(def: StateDefinition, can: 'canRead' | 'canWrite', ctx: StateCallContext): Promise<void>\n{\n const predicate = can === 'canWrite' ? def.canWrite ?? def.canRead : def.canRead;\n\n if (predicate && !await predicate(ctx))\n {\n throw new ForbiddenError({ message: `${can === 'canWrite' ? 'Writing' : 'Reading'} state \"${def.id}\" is not permitted` });\n }\n}\n\n/** `{ value, data? }` as declared: one state's response without `at`, and its entry in the snapshot. */\nfunction resultSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n value: def.value!,\n ...(def.data ? { data: def.data } : {}),\n });\n}\n\nfunction responseSchema(def: StateDefinition): TObject\n{\n return Type.Object({ at: Type.String(), ...resultSchema(def).properties });\n}\n\n/**\n * The route contract, or none when the state declares no `.contract()`. A\n * write's own version markers win over the state's, field by field.\n */\nfunction routeContract(def: StateDefinition, write: boolean): RouteContract | undefined\n{\n if (!def.contract)\n {\n return undefined;\n }\n\n const own = write ? stripUndefined(def.write!.options) : {};\n\n return stripUndefined({ ...def.contract, ...own, response: responseSchema(def) }) as RouteContract;\n}\n\nfunction stripUndefined<T extends object>(value: T): Partial<T>\n{\n return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined)) as Partial<T>;\n}\n\n/** `route.<method>(path)` with the state's input, middleware, skips and contract. */\nfunction stateRoute(def: StateDefinition, method: 'get' | 'put', path: string, input: RouteInput)\n{\n const contract = routeContract(def, method === 'put');\n const builder = route[method](path).input(input).use(def.middlewares);\n const skipped = def.skipMiddlewares ? builder.skip(def.skipMiddlewares) : builder;\n\n return contract ? skipped.contract(contract) : skipped;\n}\n\nfunction keyInput(def: StateDefinition): { query?: TObject }\n{\n return def.key ? { query: def.key } : {};\n}\n\n/** `GET <path>/<id>`: subject → key → `canRead` → `at` → `read()`. */\nexport function readRoute(state: RoutableState, path: string, resolveSubject: SubjectResolver): RouteDef<any, any, any>\n{\n const def = state._definition;\n\n return stateRoute(def, 'get', `${path}/${def.id}`, keyInput(def)).handler(async (c) =>\n {\n const subject = requireSubject(c.raw, resolveSubject);\n const principal = c.raw.get('auth');\n const key = def.key ? pickKey(def.key, (await c.data()).query) : undefined;\n await assertPermitted(def, 'canRead', permissionContext(def, subject, principal, key));\n\n return readSnapshot(def, readContext(def, subject, principal, key));\n });\n}\n\n/** `PUT <path>/<id>`: subject → principal (with `.principal()`) → key → `canWrite ?? canRead` → `write()` → `at` → `read()` → one frame. */\nexport function writeRoute(state: RoutableState, path: string, resolveSubject: SubjectResolver): RouteDef<any, any, any>\n{\n const def = state._definition;\n const write = def.write!;\n\n return stateRoute(def, 'put', `${path}/${def.id}`, { body: write.input, ...keyInput(def) }).handler(async (c) =>\n {\n const subject = requireSubject(c.raw, resolveSubject);\n const principal = writePrincipal(def, c.raw);\n const { body, query } = await c.data();\n const key = def.key ? pickKey(def.key, query) : undefined;\n await assertPermitted(def, 'canWrite', permissionContext(def, subject, principal, key));\n await write.fn({ ...readContext(def, subject, principal, key), input: body });\n\n return readAndPublish(def, state._event, subject, key, principal);\n });\n}\n\n/**\n * The snapshot's query: every key field of every keyed state, each optional —\n * a state whose key is incomplete is left out, not refused.\n */\nfunction snapshotQuery(states: RoutableState[]): TObject\n{\n const fields: Record<string, TSchema> = {};\n\n for (const state of states)\n {\n for (const [field, schema] of Object.entries(state._definition.key?.properties ?? {}))\n {\n fields[field] = Type.Optional(schema);\n }\n }\n\n return Type.Object(fields);\n}\n\n/** One state's entry in the snapshot, or `undefined` when its key is incomplete or `canRead` refuses. */\nasync function snapshotEntry(def: StateDefinition, subject: string, principal: unknown, query: Record<string, unknown>, at: string)\n{\n const key: Key | undefined = def.key ? pickKey(def.key, query) : undefined;\n\n if (def.key && !key)\n {\n return undefined;\n }\n\n if (def.canRead && !await def.canRead(permissionContext(def, subject, principal, key)))\n {\n return undefined;\n }\n\n const { value, data } = await readSnapshot(def, readContext(def, subject, principal, key), at);\n\n return def.data ? { value, data } : { value };\n}\n\n/**\n * What the router gives the snapshot route `GET <path>` alone. A per-state\n * route keeps its own state's `.use()` / `.skip()` / `.contract()`, and the\n * SSE stream runs none of them.\n */\nexport interface SnapshotRouteOptions\n{\n /** Middleware for the snapshot route only. Not authorization: the stream never runs it (see README). */\n use?: (MiddlewareHandler | NamedMiddleware<string>)[];\n\n /** Server-level named middlewares the snapshot route skips; `'*'` skips every one. */\n skip?: string[] | '*';\n\n /** Publishes the snapshot route in the contract; the response schema is derived. */\n contract?: StateContract;\n}\n\n/** What the browser cannot read from a type: every state, in declaration order, its key fields and whether it has a write. */\nexport type StatesManifest = Record<string, { key: string[]; write: boolean }>;\n\n/** The snapshot's `manifest`. It is static: the router builds it once and every snapshot answers the same object. */\nexport function stateManifest(states: RoutableState[]): StatesManifest\n{\n return Object.fromEntries(states.map(state => [state.id, {\n key: Object.keys(state._definition.key?.properties ?? {}),\n write: state._definition.write !== undefined,\n }]));\n}\n\n/**\n * The snapshot's response: `at`, one optional `{ value, data? }` per state and\n * one required manifest entry per state, in declaration order. Fresh objects,\n * so no per-state schema is touched.\n */\nfunction snapshotResponseSchema(states: RoutableState[]): TObject\n{\n const manifestEntry = Type.Object({ key: Type.Array(Type.String()), write: Type.Boolean() });\n\n return Type.Object({\n at: Type.String(),\n states: Type.Object(Object.fromEntries(states.map(state => [state.id, Type.Optional(resultSchema(state._definition))]))),\n manifest: Type.Object(Object.fromEntries(states.map(state => [state.id, manifestEntry]))),\n });\n}\n\n/**\n * `route.get(path)` with the router's snapshot options. Each is applied only\n * when given — an empty `use` counts as not given — so a router without them\n * derives the route it always did. The\n * contract is built here, once, when the router is built.\n */\nfunction snapshotBuilder(states: RoutableState[], path: string, options: SnapshotRouteOptions)\n{\n const builder = route.get(path).input({ query: snapshotQuery(states) });\n const used = options.use?.length ? builder.use(options.use) : builder;\n const skipped = options.skip ? used.skip(options.skip) : used;\n\n return options.contract\n ? skipped.contract(stripUndefined({ ...options.contract, response: snapshotResponseSchema(states) }) as RouteContract)\n : skipped;\n}\n\n/**\n * `GET <path>`: one `at`, then every readable state in declaration order, and\n * the router's `manifest`. A `read()` that throws fails it all.\n */\nexport function snapshotRoute(\n states: RoutableState[],\n manifest: StatesManifest,\n path: string,\n resolveSubject: SubjectResolver,\n options: SnapshotRouteOptions = {},\n): RouteDef<any, any, any>\n{\n return snapshotBuilder(states, path, options).handler(async (c) =>\n {\n const subject = requireSubject(c.raw, resolveSubject);\n const principal = c.raw.get('auth');\n const { query } = await c.data();\n const at = new Date().toISOString();\n const entries = await Promise.all(states.map(state => snapshotEntry(state._definition, subject, principal, query, at)));\n\n return {\n at,\n states: Object.fromEntries(states\n .map((state, index) => [state.id, entries[index]])\n .filter(([, entry]) => entry !== undefined)),\n manifest,\n };\n });\n}\n","/**\n * State Router\n *\n * Groups states, derives their routes into one `Router` the app mounts with\n * `.packages()`, collects their SSE events for the app's event router, and\n * subscribes each state's change signal — once, here.\n */\n\nimport type { Static, TObject, TSchema } from '@sinclair/typebox';\nimport type { EventDef } from '@spfn/core/event';\nimport { defineRouter } from '@spfn/core/route';\nimport type { RouteDef, Router } from '@spfn/core/route';\nimport { bindState, stateBinding } from './state-binding';\nimport { changedPayloadSchema, stateEventName, subscribeChangeSignals } from './state-event';\nimport { defaultSubject, readRoute, snapshotRoute, stateManifest, stateRouteName, writeRoute } from './state-routes';\nimport type { SnapshotRouteOptions, SubjectResolver } from './state-routes';\nimport type { RoutableState, StateFrame, StateKey, StateResult, StateSnapshot, StateValue } from './types';\n\n/**\n * Where the router mounts, whom a request is for, and the snapshot route's own\n * `use` / `skip` / `contract` (`SnapshotRouteOptions`) — those reach `GET <path>`\n * only, never a per-state route or the SSE stream.\n */\nexport interface StateRouterOptions extends SnapshotRouteOptions\n{\n /**\n * Where the routes are mounted: `GET <path>`, `GET|PUT <path>/<id>`.\n * @default '/state'\n */\n path?: string;\n\n /**\n * Who an HTTP request is for. `null` answers 401.\n * @default (c) => c.get('auth')?.userId ?? null\n */\n subject?: SubjectResolver;\n}\n\ntype Shape<S extends RoutableState> = S['_types'];\n\ntype KeyQuery<S extends RoutableState> = Shape<S>['key'] extends TObject ? { query: Shape<S>['key'] } : {};\n\ntype ReadRoutes<S extends Record<string, RoutableState>> = {\n [K in keyof S as `get${Capitalize<S[K]['id']>}State`]: RouteDef<KeyQuery<S[K]>, {}, StateSnapshot<Shape<S[K]>>>;\n};\n\ntype WriteInput<S extends RoutableState> = KeyQuery<S> & { body: Shape<S>['input'] };\n\ntype WriteRoutes<S extends Record<string, RoutableState>> = {\n [K in keyof S as Shape<S[K]>['input'] extends TSchema ? `put${Capitalize<S[K]['id']>}State` : never]: RouteDef<WriteInput<S[K]>, {}, StateSnapshot<Shape<S[K]>>>;\n};\n\n/**\n * What `GET <path>` answers: every state the subject may read and whose key\n * the query carries, and the manifest of every state — its key fields and\n * whether it has a write — in declaration order.\n */\nexport type StatesSnapshot<S extends Record<string, RoutableState>> = {\n at: string;\n states: { [K in keyof S as S[K]['id']]?: StateResult<Shape<S[K]>> };\n manifest: { [K in keyof S as S[K]['id']]: { key: string[]; write: boolean } };\n};\n\n/** The routes one state router derives, typed for `createApi` and the route map. */\nexport type StateRoutes<S extends Record<string, RoutableState>> = ReadRoutes<S> & WriteRoutes<S> & {\n getStates: RouteDef<{ query: TObject }, {}, StatesSnapshot<S>>;\n};\n\n/** The SSE events, keyed by their wire names, for the app's `defineEventRouter`. */\nexport type StateEvents<S extends Record<string, RoutableState>> = {\n [K in keyof S as `state.${S[K]['id']}.changed`]: EventDef<StateFrame<Shape<S[K]>>>;\n};\n\nexport interface StateRouter<S extends Record<string, RoutableState>>\n{\n /** The derived routes. Mount with `appRouter.packages([states.router])`. */\n router: Router<StateRoutes<S>>;\n\n /** The derived SSE events. Spread into `defineEventRouter({ ...appEvents, ...states.events })`. */\n events: StateEvents<S>;\n\n /** The declarations, as given. */\n states: S;\n}\n\ntype StateById<R extends StateRouter<any>, TId extends string> = Extract<R['states'][keyof R['states']], { id: TId }>;\n\n/** The value type of state `TId` of a state router. */\nexport type InferStateValue<R extends StateRouter<any>, TId extends string> = StateValue<Shape<StateById<R, TId>>>;\n\n/** The key type of state `TId`, `undefined` when it is keyless. */\nexport type InferStateKey<R extends StateRouter<any>, TId extends string> = StateKey<Shape<StateById<R, TId>>>;\n\n/** The write input of state `TId`, `never` when it declares no `.write()`. */\nexport type InferStateInput<R extends StateRouter<any>, TId extends string> =\n Shape<StateById<R, TId>>['input'] extends TSchema ? Static<Shape<StateById<R, TId>>['input']> : never;\n\n/**\n * Group states, derive their routes and SSE events, and subscribe their change\n * signals.\n *\n * @example\n * ```typescript\n * export const states = defineStateRouter({ consent, runtime, member });\n *\n * // server.config.ts\n * // .routes(appRouter.packages([states.router]))\n * // .events(defineEventRouter({ ...appEvents, ...states.events }), { auth: { enabled: true } })\n * ```\n */\nexport function defineStateRouter<S extends Record<string, RoutableState>>(\n states: S,\n options: StateRouterOptions = {},\n): StateRouter<S>\n{\n const path = options.path ?? '/state';\n const list = Object.values(states);\n\n assertPath(path);\n list.forEach(assertRoutable);\n assertSnapshotMiddlewareIsNotAuthorization(list, options);\n assertDistinctIds(list);\n list.forEach(state => assertNotRouted(state, path));\n\n list.forEach(state => bindState(state._definition, path));\n list.forEach(subscribeChangeSignals);\n\n return {\n router: defineRouter(deriveRoutes(list, path, options)) as Router<StateRoutes<S>>,\n events: Object.fromEntries(list.map(state => [stateEventName(state.id), state._event])) as StateEvents<S>,\n states,\n };\n}\n\nfunction deriveRoutes(states: RoutableState[], path: string, options: StateRouterOptions): Record<string, RouteDef<any, any, any>>\n{\n const resolveSubject = options.subject ?? defaultSubject;\n const routes: Record<string, RouteDef<any, any, any>> = {};\n\n for (const state of states)\n {\n routes[stateRouteName('get', state.id)] = readRoute(state, path, resolveSubject);\n\n if (state._definition.write)\n {\n routes[stateRouteName('put', state.id)] = writeRoute(state, path, resolveSubject);\n }\n }\n\n routes.getStates = snapshotRoute(states, stateManifest(states), path, resolveSubject, options);\n\n return routes;\n}\n\nfunction assertPath(path: string): void\n{\n if (!path.startsWith('/') || (path.length > 1 && path.endsWith('/')))\n {\n throw new Error(\n `defineStateRouter(..., { path: \"${path}\" }) is not an absolute path without a trailing slash. `\n + 'The routes are mounted at <path> and <path>/<id>. Write it as \"/state\".',\n );\n }\n}\n\n/** Refuse a state the router could not serve, naming it. */\nfunction assertRoutable(state: RoutableState): void\n{\n const def = state._definition;\n const missing = !def.value ? '.value()' : !def.read ? '.read()' : undefined;\n\n if (missing)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" declares no ${missing}. `\n + 'A state answers with the value its read returns, so both are required. '\n + `Add ${missing} to defineState('${state.id}').`,\n );\n }\n\n if (def.key && !def.canRead)\n {\n throw new Error(\n `defineStateRouter: keyed state \"${state.id}\" declares no .canRead(). `\n + 'The key comes from the request, so without it any subject could read any key. '\n + 'Add .canRead(({ subject, key }) => ...).',\n );\n }\n\n assertMiddlewareIsNotAuthorization(state);\n def.changedBy?.forEach(event => assertChangedBy(state, event));\n\n if (def.write && !def.contract && def.write.options.since)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" gives .write(..., { since: '${def.write.options.since}' }) `\n + 'but declares no .contract(), so there is nothing to fall back from: the write route would have a version '\n + 'but no auth profile, and the read route and the event would not be contracted at all. '\n + 'Add .contract({ since, ... }) to the state, or drop since from the write.',\n );\n }\n}\n\n/**\n * `.use()` / `.skip()` reach the two per-state routes only: the snapshot and\n * the SSE stream cannot run HTTP middleware. A guard there would hold on one\n * route and leak through the others, so authorization must be `canRead`.\n */\nfunction assertMiddlewareIsNotAuthorization(state: RoutableState): void\n{\n const def = state._definition;\n\n if ((def.middlewares.length > 0 || def.skipMiddlewares) && !def.canRead)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" declares .use() or .skip() but no .canRead(). `\n + `Middleware reaches only ${stateRouteName('get', state.id)} / ${stateRouteName('put', state.id)} — never the `\n + 'snapshot route or the SSE stream — so it cannot authorize. Authorization belongs in .canRead(), which is '\n + 'enforced on the read route, the snapshot and the stream. A state that needs middleware only for, say, rate '\n + 'limiting declares .canRead(() => true) to say so.',\n );\n }\n}\n\n/**\n * The router's `use` / `skip` reach the snapshot route only — never the SSE\n * stream, which serves the same states — so, as for a state's own `.use()`,\n * every state must authorize with `canRead`. A `contract` alone runs nothing.\n */\nfunction assertSnapshotMiddlewareIsNotAuthorization(states: RoutableState[], options: StateRouterOptions): void\n{\n const unguarded = states.find(state => !state._definition.canRead);\n\n if (((options.use?.length ?? 0) > 0 || options.skip) && unguarded)\n {\n throw new Error(\n `defineStateRouter: the router gives the snapshot route use or skip, but state \"${unguarded.id}\" declares no `\n + '.canRead(). The snapshot\\'s middleware never reaches the SSE stream, which serves the same state, so it '\n + 'cannot authorize. Authorization belongs in .canRead(); a state readable by every subject declares '\n + '.canRead(() => true) to say so.',\n );\n }\n}\n\n/** A `.changedBy()` event must carry every field the state's change signal names, as a required property. */\nfunction assertChangedBy(state: RoutableState, event: EventDef<any>): void\n{\n const payload = event.schema as TObject | undefined;\n const signal = changedPayloadSchema(state._definition);\n const missing = Object.keys(signal.properties).find(field =>\n !payload?.properties?.[field] || !(payload.required ?? []).includes(field));\n\n if (missing)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" lists event \"${event.name}\" in .changedBy(), but its payload schema `\n + `has no required field \"${missing}\". Its payload is read like ${state.id}.changed.emit(), which names `\n + `${Object.keys(signal.properties).join(', ')}. Add the field to the event, or emit ${state.id}.changed instead.`,\n );\n }\n}\n\nfunction assertDistinctIds(states: RoutableState[]): void\n{\n const seen = new Set<string>();\n\n for (const state of states)\n {\n if (seen.has(state.id))\n {\n throw new Error(\n `defineStateRouter: two states have the id \"${state.id}\". `\n + `Both would derive the routes ${stateRouteName('get', state.id)} and the event `\n + `${stateEventName(state.id)}, and one would shadow the other. Rename one.`,\n );\n }\n seen.add(state.id);\n }\n}\n\nfunction assertNotRouted(state: RoutableState, path: string): void\n{\n const other = stateBinding(state._definition)?.path;\n\n if (other !== undefined)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" is already in another state router (path ${other}, this one ${path}). `\n + 'Each router subscribes the state\\'s change signal, so every change would be read and sent twice. '\n + 'Group each state in one router.',\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/state/state-binding.ts","../../src/state/state-event.ts","../../src/state/state-builder.ts","../../src/state/state-routes.ts","../../src/state/state-router.ts"],"names":["Type","Value","resultSchema"],"mappings":";;;;;;;;;AAmBA,IAAM,QAAA,uBAAe,OAAA,EAAiD;AAGtE,SAAS,aAAa,GAAA,EACtB;AACI,EAAA,OAAO,IAAA,CAAK,MAAA;AAAA,IACR;AAAA,MACI,OAAO,GAAA,CAAI,KAAA;AAAA,MACX,GAAI,IAAI,IAAA,GAAO,EAAE,MAAM,GAAA,CAAI,IAAA,KAAS;AAAC,KACzC;AAAA,IACA,EAAE,sBAAsB,KAAA;AAAM,GAClC;AACJ;AAGO,SAAS,SAAA,CAAU,KAAgC,IAAA,EAC1D;AACI,EAAA,QAAA,CAAS,GAAA,CAAI,KAAK,EAAE,IAAA,EAAM,cAAc,YAAA,CAAa,GAAG,GAAG,CAAA;AAC/D;AAGO,SAAS,aAAa,GAAA,EAC7B;AACI,EAAA,OAAO,QAAA,CAAS,IAAI,GAAG,CAAA;AAC3B;;;ACxBA,IAAM,WAAA,GAAc,MAAA,CAAO,KAAA,CAAM,kBAAkB,CAAA;AAe5C,SAAS,eAAe,EAAA,EAC/B;AACI,EAAA,OAAO,SAAS,EAAE,CAAA,QAAA,CAAA;AACtB;AAOO,SAAS,OAAA,CAAQ,WAAoB,MAAA,EAC5C;AACI,EAAA,MAAM,MAAW,EAAC;AAElB,EAAA,KAAA,MAAW,KAAA,IAAS,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,UAAU,CAAA,EACpD;AACI,IAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA;AAC1B,IAAA,IAAI,OAAO,UAAU,QAAA,EACrB;AACI,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,GAAA,CAAI,KAAK,CAAA,GAAI,KAAA;AAAA,EACjB;AAEA,EAAA,OAAO,GAAA;AACX;AAGO,SAAS,WAAA,CAAY,GAAA,EAAsB,OAAA,EAA6B,SAAA,EAAoB,GAAA,EACnG;AACI,EAAA,IAAI,CAAC,IAAI,GAAA,EACT;AACI,IAAA,OAAO,EAAE,SAAS,SAAA,EAAU;AAAA,EAChC;AAEA,EAAA,OAAO,GAAA,CAAI,SAAS,EAAE,SAAA,EAAW,KAAI,GAAI,EAAE,OAAA,EAAS,SAAA,EAAW,GAAA,EAAI;AACvE;AAGO,SAAS,iBAAA,CAAkB,GAAA,EAAsB,OAAA,EAAiB,SAAA,EAAoB,GAAA,EAC7F;AACI,EAAA,OAAO,GAAA,CAAI,MAAM,EAAE,OAAA,EAAS,WAAW,GAAA,EAAI,GAAI,EAAE,OAAA,EAAS,SAAA,EAAU;AACxE;AAMO,SAAS,cAAA,CAAe,KAAsB,IAAA,EACrD;AACI,EAAA,MAAM,OAAA,GAAU,aAAa,GAAG,CAAA;AAEhC,EAAA,IAAI,CAAC,OAAA,EACL;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,OAAA,EAAU,GAAA,CAAI,EAAE,CAAA,iCAAA,EAAoC,IAAI,CAAA,wPAAA;AAAA,KAI5D;AAAA,EACJ;AAEA,EAAA,OAAO,OAAA;AACX;AAGA,SAAS,YAAA,CAAa,GAAA,EAAsB,OAAA,EAAuB,MAAA,EACnE;AACI,EAAA,MAAM,QAAQ,KAAA,CAAM,MAAA,CAAO,QAAQ,YAAA,EAAc,MAAM,EAAE,KAAA,EAAM;AAE/D,EAAA,IAAI,KAAA,EACJ;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,OAAA,EAAU,GAAA,CAAI,EAAE,CAAA,2DAAA,EAA8D,MAAM,IAAA,IAAQ,QAAQ,CAAA,CAAA,EAAI,KAAA,CAAM,OAAO,CAAA,qJAAA,EAGhH,GAAA,CAAI,IAAA,GAAO,WAAW,EAAE,CAAA,eAAA;AAAA,KACjC;AAAA,EACJ;AACJ;AAOA,eAAsB,YAAA,CAAa,KAAsB,GAAA,EAAuB,EAAA,GAAA,qBAAS,IAAA,EAAK,EAAE,aAAY,EAC5G;AACI,EAAA,MAAM,OAAA,GAAU,cAAA,CAAe,GAAA,EAAK,QAAQ,CAAA;AAC5C,EAAA,MAAM,MAAA,GAAS,MAAM,GAAA,CAAI,IAAA,CAAM,GAAG,CAAA;AAClC,EAAA,YAAA,CAAa,GAAA,EAAK,SAAS,MAAM,CAAA;AAEjC,EAAA,OAAO,EAAE,EAAA,EAAI,GAAG,MAAA,EAAO;AAC3B;AAOA,SAAS,OAAA,CAAQ,GAAA,EAAsB,QAAA,EAAoB,OAAA,EAA6B,GAAA,EACxF;AACI,EAAA,MAAM,KAAA,GAAsB,gBAAgB,QAAQ,CAAA;AAEpD,EAAA,IAAI,IAAI,GAAA,EACR;AACI,IAAA,KAAA,CAAM,GAAA,GAAM,EAAE,GAAG,GAAA,EAAI;AAAA,EACzB;AAEA,EAAA,IAAI,EAAE,GAAA,CAAI,GAAA,IAAO,GAAA,CAAI,MAAA,CAAA,EACrB;AACI,IAAA,KAAA,CAAM,OAAA,GAAU,OAAA;AAAA,EACpB;AAEA,EAAA,OAAO,KAAA;AACX;AAUA,eAAsB,cAAA,CAClB,GAAA,EACA,KAAA,EACA,OAAA,EACA,KACA,SAAA,EAEJ;AACI,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,GAAA,EAAK,YAAY,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAC,CAAA;AAClF,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,EAAK,QAAA,EAAU,SAAS,GAAG,CAAA;AAEjD,EAAA,IAAI,uBAAsB,EAC1B;AACI,IAAA,aAAA,CAAc,MAAM,KAAA,CAAM,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,EACzC,CAAA,MAEA;AACI,IAAA,MAAM,KAAA,CAAM,KAAK,KAAK,CAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,QAAA;AACX;AAMA,SAAS,mBAAmB,GAAA,EAC5B;AACI,EAAA,OAAOA,KAAK,MAAA,CAAO;AAAA,IACf,EAAA,EAAIA,KAAK,MAAA,EAAO;AAAA,IAChB,KAAA,EAAO,GAAA,CAAI,KAAA,IAASA,IAAAA,CAAK,OAAA,EAAQ;AAAA,IACjC,GAAI,IAAI,IAAA,GAAO,EAAE,MAAM,GAAA,CAAI,IAAA,KAAS,EAAC;AAAA,IACrC,GAAI,IAAI,GAAA,GAAM,EAAE,KAAK,GAAA,CAAI,GAAA,KAAQ,EAAC;AAAA,IAClC,GAAI,GAAA,CAAI,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,EAAC,GAAI,EAAE,OAAA,EAASA,IAAAA,CAAK,MAAA,EAAO;AAAE,GAC7D,CAAA;AACL;AAOA,SAAS,mBAAmB,GAAA,EAC5B;AACI,EAAA,OAAO,OAAO,EAAE,OAAA,EAAS,SAAA,EAAW,SAAQ,KAC5C;AACI,IAAA,IAAI,CAAC,IAAI,GAAA,EACT;AACI,MAAA,OAAO,GAAA,CAAI,UAAU,GAAA,CAAI,OAAA,CAAQ,EAAE,OAAA,EAAS,SAAA,EAAW,CAAA,GAAI,IAAA;AAAA,IAC/D;AAEA,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,QAAQ,KAAK,CAAA;AAE1C,IAAA,OAAO,GAAA,KAAQ,MAAA,IAAa,GAAA,CAAI,OAAA,KAAY,MAAA,IAAa,GAAA,CAAI,OAAA,CAAQ,EAAE,OAAA,EAAS,SAAA,EAAW,GAAA,EAAK,CAAA;AAAA,EACpG,CAAA;AACJ;AAQA,SAAS,eAAe,GAAA,EACxB;AACI,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,GAAM,MAAA,CAAO,KAAK,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA,GAAI,EAAC;AAE5D,EAAA,OAAO,CAAC,OAAA,EAAS,EAAE,OAAA,EAAS,OAAA,OACxB,OAAA,KAAY,MAAA,IACT,MAAA,CAAO,KAAA,CAAM,CAAA,KAAA,KAAS,OAAA,CAAQ,MAAM,KAAK,CAAA,KAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAC,MAClE,OAAA,CAAQ,OAAA,KAAY,MAAA,IAAa,OAAA,CAAQ,OAAA,KAAY,OAAA,CAAA;AACjE;AASA,SAAS,gBAAgB,GAAA,EACzB;AACI,EAAA,OAAO,OAAO,EAAE,OAAA,EAAS,SAAA,EAAW,SAAQ,KAC5C;AACI,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,GAAM,OAAA,CAAQ,IAAI,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAA,GAAI,MAAA;AAExD,IAAA,IAAI,GAAA,CAAI,GAAA,IAAO,CAAC,GAAA,EAChB;AACI,MAAA,OAAO,EAAC;AAAA,IACZ;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,GAAA,EAAK,YAAY,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAC,CAAA;AAElF,IAAA,OAAO,CAAC,OAAA,CAAQ,GAAA,EAAK,QAAA,EAAU,OAAA,EAAS,GAAG,CAAC,CAAA;AAAA,EAChD,CAAA;AACJ;AAGO,SAAS,iBAAiB,GAAA,EACjC;AACI,EAAA,MAAM,KAAA,GAAQ,YAAY,cAAA,CAAe,GAAA,CAAI,EAAE,CAAA,EAAG,kBAAA,CAAmB,GAAG,CAAC,CAAA,CACpE,YAAA,CAAa,mBAAmB,GAAG,CAAC,CAAA,CACpC,MAAA,CAAO,cAAA,CAAe,GAAG,CAA4B,CAAA,CACrD,OAAA,CAAQ,eAAA,CAAgB,GAAG,CAAyB,CAAA;AAEzD,EAAA,IAAI,IAAI,QAAA,EACR;AACI,IAAA,MAAM,EAAE,KAAA,EAAO,YAAA,EAAc,SAAA,KAAc,GAAA,CAAI,QAAA;AAC/C,IAAA,KAAA,CAAM,QAAA,CAAS;AAAA,MACX,KAAA;AAAA,MACA,GAAI,YAAA,GAAe,EAAE,YAAA,KAAiB,EAAC;AAAA,MACvC,GAAI,SAAA,GAAY,EAAE,SAAA,KAAc;AAAC,KACpC,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,KAAA;AACX;AAYO,SAAS,qBAAqB,GAAA,EACrC;AACI,EAAA,OAAOA,KAAK,MAAA,CAAO;AAAA,IACf,GAAG,IAAI,GAAA,EAAK,UAAA;AAAA,IACZ,GAAI,GAAA,CAAI,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,EAAC,GAAI,EAAE,OAAA,EAASA,IAAAA,CAAK,MAAA,EAAO;AAAE,GAC7D,CAAA;AACL;AAEA,SAAS,oBAAA,CAAqB,GAAA,EAAsB,MAAA,EAAiB,OAAA,EACrE;AACI,EAAA,MAAM,QAAQ,KAAA,CAAM,MAAA,CAAO,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAM;AAElD,EAAA,IAAI,KAAA,EACJ;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,EAAG,IAAI,EAAE,CAAA,qCAAA,EAAwC,MAAM,IAAA,IAAQ,QAAQ,CAAA,CAAA,EAAI,KAAA,CAAM,OAAO,CAAA,gHAAA,EAE9E,IAAI,GAAA,GAAM,iBAAA,GAAoB,aAAa,CAAA,EAAG,GAAA,CAAI,OAAO,CAAC,GAAA,CAAI,MAAA,GAAS,kBAAA,GAAqB,EAAE,CAAA,CAAA;AAAA,KAC5G;AAAA,EACJ;AACJ;AAOO,SAAS,oBAAoB,GAAA,EACpC;AACI,EAAA,MAAM,MAAA,GAAS,qBAAqB,GAAG,CAAA;AACvC,EAAA,MAAM,QAAQ,WAAA,CAAY,CAAA,MAAA,EAAS,GAAA,CAAI,EAAE,WAAW,MAAM,CAAA;AAC1D,EAAA,MAAM,OAAO,KAAA,CAAM,IAAA;AAEnB,EAAA,KAAA,CAAM,IAAA,IAAQ,OAAO,OAAA,KACrB;AACI,IAAA,cAAA,CAAe,GAAA,EAAK,CAAA,EAAG,GAAA,CAAI,EAAE,CAAA,eAAA,CAAiB,CAAA;AAC9C,IAAA,oBAAA,CAAqB,GAAA,EAAK,QAAQ,OAAO,CAAA;AACzC,IAAA,MAAM,KAAK,OAAkC,CAAA;AAAA,EACjD,CAAA,CAAA;AAEA,EAAA,OAAO,KAAA;AACX;AAGA,SAAS,YAAA,CAAa,KAAsB,OAAA,EAC5C;AACI,EAAA,MAAM,UAAU,GAAA,CAAI,GAAA,IAAO,GAAA,CAAI,MAAA,GAAS,SAAY,OAAA,CAAQ,OAAA;AAE5D,EAAA,OAAO,EAAE,OAAA,EAAS,GAAA,EAAK,GAAA,CAAI,GAAA,GAAM,QAAQ,GAAA,CAAI,GAAA,EAAK,OAAO,CAAA,GAAI,MAAA,EAAU;AAC3E;AAOA,eAAe,QAAA,CAAS,OAAsB,OAAA,EAC9C;AACI,EAAA,MAAM,EAAE,OAAA,EAAS,GAAA,KAAQ,YAAA,CAAa,KAAA,CAAM,aAAa,OAAO,CAAA;AAEhE,EAAA,IACA;AACI,IAAA,MAAM,eAAe,KAAA,CAAM,WAAA,EAAa,MAAM,MAAA,EAAQ,OAAA,EAAS,KAAK,MAAS,CAAA;AAAA,EACjF,SACO,KAAA,EACP;AACI,IAAA,WAAA,CAAY,IAAA,CAAK,CAAA,OAAA,EAAU,KAAA,CAAM,EAAE,CAAA,2EAAA,CAAA,EAA+E;AAAA,MAC9G,OAAO,KAAA,CAAM,EAAA;AAAA,MACb,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,KAC/D,CAAA;AAAA,EACL;AACJ;AAOA,eAAe,WAAA,CAAY,KAAA,EAAsB,KAAA,EAAsB,MAAA,EAAiB,OAAA,EACxF;AACI,EAAA,MAAM,QAAQ,KAAA,CAAM,MAAA,CAAO,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAM;AAElD,EAAA,IAAI,KAAA,EACJ;AACI,IAAA,WAAA,CAAY,KAAK,CAAA,OAAA,EAAU,KAAA,CAAM,EAAE,CAAA,cAAA,EAAiB,KAAA,CAAM,IAAI,CAAA,wCAAA,CAAA,EAA4C;AAAA,MACtG,OAAO,KAAA,CAAM,EAAA;AAAA,MACb,OAAO,KAAA,CAAM,IAAA;AAAA,MACb,OAAO,CAAA,EAAG,KAAA,CAAM,QAAQ,QAAQ,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA;AAAA,KACpD,CAAA;AAED,IAAA;AAAA,EACJ;AAEA,EAAA,MAAM,QAAA,CAAS,OAAO,OAAkC,CAAA;AAC5D;AAUO,SAAS,uBAAuB,KAAA,EACvC;AACI,EAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,KAAA,CAAM,WAAW,CAAA;AAErD,EAAA,KAAA,CAAM,QAAQ,SAAA,CAAU,CAAA,OAAA,KAAW,QAAA,CAAS,KAAA,EAAO,OAAO,CAAC,CAAA;AAC3D,EAAA,KAAA,CAAM,YAAY,SAAA,EAAW,OAAA,CAAQ,CAAA,KAAA,KACjC,KAAA,CAAM,UAAU,CAAA,OAAA,KAAW,WAAA,CAAY,KAAA,EAAO,KAAA,EAAO,QAAQ,OAAO,CAAA,EAAG,EAAE,SAAA,EAAW,IAAA,EAAM,CAAC,CAAA;AACnG;;;AC3WA,IAAM,QAAA,GAAW,qBAAA;AAMjB,IAAM,mBAAA,GAAsB,CAAC,QAAA,EAAU,OAAO,CAAA;AAKvC,IAAM,YAAA,GAAN,MAAM,aAAA,CACb;AAAA,EAOI,WAAA,CACa,IACA,WAAA,EAEb;AAHa,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA,EAGb;AAAA,EARQ,aAAA;AAAA,EACA,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaR,GAAA,CACI,MAAA,EACA,OAAA,GAA0B,EAAC,EAE/B;AACI,IAAA,IAAA,CAAK,WAAW,KAAA,EAAO,CAAC,CAAC,IAAA,CAAK,YAAY,GAAG,CAAA;AAC7C,IAAA,eAAA,CAAgB,IAAA,CAAK,IAAI,MAAM,CAAA;AAE/B,IAAA,OAAO,IAAA,CAAK,KAAK,EAAE,GAAA,EAAK,QAAQ,MAAA,EAAQ,OAAA,CAAQ,MAAA,KAAW,IAAA,EAAM,CAAA;AAAA,EACrE;AAAA;AAAA,EAGA,MAAyB,MAAA,EACzB;AACI,IAAA,IAAA,CAAK,WAAW,OAAA,EAAS,CAAC,CAAC,IAAA,CAAK,YAAY,KAAK,CAAA;AAEjD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,KAAA,EAAO,QAAQ,CAAA;AAAA,EACtC;AAAA;AAAA,EAGA,KAAwB,MAAA,EACxB;AACI,IAAA,IAAA,CAAK,WAAW,MAAA,EAAQ,CAAC,CAAC,IAAA,CAAK,YAAY,IAAI,CAAA;AAE/C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,QAAQ,CAAA;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAA,GACA;AACI,IAAA,IAAA,CAAK,UAAA,CAAW,WAAA,EAAa,IAAA,CAAK,WAAA,CAAY,SAAS,CAAA;AAEvD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,WAAA,EACJ;AACI,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,WAAA,EAAa,CAAC,GAAG,IAAA,CAAK,WAAA,CAAY,WAAA,EAAa,GAAG,WAAW,CAAA,EAAG,CAAA;AAAA,EACvF;AAAA;AAAA,EAGA,KAAK,eAAA,EACL;AACI,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,CAAY,eAAA,IAAmB,EAAC;AACrD,IAAA,MAAM,eAAA,GAAkB,OAAA,KAAY,GAAA,IAAO,eAAA,KAAoB,GAAA,GACzD,MACA,CAAC,GAAG,OAAA,EAAS,GAAG,eAAe,CAAA;AAErC,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,eAAA,EAAiB,CAAA;AAAA,EACxC;AAAA;AAAA,EAGA,SAAS,QAAA,EACT;AACI,IAAA,IAAA,CAAK,WAAW,UAAA,EAAY,CAAC,CAAC,IAAA,CAAK,YAAY,QAAQ,CAAA;AAEvD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,CAAA;AAAA,EACjC;AAAA;AAAA,EAGA,QAAQ,SAAA,EACR;AACI,IAAA,IAAA,CAAK,WAAW,SAAA,EAAW,CAAC,CAAC,IAAA,CAAK,YAAY,OAAO,CAAA;AAErD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,OAAA,EAAS,WAA6B,CAAA;AAAA,EAC7D;AAAA;AAAA,EAGA,SAAS,SAAA,EACT;AACI,IAAA,IAAA,CAAK,WAAW,UAAA,EAAY,CAAC,CAAC,IAAA,CAAK,YAAY,QAAQ,CAAA;AAEvD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,QAAA,EAAU,WAA6B,CAAA;AAAA,EAC9D;AAAA;AAAA,EAGA,KACI,EAAA,EAEJ;AACI,IAAA,IAAA,CAAK,WAAW,MAAA,EAAQ,CAAC,CAAC,IAAA,CAAK,YAAY,IAAI,CAAA;AAE/C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAE,IAAA,EAAM,IAAmB,CAAA;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAA,CACI,KAAA,EACA,EAAA,EACA,OAAA,GAA6B,EAAC,EAElC;AACI,IAAA,IAAA,CAAK,WAAW,OAAA,EAAS,CAAC,CAAC,IAAA,CAAK,YAAY,KAAK,CAAA;AAEjD,IAAA,OAAO,IAAA,CAAK,KAAK,EAAE,KAAA,EAAO,EAAE,KAAA,EAAO,EAAA,EAAuB,OAAA,EAAQ,EAAG,CAAA;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,MAAA,EACV;AACI,IAAA,IAAA,CAAK,WAAW,WAAA,EAAa,CAAC,CAAC,IAAA,CAAK,YAAY,SAAS,CAAA;AAEzD,IAAA,OAAO,IAAA,CAAK,KAAK,EAAE,SAAA,EAAW,CAAC,GAAG,MAAM,GAAG,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,OAAA,GACJ;AACI,IAAA,IAAA,CAAK,aAAA,KAAkB,mBAAA,CAAoB,IAAA,CAAK,WAAW,CAAA;AAE3D,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EAChB;AAAA;AAAA,EAGA,IAAI,MAAA,GACJ;AACI,IAAA,IAAA,CAAK,UAAA,KAAe,gBAAA,CAAiB,IAAA,CAAK,WAAW,CAAA;AAErD,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAM,MAAA,EACZ;AACI,IAAA,MAAM,EAAE,OAAA,EAAS,GAAA,EAAI,GAAI,MAAA;AACzB,IAAA,cAAA,CAAe,IAAA,CAAK,WAAA,EAAa,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,QAAA,CAAU,CAAA;AACrD,IAAA,IAAA,CAAK,eAAe,GAAG,CAAA;AACvB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,WAAA,CAAY,OAAA,EAAS,GAAG,CAAA;AAC9C,IAAA,MAAM,IAAA,CAAK,OAAO,KAAA,CAAM,OAAA,EAAS,YAAY,EAAE,KAAA,EAAO,UAAU,CAAA;AAChE,IAAA,MAAM,eAAe,IAAA,CAAK,WAAA,EAAa,KAAK,MAAA,EAAQ,OAAA,EAAS,UAAU,MAAS,CAAA;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,MAAA,EACb;AACI,IAAA,MAAM,EAAE,OAAA,EAAS,GAAA,EAAI,GAAI,MAAA;AACzB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,WAAA,CAAY,QAAA,EAAU,GAAG,CAAA;AAC/C,IAAA,MAAM,IAAA,CAAK,OAAO,MAAA,CAAO,OAAA,EAAS,YAAY,EAAE,KAAA,EAAO,UAAU,CAAA;AAAA,EACrE;AAAA;AAAA,EAGQ,eAAe,GAAA,EACvB;AACI,IAAA,IAAI,IAAA,CAAK,WAAA,CAAY,GAAA,IAAO,CAAC,GAAA,EAC7B;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,CAAA,OAAA,EAAU,KAAK,EAAE,CAAA,4MAAA;AAAA,OAGrB;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,WAAA,CAAY,QAA4B,GAAA,EAChD;AACI,IAAA,MAAM,MAAA,GAAS,KAAK,WAAA,CAAY,GAAA;AAChC,IAAA,IAAI,CAAC,MAAA,IAAU,GAAA,KAAQ,MAAA,EACvB;AACI,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,QAAQC,KAAAA,CAAM,MAAA,CAAO,EAAE,GAAGD,KAAK,MAAA,CAAO,MAAA,CAAO,UAAU,CAAA,EAAG,UAAU,MAAA,CAAO,QAAA,EAAS,EAAG,GAAG,EAAE,KAAA,EAAM;AACxG,IAAA,IAAI,KAAA,EACJ;AACI,MAAA,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA,EAAG,KAAA,CAAM,QAAQ,QAAQ,CAAA,CAAA,EAAI,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,IACvE;AAEA,IAAA,OAAO,QAAQ,MAAA,EAAQ,GAAG,KAAK,IAAA,CAAK,SAAA,CAAU,QAAQ,wDAAwD,CAAA;AAAA,EAClH;AAAA,EAEQ,SAAA,CAAU,QAA4B,MAAA,EAC9C;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,OAAA,EAAU,KAAK,EAAE,CAAA,GAAA,EAAM,MAAM,CAAA,oBAAA,EAAuB,MAAM,aAC7C,MAAM,CAAA,8EAAA;AAAA,KACvB;AAAA,EACJ;AAAA,EAEQ,KAAK,OAAA,EACb;AACI,IAAA,OAAO,IAAI,aAAA,CAAa,IAAA,CAAK,EAAA,EAAI,EAAE,GAAG,IAAA,CAAK,WAAA,EAAa,GAAG,OAAA,EAAS,CAAA;AAAA,EACxE;AAAA,EAEQ,UAAA,CAAW,QAAgB,QAAA,EACnC;AACI,IAAA,IAAI,QAAA,EACJ;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,UAAU,IAAA,CAAK,EAAE,CAAA,oBAAA,EAAuB,MAAM,oGAEhC,MAAM,CAAA,QAAA;AAAA,OACxB;AAAA,IACJ;AAAA,EACJ;AACJ;AAMA,SAAS,eAAA,CAAgB,IAAY,MAAA,EACrC;AACI,EAAA,IAAI,MAAA,CAAO,IAAI,CAAA,KAAM,QAAA,EACrB;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,UAAU,EAAE,CAAA,qCAAA,EAAwC,OAAO,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA,mJAAA;AAAA,KAG5E;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,OAAA,CAAS,MAAA,CAAmB,UAAU,CAAA;AAE5D,EAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EACtB;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,UAAU,EAAE,CAAA,gHAAA;AAAA,KAEhB;AAAA,EACJ;AAEA,EAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAC,KAAA,EAAO,WAAW,CAAA,KAAM,cAAA,CAAe,EAAA,EAAI,MAAA,EAAmB,KAAA,EAAO,WAAW,CAAC,CAAA;AACtG;AAEA,SAAS,cAAA,CAAe,EAAA,EAAY,MAAA,EAAiB,KAAA,EAAe,WAAA,EACpE;AACI,EAAA,IAAI,mBAAA,CAAoB,QAAA,CAAS,KAAK,CAAA,EACtC;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,OAAA,EAAU,EAAE,CAAA,aAAA,EAAgB,KAAK,uCACP,mBAAA,CAAoB,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,OAAO,CAAC,CAAA,wGAAA;AAAA,KAExF;AAAA,EACJ;AAEA,EAAA,IAAI,WAAA,CAAY,IAAA,KAAS,QAAA,IAAY,CAAA,CAAE,MAAA,CAAO,YAAY,EAAC,EAAG,QAAA,CAAS,KAAK,CAAA,EAC5E;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,OAAA,EAAU,EAAE,CAAA,aAAA,EAAgB,KAAK,4OAGjB,KAAK,CAAA,gBAAA;AAAA,KACzB;AAAA,EACJ;AACJ;AAYO,SAAS,YAAgC,EAAA,EAChD;AACI,EAAA,IAAI,CAAC,QAAA,CAAS,IAAA,CAAK,EAAE,CAAA,EACrB;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,aAAA,EAAgB,EAAE,CAAA,0BAAA,EAA6B,QAAQ,CAAA,uJAAA;AAAA,KAG3D;AAAA,EACJ;AAEA,EAAA,OAAO,IAAI,YAAA,CAAa,EAAA,EAAI,EAAE,EAAA,EAAI,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,WAAA,EAAa,EAAC,EAAG,CAAA;AACxF;ACzVO,IAAM,iBAAkC,CAAA,CAAA,KAC1C,CAAA,CAAE,GAAA,CAAI,MAAM,GAA0C,MAAA,IAAU,IAAA;AAK9D,SAAS,cAAA,CAAe,QAAuB,EAAA,EACtD;AACI,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,EAAA,CAAG,CAAC,CAAA,CAAE,WAAA,EAAa,CAAA,EAAG,EAAA,CAAG,KAAA,CAAM,CAAC,CAAC,CAAA,KAAA,CAAA;AACxD;AAEA,SAAS,cAAA,CAAe,GAAY,cAAA,EACpC;AACI,EAAA,MAAM,OAAA,GAAU,eAAe,CAAC,CAAA;AAEhC,EAAA,IAAI,CAAC,OAAA,EACL;AACI,IAAA,MAAM,IAAI,iBAAA,CAAkB,EAAE,OAAA,EAAS,gEAAgE,CAAA;AAAA,EAC3G;AAEA,EAAA,OAAO,OAAA;AACX;AAOA,SAAS,cAAA,CAAe,KAAsB,CAAA,EAC9C;AACI,EAAA,MAAM,SAAA,GAAY,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AAE9B,EAAA,IAAI,GAAA,CAAI,SAAA,IAAa,SAAA,IAAa,IAAA,EAClC;AACI,IAAA,MAAM,IAAI,iBAAA,CAAkB;AAAA,MACxB,OAAA,EAAS,CAAA,OAAA,EAAU,GAAA,CAAI,EAAE,CAAA,sDAAA;AAAA,KAC5B,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,SAAA;AACX;AAEA,eAAe,eAAA,CAAgB,GAAA,EAAsB,GAAA,EAA6B,GAAA,EAClF;AACI,EAAA,MAAM,YAAY,GAAA,KAAQ,UAAA,GAAa,IAAI,QAAA,IAAY,GAAA,CAAI,UAAU,GAAA,CAAI,OAAA;AAEzE,EAAA,IAAI,SAAA,IAAa,CAAC,MAAM,SAAA,CAAU,GAAG,CAAA,EACrC;AACI,IAAA,MAAM,IAAI,cAAA,CAAe,EAAE,OAAA,EAAS,CAAA,EAAG,GAAA,KAAQ,UAAA,GAAa,SAAA,GAAY,SAAS,CAAA,QAAA,EAAW,GAAA,CAAI,EAAE,sBAAsB,CAAA;AAAA,EAC5H;AACJ;AAGA,SAASE,cAAa,GAAA,EACtB;AACI,EAAA,OAAOF,KAAK,MAAA,CAAO;AAAA,IACf,OAAO,GAAA,CAAI,KAAA;AAAA,IACX,GAAI,IAAI,IAAA,GAAO,EAAE,MAAM,GAAA,CAAI,IAAA,KAAS;AAAC,GACxC,CAAA;AACL;AAEA,SAAS,eAAe,GAAA,EACxB;AACI,EAAA,OAAOA,IAAAA,CAAK,MAAA,CAAO,EAAE,EAAA,EAAIA,IAAAA,CAAK,MAAA,EAAO,EAAG,GAAGE,aAAAA,CAAa,GAAG,CAAA,CAAE,UAAA,EAAY,CAAA;AAC7E;AAMA,SAAS,aAAA,CAAc,KAAsB,KAAA,EAC7C;AACI,EAAA,IAAI,CAAC,IAAI,QAAA,EACT;AACI,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,MAAM,KAAA,GAAQ,cAAA,CAAe,IAAI,KAAA,CAAO,OAAO,IAAI,EAAC;AAE1D,EAAA,OAAO,cAAA,CAAe,EAAE,GAAG,GAAA,CAAI,QAAA,EAAU,GAAG,GAAA,EAAK,QAAA,EAAU,cAAA,CAAe,GAAG,CAAA,EAAG,CAAA;AACpF;AAEA,SAAS,eAAiC,KAAA,EAC1C;AACI,EAAA,OAAO,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,GAAG,KAAK,CAAA,KAAM,KAAA,KAAU,MAAS,CAAC,CAAA;AAC9F;AAGA,SAAS,UAAA,CAAW,GAAA,EAAsB,MAAA,EAAuB,IAAA,EAAc,KAAA,EAC/E;AACI,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,GAAA,EAAK,MAAA,KAAW,KAAK,CAAA;AACpD,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,MAAM,CAAA,CAAE,IAAI,CAAA,CAAE,KAAA,CAAM,KAAK,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,WAAW,CAAA;AACpE,EAAA,MAAM,UAAU,GAAA,CAAI,eAAA,GAAkB,QAAQ,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA,GAAI,OAAA;AAE1E,EAAA,OAAO,QAAA,GAAW,OAAA,CAAQ,QAAA,CAAS,QAAQ,CAAA,GAAI,OAAA;AACnD;AAEA,SAAS,SAAS,GAAA,EAClB;AACI,EAAA,OAAO,IAAI,GAAA,GAAM,EAAE,OAAO,GAAA,CAAI,GAAA,KAAQ,EAAC;AAC3C;AAGO,SAAS,SAAA,CAAU,KAAA,EAAsB,IAAA,EAAc,cAAA,EAC9D;AACI,EAAA,MAAM,MAAM,KAAA,CAAM,WAAA;AAElB,EAAA,OAAO,UAAA,CAAW,GAAA,EAAK,KAAA,EAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,EAAE,CAAA,CAAA,EAAI,SAAS,GAAG,CAAC,CAAA,CAAE,OAAA,CAAQ,OAAO,CAAA,KACjF;AACI,IAAA,MAAM,OAAA,GAAU,cAAA,CAAe,CAAA,CAAE,GAAA,EAAK,cAAc,CAAA;AACpD,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AAClC,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAA,CAAM,MAAM,CAAA,CAAE,IAAA,EAAK,EAAG,KAAK,CAAA,GAAI,MAAA;AACjE,IAAA,MAAM,eAAA,CAAgB,KAAK,SAAA,EAAW,iBAAA,CAAkB,KAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAC,CAAA;AAErF,IAAA,OAAO,aAAa,GAAA,EAAK,WAAA,CAAY,KAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAC,CAAA;AAAA,EACtE,CAAC,CAAA;AACL;AAGO,SAAS,UAAA,CAAW,KAAA,EAAsB,IAAA,EAAc,cAAA,EAC/D;AACI,EAAA,MAAM,MAAM,KAAA,CAAM,WAAA;AAClB,EAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAElB,EAAA,OAAO,UAAA,CAAW,KAAK,KAAA,EAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,EAAE,CAAA,CAAA,EAAI,EAAE,MAAM,KAAA,CAAM,KAAA,EAAO,GAAG,QAAA,CAAS,GAAG,GAAG,CAAA,CAAE,OAAA,CAAQ,OAAO,CAAA,KAC3G;AACI,IAAA,MAAM,OAAA,GAAU,cAAA,CAAe,CAAA,CAAE,GAAA,EAAK,cAAc,CAAA;AACpD,IAAA,MAAM,SAAA,GAAY,cAAA,CAAe,GAAA,EAAK,CAAA,CAAE,GAAG,CAAA;AAC3C,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,EAAE,IAAA,EAAK;AACrC,IAAA,MAAM,MAAM,GAAA,CAAI,GAAA,GAAM,QAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA,GAAI,MAAA;AAChD,IAAA,MAAM,eAAA,CAAgB,KAAK,UAAA,EAAY,iBAAA,CAAkB,KAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAC,CAAA;AACtF,IAAA,MAAM,KAAA,CAAM,EAAA,CAAG,EAAE,GAAG,WAAA,CAAY,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAA,EAAG,KAAA,EAAO,IAAA,EAAM,CAAA;AAE5E,IAAA,OAAO,eAAe,GAAA,EAAK,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,KAAK,SAAS,CAAA;AAAA,EACpE,CAAC,CAAA;AACL;AAMA,SAAS,cAAc,MAAA,EACvB;AACI,EAAA,MAAM,SAAkC,EAAC;AAEzC,EAAA,KAAA,MAAW,SAAS,MAAA,EACpB;AACI,IAAA,KAAA,MAAW,CAAC,KAAA,EAAO,MAAM,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,WAAA,CAAY,GAAA,EAAK,UAAA,IAAc,EAAE,CAAA,EACpF;AACI,MAAA,MAAA,CAAO,KAAK,CAAA,GAAIF,IAAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAAA,IACxC;AAAA,EACJ;AAEA,EAAA,OAAOA,IAAAA,CAAK,OAAO,MAAM,CAAA;AAC7B;AAGA,eAAe,aAAA,CAAc,GAAA,EAAsB,OAAA,EAAiB,SAAA,EAAoB,OAAgC,EAAA,EACxH;AACI,EAAA,MAAM,MAAuB,GAAA,CAAI,GAAA,GAAM,QAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA,GAAI,MAAA;AAEjE,EAAA,IAAI,GAAA,CAAI,GAAA,IAAO,CAAC,GAAA,EAChB;AACI,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,IAAI,GAAA,CAAI,OAAA,IAAW,CAAC,MAAM,GAAA,CAAI,OAAA,CAAQ,iBAAA,CAAkB,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,GAAG,CAAC,CAAA,EACrF;AACI,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAI,MAAM,YAAA,CAAa,GAAA,EAAK,WAAA,CAAY,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,GAAG,GAAG,EAAE,CAAA;AAE7F,EAAA,OAAO,IAAI,IAAA,GAAO,EAAE,OAAO,IAAA,EAAK,GAAI,EAAE,KAAA,EAAM;AAChD;AAuBO,SAAS,cAAc,MAAA,EAC9B;AACI,EAAA,OAAO,OAAO,WAAA,CAAY,MAAA,CAAO,IAAI,CAAA,KAAA,KAAS,CAAC,MAAM,EAAA,EAAI;AAAA,IACrD,GAAA,EAAK,OAAO,IAAA,CAAK,KAAA,CAAM,YAAY,GAAA,EAAK,UAAA,IAAc,EAAE,CAAA;AAAA,IACxD,KAAA,EAAO,KAAA,CAAM,WAAA,CAAY,KAAA,KAAU;AAAA,GACtC,CAAC,CAAC,CAAA;AACP;AAOA,SAAS,uBAAuB,MAAA,EAChC;AACI,EAAA,MAAM,aAAA,GAAgBA,IAAAA,CAAK,MAAA,CAAO,EAAE,KAAKA,IAAAA,CAAK,KAAA,CAAMA,IAAAA,CAAK,MAAA,EAAQ,CAAA,EAAG,KAAA,EAAOA,IAAAA,CAAK,OAAA,IAAW,CAAA;AAE3F,EAAA,OAAOA,KAAK,MAAA,CAAO;AAAA,IACf,EAAA,EAAIA,KAAK,MAAA,EAAO;AAAA,IAChB,MAAA,EAAQA,KAAK,MAAA,CAAO,MAAA,CAAO,YAAY,MAAA,CAAO,GAAA,CAAI,WAAS,CAAC,KAAA,CAAM,IAAIA,IAAAA,CAAK,QAAA,CAASE,cAAa,KAAA,CAAM,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAAA,IACvH,QAAA,EAAUF,IAAAA,CAAK,MAAA,CAAO,MAAA,CAAO,YAAY,MAAA,CAAO,GAAA,CAAI,CAAA,KAAA,KAAS,CAAC,KAAA,CAAM,EAAA,EAAI,aAAa,CAAC,CAAC,CAAC;AAAA,GAC3F,CAAA;AACL;AAQA,SAAS,eAAA,CAAgB,MAAA,EAAyB,IAAA,EAAc,OAAA,EAChE;AACI,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA,CAAE,KAAA,CAAM,EAAE,KAAA,EAAO,aAAA,CAAc,MAAM,CAAA,EAAG,CAAA;AACtE,EAAA,MAAM,IAAA,GAAO,QAAQ,GAAA,EAAK,MAAA,GAAS,QAAQ,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,GAAI,OAAA;AAC9D,EAAA,MAAM,UAAU,OAAA,CAAQ,IAAA,GAAO,KAAK,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA;AAEzD,EAAA,OAAO,OAAA,CAAQ,QAAA,GACT,OAAA,CAAQ,QAAA,CAAS,eAAe,EAAE,GAAG,OAAA,CAAQ,QAAA,EAAU,UAAU,sBAAA,CAAuB,MAAM,CAAA,EAAG,CAAkB,CAAA,GACnH,OAAA;AACV;AAMO,SAAS,cACZ,MAAA,EACA,QAAA,EACA,MACA,cAAA,EACA,OAAA,GAAgC,EAAC,EAErC;AACI,EAAA,OAAO,gBAAgB,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA,CAAE,OAAA,CAAQ,OAAO,CAAA,KAC7D;AACI,IAAA,MAAM,OAAA,GAAU,cAAA,CAAe,CAAA,CAAE,GAAA,EAAK,cAAc,CAAA;AACpD,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AAClC,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,EAAE,IAAA,EAAK;AAC/B,IAAA,MAAM,EAAA,GAAA,iBAAK,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAClC,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,GAAA,CAAI,OAAO,GAAA,CAAI,CAAA,KAAA,KAAS,aAAA,CAAc,KAAA,CAAM,aAAa,OAAA,EAAS,SAAA,EAAW,KAAA,EAAO,EAAE,CAAC,CAAC,CAAA;AAEtH,IAAA,OAAO;AAAA,MACH,EAAA;AAAA,MACA,MAAA,EAAQ,MAAA,CAAO,WAAA,CAAY,MAAA,CACtB,GAAA,CAAI,CAAC,KAAA,EAAO,KAAA,KAAU,CAAC,KAAA,CAAM,EAAA,EAAI,OAAA,CAAQ,KAAK,CAAC,CAAC,CAAA,CAChD,MAAA,CAAO,CAAC,GAAG,KAAK,CAAA,KAAM,KAAA,KAAU,MAAS,CAAC,CAAA;AAAA,MAC/C;AAAA,KACJ;AAAA,EACJ,CAAC,CAAA;AACL;;;ACvLO,SAAS,iBAAA,CACZ,MAAA,EACA,OAAA,GAA8B,EAAC,EAEnC;AACI,EAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,IAAQ,QAAA;AAC7B,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA;AAEjC,EAAA,UAAA,CAAW,IAAI,CAAA;AACf,EAAA,IAAA,CAAK,QAAQ,cAAc,CAAA;AAC3B,EAAA,0CAAA,CAA2C,MAAM,OAAO,CAAA;AACxD,EAAA,iBAAA,CAAkB,IAAI,CAAA;AACtB,EAAA,IAAA,CAAK,OAAA,CAAQ,CAAA,KAAA,KAAS,eAAA,CAAgB,KAAA,EAAO,IAAI,CAAC,CAAA;AAElD,EAAA,IAAA,CAAK,QAAQ,CAAA,KAAA,KAAS,SAAA,CAAU,KAAA,CAAM,WAAA,EAAa,IAAI,CAAC,CAAA;AACxD,EAAA,IAAA,CAAK,QAAQ,sBAAsB,CAAA;AAEnC,EAAA,OAAO;AAAA,IACH,QAAQ,YAAA,CAAa,YAAA,CAAa,IAAA,EAAM,IAAA,EAAM,OAAO,CAAC,CAAA;AAAA,IACtD,MAAA,EAAQ,MAAA,CAAO,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA,KAAA,KAAS,CAAC,cAAA,CAAe,KAAA,CAAM,EAAE,CAAA,EAAG,KAAA,CAAM,MAAM,CAAC,CAAC,CAAA;AAAA,IACtF;AAAA,GACJ;AACJ;AAEA,SAAS,YAAA,CAAa,MAAA,EAAyB,IAAA,EAAc,OAAA,EAC7D;AACI,EAAA,MAAM,cAAA,GAAiB,QAAQ,OAAA,IAAW,cAAA;AAC1C,EAAA,MAAM,SAAkD,EAAC;AAEzD,EAAA,KAAA,MAAW,SAAS,MAAA,EACpB;AACI,IAAA,MAAA,CAAO,cAAA,CAAe,OAAO,KAAA,CAAM,EAAE,CAAC,CAAA,GAAI,SAAA,CAAU,KAAA,EAAO,IAAA,EAAM,cAAc,CAAA;AAE/E,IAAA,IAAI,KAAA,CAAM,YAAY,KAAA,EACtB;AACI,MAAA,MAAA,CAAO,cAAA,CAAe,OAAO,KAAA,CAAM,EAAE,CAAC,CAAA,GAAI,UAAA,CAAW,KAAA,EAAO,IAAA,EAAM,cAAc,CAAA;AAAA,IACpF;AAAA,EACJ;AAEA,EAAA,MAAA,CAAO,SAAA,GAAY,cAAc,MAAA,EAAQ,aAAA,CAAc,MAAM,CAAA,EAAG,IAAA,EAAM,gBAAgB,OAAO,CAAA;AAE7F,EAAA,OAAO,MAAA;AACX;AAEA,SAAS,WAAW,IAAA,EACpB;AACI,EAAA,IAAI,CAAC,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,IAAM,IAAA,CAAK,MAAA,GAAS,CAAA,IAAK,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAClE;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,mCAAmC,IAAI,CAAA,8HAAA;AAAA,KAE3C;AAAA,EACJ;AACJ;AAGA,SAAS,eAAe,KAAA,EACxB;AACI,EAAA,MAAM,MAAM,KAAA,CAAM,WAAA;AAClB,EAAA,MAAM,OAAA,GAAU,CAAC,GAAA,CAAI,KAAA,GAAQ,aAAa,CAAC,GAAA,CAAI,OAAO,SAAA,GAAY,MAAA;AAElE,EAAA,IAAI,OAAA,EACJ;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,0BAAA,EAA6B,MAAM,EAAE,CAAA,cAAA,EAAiB,OAAO,CAAA,6EAAA,EAEpD,OAAO,CAAA,iBAAA,EAAoB,KAAA,CAAM,EAAE,CAAA,GAAA;AAAA,KAChD;AAAA,EACJ;AAEA,EAAA,IAAI,GAAA,CAAI,GAAA,IAAO,CAAC,GAAA,CAAI,OAAA,EACpB;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,gCAAA,EAAmC,MAAM,EAAE,CAAA,gJAAA;AAAA,KAG/C;AAAA,EACJ;AAEA,EAAA,kCAAA,CAAmC,KAAK,CAAA;AACxC,EAAA,GAAA,CAAI,WAAW,OAAA,CAAQ,CAAA,KAAA,KAAS,eAAA,CAAgB,KAAA,EAAO,KAAK,CAAC,CAAA;AAE7D,EAAA,IAAI,GAAA,CAAI,SAAS,CAAC,GAAA,CAAI,YAAY,GAAA,CAAI,KAAA,CAAM,QAAQ,KAAA,EACpD;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,6BAA6B,KAAA,CAAM,EAAE,iCAAiC,GAAA,CAAI,KAAA,CAAM,QAAQ,KAAK,CAAA,6QAAA;AAAA,KAIjG;AAAA,EACJ;AACJ;AAOA,SAAS,mCAAmC,KAAA,EAC5C;AACI,EAAA,MAAM,MAAM,KAAA,CAAM,WAAA;AAElB,EAAA,IAAA,CAAK,GAAA,CAAI,YAAY,MAAA,GAAS,CAAA,IAAK,IAAI,eAAA,KAAoB,CAAC,IAAI,OAAA,EAChE;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,0BAAA,EAA6B,KAAA,CAAM,EAAE,CAAA,wEAAA,EACR,eAAe,KAAA,EAAO,KAAA,CAAM,EAAE,CAAC,CAAA,GAAA,EAAM,cAAA,CAAe,KAAA,EAAO,KAAA,CAAM,EAAE,CAAC,CAAA,4RAAA;AAAA,KAIrG;AAAA,EACJ;AACJ;AAOA,SAAS,0CAAA,CAA2C,QAAyB,OAAA,EAC7E;AACI,EAAA,MAAM,YAAY,MAAA,CAAO,IAAA,CAAK,WAAS,CAAC,KAAA,CAAM,YAAY,OAAO,CAAA;AAEjE,EAAA,IAAA,CAAA,CAAM,QAAQ,GAAA,EAAK,MAAA,IAAU,KAAK,CAAA,IAAK,OAAA,CAAQ,SAAS,SAAA,EACxD;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,+EAAA,EAAkF,UAAU,EAAE,CAAA,sPAAA;AAAA,KAIlG;AAAA,EACJ;AACJ;AAGA,SAAS,eAAA,CAAgB,OAAsB,KAAA,EAC/C;AACI,EAAA,MAAM,UAAU,KAAA,CAAM,MAAA;AACtB,EAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,KAAA,CAAM,WAAW,CAAA;AACrD,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,CAAK,MAAA,CAAO,UAAU,CAAA,CAAE,IAAA,CAAK,WAChD,CAAC,OAAA,EAAS,aAAa,KAAK,CAAA,IAAK,EAAE,OAAA,CAAQ,QAAA,IAAY,EAAC,EAAG,QAAA,CAAS,KAAK,CAAC,CAAA;AAE9E,EAAA,IAAI,OAAA,EACJ;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,CAAA,0BAAA,EAA6B,MAAM,EAAE,CAAA,eAAA,EAAkB,MAAM,IAAI,CAAA,iEAAA,EACrC,OAAO,CAAA,4BAAA,EAA+B,KAAA,CAAM,EAAE,gCACrE,MAAA,CAAO,IAAA,CAAK,OAAO,UAAU,CAAA,CAAE,KAAK,IAAI,CAAC,CAAA,sCAAA,EAAyC,KAAA,CAAM,EAAE,CAAA,iBAAA;AAAA,KACnG;AAAA,EACJ;AACJ;AAEA,SAAS,kBAAkB,MAAA,EAC3B;AACI,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAE7B,EAAA,KAAA,MAAW,SAAS,MAAA,EACpB;AACI,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA,EACrB;AACI,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,CAAA,2CAAA,EAA8C,KAAA,CAAM,EAAE,CAAA,gCAAA,EACpB,cAAA,CAAe,KAAA,EAAO,KAAA,CAAM,EAAE,CAAC,CAAA,eAAA,EAC5D,cAAA,CAAe,KAAA,CAAM,EAAE,CAAC,CAAA,6CAAA;AAAA,OACjC;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,GAAA,CAAI,MAAM,EAAE,CAAA;AAAA,EACrB;AACJ;AAEA,SAAS,eAAA,CAAgB,OAAsB,IAAA,EAC/C;AACI,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,KAAA,CAAM,WAAW,CAAA,EAAG,IAAA;AAE/C,EAAA,IAAI,UAAU,MAAA,EACd;AACI,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,6BAA6B,KAAA,CAAM,EAAE,CAAA,2CAAA,EAA8C,KAAK,cAAc,IAAI,CAAA,kIAAA;AAAA,KAG9G;AAAA,EACJ;AACJ","file":"index.js","sourcesContent":["/**\n * State Binding\n *\n * What a state router fixes for each state it groups, keyed by the builder's\n * definition — one per builder instance, so an intermediate builder is never\n * bound: the router's path (a state belongs to one router) and the schema\n * every `read()` result is checked against, built once here rather than per\n * request.\n */\n\nimport { Type, type TObject } from '@sinclair/typebox';\nimport type { StateDefinition } from './types';\n\nexport interface StateBinding\n{\n path: string;\n resultSchema: TObject;\n}\n\nconst bindings = new WeakMap<Readonly<StateDefinition>, StateBinding>();\n\n/** `{ value, data }` exactly as declared: `data` when `.data()` is, and nothing else. */\nfunction resultSchema(def: StateDefinition): TObject\n{\n return Type.Object(\n {\n value: def.value!,\n ...(def.data ? { data: def.data } : {}),\n },\n { additionalProperties: false },\n );\n}\n\n/** Bind a state to the router at `path`. */\nexport function bindState(def: Readonly<StateDefinition>, path: string): void\n{\n bindings.set(def, { path, resultSchema: resultSchema(def) });\n}\n\n/** The binding of a state, `undefined` until a router groups that builder. */\nexport function stateBinding(def: Readonly<StateDefinition>): StateBinding | undefined\n{\n return bindings.get(def);\n}\n","/**\n * State Event\n *\n * The two events a state derives: the change signal the app emits\n * (`state.changed`, in-process) and the SSE event the browser receives\n * (`state.<id>.changed`). Between them sits the one rule of the module — a\n * value is only ever what `read()` returned, re-read here, never computed from\n * the signal.\n */\n\nimport { Type, type TObject, type TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { getTransactionContext, onAfterCommit } from '@spfn/core/db';\nimport { defineEvent } from '@spfn/core/event';\nimport type { EventDef, FramePredicate, InitialProducer, SubscribePredicate } from '@spfn/core/event';\nimport { logger } from '@spfn/core/logger';\nimport { stateBinding, type StateBinding } from './state-binding';\nimport type { RoutableState, StateCallContext, StateDefinition } from './types';\n\nconst stateLogger = logger.child('@spfn/core:state');\n\n/** `{ at, value, data? }` as a response and a frame carry it. */\nexport interface Snapshot\n{\n at: string;\n value: unknown;\n data?: unknown;\n}\n\ntype Key = Record<string, string>;\n\ntype FramePayload = Snapshot & { key?: Key; subject?: string };\n\n/** The wire name of a state's SSE event. */\nexport function stateEventName(id: string): string\n{\n return `state.${id}.changed`;\n}\n\n/**\n * The key fields of `keySchema` taken from `source`, or `undefined` when any\n * is absent. Key fields are strings by construction (`.key()` refuses others),\n * so a query value and a payload field compare as they are.\n */\nexport function pickKey(keySchema: TObject, source: Record<string, unknown>): Key | undefined\n{\n const key: Key = {};\n\n for (const field of Object.keys(keySchema.properties))\n {\n const value = source[field];\n if (typeof value !== 'string')\n {\n return undefined;\n }\n key[field] = value;\n }\n\n return key;\n}\n\n/** What `read()` receives: a shared state's value does not depend on the subject, so it gets none. */\nexport function readContext(def: StateDefinition, subject: string | undefined, principal: unknown, key?: Key): StateCallContext\n{\n if (!def.key)\n {\n return { subject, principal };\n }\n\n return def.shared ? { principal, key } : { subject, principal, key };\n}\n\n/** What `.canRead()` / `.canWrite()` receive. */\nexport function permissionContext(def: StateDefinition, subject: string, principal: unknown, key?: Key): StateCallContext\n{\n return def.key ? { subject, principal, key } : { subject, principal };\n}\n\n/**\n * The router binding of a state, or a refusal: an unrouted builder has no\n * subscriber for its signal and no schema to check a read against.\n */\nexport function requireBinding(def: StateDefinition, what: string): StateBinding\n{\n const binding = stateBinding(def);\n\n if (!binding)\n {\n throw new Error(\n `State \"${def.id}\" is in no defineStateRouter, so ${what} reaches nothing. `\n + 'The router subscribes the change signal and checks every read, and every builder call returns a new '\n + 'builder — an earlier one is never routed. Group the state with defineStateRouter and export the builder '\n + 'instance you grouped.',\n );\n }\n\n return binding;\n}\n\n/** Refuse a `read()` result that is not what the state declares, naming the state, never the value. */\nfunction assertResult(def: StateDefinition, binding: StateBinding, result: unknown): asserts result is Omit<Snapshot, 'at'>\n{\n const error = Value.Errors(binding.resultSchema, result).First();\n\n if (error)\n {\n throw new Error(\n `State \"${def.id}\" read() returned a result its declaration does not allow: ${error.path || '(root)'} ${error.message}. `\n + 'Every response and frame is checked against .value() and .data(), so a client never receives a value the '\n + 'contract does not describe. Return { value'\n + `${def.data ? ', data' : ''} } as declared.`,\n );\n }\n}\n\n/**\n * Read the current value and check it against the declaration. `at` is taken\n * before `read()` starts, so a frame is never stamped later than the data it\n * carries.\n */\nexport async function readSnapshot(def: StateDefinition, ctx: StateCallContext, at = new Date().toISOString()): Promise<Snapshot>\n{\n const binding = requireBinding(def, 'a read');\n const result = await def.read!(ctx);\n assertResult(def, binding, result);\n\n return { at, ...result };\n}\n\n/**\n * The frame for a snapshot. A copy: the response keeps the snapshot itself, so\n * neither a handler mutating what it returned nor a subscriber mutating the\n * payload changes the other.\n */\nfunction toFrame(def: StateDefinition, snapshot: Snapshot, subject: string | undefined, key: Key | undefined): FramePayload\n{\n const frame: FramePayload = structuredClone(snapshot);\n\n if (def.key)\n {\n frame.key = { ...key };\n }\n\n if (!(def.key && def.shared))\n {\n frame.subject = subject;\n }\n\n return frame;\n}\n\n/**\n * Read the value for one subject and key, send it as one frame, and return the\n * snapshot. Inside a transaction the frame is published after the root\n * transaction commits, and never when the transaction that read it, or any\n * transaction around it, rolls back; it still carries the value read inside\n * it. Outside a transaction the frame is sent here and awaited, so an emit\n * that throws fails the caller — `onAfterCommit` would only log it.\n */\nexport async function readAndPublish(\n def: StateDefinition,\n event: EventDef<any>,\n subject: string | undefined,\n key: Key | undefined,\n principal: unknown,\n): Promise<Snapshot>\n{\n const snapshot = await readSnapshot(def, readContext(def, subject, principal, key));\n const frame = toFrame(def, snapshot, subject, key);\n\n if (getTransactionContext())\n {\n onAfterCommit(() => event.emit(frame));\n }\n else\n {\n await event.emit(frame);\n }\n\n return snapshot;\n}\n\n// ============================================================================\n// The derived SSE event\n// ============================================================================\n\nfunction framePayloadSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n at: Type.String(),\n value: def.value ?? Type.Unknown(),\n ...(def.data ? { data: def.data } : {}),\n ...(def.key ? { key: def.key } : {}),\n ...(def.key && def.shared ? {} : { subject: Type.String() }),\n });\n}\n\n/**\n * `canRead` translated to the connect: the connection declares its key in the\n * connect query. A key field absent from it denies, without asking `canRead` —\n * there is no key to ask about.\n */\nfunction subscribePredicate(def: StateDefinition): SubscribePredicate\n{\n return async ({ subject, principal, request }) =>\n {\n if (!def.key)\n {\n return def.canRead ? def.canRead({ subject, principal }) : true;\n }\n\n const key = pickKey(def.key, request.query);\n\n return key !== undefined && def.canRead !== undefined && def.canRead({ subject, principal, key });\n };\n}\n\n/**\n * A frame reaches a connection on the key it declared, and only its own\n * subject's value unless shared. Never a connection without a subject: on a\n * stream without auth `canSubscribe` is never asked, so `canRead` would be\n * skipped.\n */\nfunction framePredicate(def: StateDefinition): FramePredicate<FramePayload>\n{\n const fields = def.key ? Object.keys(def.key.properties) : [];\n\n return (payload, { subject, request }) =>\n subject !== undefined\n && fields.every(field => payload.key?.[field] === request.query[field])\n && (payload.subject === undefined || payload.subject === subject);\n}\n\n/**\n * The current frame a connection receives right after `connected`: one read\n * for its subject and the key its query declares, at the read's start like any\n * frame. Asked only after `subscribePredicate` allowed the connection; a keyed\n * state whose key the query lacks was denied there, and is never read here. A\n * read that throws or returns an undeclared result is the handler's to warn.\n */\nfunction initialProducer(def: StateDefinition): InitialProducer<FramePayload>\n{\n return async ({ subject, principal, request }) =>\n {\n const key = def.key ? pickKey(def.key, request.query) : undefined;\n\n if (def.key && !key)\n {\n return [];\n }\n\n const snapshot = await readSnapshot(def, readContext(def, subject, principal, key));\n\n return [toFrame(def, snapshot, subject, key)];\n };\n}\n\n/** The SSE event `state.<id>.changed`, its permissions, initial frame and contract set once, here. */\nexport function createStateEvent(def: StateDefinition): EventDef<any>\n{\n const event = defineEvent(stateEventName(def.id), framePayloadSchema(def))\n .canSubscribe(subscribePredicate(def))\n .filter(framePredicate(def) as FramePredicate<unknown>)\n .initial(initialProducer(def) as InitialProducer<any>);\n\n if (def.contract)\n {\n const { since, deprecatedIn, removedIn } = def.contract;\n event.contract({\n since,\n ...(deprecatedIn ? { deprecatedIn } : {}),\n ...(removedIn ? { removedIn } : {}),\n });\n }\n\n return event;\n}\n\n// ============================================================================\n// The change signal\n// ============================================================================\n\n/**\n * What a change signal must name: keyless `subject`; keyed the key fields, and\n * `subject` unless shared. Always a fresh object, never `def.key` itself: a key\n * declared `additionalProperties: false` would otherwise refuse every\n * `.changedBy()` payload carrying one more field. Extra fields are ignored.\n */\nexport function changedPayloadSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n ...def.key?.properties,\n ...(def.key && def.shared ? {} : { subject: Type.String() }),\n });\n}\n\nfunction assertChangedPayload(def: StateDefinition, schema: TSchema, payload: unknown): void\n{\n const error = Value.Errors(schema, payload).First();\n\n if (error)\n {\n throw new Error(\n `${def.id}.changed.emit() refused its payload: ${error.path || '(root)'} ${error.message}. `\n + 'The module re-reads exactly the subject and key the payload names, so a missing field cannot be guessed. '\n + `Pass ${def.key ? 'every key field' : 'the subject'}${def.key && !def.shared ? ' and the subject' : ''}.`,\n );\n }\n}\n\n/**\n * The change signal `state.changed`: a plain in-process event whose `emit`\n * refuses a builder no router grouped, and validates its payload, since the\n * module reads the state the payload names.\n */\nexport function createChangedSignal(def: StateDefinition): EventDef<any>\n{\n const schema = changedPayloadSchema(def);\n const event = defineEvent(`state.${def.id}.signal`, schema);\n const emit = event.emit;\n\n event.emit = (async (payload: unknown) =>\n {\n requireBinding(def, `${def.id}.changed.emit()`);\n assertChangedPayload(def, schema, payload);\n await emit(payload as Record<string, unknown>);\n }) as EventDef<any>['emit'];\n\n return event;\n}\n\n/** Subject and key a signal payload names; a shared state's names no subject. */\nfunction signalTarget(def: StateDefinition, payload: Record<string, unknown>): { subject?: string; key?: Key }\n{\n const subject = def.key && def.shared ? undefined : payload.subject as string;\n\n return { subject, key: def.key ? pickKey(def.key, payload) : undefined };\n}\n\n/**\n * Re-read and publish for one signal. A `read()` that throws or returns an\n * undeclared result sends nothing and is logged — the signal came after a\n * commit the app already made, so there is nobody to throw to.\n */\nasync function onSignal(state: RoutableState, payload: Record<string, unknown>): Promise<void>\n{\n const { subject, key } = signalTarget(state._definition, payload);\n\n try\n {\n await readAndPublish(state._definition, state._event, subject, key, undefined);\n }\n catch (error)\n {\n stateLogger.warn(`State \"${state.id}\" could not be re-read or published on its change signal; no frame was sent`, {\n state: state.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/**\n * A `.changedBy()` event's payload, checked as the state's own `emit` checks\n * its signal. The app emitted it, so a payload missing a field is logged and\n * skipped rather than thrown.\n */\nasync function onChangedBy(state: RoutableState, event: EventDef<any>, schema: TObject, payload: unknown): Promise<void>\n{\n const error = Value.Errors(schema, payload).First();\n\n if (error)\n {\n stateLogger.warn(`State \"${state.id}\" skipped an \"${event.name}\" payload that names no value to re-read`, {\n state: state.id,\n event: event.name,\n error: `${error.path || '(root)'} ${error.message}`,\n });\n\n return;\n }\n\n await onSignal(state, payload as Record<string, unknown>);\n}\n\n/**\n * Re-read and publish on every signal of `state` and every event it lists in\n * `.changedBy()`, on the pod that emitted it only. A `.changedBy()` event in an\n * event router is cached, so a plain subscriber would re-read on every pod and\n * each would send its own frame; the derived state event is cached too, so the\n * emitting pod's one frame already reaches every pod. The `changed` signal\n * needs no such option: it is in no event router and never cached.\n */\nexport function subscribeChangeSignals(state: RoutableState): void\n{\n const schema = changedPayloadSchema(state._definition);\n\n state.changed.subscribe(payload => onSignal(state, payload));\n state._definition.changedBy?.forEach(event =>\n event.subscribe(payload => onChangedBy(state, event, schema, payload), { localOnly: true }));\n}\n","/**\n * State Builder\n *\n * `defineState(id)` starts an immutable builder: every call returns a new one,\n * order is free and there is no terminal method. `defineStateRouter` reads\n * what a builder declared and derives its routes and SSE event from it.\n */\n\nimport { Kind, Type, type TObject, type TSchema } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport type { MiddlewareHandler } from 'hono';\nimport type { EventDef } from '@spfn/core/event';\nimport type { NamedMiddleware } from '@spfn/core/route';\nimport { createChangedSignal, createStateEvent, pickKey, readAndPublish, requireBinding } from './state-event';\nimport type {\n ChangedPayload,\n EmptyStateShape,\n GrantTarget,\n PermissionContext,\n ReadContext,\n RevokeTarget,\n StateContract,\n StateDefinition,\n StateFrame,\n StatePredicate,\n StateReader,\n StateResult,\n StateShape,\n StateWriteOptions,\n StateWriter,\n WithShape,\n WriteContext,\n WritePermissionContext,\n} from './types';\n\n/** A state id becomes part of a route name (`get<Id>State`) and of an event name (`state.<id>.changed`). */\nconst STATE_ID = /^[a-z][a-zA-Z0-9]*$/;\n\n/**\n * Query names the SSE stream reads itself. Key fields share the connect query\n * with them, so a key field under one of these names would be read as both.\n */\nconst RESERVED_KEY_FIELDS = ['events', 'token'];\n\n/**\n * A server-owned value declared once. See the module README.\n */\nexport class StateBuilder<TId extends string, T extends StateShape>\n{\n /** Type inference helper: what this builder declared. Never set at runtime. */\n declare readonly _types: T;\n\n private changedSignal?: EventDef<any>;\n private stateEvent?: EventDef<any>;\n\n constructor(\n readonly id: TId,\n readonly _definition: Readonly<StateDefinition>,\n )\n {\n }\n\n /**\n * Declare the key: one value per key (and per subject, unless `shared`).\n * Fields are required strings: they travel as query parameters.\n */\n key<K extends TObject, S extends boolean = false>(\n schema: K,\n options: { shared?: S } = {},\n ): StateBuilder<TId, WithShape<T, { key: K; shared: S }>>\n {\n this.assertOnce('key', !!this._definition.key);\n assertKeySchema(this.id, schema);\n\n return this.with({ key: schema, shared: options.shared === true });\n }\n\n /** Declare the value schema. Required. */\n value<V extends TSchema>(schema: V): StateBuilder<TId, WithShape<T, { value: V }>>\n {\n this.assertOnce('value', !!this._definition.value);\n\n return this.with({ value: schema });\n }\n\n /** Declare data attached to the value. Optional. */\n data<D extends TSchema>(schema: D): StateBuilder<TId, WithShape<T, { data: D }>>\n {\n this.assertOnce('data', !!this._definition.data);\n\n return this.with({ data: schema });\n }\n\n /**\n * Declare who the callbacks' `principal` is — what the app's auth\n * middleware sets as `c.get('auth')`. Type-only, but for one rule: the write\n * route refuses a request without a principal (401), so `.canWrite()` and\n * `.write()` receive `P`; `.read()` and `.canRead()` receive `P | undefined`.\n */\n principal<P>(): StateBuilder<TId, WithShape<T, { principal: P }>>\n {\n this.assertOnce('principal', this._definition.principal);\n\n return this.with({ principal: true });\n }\n\n /**\n * Middleware for the two per-state routes, `get<Id>State` / `put<Id>State`.\n * Not authorization: the snapshot and the SSE stream cannot run it, so a\n * state with `.use()` must declare `.canRead()`. Calls add up.\n */\n use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): StateBuilder<TId, T>\n {\n return this.with({ middlewares: [...this._definition.middlewares, ...middlewares] });\n }\n\n /** Server-level named middlewares the two per-state routes skip. Calls add up; `'*'` wins. Needs `.canRead()`, as `.use()`. */\n skip(middlewareNames: string[] | '*'): StateBuilder<TId, T>\n {\n const current = this._definition.skipMiddlewares ?? [];\n const skipMiddlewares = current === '*' || middlewareNames === '*'\n ? '*'\n : [...current, ...middlewareNames];\n\n return this.with({ skipMiddlewares });\n }\n\n /** Publish the read route, the write route and the event in the contract. */\n contract(contract: StateContract): StateBuilder<TId, T>\n {\n this.assertOnce('contract', !!this._definition.contract);\n\n return this.with({ contract });\n }\n\n /** Who may read. Required on a keyed state; checked on HTTP and at SSE connect. */\n canRead(predicate: (ctx: PermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>\n {\n this.assertOnce('canRead', !!this._definition.canRead);\n\n return this.with({ canRead: predicate as StatePredicate });\n }\n\n /** Who may write. Absent → `canRead` decides. */\n canWrite(predicate: (ctx: WritePermissionContext<T>) => boolean | Promise<boolean>): StateBuilder<TId, T>\n {\n this.assertOnce('canWrite', !!this._definition.canWrite);\n\n return this.with({ canWrite: predicate as StatePredicate });\n }\n\n /** The only source of a value. Required. */\n read(\n fn: (ctx: ReadContext<T>) => StateResult<T> | Promise<StateResult<T>>,\n ): StateBuilder<TId, WithShape<T, { read: true }>>\n {\n this.assertOnce('read', !!this._definition.read);\n\n return this.with({ read: fn as StateReader });\n }\n\n /**\n * Derive `PUT <path>/<id>`. `fn` runs the command; the value the route\n * answers and broadcasts is re-read afterwards, never taken from `input`.\n */\n write<I extends TSchema>(\n input: I,\n fn: (ctx: WriteContext<T, I>) => void | Promise<void>,\n options: StateWriteOptions = {},\n ): StateBuilder<TId, WithShape<T, { input: I }>>\n {\n this.assertOnce('write', !!this._definition.write);\n\n return this.with({ write: { input, fn: fn as StateWriter, options } });\n }\n\n /**\n * Domain events whose payload names a changed value of this state, treated\n * like `changed.emit` with that payload: keyless `subject`, keyed the key\n * fields and `subject`, shared the key fields. The router subscribes them\n * and refuses one whose payload schema lacks a field.\n */\n changedBy(events: EventDef<any>[]): StateBuilder<TId, T>\n {\n this.assertOnce('changedBy', !!this._definition.changedBy);\n\n return this.with({ changedBy: [...events] });\n }\n\n /**\n * The change signal. Emit it after the change is committed; the module\n * re-reads the value and sends the frame. Refused on a builder no router\n * grouped — export the instance you passed to `defineStateRouter`.\n */\n get changed(): EventDef<ChangedPayload<T>>\n {\n this.changedSignal ??= createChangedSignal(this._definition);\n\n return this.changedSignal;\n }\n\n /** Internal: the SSE event `state.<id>.changed` (`defineStateRouter(...).events`). */\n get _event(): EventDef<StateFrame<T>>\n {\n this.stateEvent ??= createStateEvent(this._definition);\n\n return this.stateEvent;\n }\n\n /**\n * Activate the state's event on `subject`'s open connections and send it\n * the current value at once. On a keyed state only the connections that\n * declared `key` are opened. Does not consult `canRead`.\n */\n async grant(target: GrantTarget<T>): Promise<void>\n {\n const { subject, key } = target as { subject: string; key?: Record<string, string> };\n requireBinding(this._definition, `${this.id}.grant()`);\n this.assertGrantKey(key);\n const declared = this.declaredKey('grant', key);\n await this._event.grant(subject, declared && { query: declared });\n await readAndPublish(this._definition, this._event, subject, declared, undefined);\n }\n\n /**\n * Deactivate the state's event on `subject`'s open connections: on a keyed\n * state those that declared `key`, or every one when no key is given.\n */\n async revoke(target: RevokeTarget<T>): Promise<void>\n {\n const { subject, key } = target as { subject: string; key?: Record<string, string> };\n const declared = this.declaredKey('revoke', key);\n await this._event.revoke(subject, declared && { query: declared });\n }\n\n /** A keyed grant opens one key: without it there is no key to open and no value to send. */\n private assertGrantKey(key: Record<string, string> | undefined): void\n {\n if (this._definition.key && !key)\n {\n throw new Error(\n `State \"${this.id}\" .grant() was called without a key. `\n + 'A keyed grant opens exactly one key — every connection of the subject on any key would otherwise be '\n + 'opened, including keys canRead refused. Pass { subject, key }.',\n );\n }\n }\n\n /**\n * The declared key fields of `key`, checked against `.key()`: an empty or\n * partial key would match every connection of the subject. A field the\n * declaration does not name is dropped, never matched on — so the check\n * runs on a fresh object of the declared fields, never `.key()` itself: a\n * key declared `additionalProperties: false` would otherwise refuse it.\n * The fresh object keeps `.key()`'s `required`, and a key that still picks\n * to nothing is refused: `undefined` here would mean every connection.\n */\n private declaredKey(method: 'grant' | 'revoke', key: Record<string, string> | undefined): Record<string, string> | undefined\n {\n const schema = this._definition.key;\n if (!schema || key === undefined)\n {\n return undefined;\n }\n\n const error = Value.Errors({ ...Type.Object(schema.properties), required: schema.required }, key).First();\n if (error)\n {\n this.refuseKey(method, `${error.path || '(root)'} ${error.message}`);\n }\n\n return pickKey(schema, key) ?? this.refuseKey(method, '(root) a declared key field is missing or not a string');\n }\n\n private refuseKey(method: 'grant' | 'revoke', reason: string): never\n {\n throw new Error(\n `State \"${this.id}\" .${method}() refused its key: ${reason}. `\n + `A keyed ${method} reaches exactly the connections on one key, so every key field must be named.`,\n );\n }\n\n private with(changes: Partial<StateDefinition>): StateBuilder<TId, any>\n {\n return new StateBuilder(this.id, { ...this._definition, ...changes });\n }\n\n private assertOnce(method: string, declared: boolean): void\n {\n if (declared)\n {\n throw new Error(\n `State \"${this.id}\" already declares .${method}(). `\n + 'A state is declared once, so a second declaration would silently replace the first. '\n + `Declare .${method}() once.`,\n );\n }\n }\n}\n\n/**\n * Refuse a key the module could not carry: it must be an object of required\n * string fields, none named like a query parameter the SSE stream reads.\n */\nfunction assertKeySchema(id: string, schema: TSchema): void\n{\n if (schema[Kind] !== 'Object')\n {\n throw new Error(\n `State \"${id}\" .key() takes an object schema, got ${String(schema[Kind])}. `\n + 'Each key field becomes a named query parameter and a field of the frame\\'s key. '\n + 'Wrap the field: .key(Type.Object({ workspaceId: Type.String() })).',\n );\n }\n\n const fields = Object.entries((schema as TObject).properties);\n\n if (fields.length === 0)\n {\n throw new Error(\n `State \"${id}\" .key() declares no field. A key with no field names nothing to read by. `\n + 'Drop .key() for one value per subject.',\n );\n }\n\n fields.forEach(([field, fieldSchema]) => assertKeyField(id, schema as TObject, field, fieldSchema));\n}\n\nfunction assertKeyField(id: string, schema: TObject, field: string, fieldSchema: TSchema): void\n{\n if (RESERVED_KEY_FIELDS.includes(field))\n {\n throw new Error(\n `State \"${id}\" key field \"${field}\" is reserved. `\n + `The SSE stream reads ${RESERVED_KEY_FIELDS.map(name => `\"${name}\"`).join(' and ')} from the connect query `\n + 'that also carries the key, so the field would be read as both. Rename the field.',\n );\n }\n\n if (fieldSchema.type !== 'string' || !(schema.required ?? []).includes(field))\n {\n throw new Error(\n `State \"${id}\" key field \"${field}\" is not a required string. `\n + 'A key travels as a query parameter, which is always a string, and the SSE filter compares it with the '\n + 'connect query as one — a number would never match, and a missing field names no value. '\n + `Declare it ${field}: Type.String().`,\n );\n }\n}\n\n/**\n * Declare a server-owned state.\n *\n * @example\n * ```typescript\n * export const consent = defineState('consent')\n * .value(Type.Union([Type.Literal('pending'), Type.Literal('agreed')]))\n * .read(({ subject }) => consents.current(subject));\n * ```\n */\nexport function defineState<TId extends string>(id: TId): StateBuilder<TId, EmptyStateShape>\n{\n if (!STATE_ID.test(id))\n {\n throw new Error(\n `defineState(\"${id}\"): a state id must match ${STATE_ID}. `\n + 'It becomes part of the route names get<Id>State / put<Id>State and of the event name state.<id>.changed. '\n + 'Use a lowerCamel identifier, e.g. \"myState\".',\n );\n }\n\n return new StateBuilder(id, { id, shared: false, principal: false, middlewares: [] });\n}\n","/**\n * State Routes\n *\n * The read, write and snapshot routes a state router derives. They are\n * ordinary `RouteDef`s registered through `registerRoutes`, so a state's\n * `.use()` middleware runs inside the route's own chain after the app's\n * global middleware.\n */\n\nimport { Type, type TObject, type TSchema } from '@sinclair/typebox';\nimport type { Context, MiddlewareHandler } from 'hono';\nimport { ForbiddenError, UnauthorizedError } from '@spfn/core/errors';\nimport { route } from '@spfn/core/route';\nimport type { NamedMiddleware, RouteContract, RouteDef, RouteInput } from '@spfn/core/route';\nimport {\n permissionContext,\n pickKey,\n readAndPublish,\n readContext,\n readSnapshot,\n} from './state-event';\nimport type { RoutableState, StateCallContext, StateContract, StateDefinition } from './types';\n\n/** Who a request is for. `null` answers 401. */\nexport type SubjectResolver = (c: Context) => string | null;\n\n/** The SSE `getSubject` default: the `userId` the app's auth middleware set. */\nexport const defaultSubject: SubjectResolver = c =>\n (c.get('auth') as Record<string, string> | undefined)?.userId ?? null;\n\ntype Key = Record<string, string>;\n\n/** `get<Id>State` / `put<Id>State`. */\nexport function stateRouteName(method: 'get' | 'put', id: string): string\n{\n return `${method}${id[0].toUpperCase()}${id.slice(1)}State`;\n}\n\nfunction requireSubject(c: Context, resolveSubject: SubjectResolver): string\n{\n const subject = resolveSubject(c);\n\n if (!subject)\n {\n throw new UnauthorizedError({ message: 'A state is read for a subject, and this request carries none' });\n }\n\n return subject;\n}\n\n/**\n * The write route's principal, `c.get('auth')`. A state that declared\n * `.principal()` types `canWrite` / `write` without `undefined`, so a request\n * whose `auth` is `undefined` or `null` is refused here, before either runs.\n */\nfunction writePrincipal(def: StateDefinition, c: Context): unknown\n{\n const principal = c.get('auth');\n\n if (def.principal && principal == null)\n {\n throw new UnauthorizedError({\n message: `State \"${def.id}\" declares .principal(), and this request carries none`,\n });\n }\n\n return principal;\n}\n\nasync function assertPermitted(def: StateDefinition, can: 'canRead' | 'canWrite', ctx: StateCallContext): Promise<void>\n{\n const predicate = can === 'canWrite' ? def.canWrite ?? def.canRead : def.canRead;\n\n if (predicate && !await predicate(ctx))\n {\n throw new ForbiddenError({ message: `${can === 'canWrite' ? 'Writing' : 'Reading'} state \"${def.id}\" is not permitted` });\n }\n}\n\n/** `{ value, data? }` as declared: one state's response without `at`, and its entry in the snapshot. */\nfunction resultSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n value: def.value!,\n ...(def.data ? { data: def.data } : {}),\n });\n}\n\nfunction responseSchema(def: StateDefinition): TObject\n{\n return Type.Object({ at: Type.String(), ...resultSchema(def).properties });\n}\n\n/**\n * The route contract, or none when the state declares no `.contract()`. A\n * write's own version markers win over the state's, field by field.\n */\nfunction routeContract(def: StateDefinition, write: boolean): RouteContract | undefined\n{\n if (!def.contract)\n {\n return undefined;\n }\n\n const own = write ? stripUndefined(def.write!.options) : {};\n\n return stripUndefined({ ...def.contract, ...own, response: responseSchema(def) }) as RouteContract;\n}\n\nfunction stripUndefined<T extends object>(value: T): Partial<T>\n{\n return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined)) as Partial<T>;\n}\n\n/** `route.<method>(path)` with the state's input, middleware, skips and contract. */\nfunction stateRoute(def: StateDefinition, method: 'get' | 'put', path: string, input: RouteInput)\n{\n const contract = routeContract(def, method === 'put');\n const builder = route[method](path).input(input).use(def.middlewares);\n const skipped = def.skipMiddlewares ? builder.skip(def.skipMiddlewares) : builder;\n\n return contract ? skipped.contract(contract) : skipped;\n}\n\nfunction keyInput(def: StateDefinition): { query?: TObject }\n{\n return def.key ? { query: def.key } : {};\n}\n\n/** `GET <path>/<id>`: subject → key → `canRead` → `at` → `read()`. */\nexport function readRoute(state: RoutableState, path: string, resolveSubject: SubjectResolver): RouteDef<any, any, any>\n{\n const def = state._definition;\n\n return stateRoute(def, 'get', `${path}/${def.id}`, keyInput(def)).handler(async (c) =>\n {\n const subject = requireSubject(c.raw, resolveSubject);\n const principal = c.raw.get('auth');\n const key = def.key ? pickKey(def.key, (await c.data()).query) : undefined;\n await assertPermitted(def, 'canRead', permissionContext(def, subject, principal, key));\n\n return readSnapshot(def, readContext(def, subject, principal, key));\n });\n}\n\n/** `PUT <path>/<id>`: subject → principal (with `.principal()`) → key → `canWrite ?? canRead` → `write()` → `at` → `read()` → one frame. */\nexport function writeRoute(state: RoutableState, path: string, resolveSubject: SubjectResolver): RouteDef<any, any, any>\n{\n const def = state._definition;\n const write = def.write!;\n\n return stateRoute(def, 'put', `${path}/${def.id}`, { body: write.input, ...keyInput(def) }).handler(async (c) =>\n {\n const subject = requireSubject(c.raw, resolveSubject);\n const principal = writePrincipal(def, c.raw);\n const { body, query } = await c.data();\n const key = def.key ? pickKey(def.key, query) : undefined;\n await assertPermitted(def, 'canWrite', permissionContext(def, subject, principal, key));\n await write.fn({ ...readContext(def, subject, principal, key), input: body });\n\n return readAndPublish(def, state._event, subject, key, principal);\n });\n}\n\n/**\n * The snapshot's query: every key field of every keyed state, each optional —\n * a state whose key is incomplete is left out, not refused.\n */\nfunction snapshotQuery(states: RoutableState[]): TObject\n{\n const fields: Record<string, TSchema> = {};\n\n for (const state of states)\n {\n for (const [field, schema] of Object.entries(state._definition.key?.properties ?? {}))\n {\n fields[field] = Type.Optional(schema);\n }\n }\n\n return Type.Object(fields);\n}\n\n/** One state's entry in the snapshot, or `undefined` when its key is incomplete or `canRead` refuses. */\nasync function snapshotEntry(def: StateDefinition, subject: string, principal: unknown, query: Record<string, unknown>, at: string)\n{\n const key: Key | undefined = def.key ? pickKey(def.key, query) : undefined;\n\n if (def.key && !key)\n {\n return undefined;\n }\n\n if (def.canRead && !await def.canRead(permissionContext(def, subject, principal, key)))\n {\n return undefined;\n }\n\n const { value, data } = await readSnapshot(def, readContext(def, subject, principal, key), at);\n\n return def.data ? { value, data } : { value };\n}\n\n/**\n * What the router gives the snapshot route `GET <path>` alone. A per-state\n * route keeps its own state's `.use()` / `.skip()` / `.contract()`, and the\n * SSE stream runs none of them.\n */\nexport interface SnapshotRouteOptions\n{\n /** Middleware for the snapshot route only. Not authorization: the stream never runs it (see README). */\n use?: (MiddlewareHandler | NamedMiddleware<string>)[];\n\n /** Server-level named middlewares the snapshot route skips; `'*'` skips every one. */\n skip?: string[] | '*';\n\n /** Publishes the snapshot route in the contract; the response schema is derived. */\n contract?: StateContract;\n}\n\n/** What the browser cannot read from a type: every state, in declaration order, its key fields and whether it has a write. */\nexport type StatesManifest = Record<string, { key: string[]; write: boolean }>;\n\n/** The snapshot's `manifest`. It is static: the router builds it once and every snapshot answers the same object. */\nexport function stateManifest(states: RoutableState[]): StatesManifest\n{\n return Object.fromEntries(states.map(state => [state.id, {\n key: Object.keys(state._definition.key?.properties ?? {}),\n write: state._definition.write !== undefined,\n }]));\n}\n\n/**\n * The snapshot's response: `at`, one optional `{ value, data? }` per state and\n * one required manifest entry per state, in declaration order. Fresh objects,\n * so no per-state schema is touched.\n */\nfunction snapshotResponseSchema(states: RoutableState[]): TObject\n{\n const manifestEntry = Type.Object({ key: Type.Array(Type.String()), write: Type.Boolean() });\n\n return Type.Object({\n at: Type.String(),\n states: Type.Object(Object.fromEntries(states.map(state => [state.id, Type.Optional(resultSchema(state._definition))]))),\n manifest: Type.Object(Object.fromEntries(states.map(state => [state.id, manifestEntry]))),\n });\n}\n\n/**\n * `route.get(path)` with the router's snapshot options. Each is applied only\n * when given — an empty `use` counts as not given — so a router without them\n * derives the route it always did. The\n * contract is built here, once, when the router is built.\n */\nfunction snapshotBuilder(states: RoutableState[], path: string, options: SnapshotRouteOptions)\n{\n const builder = route.get(path).input({ query: snapshotQuery(states) });\n const used = options.use?.length ? builder.use(options.use) : builder;\n const skipped = options.skip ? used.skip(options.skip) : used;\n\n return options.contract\n ? skipped.contract(stripUndefined({ ...options.contract, response: snapshotResponseSchema(states) }) as RouteContract)\n : skipped;\n}\n\n/**\n * `GET <path>`: one `at`, then every readable state in declaration order, and\n * the router's `manifest`. A `read()` that throws fails it all.\n */\nexport function snapshotRoute(\n states: RoutableState[],\n manifest: StatesManifest,\n path: string,\n resolveSubject: SubjectResolver,\n options: SnapshotRouteOptions = {},\n): RouteDef<any, any, any>\n{\n return snapshotBuilder(states, path, options).handler(async (c) =>\n {\n const subject = requireSubject(c.raw, resolveSubject);\n const principal = c.raw.get('auth');\n const { query } = await c.data();\n const at = new Date().toISOString();\n const entries = await Promise.all(states.map(state => snapshotEntry(state._definition, subject, principal, query, at)));\n\n return {\n at,\n states: Object.fromEntries(states\n .map((state, index) => [state.id, entries[index]])\n .filter(([, entry]) => entry !== undefined)),\n manifest,\n };\n });\n}\n","/**\n * State Router\n *\n * Groups states, derives their routes into one `Router` the app mounts with\n * `.packages()`, collects their SSE events for the app's event router, and\n * subscribes each state's change signal — once, here.\n */\n\nimport type { Static, TObject, TSchema } from '@sinclair/typebox';\nimport type { EventDef } from '@spfn/core/event';\nimport { defineRouter } from '@spfn/core/route';\nimport type { RouteDef, Router } from '@spfn/core/route';\nimport { bindState, stateBinding } from './state-binding';\nimport { changedPayloadSchema, stateEventName, subscribeChangeSignals } from './state-event';\nimport { defaultSubject, readRoute, snapshotRoute, stateManifest, stateRouteName, writeRoute } from './state-routes';\nimport type { SnapshotRouteOptions, SubjectResolver } from './state-routes';\nimport type { RoutableState, StateFrame, StateKey, StateResult, StateSnapshot, StateValue } from './types';\n\n/**\n * Where the router mounts, whom a request is for, and the snapshot route's own\n * `use` / `skip` / `contract` (`SnapshotRouteOptions`) — those reach `GET <path>`\n * only, never a per-state route or the SSE stream.\n */\nexport interface StateRouterOptions extends SnapshotRouteOptions\n{\n /**\n * Where the routes are mounted: `GET <path>`, `GET|PUT <path>/<id>`.\n * @default '/state'\n */\n path?: string;\n\n /**\n * Who an HTTP request is for. `null` answers 401.\n * @default (c) => c.get('auth')?.userId ?? null\n */\n subject?: SubjectResolver;\n}\n\ntype Shape<S extends RoutableState> = S['_types'];\n\ntype KeyQuery<S extends RoutableState> = Shape<S>['key'] extends TObject ? { query: Shape<S>['key'] } : {};\n\ntype ReadRoutes<S extends Record<string, RoutableState>> = {\n [K in keyof S as `get${Capitalize<S[K]['id']>}State`]: RouteDef<KeyQuery<S[K]>, {}, StateSnapshot<Shape<S[K]>>>;\n};\n\ntype WriteInput<S extends RoutableState> = KeyQuery<S> & { body: Shape<S>['input'] };\n\ntype WriteRoutes<S extends Record<string, RoutableState>> = {\n [K in keyof S as Shape<S[K]>['input'] extends TSchema ? `put${Capitalize<S[K]['id']>}State` : never]: RouteDef<WriteInput<S[K]>, {}, StateSnapshot<Shape<S[K]>>>;\n};\n\n/**\n * What `GET <path>` answers: every state the subject may read and whose key\n * the query carries, and the manifest of every state — its key fields and\n * whether it has a write — in declaration order.\n */\nexport type StatesSnapshot<S extends Record<string, RoutableState>> = {\n at: string;\n states: { [K in keyof S as S[K]['id']]?: StateResult<Shape<S[K]>> };\n manifest: { [K in keyof S as S[K]['id']]: { key: string[]; write: boolean } };\n};\n\n/** The routes one state router derives, typed for `createApi` and the route map. */\nexport type StateRoutes<S extends Record<string, RoutableState>> = ReadRoutes<S> & WriteRoutes<S> & {\n getStates: RouteDef<{ query: TObject }, {}, StatesSnapshot<S>>;\n};\n\n/** The SSE events, keyed by their wire names, for the app's `defineEventRouter`. */\nexport type StateEvents<S extends Record<string, RoutableState>> = {\n [K in keyof S as `state.${S[K]['id']}.changed`]: EventDef<StateFrame<Shape<S[K]>>>;\n};\n\nexport interface StateRouter<S extends Record<string, RoutableState>>\n{\n /** The derived routes. Mount with `appRouter.packages([states.router])`. */\n router: Router<StateRoutes<S>>;\n\n /** The derived SSE events. Spread into `defineEventRouter({ ...appEvents, ...states.events })`. */\n events: StateEvents<S>;\n\n /** The declarations, as given. */\n states: S;\n}\n\ntype StateById<R extends StateRouter<any>, TId extends string> = Extract<R['states'][keyof R['states']], { id: TId }>;\n\n/** The value type of state `TId` of a state router. */\nexport type InferStateValue<R extends StateRouter<any>, TId extends string> = StateValue<Shape<StateById<R, TId>>>;\n\n/** The key type of state `TId`, `undefined` when it is keyless. */\nexport type InferStateKey<R extends StateRouter<any>, TId extends string> = StateKey<Shape<StateById<R, TId>>>;\n\n/** The write input of state `TId`, `never` when it declares no `.write()`. */\nexport type InferStateInput<R extends StateRouter<any>, TId extends string> =\n Shape<StateById<R, TId>>['input'] extends TSchema ? Static<Shape<StateById<R, TId>>['input']> : never;\n\n/**\n * Group states, derive their routes and SSE events, and subscribe their change\n * signals.\n *\n * @example\n * ```typescript\n * export const states = defineStateRouter({ consent, runtime, member });\n *\n * // server.config.ts\n * // .routes(appRouter.packages([states.router]))\n * // .events(defineEventRouter({ ...appEvents, ...states.events }), { auth: { enabled: true } })\n * ```\n */\nexport function defineStateRouter<S extends Record<string, RoutableState>>(\n states: S,\n options: StateRouterOptions = {},\n): StateRouter<S>\n{\n const path = options.path ?? '/state';\n const list = Object.values(states);\n\n assertPath(path);\n list.forEach(assertRoutable);\n assertSnapshotMiddlewareIsNotAuthorization(list, options);\n assertDistinctIds(list);\n list.forEach(state => assertNotRouted(state, path));\n\n list.forEach(state => bindState(state._definition, path));\n list.forEach(subscribeChangeSignals);\n\n return {\n router: defineRouter(deriveRoutes(list, path, options)) as Router<StateRoutes<S>>,\n events: Object.fromEntries(list.map(state => [stateEventName(state.id), state._event])) as StateEvents<S>,\n states,\n };\n}\n\nfunction deriveRoutes(states: RoutableState[], path: string, options: StateRouterOptions): Record<string, RouteDef<any, any, any>>\n{\n const resolveSubject = options.subject ?? defaultSubject;\n const routes: Record<string, RouteDef<any, any, any>> = {};\n\n for (const state of states)\n {\n routes[stateRouteName('get', state.id)] = readRoute(state, path, resolveSubject);\n\n if (state._definition.write)\n {\n routes[stateRouteName('put', state.id)] = writeRoute(state, path, resolveSubject);\n }\n }\n\n routes.getStates = snapshotRoute(states, stateManifest(states), path, resolveSubject, options);\n\n return routes;\n}\n\nfunction assertPath(path: string): void\n{\n if (!path.startsWith('/') || (path.length > 1 && path.endsWith('/')))\n {\n throw new Error(\n `defineStateRouter(..., { path: \"${path}\" }) is not an absolute path without a trailing slash. `\n + 'The routes are mounted at <path> and <path>/<id>. Write it as \"/state\".',\n );\n }\n}\n\n/** Refuse a state the router could not serve, naming it. */\nfunction assertRoutable(state: RoutableState): void\n{\n const def = state._definition;\n const missing = !def.value ? '.value()' : !def.read ? '.read()' : undefined;\n\n if (missing)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" declares no ${missing}. `\n + 'A state answers with the value its read returns, so both are required. '\n + `Add ${missing} to defineState('${state.id}').`,\n );\n }\n\n if (def.key && !def.canRead)\n {\n throw new Error(\n `defineStateRouter: keyed state \"${state.id}\" declares no .canRead(). `\n + 'The key comes from the request, so without it any subject could read any key. '\n + 'Add .canRead(({ subject, key }) => ...).',\n );\n }\n\n assertMiddlewareIsNotAuthorization(state);\n def.changedBy?.forEach(event => assertChangedBy(state, event));\n\n if (def.write && !def.contract && def.write.options.since)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" gives .write(..., { since: '${def.write.options.since}' }) `\n + 'but declares no .contract(), so there is nothing to fall back from: the write route would have a version '\n + 'but no auth profile, and the read route and the event would not be contracted at all. '\n + 'Add .contract({ since, ... }) to the state, or drop since from the write.',\n );\n }\n}\n\n/**\n * `.use()` / `.skip()` reach the two per-state routes only: the snapshot and\n * the SSE stream cannot run HTTP middleware. A guard there would hold on one\n * route and leak through the others, so authorization must be `canRead`.\n */\nfunction assertMiddlewareIsNotAuthorization(state: RoutableState): void\n{\n const def = state._definition;\n\n if ((def.middlewares.length > 0 || def.skipMiddlewares) && !def.canRead)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" declares .use() or .skip() but no .canRead(). `\n + `Middleware reaches only ${stateRouteName('get', state.id)} / ${stateRouteName('put', state.id)} — never the `\n + 'snapshot route or the SSE stream — so it cannot authorize. Authorization belongs in .canRead(), which is '\n + 'enforced on the read route, the snapshot and the stream. A state that needs middleware only for, say, rate '\n + 'limiting declares .canRead(() => true) to say so.',\n );\n }\n}\n\n/**\n * The router's `use` / `skip` reach the snapshot route only — never the SSE\n * stream, which serves the same states — so, as for a state's own `.use()`,\n * every state must authorize with `canRead`. A `contract` alone runs nothing.\n */\nfunction assertSnapshotMiddlewareIsNotAuthorization(states: RoutableState[], options: StateRouterOptions): void\n{\n const unguarded = states.find(state => !state._definition.canRead);\n\n if (((options.use?.length ?? 0) > 0 || options.skip) && unguarded)\n {\n throw new Error(\n `defineStateRouter: the router gives the snapshot route use or skip, but state \"${unguarded.id}\" declares no `\n + '.canRead(). The snapshot\\'s middleware never reaches the SSE stream, which serves the same state, so it '\n + 'cannot authorize. Authorization belongs in .canRead(); a state readable by every subject declares '\n + '.canRead(() => true) to say so.',\n );\n }\n}\n\n/** A `.changedBy()` event must carry every field the state's change signal names, as a required property. */\nfunction assertChangedBy(state: RoutableState, event: EventDef<any>): void\n{\n const payload = event.schema as TObject | undefined;\n const signal = changedPayloadSchema(state._definition);\n const missing = Object.keys(signal.properties).find(field =>\n !payload?.properties?.[field] || !(payload.required ?? []).includes(field));\n\n if (missing)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" lists event \"${event.name}\" in .changedBy(), but its payload schema `\n + `has no required field \"${missing}\". Its payload is read like ${state.id}.changed.emit(), which names `\n + `${Object.keys(signal.properties).join(', ')}. Add the field to the event, or emit ${state.id}.changed instead.`,\n );\n }\n}\n\nfunction assertDistinctIds(states: RoutableState[]): void\n{\n const seen = new Set<string>();\n\n for (const state of states)\n {\n if (seen.has(state.id))\n {\n throw new Error(\n `defineStateRouter: two states have the id \"${state.id}\". `\n + `Both would derive the routes ${stateRouteName('get', state.id)} and the event `\n + `${stateEventName(state.id)}, and one would shadow the other. Rename one.`,\n );\n }\n seen.add(state.id);\n }\n}\n\nfunction assertNotRouted(state: RoutableState, path: string): void\n{\n const other = stateBinding(state._definition)?.path;\n\n if (other !== undefined)\n {\n throw new Error(\n `defineStateRouter: state \"${state.id}\" is already in another state router (path ${other}, this one ${path}). `\n + 'Each router subscribes the state\\'s change signal, so every change would be read and sent twice. '\n + 'Group each state in one router.',\n );\n }\n}\n"]}
|
package/dist/state/react.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { AnyStateRouter, StateId, StateCatalog, StateFrameOf, StateClient, StateKeyArgs } from '@spfn/core/state/client';
|
|
1
|
+
import { AnyStateRouter, StateId, StateCatalog, StateFrameOf, StateClient, ConnectionStatus, StateKeyArgs } from '@spfn/core/state/client';
|
|
2
2
|
|
|
3
3
|
type StateWrite<TStates extends AnyStateRouter, Id extends StateId<TStates>> = (input: StateCatalog<TStates>[Id]['input']) => Promise<StateFrameOf<TStates, Id>>;
|
|
4
4
|
/**
|
|
5
5
|
* What `useServerState` returns. `'loading'` until the client holds an entry —
|
|
6
|
-
*
|
|
6
|
+
* during a server render too, unless the client was built with `initial`. `write` is typed only on a state that
|
|
7
7
|
* declares one, bound to the key the hook was given.
|
|
8
8
|
*/
|
|
9
9
|
type ServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>> = {
|
|
@@ -30,5 +30,20 @@ type ServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>> =
|
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
declare function useServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>>(client: StateClient<TStates>, id: Id, ...key: StateKeyArgs<TStates, Id>): ServerState<TStates, Id>;
|
|
33
|
+
/**
|
|
34
|
+
* The client's connection status, re-rendered on every change: `'connected'`,
|
|
35
|
+
* `'reconnecting'` with the attempt, or `'failed'` until `client.retry()`.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* const connection = useConnectionStatus(state);
|
|
40
|
+
*
|
|
41
|
+
* if (connection.status === 'failed')
|
|
42
|
+
* {
|
|
43
|
+
* return <Banner onRetry={() => state.retry()}>The server is not answering.</Banner>;
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
declare function useConnectionStatus(client: StateClient<any>): ConnectionStatus;
|
|
33
48
|
|
|
34
|
-
export { type ServerState, useServerState };
|
|
49
|
+
export { type ServerState, useConnectionStatus, useServerState };
|
package/dist/state/react.js
CHANGED
|
@@ -14,9 +14,6 @@ function parseKey(text) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// src/state/react.tsx
|
|
17
|
-
function serverSnapshot() {
|
|
18
|
-
return void 0;
|
|
19
|
-
}
|
|
20
17
|
function keyArgs(keyText) {
|
|
21
18
|
const key = parseKey(keyText);
|
|
22
19
|
return key ? [key] : [];
|
|
@@ -34,11 +31,20 @@ function useServerState(client, id, ...key) {
|
|
|
34
31
|
const args = useMemo(() => keyArgs(keyText), [keyText]);
|
|
35
32
|
const subscribe = useCallback((onChange) => loose.subscribe(id, ...args, onChange), [loose, id, args]);
|
|
36
33
|
const getSnapshot = useCallback(() => loose.current(id, ...args), [loose, id, args]);
|
|
37
|
-
const entry = useSyncExternalStore(subscribe, getSnapshot,
|
|
34
|
+
const entry = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
38
35
|
const write = useCallback((input) => loose.write(id, input, ...args), [loose, id, args]);
|
|
39
36
|
return useMemo(() => toServerState(entry, write), [entry, write]);
|
|
40
37
|
}
|
|
38
|
+
var SERVER_CONNECTION = Object.freeze({ status: "reconnecting", attempt: 0 });
|
|
39
|
+
function serverConnection() {
|
|
40
|
+
return SERVER_CONNECTION;
|
|
41
|
+
}
|
|
42
|
+
function useConnectionStatus(client) {
|
|
43
|
+
const subscribe = useCallback((onChange) => client.subscribeConnection(onChange), [client]);
|
|
44
|
+
const getSnapshot = useCallback(() => client.connection(), [client]);
|
|
45
|
+
return useSyncExternalStore(subscribe, getSnapshot, serverConnection);
|
|
46
|
+
}
|
|
41
47
|
|
|
42
|
-
export { useServerState };
|
|
48
|
+
export { useConnectionStatus, useServerState };
|
|
43
49
|
//# sourceMappingURL=react.js.map
|
|
44
50
|
//# sourceMappingURL=react.js.map
|
package/dist/state/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/state/client-store.ts","../../src/state/react.tsx"],"names":[],"mappings":";;;;;AAgBO,SAAS,UAAU,GAAA,EAC1B;AACI,EAAA,IAAI,CAAC,GAAA,EACL;AACI,IAAA,OAAO,EAAA;AAAA,EACX;AAEA,EAAA,OAAO,KAAK,SAAA,CAAU,MAAA,CAAO,IAAA,CAAK,GAAG,EAAE,IAAA,EAAK,CAAE,GAAA,CAAI,CAAA,KAAA,KAAS,CAAC,KAAA,EAAO,GAAA,CAAI,KAAK,CAAC,CAAC,CAAC,CAAA;AACnF;AAGO,SAAS,SAAS,IAAA,EACzB;AACI,EAAA,OAAO,OAAO,MAAA,CAAO,WAAA,CAAY,KAAK,KAAA,CAAM,IAAI,CAAuB,CAAA,GAAI,MAAA;AAC/E;;;ACSA,SAAS,cAAA,GACT;AACI,EAAA,OAAO,MAAA;AACX;AAEA,SAAS,QAAQ,OAAA,EACjB;AACI,EAAA,MAAM,GAAA,GAAM,SAAS,OAAO,CAAA;AAE5B,EAAA,OAAO,GAAA,GAAM,CAAC,GAAG,CAAA,GAAI,EAAC;AAC1B;AAOA,SAAS,aAAA,CAAc,OAA+B,KAAA,EACtD;AACI,EAAA,IAAI,CAAC,KAAA,EACL;AACI,IAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAM;AAAA,EACtC;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,EAAA,EAAI,KAAA,EAAO,MAAK,GAAI,KAAA;AAEpC,EAAA,OAAO,EAAE,MAAA,EAAQ,GAAI,OAAO,MAAA,GAAY,KAAK,EAAE,EAAA,EAAI,OAAM,EAAI,GAAI,SAAS,MAAA,GAAY,KAAK,EAAE,IAAA,IAAS,KAAA,EAAM;AAChH;AAiBO,SAAS,cAAA,CACZ,MAAA,EACA,EAAA,EAAA,GACG,GAAA,EAEP;AACI,EAAA,MAAM,KAAA,GAAQ,MAAA;AACd,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,GAAA,CAAI,CAAC,CAA+B,CAAA;AAC9D,EAAA,MAAM,IAAA,GAAO,QAAQ,MAAM,OAAA,CAAQ,OAAO,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEtD,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,CAAC,QAAA,KAAyB,MAAM,SAAA,CAAU,EAAA,EAAI,GAAG,IAAA,EAAM,QAAQ,CAAA,EAAG,CAAC,KAAA,EAAO,EAAA,EAAI,IAAI,CAAC,CAAA;AACjH,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,MAAM,KAAA,CAAM,OAAA,CAAQ,EAAA,EAAI,GAAG,IAAI,CAAA,EAAG,CAAC,KAAA,EAAO,EAAA,EAAI,IAAI,CAAC,CAAA;AACnF,EAAA,MAAM,KAAA,GAAQ,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,cAAc,CAAA;AACzE,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,CAAC,KAAA,KAAmB,MAAM,KAAA,CAAM,EAAA,EAAI,KAAA,EAAO,GAAG,IAAI,CAAA,EAAG,CAAC,KAAA,EAAO,EAAA,EAAI,IAAI,CAAC,CAAA;AAEhG,EAAA,OAAO,OAAA,CAAQ,MAAM,aAAA,CAAc,KAAA,EAAO,KAAK,CAAA,EAAG,CAAC,KAAA,EAAO,KAAK,CAAC,CAAA;AACpE","file":"react.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","import { useCallback, useMemo, useSyncExternalStore } from 'react';\n// Types come from the client entry, so react.d.ts refers to the one declaration\n// of StateClient there instead of carrying a copy of its own.\nimport type {\n AnyStateRouter,\n StateCatalog,\n StateClient,\n StateEntry,\n StateFrame,\n StateFrameOf,\n StateId,\n StateKeyArgs,\n} from '@spfn/core/state/client';\nimport { parseKey, stableKey, type StateKeyFields } from './client-store';\n\ntype StateWrite<TStates extends AnyStateRouter, Id extends StateId<TStates>> =\n (input: StateCatalog<TStates>[Id]['input']) => Promise<StateFrameOf<TStates, Id>>;\n\n/**\n * What `useServerState` returns. `'loading'` until the client holds an entry —\n * and always during a server render. `write` is typed only on a state that\n * declares one, bound to the key the hook was given.\n */\nexport type ServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>> = {\n status: 'loading' | 'ready' | 'revoked';\n at?: string;\n value?: StateCatalog<TStates>[Id]['value'];\n data?: StateCatalog<TStates>[Id]['data'];\n} & ([StateCatalog<TStates>[Id]['input']] extends [never] ? {} : { write: StateWrite<TStates, Id> });\n\n/** The client as the hook drives it: the hook's own signature already checked id, key and input. */\ninterface LooseClient\n{\n subscribe(id: string, ...args: unknown[]): () => void;\n current(id: string, ...key: unknown[]): StateEntry | undefined;\n write(id: string, input: unknown, ...key: unknown[]): Promise<StateFrame>;\n}\n\n/** A server render has no store: the first client render takes over from `'loading'`. */\nfunction serverSnapshot(): undefined\n{\n return undefined;\n}\n\nfunction keyArgs(keyText: string): [] | [StateKeyFields]\n{\n const key = parseKey(keyText);\n\n return key ? [key] : [];\n}\n\n/**\n * The entry as the hook returns it. `write` is always attached at runtime — the\n * type hides it on a state without one, whose client `write` rejects — so the\n * result never waits on the manifest.\n */\nfunction toServerState(entry: StateEntry | undefined, write: (input: unknown) => Promise<StateFrame>): object\n{\n if (!entry)\n {\n return { status: 'loading', write };\n }\n\n const { status, at, value, data } = entry;\n\n return { status, ...(at === undefined ? {} : { at, value }), ...(data === undefined ? {} : { data }), write };\n}\n\n/**\n * Subscribe a component to one state of a state client. The key is compared\n * by its fields, so a new object literal on every render does not resubscribe.\n *\n * @example\n * ```tsx\n * const consent = useServerState(state, 'consent');\n * const runtime = useServerState(state, 'runtime', { workspaceId });\n *\n * if (consent.status === 'ready' && consent.value === 'pending')\n * {\n * await consent.write({ termsVersion, marketing });\n * }\n * ```\n */\nexport function useServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>>(\n client: StateClient<TStates>,\n id: Id,\n ...key: StateKeyArgs<TStates, Id>\n): ServerState<TStates, Id>\n{\n const loose = client as unknown as LooseClient;\n const keyText = stableKey(key[0] as StateKeyFields | undefined);\n const args = useMemo(() => keyArgs(keyText), [keyText]);\n\n const subscribe = useCallback((onChange: () => void) => loose.subscribe(id, ...args, onChange), [loose, id, args]);\n const getSnapshot = useCallback(() => loose.current(id, ...args), [loose, id, args]);\n const entry = useSyncExternalStore(subscribe, getSnapshot, serverSnapshot);\n const write = useCallback((input: unknown) => loose.write(id, input, ...args), [loose, id, args]);\n\n return useMemo(() => toServerState(entry, write), [entry, write]) as ServerState<TStates, Id>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/state/client-store.ts","../../src/state/react.tsx"],"names":[],"mappings":"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","file":"react.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","import { useCallback, useMemo, useSyncExternalStore } from 'react';\n// Types come from the client entry, so react.d.ts refers to the one declaration\n// of StateClient there instead of carrying a copy of its own.\nimport type {\n AnyStateRouter,\n ConnectionStatus,\n StateCatalog,\n StateClient,\n StateEntry,\n StateFrame,\n StateFrameOf,\n StateId,\n StateKeyArgs,\n} from '@spfn/core/state/client';\nimport { parseKey, stableKey, type StateKeyFields } from './client-store';\n\ntype StateWrite<TStates extends AnyStateRouter, Id extends StateId<TStates>> =\n (input: StateCatalog<TStates>[Id]['input']) => Promise<StateFrameOf<TStates, Id>>;\n\n/**\n * What `useServerState` returns. `'loading'` until the client holds an entry —\n * during a server render too, unless the client was built with `initial`. `write` is typed only on a state that\n * declares one, bound to the key the hook was given.\n */\nexport type ServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>> = {\n status: 'loading' | 'ready' | 'revoked';\n at?: string;\n value?: StateCatalog<TStates>[Id]['value'];\n data?: StateCatalog<TStates>[Id]['data'];\n} & ([StateCatalog<TStates>[Id]['input']] extends [never] ? {} : { write: StateWrite<TStates, Id> });\n\n/** The client as the hook drives it: the hook's own signature already checked id, key and input. */\ninterface LooseClient\n{\n subscribe(id: string, ...args: unknown[]): () => void;\n current(id: string, ...key: unknown[]): StateEntry | undefined;\n write(id: string, input: unknown, ...key: unknown[]): Promise<StateFrame>;\n}\n\nfunction keyArgs(keyText: string): [] | [StateKeyFields]\n{\n const key = parseKey(keyText);\n\n return key ? [key] : [];\n}\n\n/**\n * The entry as the hook returns it. `write` is always attached at runtime — the\n * type hides it on a state without one, whose client `write` rejects — so the\n * result never waits on the manifest.\n */\nfunction toServerState(entry: StateEntry | undefined, write: (input: unknown) => Promise<StateFrame>): object\n{\n if (!entry)\n {\n return { status: 'loading', write };\n }\n\n const { status, at, value, data } = entry;\n\n return { status, ...(at === undefined ? {} : { at, value }), ...(data === undefined ? {} : { data }), write };\n}\n\n/**\n * Subscribe a component to one state of a state client. The key is compared\n * by its fields, so a new object literal on every render does not resubscribe.\n *\n * @example\n * ```tsx\n * const consent = useServerState(state, 'consent');\n * const runtime = useServerState(state, 'runtime', { workspaceId });\n *\n * if (consent.status === 'ready' && consent.value === 'pending')\n * {\n * await consent.write({ termsVersion, marketing });\n * }\n * ```\n */\nexport function useServerState<TStates extends AnyStateRouter, Id extends StateId<TStates>>(\n client: StateClient<TStates>,\n id: Id,\n ...key: StateKeyArgs<TStates, Id>\n): ServerState<TStates, Id>\n{\n const loose = client as unknown as LooseClient;\n const keyText = stableKey(key[0] as StateKeyFields | undefined);\n const args = useMemo(() => keyArgs(keyText), [keyText]);\n\n const subscribe = useCallback((onChange: () => void) => loose.subscribe(id, ...args, onChange), [loose, id, args]);\n const getSnapshot = useCallback(() => loose.current(id, ...args), [loose, id, args]);\n // The server snapshot is the client's entry too: a client built with `initial`\n // renders its value on the server, and hydration on the browser matches it.\n const entry = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n const write = useCallback((input: unknown) => loose.write(id, input, ...args), [loose, id, args]);\n\n return useMemo(() => toServerState(entry, write), [entry, write]) as ServerState<TStates, Id>;\n}\n\n/** A server render opens nothing: its status is the one a client starts with. */\nconst SERVER_CONNECTION: ConnectionStatus = Object.freeze({ status: 'reconnecting', attempt: 0 });\n\nfunction serverConnection(): ConnectionStatus\n{\n return SERVER_CONNECTION;\n}\n\n/**\n * The client's connection status, re-rendered on every change: `'connected'`,\n * `'reconnecting'` with the attempt, or `'failed'` until `client.retry()`.\n *\n * @example\n * ```tsx\n * const connection = useConnectionStatus(state);\n *\n * if (connection.status === 'failed')\n * {\n * return <Banner onRetry={() => state.retry()}>The server is not answering.</Banner>;\n * }\n * ```\n */\nexport function useConnectionStatus(client: StateClient<any>): ConnectionStatus\n{\n const subscribe = useCallback((onChange: () => void) => client.subscribeConnection(onChange), [client]);\n const getSnapshot = useCallback(() => client.connection(), [client]);\n\n return useSyncExternalStore(subscribe, getSnapshot, serverConnection);\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context } from 'hono';
|
|
2
|
-
import { E as EventRouterDef, I as InferEventNames, i as InferEventPayload,
|
|
2
|
+
import { E as EventRouterDef, I as InferEventNames, i as InferEventPayload, l as SubscribeRequest } from './router-CX5uVjmU.js';
|
|
3
3
|
import { c as SSETokenStore, S as SSETokenManager } from './token-manager-Ckjt2-qq.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -295,6 +295,23 @@ interface SSESubscribeOptions<TRouter extends EventRouterDef<any>> {
|
|
|
295
295
|
* `events` can change: an event never requested cannot be granted.
|
|
296
296
|
*/
|
|
297
297
|
onSubscriptionChange?: (change: SSESubscriptionChange<TRouter>) => void;
|
|
298
|
+
/**
|
|
299
|
+
* Called with the server's `connected` frame on every connection — the
|
|
300
|
+
* first open and each reconnect — after `onOpen`.
|
|
301
|
+
*/
|
|
302
|
+
onConnected?: (payload: ConnectedPayload) => void;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The `connected` frame, the first a connection receives: which requested
|
|
306
|
+
* events it is subscribed to, which are denied, and which were served their
|
|
307
|
+
* initial frames (written right after this one). `initial` is absent from a
|
|
308
|
+
* server that predates it.
|
|
309
|
+
*/
|
|
310
|
+
interface ConnectedPayload {
|
|
311
|
+
subscribedEvents: string[];
|
|
312
|
+
deniedEvents: string[];
|
|
313
|
+
initial?: string[];
|
|
314
|
+
timestamp: number;
|
|
298
315
|
}
|
|
299
316
|
/**
|
|
300
317
|
* A `grant` / `revoke` the server applied to the open connection.
|
|
@@ -312,4 +329,4 @@ type SSEConnectionState = 'connecting' | 'open' | 'closed' | 'error';
|
|
|
312
329
|
*/
|
|
313
330
|
type SSEUnsubscribe = () => void;
|
|
314
331
|
|
|
315
|
-
export type { SSEClientConfig as S, SSESubscribeOptions as a, SSEUnsubscribe as b, SSEConnectionState as c, SSEHandlerConfig as d, SSEAuthConfig as e, SSEEventHandler as f, SSEEventHandlers as g, SSEHandlerAuthConfig as h, SSEMessage as i, SSESubscriptionChange as j };
|
|
332
|
+
export type { ConnectedPayload as C, SSEClientConfig as S, SSESubscribeOptions as a, SSEUnsubscribe as b, SSEConnectionState as c, SSEHandlerConfig as d, SSEAuthConfig as e, SSEEventHandler as f, SSEEventHandlers as g, SSEHandlerAuthConfig as h, SSEMessage as i, SSESubscriptionChange as j };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context } from 'hono';
|
|
2
|
-
import { a as EventDef,
|
|
2
|
+
import { a as EventDef, o as EventRouterBase, I as InferEventNames, i as InferEventPayload } from './router-CX5uVjmU.js';
|
|
3
3
|
import { c as SSETokenStore, S as SSETokenManager } from './token-manager-Ckjt2-qq.js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfn/core",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.28",
|
|
4
4
|
"description": "Full-stack TypeScript backend for Next.js: file-based typed routes, Drizzle entities and repositories, PostgreSQL transactions and a generated end-to-end client, in one fixed vertical slice per feature",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|