@spfn/core 0.3.0-beta.23 → 0.3.0-beta.25

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.
@@ -0,0 +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"],"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 { 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/** Read the value for one subject and key, send it as one frame, and return the snapshot. */\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 await event.emit(toFrame(def, snapshot, subject, key));\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 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} 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 * 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: PermissionContext<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, 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 } from 'hono';\nimport { ForbiddenError, UnauthorizedError } from '@spfn/core/errors';\nimport { route } from '@spfn/core/route';\nimport type { 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, 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\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\nfunction responseSchema(def: StateDefinition): TObject\n{\n return Type.Object({\n at: Type.String(),\n value: def.value!,\n ...(def.data ? { data: def.data } : {}),\n });\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 → 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 = c.raw.get('auth');\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/** `GET <path>`: one `at`, then every readable state in declaration order. A `read()` that throws fails it all. */\nexport function snapshotRoute(states: RoutableState[], path: string, resolveSubject: SubjectResolver): RouteDef<any, any, any>\n{\n return route.get(path).input({ query: snapshotQuery(states) }).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 };\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, stateRouteName, writeRoute } from './state-routes';\nimport type { SubjectResolver } from './state-routes';\nimport type { RoutableState, StateFrame, StateKey, StateResult, StateSnapshot, StateValue } from './types';\n\nexport interface StateRouterOptions\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/** What `GET <path>` answers: every state the subject may read and whose key the query carries. */\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};\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 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.subject ?? defaultSubject)) 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, resolveSubject: SubjectResolver): Record<string, RouteDef<any, any, any>>\n{\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, path, resolveSubject);\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/** 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,5 +1,5 @@
1
1
  import { Context } from 'hono';
2
- import { a as EventDef, i as EventRouterBase, I as InferEventNames, g as InferEventPayload } from './router-CysBmgv3.js';
2
+ import { a as EventDef, n as EventRouterBase, I as InferEventNames, i as InferEventPayload } from './router-D5B0cpVN.js';
3
3
  import { c as SSETokenStore, S as SSETokenManager } from './token-manager-Ckjt2-qq.js';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { Context } from 'hono';
2
- import { E as EventRouterDef, I as InferEventNames, g as InferEventPayload } from './router-CysBmgv3.js';
2
+ import { E as EventRouterDef, I as InferEventNames, i as InferEventPayload, k as SubscribeRequest } from './router-D5B0cpVN.js';
3
3
  import { c as SSETokenStore, S as SSETokenManager } from './token-manager-Ckjt2-qq.js';
4
4
 
5
5
  /**
@@ -79,6 +79,17 @@ interface SSEHandlerAuthConfig {
79
79
  * Return false to skip sending the event to this user.
80
80
  */
81
81
  filter?: Record<string, (subject: string, payload: unknown) => boolean>;
82
+ /**
83
+ * Resolve the principal every `.canSubscribe()` of a connection receives
84
+ * (called once per connection, after the subject is known).
85
+ *
86
+ * The stream request carries no `AuthContext`: the subject comes from the
87
+ * one-time token, and the stream is mounted without the app's middleware.
88
+ * So an app that decides on roles or memberships loads them here, once,
89
+ * instead of in every predicate. Absent → predicates see
90
+ * `principal: undefined`. A throw refuses the connection with 403.
91
+ */
92
+ resolvePrincipal?: (subject: string, request: SubscribeRequest) => unknown | Promise<unknown>;
82
93
  }
83
94
  /**
84
95
  * SSE auth configuration (user-facing, generic)
@@ -114,6 +125,7 @@ interface SSEAuthConfig<TRouter extends EventRouterDef<any>> {
114
125
  filter?: {
115
126
  [K in InferEventNames<TRouter>]?: (subject: string, payload: InferEventPayload<TRouter, K>) => boolean;
116
127
  };
128
+ resolvePrincipal?: (subject: string, request: SubscribeRequest) => unknown | Promise<unknown>;
117
129
  }
118
130
  /**
119
131
  * SSE Handler configuration
@@ -270,6 +282,19 @@ interface SSESubscribeOptions<TRouter extends EventRouterDef<any>> {
270
282
  * Called when reconnecting
271
283
  */
