Update architecture docs for handler separation and pubsub API changes
- api-surface.md: Updated registry API table (registerSpec, registerHandler,
getHandler, separated spec/handler storage), OperationSpec description,
IOperationDefinition marked as convenience type, adapter return types
- call-protocol.md: Added pubsub EventEnvelope unwrapping details,
subscribe(type, id) 2-arg API, handler separation in buildCallHandler
and subscribe(), handler separation section
- adapters.md: Updated return types (OperationSpec & { handler }),
scanner validates against OperationSpecSchema, new module shape examples
showing spec-only and spec+handler patterns, typemap mention
- README.md: Core principle updated for spec/handler separation
- build-distribution.md: Updated pubsub dep description, registry.ts description
- AGENTS.md: Updated key points, source layout, provenance status
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-04-30
|
||||
last_updated: 2026-05-09
|
||||
---
|
||||
|
||||
# Call Protocol
|
||||
@@ -103,6 +103,7 @@ const callMap = new PendingRequestMap(eventTarget?)
|
||||
- Creates an internal `PubSub<CallPubSubMap>` using `createPubSub`
|
||||
- If `eventTarget` is provided, passes it to `createPubSub` for transport-level event routing (Redis, WebSocket, etc.)
|
||||
- Wires subscription handlers for `call.responded`, `call.error`, and `call.aborted` to route events back to waiting callers
|
||||
- Subscriptions use empty-string id (`subscribe("call.responded", "")`) to receive all events of each type. Events are unwrapped from `EventEnvelope` via `.payload`
|
||||
|
||||
### `call(operationId, input, options?)`
|
||||
|
||||
@@ -158,13 +159,15 @@ type CallHandler = (event: CallRequestedEvent) => Promise<void>
|
||||
|
||||
### Handler Flow
|
||||
|
||||
1. Look up operation by `operationId` from the registry
|
||||
1. Look up spec by `operationId` from the registry via `getSpec()`
|
||||
2. If not found, throw `CallError(OPERATION_NOT_FOUND, ...)`
|
||||
3. Check access control (see below)
|
||||
4. Validate input with `validateOrThrow`
|
||||
5. Execute operation handler
|
||||
6. On success: the handler is expected to have published `call.responded` through whatever mechanism
|
||||
7. On failure: `mapError` converts the thrown value to `CallError`
|
||||
3. Look up handler by `operationId` via `getHandler()`
|
||||
4. If not found, throw `CallError(OPERATION_NOT_FOUND, "No handler registered for operation: ...")`
|
||||
5. Check access control (see below)
|
||||
6. Validate input with `validateOrThrow`
|
||||
7. Execute operation handler
|
||||
8. On success: the handler is expected to have published `call.responded` through whatever mechanism
|
||||
9. On failure: `mapError` converts the thrown value to `CallError`
|
||||
|
||||
The `CallHandler` is designed to be wired into a pubsub subscription:
|
||||
|
||||
@@ -280,4 +283,13 @@ async function* subscribe(
|
||||
|
||||
Gets the operation from the registry, casts its handler to `AsyncGenerator`, and yields values. Properly cleans up with `generator.return()` in a `finally` block.
|
||||
|
||||
Use `subscribe()` for in-process consumption. Use `PendingRequestMap.call()` for cross-transport invocation that resolves after one event. For cross-transport streaming, use `PendingRequestMap.subscribe()` to yield multiple events.
|
||||
Use `subscribe()` for in-process consumption. Use `PendingRequestMap.call()` for cross-transport invocation that resolves after one event. For cross-transport streaming, use `PendingRequestMap.subscribe()` to yield multiple events.
|
||||
|
||||
### Handler Separation
|
||||
|
||||
The `subscribe()` function looks up both spec and handler separately from the registry:
|
||||
|
||||
1. `registry.getSpec(operationId)` — throws if spec not found
|
||||
2. `registry.getHandler(operationId)` — throws if handler not found
|
||||
|
||||
This allows spec-only registration for scenarios where handlers are provided separately (e.g., ujsx host interpretation, dynamic handler injection).
|
||||
Reference in New Issue
Block a user