@procedures/client 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +62 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/transport.d.ts +2 -1
- package/dist/adapters/http/client/transport.d.ts.map +1 -1
- package/dist/adapters/http/client/transport.js +9 -2
- package/dist/adapters/http/client/transport.js.map +1 -1
- package/dist/adapters/http/server/transport.d.ts +3 -1
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +8 -6
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/local/client/transport.d.ts +2 -1
- package/dist/adapters/local/client/transport.d.ts.map +1 -1
- package/dist/adapters/local/client/transport.js +12 -4
- package/dist/adapters/local/client/transport.js.map +1 -1
- package/dist/adapters/local/client/types.d.ts +6 -0
- package/dist/adapters/local/client/types.d.ts.map +1 -1
- package/dist/adapters/local/client/types.js.map +1 -1
- package/dist/adapters/sequence.d.ts +36 -0
- package/dist/adapters/sequence.d.ts.map +1 -0
- package/dist/adapters/sequence.js +55 -0
- package/dist/adapters/sequence.js.map +1 -0
- package/dist/adapters/websocket/client/node.d.ts +30 -0
- package/dist/adapters/websocket/client/node.d.ts.map +1 -0
- package/dist/adapters/websocket/client/node.js +32 -0
- package/dist/adapters/websocket/client/node.js.map +1 -0
- package/dist/adapters/websocket/client/transport.d.ts +39 -1
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +119 -16
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/adapters/websocket/client/types.d.ts +15 -5
- package/dist/adapters/websocket/client/types.d.ts.map +1 -1
- package/dist/adapters/websocket/client/types.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts +22 -1
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +29 -1
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/adapters/websocket/server/types.d.ts +6 -0
- package/dist/adapters/websocket/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.d.ts +4 -3
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.js +18 -9
- package/dist/adapters/websocket/shared/rpc.js.map +1 -1
- package/dist/browser.d.ts +3 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -0
- package/dist/browser.js.map +1 -1
- package/dist/client/errors/http-errors.d.ts.map +1 -1
- package/dist/client/errors/http-errors.js +6 -0
- package/dist/client/errors/http-errors.js.map +1 -1
- package/dist/client/errors/index.d.ts +1 -1
- package/dist/client/errors/index.js +1 -1
- package/dist/client/errors/registry.d.ts +2 -1
- package/dist/client/errors/registry.d.ts.map +1 -1
- package/dist/client/errors/registry.js +73 -22
- package/dist/client/errors/registry.js.map +1 -1
- package/dist/client/middleware/rate-limit.d.ts.map +1 -1
- package/dist/client/middleware/rate-limit.js +0 -1
- package/dist/client/middleware/rate-limit.js.map +1 -1
- package/dist/client/middleware/timeout.d.ts.map +1 -1
- package/dist/client/middleware/timeout.js +2 -9
- package/dist/client/middleware/timeout.js.map +1 -1
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +3 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/publisher.d.ts +95 -0
- package/dist/events/publisher.d.ts.map +1 -0
- package/dist/events/publisher.js +268 -0
- package/dist/events/publisher.js.map +1 -0
- package/dist/events/remote.d.ts +164 -0
- package/dist/events/remote.d.ts.map +1 -0
- package/dist/events/remote.js +250 -0
- package/dist/events/remote.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +11 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/dist/procedures/collection/index.d.ts +1 -1
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js.map +1 -1
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +6 -1
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/collection/schemas.d.ts +16 -1
- package/dist/procedures/collection/schemas.d.ts.map +1 -1
- package/dist/procedures/collection/schemas.js +13 -2
- package/dist/procedures/collection/schemas.js.map +1 -1
- package/dist/procedures/collection/storage.d.ts +14 -2
- package/dist/procedures/collection/storage.d.ts.map +1 -1
- package/dist/procedures/core/array.d.ts +1 -1
- package/dist/procedures/core/array.d.ts.map +1 -1
- package/dist/procedures/core/array.js +56 -15
- package/dist/procedures/core/array.js.map +1 -1
- package/dist/procedures/core/index.d.ts.map +1 -1
- package/dist/procedures/core/index.js +9 -2
- package/dist/procedures/core/index.js.map +1 -1
- package/dist/procedures/core/logic.js +1 -1
- package/dist/procedures/core/logic.js.map +1 -1
- package/dist/procedures/core/math.d.ts.map +1 -1
- package/dist/procedures/core/math.js +5 -1
- package/dist/procedures/core/math.js.map +1 -1
- package/dist/procedures/core/meta.d.ts.map +1 -1
- package/dist/procedures/core/meta.js +41 -7
- package/dist/procedures/core/meta.js.map +1 -1
- package/dist/procedures/core/type.d.ts.map +1 -1
- package/dist/procedures/core/type.js +4 -2
- package/dist/procedures/core/type.js.map +1 -1
- package/dist/procedures/discovery.d.ts +4 -6
- package/dist/procedures/discovery.d.ts.map +1 -1
- package/dist/procedures/discovery.js +4 -6
- package/dist/procedures/discovery.js.map +1 -1
- package/dist/procedures/invoke.d.ts +4 -2
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +1 -1
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/ref.d.ts +4 -2
- package/dist/procedures/ref.d.ts.map +1 -1
- package/dist/procedures/ref.js +2 -1
- package/dist/procedures/ref.js.map +1 -1
- package/dist/protocol.d.ts +10 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +13 -2
- package/dist/protocol.js.map +1 -1
- package/dist/server/host-policy.d.ts +11 -0
- package/dist/server/host-policy.d.ts.map +1 -1
- package/dist/server/host-policy.js +5 -0
- package/dist/server/host-policy.js.map +1 -1
- package/dist/server/procedure-server.d.ts +26 -3
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +38 -6
- package/dist/server/procedure-server.js.map +1 -1
- package/package.json +7 -6
- package/src/adapters/http/client/transport.ts +9 -2
- package/src/adapters/http/server/transport.ts +8 -6
- package/src/adapters/local/client/transport.ts +12 -4
- package/src/adapters/local/client/types.ts +7 -0
- package/src/adapters/sequence.ts +55 -0
- package/src/adapters/websocket/client/node.ts +34 -0
- package/src/adapters/websocket/client/transport.ts +128 -16
- package/src/adapters/websocket/client/types.ts +16 -5
- package/src/adapters/websocket/server/transport.ts +43 -1
- package/src/adapters/websocket/server/types.ts +7 -0
- package/src/adapters/websocket/shared/rpc.ts +19 -9
- package/src/browser.ts +15 -0
- package/src/client/errors/README.md +2 -0
- package/src/client/errors/http-errors.ts +5 -0
- package/src/client/errors/index.ts +1 -1
- package/src/client/errors/registry.ts +80 -22
- package/src/client/middleware/rate-limit.ts +0 -2
- package/src/client/middleware/timeout.ts +2 -9
- package/src/events/index.ts +16 -0
- package/src/events/publisher.ts +314 -0
- package/src/events/remote.ts +369 -0
- package/src/index.ts +4 -0
- package/src/node.ts +11 -0
- package/src/procedures/collection/index.ts +1 -0
- package/src/procedures/collection/procedures.ts +6 -1
- package/src/procedures/collection/schemas.ts +28 -2
- package/src/procedures/collection/storage.ts +14 -2
- package/src/procedures/core/array.ts +53 -16
- package/src/procedures/core/index.ts +9 -2
- package/src/procedures/core/logic.ts +1 -1
- package/src/procedures/core/math.ts +5 -1
- package/src/procedures/core/meta.ts +43 -7
- package/src/procedures/core/type.ts +4 -2
- package/src/procedures/discovery.ts +4 -6
- package/src/procedures/invoke.ts +4 -2
- package/src/procedures/ref.ts +4 -2
- package/src/protocol.ts +16 -3
- package/src/server/host-policy.ts +14 -0
- package/src/server/procedure-server.ts +45 -8
- package/dist/procedures/cli/announce.d.ts +0 -67
- package/dist/procedures/cli/announce.d.ts.map +0 -1
- package/dist/procedures/cli/announce.js +0 -392
- package/dist/procedures/cli/announce.js.map +0 -1
- package/dist/procedures/cli/discover.d.ts +0 -66
- package/dist/procedures/cli/discover.d.ts.map +0 -1
- package/dist/procedures/cli/discover.js +0 -310
- package/dist/procedures/cli/discover.js.map +0 -1
- package/dist/procedures/cli/index.d.ts +0 -13
- package/dist/procedures/cli/index.d.ts.map +0 -1
- package/dist/procedures/cli/index.js +0 -203
- package/dist/procedures/cli/index.js.map +0 -1
- package/dist/procedures/cli/init.d.ts +0 -17
- package/dist/procedures/cli/init.d.ts.map +0 -1
- package/dist/procedures/cli/init.js +0 -336
- package/dist/procedures/cli/init.js.map +0 -1
- package/src/procedures/cli/announce.ts +0 -470
- package/src/procedures/cli/discover.ts +0 -390
- package/src/procedures/cli/index.ts +0 -231
- package/src/procedures/cli/init.ts +0 -370
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
1
|
# @procedures/client
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 09e5566: Remote events (`documentation/EVENTS.md`): a peer subscribes to the topics of the event bus of another peer. A subscription is a streaming call of the procedure `events.subscribe`, so it uses the frames of each call: credit is the backpressure, and a cancel is the unsubscribe. It works over WebSocket in the two directions, HTTP with NDJSON, a message port and the local transport. The protocol stays v1.
|
|
8
|
+
|
|
9
|
+
- `WebSocketTransport.subscribe(topics, handler)` gives the unsubscribe function, with the `ready` promise of the first confirmation. `WebSocketTransport.events(topics)` gives an async iterator. After a drop, the transport resubscribes on the new connection.
|
|
10
|
+
- `subscribeRemote()` and `remoteEvents()` do the same over each transport. After a failure that a retry can repair, they resubscribe with a backoff.
|
|
11
|
+
- `ProcedureServer` has the option `events`: a bus, a topic rule (`topics`), an `allow` function and a `buffer`. The server pins `events.subscribe` outside its registry, so it is not an MCP tool. `ProcedureServer.publish()` emits on the bus. `stop()` ends each subscription with `UNAVAILABLE`.
|
|
12
|
+
- `WebSocketServerTransport.subscribe(connectionId, topics, handler)` and `events(connectionId, topics)` subscribe to the bus of a client. `LocalTransport` takes an `EventPublisher` with its option `events`.
|
|
13
|
+
- The delivery is at most once, in the order of `emit()`. When the queue of a slow subscriber is full, the publisher drops the oldest event, and the next event tells the loss (`dropped`).
|
|
14
|
+
- The new error code `TOPIC_NOT_FOUND` (404) is for a topic outside the topic rule. A topic that `allow` refuses gives `FORBIDDEN` with the reason `topic-not-allowed`.
|
|
15
|
+
- The `onEvent` option of `WebSocketTransport` is now a wrapper: it gets each event of each subscription of the transport. A raw `event` frame still goes to it.
|
|
16
|
+
- The WebSocket client does not warn about a `stream` frame of a call that it cancelled. The WebSocket server ignores a late `stream`, `response` or `error` frame of a call that it stopped. Before, it answered such a frame with an `error` frame.
|
|
17
|
+
- ab77f75: Sequence numbers of the stream frames (`documentation/WIRE-PROTOCOL.md`). Before, a duplicate `stream` frame gave a duplicate item. The change is additive, and the protocol stays v1.
|
|
18
|
+
|
|
19
|
+
- Each `stream` frame has the field `seq`: 1 in the first frame of a call, and one more in each next frame. The end frame has a number too. Each NDJSON line `item` or `done` has the same field.
|
|
20
|
+
- The receiver drops a frame whose `seq` is not above the last one. Thus a duplicate frame or a late frame gives no item.
|
|
21
|
+
- A gap does not end the stream, because the delivery is at most once. The next item has the number of lost frames in the metadata key `stream-gap` (`STREAM_GAP_METADATA_KEY`), and the console gets a warning. The receiver gives back the credit of the lost frames.
|
|
22
|
+
- A frame without `seq` comes from an older peer. The receiver takes it as before.
|
|
23
|
+
- The rules apply to WebSocket in the two directions, the message port and HTTP with NDJSON. The local transport has no frames.
|
|
24
|
+
- `WebSocketMessage` has the optional field `seq`.
|
|
25
|
+
- a47bee6: The hosts serve the remote events (`documentation/EVENTS.md`).
|
|
26
|
+
|
|
27
|
+
- `hostPolicy()` takes the option `events`: the topic rule, with an optional bus, `allow` function and buffer. `procedureServerOptions()` gives it to the `ProcedureServer`, which pins `events.subscribe`. `HostPolicy` has the new field `events`.
|
|
28
|
+
- `procedure-server` and `mark serve` have the option `--events <patterns>`. The option is repeatable, and it takes comma-separated patterns: exact names, prefixes (`orders.*`) or `*`. `parseTopicPatterns()` checks them.
|
|
29
|
+
- `mark --server` has the same option `--events`.
|
|
30
|
+
- These hosts always serve `events.subscribe`. Without `--events`, they serve no topic, and each subscription gets `TOPIC_NOT_FOUND`. The bus is the global bus, which is also the `ctx.bus` of their procedures.
|
|
31
|
+
- The MCP server serves no events, because its transport carries only MCP.
|
|
32
|
+
- `mark --port`, `--host`, `--transport` and `--events` without `--server` are an error now, also before `serve` and `mcp`. Before, `mark --port 4000 serve` ignored `--port`.
|
|
33
|
+
- bc74091: Two changes for the contract of `CollectionStorage`:
|
|
34
|
+
|
|
35
|
+
- The collection procedure `collections.<name>.get` takes the input `found: true`. Then its output is `{ found, value }`, so a stored `null` and a missing id give different outputs in one call. Without `found`, the output is the item, as before.
|
|
36
|
+
- `ProcedureServer.stop()` and `closeStorages()` close each storage one time, also a storage of two collections, and remove the storages from the server. Before, a server that started again used the closed storages.
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- 0f75644: The dependencies go to `@mark1russell7/algebra` `^0.7.0` and `@mark1russell7/service` `^0.4.1`. A host that uses algebra 0.7.0 gets one copy of it. Algebra 0.7.0 only adds to 0.6.0.
|
|
41
|
+
|
|
42
|
+
## 0.4.0
|
|
43
|
+
|
|
44
|
+
### Minor Changes
|
|
45
|
+
|
|
46
|
+
- d919678: `ERROR_REGISTRY` agrees with `ERROR_CODES` of the wire protocol. `ERROR_CODES` is the source: the registry has an entry for each code of the protocol, and that entry takes its HTTP status and its retry flag from `ERROR_CODES`. Changes that a caller sees:
|
|
47
|
+
|
|
48
|
+
- `TIMEOUT` has the status 504 and is not retryable (before: 408, retryable).
|
|
49
|
+
- `INTERNAL_ERROR` is not retryable (before: retryable).
|
|
50
|
+
- `ABORTED` has the status 499 (before: none).
|
|
51
|
+
- The codes of the protocol that the registry did not have (`HANDLER_ERROR`, `VALIDATION_ERROR`, `UNAVAILABLE` and the others) have entries, with the meaning of the protocol as their texts.
|
|
52
|
+
- `errorCodeToHTTPStatus` gives the status of `ERROR_CODES` for a code of the protocol, for example 504 for `TIMEOUT`.
|
|
53
|
+
- b2a1a1e: A handler error keeps its own code on each face, as `WIRE-PROTOCOL.md` says. `errorResponse` (the server, and through it HTTP, WebSocket and MCP) and `LocalTransport` give the code of the thrown error when it is a string. Before, they changed it to `HANDLER_ERROR` for a value, and `LocalTransport` also for a stream. An error without a code is still `HANDLER_ERROR`.
|
|
54
|
+
|
|
55
|
+
The HTTP status follows the code: a code of `ERROR_CODES` gets its status, another code of `ERROR_REGISTRY` gets the status of the registry, and each other code gets 500. For example, a handler error with the code `UNAVAILABLE` now gives 503.
|
|
56
|
+
- 057d69a: `client.import` gives `success: true` only for a procedure reference: an object whose `$proc` is a list of texts. Other JSON, for example `42`, an object without `$proc` or a list of calls, gives `success: false` and an error. Before, each JSON value gave `success: true`.
|
|
57
|
+
- 057d69a: `client.isObject` gives true only for a plain object: an object literal, or an object without a prototype. A `Date`, a `Map` or another class instance gives false. Before, each object that is not an array gave true.
|
|
58
|
+
- 057d69a: `client.stringifyJson` and `client.export` refuse a missing value (undefined) with `VALIDATION_ERROR`, because undefined has no JSON text. Before, the call failed with `OUTPUT_VALIDATION_ERROR`. The `value` field of `client.stringifyJson` and the `procedure` field of `client.export` are now required. A value without JSON text, for example a function, gives an error from `client.stringifyJson` and an error result from `client.export`.
|
|
59
|
+
- 057d69a: `client.mod` refuses `b = 0` with the error `Division by zero`, as `client.divide` does. Before, it gave `NaN`.
|
|
60
|
+
- afd6a19: The `client-registry` command is removed. `mark` finds the packages, and the `/register` entries register them.
|
|
61
|
+
- 057d69a: `client.unique` compares the items by value: two equal JSON values are duplicates. An array compares by its items in order, and a plain object by its fields in any order. With `key`, the values of the field compare by value too. The result keeps the first item of each value, in the order of the input. Before, objects and arrays compared by reference, so two equal objects both stayed.
|
|
62
|
+
- 4e751da: The new entry `@procedures/client/node` exports a `WebSocketTransport` whose sockets are of the `WebSocket` class of the `ws` package. A Node application gets `terminate()` without the option `webSocket`: a drop by the heartbeat or by the time limit of the handshake ends the socket at once. The option `webSocket` still gives another class. The other entries keep the global `WebSocket`, and the browser entry does not import `ws`.
|
|
63
|
+
|
|
64
|
+
A socket of the `ws` package that closes during its handshake emits an error. Before, the transport removed its error handler first, so a handshake timeout or a `close()` during the handshake ended the process with an uncaught error. The transport now keeps a handler that ignores that error.
|
|
65
|
+
|
|
66
|
+
### Patch Changes
|
|
67
|
+
|
|
68
|
+
- 057d69a: The description of the `match` field of `client.filter` lists all 11 modes. Before, it did not name `gt`, `gte`, `lt` and `lte`.
|
|
69
|
+
- 057d69a: The input schemas of `client.not`, `client.all`, `client.any` and `client.none` say "values", not "values or calls". These procedures do not run a call in their operands: a call is an object, thus truthy. In a program, the calls of their input run first, as for each procedure that is not control flow. `client.and` and `client.or` run calls. The behavior does not change.
|
|
70
|
+
|
|
3
71
|
## 0.3.0
|
|
4
72
|
|
|
5
73
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The core depends on no other package of the repository. The procedure components
|
|
|
27
27
|
- **Type-Safe Procedures**: Schema-validated procedures with auto-discovery and registry management
|
|
28
28
|
- **Middleware System**: Composable middleware for retry, cache, timeout, circuit breaker, rate limiting, auth, tracing, and validation
|
|
29
29
|
- **Procedure-as-Data**: JSON programs of calls, references and control flow ([PROGRAM-FORMAT.md](../documentation/PROGRAM-FORMAT.md))
|
|
30
|
-
- **Event Bus**: Pub/sub messaging with async iteration support
|
|
30
|
+
- **Event Bus**: Pub/sub messaging with async iteration support. A peer subscribes to the bus of another peer over each transport ([EVENTS.md](../documentation/EVENTS.md))
|
|
31
31
|
- **Error System**: 70+ error codes with rich metadata and recovery suggestions
|
|
32
32
|
|
|
33
33
|
## Installation
|
|
@@ -267,8 +267,14 @@ interface WebSocketTransportOptions {
|
|
|
267
267
|
timeout?: number; // default: 5000ms
|
|
268
268
|
misses?: number; // default: 1; pings without a pong that drop the connection
|
|
269
269
|
};
|
|
270
|
+
webSocket?: WebSocketClass; // the class of the sockets; default: the global WebSocket
|
|
270
271
|
}
|
|
271
272
|
|
|
273
|
+
// In Node, import the transport of the Node entry. Its sockets are of the `ws` package, so a
|
|
274
|
+
// drop ends a socket at once with terminate(). The global WebSocket has only close(): with a
|
|
275
|
+
// dead peer, its socket stays open until the kernel stops it (924.6 s in the cells of R36).
|
|
276
|
+
import { WebSocketTransport } from "@procedures/client/node";
|
|
277
|
+
|
|
272
278
|
// The server pings each connection, and closes one that misses 2 pongs (option heartbeat)
|
|
273
279
|
new WebSocketServerTransport(server, { server: http, heartbeat: { interval: seconds(30), misses: 2 } });
|
|
274
280
|
|
|
@@ -278,6 +284,17 @@ new WebSocketServerTransport(server, { server: http, heartbeat: { interval: seco
|
|
|
278
284
|
const transport = new WebSocketTransport({ url, serve: new ProcedureServer({ registry: uiRegistry, autoRegister: true }) });
|
|
279
285
|
const toClient = new Client({ transport: wsServer.transportFor(connectionId) }); // or peer.clientFor(id)
|
|
280
286
|
await toClient.call({ service: "ui", operation: "notify" }, { text: "Build done" });
|
|
287
|
+
|
|
288
|
+
// Remote events (documentation/EVENTS.md): subscribe to topics of the bus of the server. The
|
|
289
|
+
// function gives the unsubscribe. After a drop, the transport resubscribes on the new connection.
|
|
290
|
+
const unsubscribe = transport.subscribe("orders.created", (order, event) => console.log(event.seq, order), {
|
|
291
|
+
onSubscribed: (topics) => console.log("subscribed", topics),
|
|
292
|
+
onError: (error) => console.error(error.code), // TOPIC_NOT_FOUND, FORBIDDEN, NOT_FOUND
|
|
293
|
+
});
|
|
294
|
+
await unsubscribe.ready;
|
|
295
|
+
unsubscribe();
|
|
296
|
+
// The same events as an async iterator: a break or the signal unsubscribes
|
|
297
|
+
for await (const event of transport.events(["orders.created", "orders.paid"], { signal })) console.log(event.topic, event.data);
|
|
281
298
|
```
|
|
282
299
|
|
|
283
300
|
#### `LocalTransport`
|
|
@@ -494,6 +511,39 @@ const unsubscribe = bus.on("event", handler)
|
|
|
494
511
|
unsubscribe()
|
|
495
512
|
```
|
|
496
513
|
|
|
514
|
+
#### Remote events
|
|
515
|
+
|
|
516
|
+
A peer subscribes to the topics of the bus of another peer ([EVENTS.md](../documentation/EVENTS.md)). A subscription is a streaming call of the procedure `events.subscribe`, so it works over WebSocket (in the two directions), HTTP with NDJSON, a message port and the local transport.
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// The publisher: a ProcedureServer with the option events
|
|
520
|
+
const bus = createEventBus()
|
|
521
|
+
const server = new ProcedureServer({
|
|
522
|
+
registry,
|
|
523
|
+
autoRegister: true,
|
|
524
|
+
events: {
|
|
525
|
+
bus, // the procedures of the server get it as ctx.bus
|
|
526
|
+
topics: ["orders.*", "system:*"], // the topic rule: an exact name, a prefix with *, or *
|
|
527
|
+
allow: (topic, call) => !topic.startsWith("system:") || call.capability.clearance === "admin",
|
|
528
|
+
buffer: 1024, // the events that one subscription holds for a slow subscriber
|
|
529
|
+
},
|
|
530
|
+
})
|
|
531
|
+
server.publish("orders.created", { order: 1 }) // the same as bus.emit()
|
|
532
|
+
|
|
533
|
+
// The subscriber, over each transport
|
|
534
|
+
const unsubscribe = subscribeRemote(transport, "orders.created", (order, event) => console.log(order, event.seq))
|
|
535
|
+
for await (const event of remoteEvents(transport, ["orders.created"], { signal })) console.log(event.data)
|
|
536
|
+
|
|
537
|
+
// The server subscribes to the bus of a client: the client serves it with `serve`
|
|
538
|
+
new WebSocketTransport({ url, serve: new ProcedureServer({ events: { bus: clientBus, topics: ["ui.*"] } }) })
|
|
539
|
+
wsServer.subscribe(connectionId, "ui.clicked", (click) => console.log(click))
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
- The delivery is at most once, in the order of `emit()`. `seq` numbers the events of a subscription, and `dropped` tells the events that the publisher dropped for a slow subscriber.
|
|
543
|
+
- A topic outside the topic rule gives `TOPIC_NOT_FOUND`. A topic that `allow` refuses gives `FORBIDDEN` with the reason `topic-not-allowed`. The procedure reads the resource `events`, so the capability of the host applies to it.
|
|
544
|
+
- `ProcedureServer.stop()` ends each subscription with `UNAVAILABLE`, and the subscriber resubscribes later.
|
|
545
|
+
- `events.subscribe` is not in a registry: it is not an MCP tool.
|
|
546
|
+
|
|
497
547
|
### Server
|
|
498
548
|
|
|
499
549
|
```typescript
|
|
@@ -544,6 +594,7 @@ interface ProcedureServerOptions {
|
|
|
544
594
|
registry?: ProcedureRegistry;
|
|
545
595
|
storage?: StorageConfig;
|
|
546
596
|
transports?: ServerTransport[];
|
|
597
|
+
events?: EventPublisherOptions | EventPublisher; // the remote events: refer to "Remote events"
|
|
547
598
|
}
|
|
548
599
|
```
|
|
549
600
|
|
|
@@ -587,22 +638,26 @@ import { ... } from "@procedures/client/procedures";
|
|
|
587
638
|
import { ... } from "@procedures/client/events";
|
|
588
639
|
```
|
|
589
640
|
|
|
590
|
-
|
|
641
|
+
| Entry | Use |
|
|
642
|
+
|---|---|
|
|
643
|
+
| `@procedures/client` | Node. The browser entry and the server transports. Its `WebSocketTransport` uses the global `WebSocket`. |
|
|
644
|
+
| `@procedures/client/browser` | A browser bundle. It does not import `http` or `ws`. |
|
|
645
|
+
| `@procedures/client/node` | Node. A `WebSocketTransport` whose sockets are of the `ws` package: a drop ends a socket at once. |
|
|
646
|
+
| `@procedures/client/register` | It registers the core procedures in the global registry. |
|
|
591
647
|
|
|
592
|
-
|
|
648
|
+
## Procedure Discovery
|
|
649
|
+
|
|
650
|
+
A package declares its procedures in `package.json`:
|
|
593
651
|
|
|
594
652
|
```json
|
|
595
653
|
{
|
|
596
654
|
"client": {
|
|
597
655
|
"procedures": "./dist/register.js"
|
|
598
|
-
},
|
|
599
|
-
"scripts": {
|
|
600
|
-
"postinstall": "client-registry announce"
|
|
601
656
|
}
|
|
602
657
|
}
|
|
603
658
|
```
|
|
604
659
|
|
|
605
|
-
|
|
660
|
+
The `mark` CLI finds the packages by the `client.procedures` field. An app imports the `/register` entry of each package that it uses, for example `import "@procedures/fs/register"`.
|
|
606
661
|
|
|
607
662
|
## Type-Safe Middleware
|
|
608
663
|
|