272
284
  onReconnect?: (attempt: number) => void;
285
+ /**
286
+ * Called when the server grants or revokes one event on the open connection
287
+ * (`subscription.granted` / `subscription.revoked` frames). Only events in
288
+ * `events` can change: an event never requested cannot be granted.
289
+ */
290
+ onSubscriptionChange?: (change: SSESubscriptionChange<TRouter>) => void;
291
+ }
292
+ /**
293
+ * A `grant` / `revoke` the server applied to the open connection.
294
+ */
295
+ interface SSESubscriptionChange<TRouter extends EventRouterDef<any>> {
296
+ event: InferEventNames<TRouter>;
297
+ status: 'granted' | 'revoked';
273
298
  }
274
299
  /**
275
300
  * SSE connection state
@@ -280,4 +305,4 @@ type SSEConnectionState = 'connecting' | 'open' | 'closed' | 'error';
280
305
  */
281
306
  type SSEUnsubscribe = () => void;
282
307
 
283
- 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 };
308
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spfn/core",
3
- "version": "0.3.0-beta.23",
3
+ "version": "0.3.0-beta.25",
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": {
@@ -119,6 +119,11 @@
119
119
  "import": "./dist/event/index.js",
120
120
  "require": "./dist/event/index.js"
121
121
  },
122
+ "./state": {
123
+ "types": "./dist/state/index.d.ts",
124
+ "import": "./dist/state/index.js",
125
+ "require": "./dist/state/index.js"
126
+ },
122
127
  "./event/sse": {
123
128
  "types": "./dist/event/sse/index.d.ts",
124
129
  "import": "./dist/event/sse/index.js",
@@ -235,7 +235,8 @@ throws instead of one declaration silently winning. A router `streamPath` that d
235
235
  a client must exchange a token first;
236
236
  - a contracted event without a payload schema (`defineEvent(name)`) — its frame carries no `data`;
237
237
  declare `Type.Object({})` and emit `{}` instead;
238
- - a contracted event under the key `connected` or `ping` — those are the stream's own frames;
238
+ - a contracted event under the key `connected`, `ping`, `subscription.granted` or
239
+ `subscription.revoked` — those are the stream's own frames;
239
240
  - a contracted event with no `since`.
240
241
 
241
242
  **What a reader of the document needs.**
@@ -254,8 +255,9 @@ throws instead of one declaration silently winning. A router `streamPath` that d
254
255
  `EventDef` registered under two keys is two items.
255
256
  - A frame's `data` is the JSON `{ "event": name, "data": payload }`, with `payload` matching the
256
257
  item's schema.
257
- - `connected` (once, when the stream opens) and `ping` (keep-alive) frames are the stream's own and
258
- are not items.
258
+ - `connected` (once, when the stream opens), `ping` (keep-alive), `subscription.granted` and
259
+ `subscription.revoked` (an event activated or deactivated on the open connection) frames are the
260
+ stream's own and are not items.
259
261
  - With `auth: "tokenExchange"` the client first posts to `tokenPath`, then opens
260
262
  `streamPath?events=…&token=…`. `tokenPath` is derived from `streamPath` exactly as the server
261
263
  derives it.
@@ -45,8 +45,12 @@ From `@spfn/core/event`:
45
45
  - `.contract({ since, deprecatedIn?, removedIn? })` on an event and `.contract({ auth, streamPath? })`
46
46
  on an event router — publish the event in `contracts/current.json` (see
47
47
  [Contracted events](#contracted-events-for-separately-compiled-clients))
48
- - Types: `EventDef`, `EventHandler`, `InferEventPayload`, `PubSubCache`, `JobQueueSender`,
49
- `EventContract`, `EventRouterContract`,
48
+ - `.canSubscribe(predicate)`, `.filter(predicate)`, `.grant(subject)`, `.revoke(subject)` on an
49
+ event — who may subscribe over SSE, which frames a connection receives, and changing an open
50
+ connection (see [Per-event permission](#per-event-permission-cansubscribe-filter-grant--revoke))
51
+ - Types: `EventDef`, `EventHandler`, `EventSubscribeOptions`, `InferEventPayload`, `PubSubCache`, `JobQueueSender`,
52
+ `EventContract`, `EventRouterContract`, `SubscribePredicate`, `SubscribeContext`,
53
+ `SubscribeRequest`, `FramePredicate`, `FrameContext`,
50
54
  `EventRouterDef`, `InferEventNames`, `InferEventPayloads`,
51
55
  `InferRouterEventPayload` (the two-arg router variant — see note)
52
56
 
@@ -56,8 +60,8 @@ From `@spfn/core/event/sse`:
56
60
  - `SSETokenManager` (class), `CacheTokenStore` (class)
57
61
  - Types: `SSEToken`, `SSETokenStore`, `SSETokenManagerConfig`, `SSEMessage`,
58
62
  `SSEHandlerConfig`, `SSEHandlerAuthConfig`, `SSEAuthConfig`, `SSEClientConfig`,
59
- `SSEEventHandler`, `SSEEventHandlers`, `SSESubscribeOptions`, `SSEConnectionState`,
60
- `SSEUnsubscribe`
63
+ `SSEEventHandler`, `SSEEventHandlers`, `SSESubscribeOptions`, `SSESubscriptionChange`,
64
+ `SSEConnectionState`, `SSEUnsubscribe`
61
65
 
62
66
  From `@spfn/core/event/sse/client`:
63
67
 
@@ -128,15 +132,21 @@ Promise<void>` otherwise. Handlers run via `Promise.allSettled` — see
128
132
  |--------|-----------|-------|
129
133
  | `name` | `readonly string` | event name (e.g. `'user.created'`) |
130
134
  | `schema` | `readonly TSchema?` | the TypeBox schema, if provided |
131
- | `subscribe(handler)` | `(payload) => void \| Promise<void>` ⇒ returns `() => void` | in-memory; returns unsubscribe |
132
- | `unsubscribeAll()` | `() => void` | drop all in-memory handlers |
135
+ | `subscribe(handler, options?)` | `(payload) => void \| Promise<void>`, `{ localOnly?: boolean }` ⇒ returns `() => void` | in-memory; returns unsubscribe. `localOnly`: runs for this process's `emit` only, never for a cache delivery from another pod |
136
+ | `unsubscribeAll()` | `() => void` | drop all in-memory handlers, `localOnly` ones included |
133
137
  | `emit(payload?)` | `Promise<void>` | fan-out to handlers (or cache) + job queues |
134
138
  | `useCache(cache)` | `(PubSubCache) => Promise<EventDef>` | enable cross-instance broadcast (must `await`) |
135
139
  | `contract(contract)` | `(EventContract) => EventDef` | publish in the contract; returns the same event; a second call throws |
140
+ | `canSubscribe(predicate)` | `<P>(SubscribePredicate<P>) => EventDef` | who may subscribe over SSE (once per connection); a second call throws |
141
+ | `filter(predicate)` | `(FramePredicate<TPayload>) => EventDef` | which frames a connection receives (sync, per frame); a second call throws |
142
+ | `grant(subject, match?)` | `Promise<void>` | activate the event on the subject's open SSE connections (those matching `match`, when given) where it is denied |
143
+ | `revoke(subject, match?)` | `Promise<void>` | deactivate the event on the subject's open SSE connections (those matching `match`, when given) where it is allowed |
136
144
 
137
- > `_registerJobQueue` and `_payload` exist on the interface but are **internal** —
138
- > `_registerJobQueue` is called by `@spfn/core/job` when a job does `.on(event)`; `_payload`
139
- > is a type-inference carrier (always `undefined` at runtime). Don't call them.
145
+ > `_registerJobQueue`, `_registerSubscriptionControl` and `_payload` exist on the interface but
146
+ > are **internal** — `_registerJobQueue` is called by `@spfn/core/job` when a job does
147
+ > `.on(event)`; `_registerSubscriptionControl` by `createSSEHandler` so `grant` / `revoke` reach
148
+ > its connections; `_payload` is a type-inference carrier (always `undefined` at runtime). Don't
149
+ > call them.
140
150
 
141
151
  ---
142
152
 
@@ -262,8 +272,10 @@ Client Server
262
272
  │ GET /events/stream?token=…&events=…
263
273
  │ ───────────────────────────────► │ verify+consume token (one-time)
264
274
  │ │ → authorize(subject, events)
265
- │ ◄═══════════════════════════════ │ SSE stream opens
266
- │ event: <name> / data: {…} │ ← filter(subject, payload) per emission
275
+ │ │ → resolvePrincipal(subject, request) (once)
276
+ │ │ → event.canSubscribe(ctx) (once per event)
277
+ │ ◄═══════════════════════════════ │ SSE stream opens (connected: subscribed + denied)
278
+ │ event: <name> / data: {…} │ ← filter(subject, payload) + event.filter(payload, ctx) per emission
267
279
  ```
268
280
 
269
281
  Enable it with `auth: { enabled: true }`:
@@ -381,6 +393,114 @@ Return `false` to skip. Payload is typed per event.
381
393
  })
382
394
  ```
383
395
 
396
+ ### Per-event permission (`canSubscribe`, `filter`, `grant` / `revoke`)
397
+
398
+ The global hooks above are one function for the whole router. When the rule belongs to one event,
399
+ declare it where the event is defined:
400
+
401
+ ```typescript
402
+ export const auditLogged = defineEvent('audit.logged', payload)
403
+ .canSubscribe<AuthPrincipal>(({ subject, principal, request }) => principal?.role === 'admin');
404
+
405
+ export const runtimeChanged = defineEvent('runtime.changed', payload)
406
+ .filter((payload, { subject, request }) => payload.workspaceId === request.query.workspaceId);
407
+ ```
408
+
409
+ - **`canSubscribe(ctx)`** — once per connection per requested event, only when the connection has a
410
+ subject. `true` allows; `false` or a throw denies (a throw is logged at warn with the event name).
411
+ A denial never refuses the connection: an event denied here is listed in the `connected` frame's
412
+ `deniedEvents`, and a connection whose every event is denied stays open with zero subscriptions,
413
+ so a later `grant` can activate one. May be async.
414
+ - **`filter(payload, ctx)`** — per frame, with or without a subject; it matches the payload against
415
+ the connect request. It runs beside the global `filter` map — both must return `true`. Sync. A
416
+ throw drops that frame (warn) and leaves the connection alive.
417
+ - **`request`** in both is a snapshot taken once at connect: `{ query, headers }` with lowercased
418
+ header names — never the live Hono context. It is the connect request as sent: it holds the
419
+ `cookie` / `authorization` headers and the consumed one-time `token` in `query`, so the app must
420
+ never log the snapshot.
421
+ - Both are set once per event: an `EventDef` is a shared singleton, so a second call throws.
422
+
423
+ **`resolvePrincipal` — why the stream has no `AuthContext`.** The subject comes from the one-time
424
+ token, and the stream is mounted without the app's middleware, so there is no request auth object
425
+ to hand a predicate. `auth.resolvePrincipal(subject, request)` loads it instead, **once per
426
+ connection**, after the subject is known and before any `canSubscribe`; every predicate of that
427
+ connection receives the same value as `principal` (type it with `.canSubscribe<P>()`). Absent →
428
+ `principal: undefined`. A throw refuses the connection with `403 { error: 'Principal could not be
429
+ resolved' }`.
430
+
431
+ ```typescript
432
+ .events(eventRouter, {
433
+ auth: {
434
+ enabled: true,
435
+ resolvePrincipal: async (subject) => principalsRepository.findBySubject(subject),
436
+ },
437
+ })
438
+ ```
439
+
440
+ **Order.** `authorize` (an empty result is still `403`) → `canSubscribe` of each event `authorize`
441
+ kept → per frame, the global `filter` and the event's `filter`. An event `authorize` dropped is
442
+ never asked and counts as denied too.
443
+
444
+ **Cost.** At connect: one predicate call per requested event (plus one `resolvePrincipal`). Per
445
+ frame: sync filters only — never storage. The handler sets no timeout on them: a hanging
446
+ `canSubscribe` or `resolvePrincipal` holds the connect open, so the app owns the timeout. A
447
+ client that leaves meanwhile is forgotten at once; the late verdict opens nothing.
448
+
449
+ **`grant` / `revoke` — changing an open connection.**
450
+
451
+ ```typescript
452
+ await workspaceRuntime.revoke(userId); // membership ended
453
+ await workspaceRuntime.grant(userId); // membership restored
454
+ ```
455
+
456
+ - `revoke(subject)` — on every open connection of the subject where the event is allowed: drop the
457
+ subscription and send a `subscription.revoked` frame `{ event }` (the router key). `grant(subject)`
458
+ — where it is denied: subscribe (the same code path as at connect, with the stored request
459
+ snapshot) and send `subscription.granted`. Anything else is a no-op, and no connection resolves
460
+ without error.
461
+ - `match: { query: Record<string, string> }` narrows either call to the connections whose connect
462
+ query carries every named field with exactly that value — string to string, over the request
463
+ snapshot. A connection that declared no such field never matches, not even against `''`.
464
+ Without `match`, every connection of the subject is reached.
465
+
466
+ ```typescript
467
+ // the tab on w1 only; a tab of the same user on w2 stays denied
468
+ await workspaceRuntime.grant(userId, { query: { workspaceId: 'w1' } });
469
+ ```
470
+
471
+ - With the cache transport attached (`CACHE_URL`), the call applies on this pod and is published
472
+ once on the control channel `<channelPrefix>$control`; every other pod applies it to its own
473
+ connections, with the same `match` rule. `grant` / `revoke` reject a `match` whose `query` is
474
+ empty or holds a non-string value (an `undefined` field would vanish in JSON and widen the call
475
+ on every other pod) before applying or publishing it. A control message whose `match` is not an
476
+ object with a non-empty string-to-string `query` is malformed and ignored. Without a cache it is local only.
477
+ - Three rules:
478
+ 1. **Open connections only.** Neither re-runs `canSubscribe` or `authorize` — they are an
479
+ operator's statement about connections that exist now.
480
+ 2. **Permission lives in app data.** The next connect asks `canSubscribe` afresh, so a missed
481
+ call delays until the client reconnects; it never produces a wrong answer after. A call that
482
+ lands while a connect is still in `authorize` / `resolvePrincipal` / `canSubscribe` is not
483
+ missed: it is recorded and applied over the predicates' verdict when that connection opens
484
+ (the last call wins; no frame — the `connected` frame already lists the result).
485
+ 3. **An event never requested cannot be granted.** The connection only knows the events in its
486
+ `?events=` list — request every event the page may need, and let `canSubscribe` deny.
487
+ - A frame already enqueued before a `revoke` may still be written; none after it is.
488
+ - WebSocket routers are not covered.
489
+
490
+ `subscription.granted` and `subscription.revoked` are stream-owned frames like `connected` and
491
+ `ping`: not in the contract, and reserved as router keys of contracted events. On the client:
492
+
493
+ ```typescript
494
+ client.subscribe({
495
+ events: ['runtimeChanged'],
496
+ handlers: { runtimeChanged: (p) => update(p) },
497
+ onSubscriptionChange: ({ event, status }) =>
498
+ {
499
+ // event: 'runtimeChanged' (typed); status: 'granted' | 'revoked'
500
+ },
501
+ });
502
+ ```
503
+
384
504
  ### SSE auth config (`SSEAuthConfig<TRouter>`)
385
505
 
386
506
  | Option | Type | Default | Description |
@@ -392,6 +512,7 @@ Return `false` to skip. Payload is typed per event.
392
512
  | `getSubject` | `(c: Context) => string \| null` | `c.get('auth')?.userId ?? null` | extract subject on the token endpoint |
393
513
  | `authorize` | `(subject, events[]) => events[] \| Promise<…>` | — | subscription authorization (once on connect) |
394
514
  | `filter` | `{ [event]?: (subject, payload) => boolean }` | — | per-event payload filter |
515
+ | `resolvePrincipal` | `(subject, request) => unknown \| Promise<unknown>` | — | principal for every `canSubscribe` (once per connection); a throw ⇒ `403` |
395
516
 
396
517
  ---
397
518
 
@@ -519,7 +640,10 @@ export default defineServerConfig()
519
640
  Mechanics (from `event.ts`): once a cache is wired, `emit` calls `cache.publish(name,
520
641
  payload)` **instead of** triggering local handlers directly — local handlers (including the
521
642
  SSE stream on the same pod) fire when the message comes back through the pub/sub `subscribe`
522
- callback. Auth `filter` still runs per-subscriber on the **receiving** pod's SSE handler —
643
+ callback. A handler subscribed with `{ localOnly: true }` is the exception: `emit` runs it directly
644
+ on the emitting pod, and a delivery through the cache never does — use it for work that must
645
+ happen once per emit, not once per pod. Without a cache it runs on every `emit` like any handler.
646
+ Auth `filter` still runs per-subscriber on the **receiving** pod's SSE handler —
523
647
  the transport only fans out by event name. Job queues receive the payload on every emit
524
648
  regardless of cache.
525
649
 
@@ -736,14 +860,27 @@ are reaped instead of lingering with their subscriptions and buffers.
736
860
  interface EventDef<TPayload = void> {
737
861
  readonly name: string;
738
862
  readonly schema?: TSchema;
739
- subscribe: (handler: EventHandler<TPayload>) => () => void;
863
+ subscribe: (handler: EventHandler<TPayload>, options?: { localOnly?: boolean }) => () => void;
740
864
  unsubscribeAll: () => void;
741
865
  emit: TPayload extends void ? () => Promise<void> : (payload: TPayload) => Promise<void>;
742
866
  useCache: (cache: PubSubCache) => Promise<EventDef<TPayload>>;
743
867
  readonly _contract?: EventContract;
744
868
  contract: (contract: EventContract) => EventDef<TPayload>;
869
+ readonly _canSubscribe?: SubscribePredicate<any>;
870
+ canSubscribe: <P = unknown>(predicate: SubscribePredicate<P>) => EventDef<TPayload>;
871
+ readonly _filter?: FramePredicate<TPayload>;
872
+ filter: (predicate: FramePredicate<TPayload>) => EventDef<TPayload>;
873
+ grant: (subject: string, match?: SubscriptionMatch) => Promise<void>;
874
+ revoke: (subject: string, match?: SubscriptionMatch) => Promise<void>;
745
875
  }
746
876
 
877
+ interface SubscriptionMatch { query: Record<string, string>; }
878
+ interface SubscribeRequest { query: Record<string, string>; headers: Record<string, string>; }
879
+ interface SubscribeContext<P = unknown> { subject: string; principal: P | undefined; request: SubscribeRequest; }
880
+ type SubscribePredicate<P = unknown> = (ctx: SubscribeContext<P>) => boolean | Promise<boolean>;
881
+ interface FrameContext { subject: string | undefined; request: SubscribeRequest; }
882
+ type FramePredicate<TPayload> = (payload: TPayload, ctx: FrameContext) => boolean;
883
+
747
884
  interface EventContract { since: string; deprecatedIn?: string; removedIn?: string; }
748
885
 
749
886
  type EventHandler<TPayload> = (payload: TPayload) => void | Promise<void>;