@procedures/client 0.6.0 → 0.8.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 +66 -0
- package/README.md +63 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/transport.d.ts +5 -0
- package/dist/adapters/http/client/transport.d.ts.map +1 -1
- package/dist/adapters/http/client/transport.js +10 -4
- package/dist/adapters/http/client/transport.js.map +1 -1
- package/dist/adapters/http/server/ndjson.d.ts +73 -0
- package/dist/adapters/http/server/ndjson.d.ts.map +1 -0
- package/dist/adapters/http/server/ndjson.js +228 -0
- package/dist/adapters/http/server/ndjson.js.map +1 -0
- package/dist/adapters/http/server/transport.d.ts +15 -4
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +29 -69
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/http/server/types.d.ts +15 -0
- package/dist/adapters/http/server/types.d.ts.map +1 -1
- package/dist/adapters/sequence.d.ts +14 -0
- package/dist/adapters/sequence.d.ts.map +1 -1
- package/dist/adapters/sequence.js +16 -0
- package/dist/adapters/sequence.js.map +1 -1
- package/dist/adapters/websocket/client/transport.d.ts +7 -3
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +9 -3
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts +10 -0
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +22 -2
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/browser.d.ts +5 -5
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +4 -3
- package/dist/browser.js.map +1 -1
- package/dist/client/errors/registry.d.ts.map +1 -1
- package/dist/client/errors/registry.js +1 -0
- package/dist/client/errors/registry.js.map +1 -1
- package/dist/client/middleware/retry.d.ts +4 -0
- package/dist/client/middleware/retry.d.ts.map +1 -1
- package/dist/client/middleware/retry.js +86 -78
- package/dist/client/middleware/retry.js.map +1 -1
- package/dist/events/bus.d.ts +32 -14
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +119 -65
- package/dist/events/bus.js.map +1 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/events/publisher.d.ts.map +1 -1
- package/dist/events/publisher.js +20 -6
- package/dist/events/publisher.js.map +1 -1
- package/dist/events/remote.d.ts +80 -18
- package/dist/events/remote.d.ts.map +1 -1
- package/dist/events/remote.js +448 -58
- package/dist/events/remote.js.map +1 -1
- package/dist/events/types.d.ts +13 -2
- package/dist/events/types.d.ts.map +1 -1
- package/dist/node.d.ts +6 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +5 -0
- package/dist/node.js.map +1 -1
- package/dist/procedures/caller.d.ts +33 -0
- package/dist/procedures/caller.d.ts.map +1 -0
- package/dist/procedures/caller.js +27 -0
- package/dist/procedures/caller.js.map +1 -0
- package/dist/procedures/collection/index.d.ts +2 -2
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js +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 +21 -1
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/collection/schemas.d.ts +9 -0
- package/dist/procedures/collection/schemas.d.ts.map +1 -1
- package/dist/procedures/collection/schemas.js +28 -0
- package/dist/procedures/collection/schemas.js.map +1 -1
- package/dist/procedures/collection/storage.d.ts +6 -0
- package/dist/procedures/collection/storage.d.ts.map +1 -1
- package/dist/procedures/index.d.ts +3 -1
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +2 -0
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +3 -0
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/types.d.ts +50 -0
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/protocol.d.ts +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +1 -0
- package/dist/protocol.js.map +1 -1
- package/dist/server/procedure-server.d.ts +41 -5
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +94 -9
- package/dist/server/procedure-server.js.map +1 -1
- package/dist/timers.d.ts +9 -0
- package/dist/timers.d.ts.map +1 -1
- package/dist/timers.js +6 -1
- package/dist/timers.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/http/client/transport.ts +10 -4
- package/src/adapters/http/server/ndjson.ts +259 -0
- package/src/adapters/http/server/transport.ts +31 -69
- package/src/adapters/http/server/types.ts +17 -0
- package/src/adapters/sequence.ts +18 -0
- package/src/adapters/websocket/client/transport.ts +9 -3
- package/src/adapters/websocket/server/transport.ts +23 -2
- package/src/browser.ts +9 -2
- package/src/client/errors/registry.ts +1 -0
- package/src/client/middleware/retry.ts +89 -78
- package/src/events/bus.ts +141 -73
- package/src/events/index.ts +3 -0
- package/src/events/publisher.ts +23 -6
- package/src/events/remote.ts +575 -73
- package/src/events/types.ts +15 -2
- package/src/node.ts +6 -0
- package/src/procedures/caller.ts +45 -0
- package/src/procedures/collection/index.ts +3 -0
- package/src/procedures/collection/procedures.ts +22 -0
- package/src/procedures/collection/schemas.ts +37 -0
- package/src/procedures/collection/storage.ts +6 -0
- package/src/procedures/index.ts +4 -0
- package/src/procedures/invoke.ts +2 -0
- package/src/procedures/types.ts +53 -0
- package/src/protocol.ts +3 -1
- package/src/server/procedure-server.ts +111 -11
- package/src/timers.ts +15 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
# @procedures/client
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4b09a03: `debounceChannel()` and `throttleChannel()` take a `Duration`, as every other time of the client does since 0.3.0. A plain number of milliseconds throws a `TypeError`. A new option `clock` gives the clock of their timers, for example a fake clock in a test. MINOR: a breaking change of a signature (the API is before 1.0.0).
|
|
8
|
+
|
|
9
|
+
Migration: `debounceChannel(bus, "in", "out", 300)` becomes `debounceChannel(bus, "in", "out", ms(300))`.
|
|
10
|
+
- 698e24d: Three gaps of the remote events (`documentation/EVENTS.md`) are closed. The option `backoff` of a subscription changes, so this release is breaking for a caller that gives it.
|
|
11
|
+
|
|
12
|
+
- **Shared subscriptions.** The subscribers of the same topics with the same options on one transport share one call: one `request` on the wire, with a count of its subscribers. Only the last unsubscribe sends the `cancel`. Before, each `subscribeRemote()`, `remoteEvents()` or `transport.subscribe()` made its own call. Each subscriber keeps its own id, its own `ready` and its own queue of at most 1024 events. A late subscriber gets no replay: its `ready` resolves at once, and its `seq` starts at 1. An error or an end of the shared call goes to each subscriber. `WebSocketServerTransport` shares the subscriptions of one connection.
|
|
13
|
+
- **The end at `clear()`.** `clear()` and `clearAll()` of the bus of the publisher end each subscription of a cleared topic. The subscriber first gets the events of the queue, then the error `TOPIC_CLEARED` (new protocol code, HTTP 410, not retryable) through `onError`, `ready` or the iterator. Before, the listeners went, and the subscriber saw no end. The new option `onClear` of `EventBus.on(channel, handler, { onClear })` (type `EventListenOptions`) gives the same notice to a local handler.
|
|
14
|
+
- **The limit of the resubscribe.** The new option `maxAttempts` (default 10) is the number of resubscribe tries without a confirmation. When the tries run out, the subscription ends with a `SubscriptionError` that has the code of the last failure. Before, a subscription over HTTP resubscribed forever. The WebSocket transport uses `reconnect.maxAttempts` (default `Infinity`), so its behavior stays. The new option `clock` is the clock of the backoff, for a fake clock in a test. `SubscriptionHooks` has the new fields `maxAttempts` and `clock`.
|
|
15
|
+
|
|
16
|
+
Migration: the option `backoff` is an object of durations (type `ResubscribeBackoff`), with the names of the `reconnect` options of `WebSocketTransport`: `initialDelay` (default `ms(250)`), `maxDelay` (default `seconds(30)`), `backoffMultiplier` (default 2) and `jitter` (default 1). Before, it was a function of milliseconds. A function or a plain number throws a `TypeError` at the call. For the old behavior over HTTP, give `maxAttempts: Infinity`.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// Before
|
|
20
|
+
subscribeRemote(transport, "orders.created", handler, { backoff: (attempt) => Math.min(30_000, 500 * 2 ** (attempt - 1)) });
|
|
21
|
+
// Now
|
|
22
|
+
subscribeRemote(transport, "orders.created", handler, { backoff: { initialDelay: ms(500), maxDelay: seconds(30), jitter: 0 }, maxAttempts: Infinity });
|
|
23
|
+
```
|
|
24
|
+
- d25805a: `ProcedureServer.useStorage` with `close` waits for the calls on the old storage before it closes it. Before, it closed the old storage at once, and a call that started on it could fail as closed.
|
|
25
|
+
|
|
26
|
+
MINOR: the call of `useStorage` with `close` can now take up to 30 seconds to resolve. That is a change of behaviour that a caller can see. Give `grace: ms(0)` for the old behaviour.
|
|
27
|
+
|
|
28
|
+
- The new calls use the new storage at once. The server waits only for the calls that took the old storage. A stream holds its storage until its end.
|
|
29
|
+
- The new option `grace` of `UseStorageOptions` is the longest wait, a `Duration`. A plain number throws a `TypeError`. The default is 30 seconds, `TIMER_DEFAULTS.server.storageGraceMs`. 0 closes at once.
|
|
30
|
+
- When the time ends, the server closes the old storage. A call that still uses it then fails with the error of a closed storage.
|
|
31
|
+
- The new option `clock` of `ProcedureServerOptions` is the clock of this wait. A test gives a fake clock.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- 4b09a03: New `callerOf(...modules)` in the root and the browser entry. It gives a function that runs the procedures of the modules and gives one value. It makes the registry of the modules at the first call and does not use the global registry. An app wrote the three steps itself before: `createRegistry`, `invokePath` and `outputValue`. PATCH: a new export (the API is before 1.0.0).
|
|
36
|
+
- aa6e3aa: `ApiStorage` has `getIds()`. A full pull through it does not read each entry for the ids.
|
|
37
|
+
|
|
38
|
+
- The collections have the procedure `collections.<name>.ids`. It gives the ids in the order of the entries, without the values. Its effects read the collection and write nothing. A storage without `getIds()` gives the ids of its entries.
|
|
39
|
+
- The `CollectionStorage` of the core has the optional method `getIds()`, as the `CollectionStorage` of `@procedures/collections` has.
|
|
40
|
+
- `ApiStorage.getIds()` uses the procedure `ids`. An older server without it answers `NOT_FOUND`. Then the storage reads the ids from the entries, and it does not ask for `ids` again.
|
|
41
|
+
- `ApiStorageOptions.operations` takes `ids`, the name of the operation. The default is `ids`.
|
|
42
|
+
- 4b09a03: `bus.once()` rejects with an `AbortError` when `clear()` or `clearAll()` removes its channel. Before, its promise never settled.
|
|
43
|
+
- 600c497: A server on port 0 tells its real port.
|
|
44
|
+
|
|
45
|
+
- New `HttpServerTransport.port()` returns the port that the server listens on. With the option `port` 0, the system gives a free port, and the function returns that port.
|
|
46
|
+
- `peer.getEndpoints()` gives the real port of each HTTP and WebSocket endpoint. Before, a peer on port 0 gave the address `:0`.
|
|
47
|
+
- The tests of the body and the stop of the peer listen on port 0. Before, a helper found a free port and closed it before the peer took it, so one CI run failed with `EADDRINUSE`.
|
|
48
|
+
- 7c2e7f7: The `onAfterRetry` hook of `createRetryMiddleware` hears of a failure when the reader stops at the error item. A `Client` with `throwOnError` (the default) stops there. Before, the hook ran after the yield of that item, so it ran only for a success. Now it runs when the call ends: after the last item, or when the reader stops. A thrown error and an abort still do not call it. PATCH: a bug fix.
|
|
49
|
+
- 90ea741: A procedure can declare its rules under a root of the files in `metadata.fsRoot` (the new type `FsRootRules`): the inputs that name a path, or the reason that a root cannot confine it. A handler gets `ctx.caller`, the procedure whose handler made the nested call. The framework sets it from the invocation, so an input cannot change it (roadmap R33).
|
|
50
|
+
- c4cfa34: `ProcedureServer.useStorage` with `close` keeps an old storage open while another collection still uses it. The server closes it when the last collection leaves it. Before, the close broke the calls of the other collection.
|
|
51
|
+
|
|
52
|
+
## 0.7.0
|
|
53
|
+
|
|
54
|
+
### Minor Changes
|
|
55
|
+
|
|
56
|
+
- 841d083: An NDJSON stream over HTTP gets keep-alive lines. Before, a proxy or a client with an idle timeout (for example 300 s) closed a long procedure that was quiet for that time.
|
|
57
|
+
|
|
58
|
+
- `HttpServerTransport` writes a keep-alive line, a blank line, after each quiet time of the new option `keepAlive` (default `seconds(15)`, `TIMER_DEFAULTS.httpServer.keepAliveMs`). Each line starts the time again. No keep-alive line comes after the `done` line, an `error` line, an abort or a closed connection. `keepAlive: false` turns it off.
|
|
59
|
+
- The new option `clock` of `HttpServerTransport` is the clock of the keep-alive timers. A test gives the `fakeClock` of `@mark1russell7/service/testing`.
|
|
60
|
+
- A keep-alive line has no `seq`, so the sequence check of the reader sees no gap. `WIRE-PROTOCOL.md` specifies the line: it is an additive change of protocol v1, because each reader skipped a blank line before.
|
|
61
|
+
- New `writeNdjsonStream(res, items, options?)` in `@procedures/client/node`. It writes the NDJSON stream response of protocol v1 for a `ServerResponse` of `node:http`, without Express. It writes the headers, the `item`, `done` and `error` lines with `seq`, and the keep-alive lines. It waits for `drain` when the buffer is full, and it stops when the connection closes or `signal` aborts. Its options are `signal`, `keepAlive`, `errorOf` and `clock`. `HttpServerTransport` uses it, so the two write the same bytes.
|
|
62
|
+
- `HttpTransport` gives each item of an NDJSON stream the metadata key `stream` with the value `true` (the new constant `STREAM_ITEM_METADATA_KEY`). The error item that ends the stream has it too. The key does not cross the wire, and the client removes a key `stream` that the peer sent.
|
|
63
|
+
- When the client goes away while the handler is quiet, the writer of the stream stops at once. Before, it waited for the next item of a handler that ignores its signal.
|
|
64
|
+
|
|
65
|
+
### Patch Changes
|
|
66
|
+
|
|
67
|
+
- 389b054: A token check no longer tells the length of the token through its time. The peer compares the SHA-256 digests of the two tokens with `timingSafeEqual`. The `tokenMatches` of the client, which runs in a browser too, compares each character of the expected token for each presented token. Before, a token of another length returned at once. The re-audit of jarvis found it.
|
|
68
|
+
|
|
3
69
|
## 0.6.0
|
|
4
70
|
|
|
5
71
|
No changes in this release.
|
package/README.md
CHANGED
|
@@ -179,6 +179,18 @@ const getUserProcedure = defineProcedure({
|
|
|
179
179
|
registerProcedures([getUserProcedure]);
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
+
### Procedures of a package in the same process
|
|
183
|
+
|
|
184
|
+
`callerOf(...modules)` gives a function that runs the procedures of the modules. It makes the registry of the modules at the first call, and it does not use the global registry. Each call gives one value: the output, or the last item of a stream.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { callerOf } from "@procedures/client";
|
|
188
|
+
import { ghModule } from "@procedures/gh";
|
|
189
|
+
|
|
190
|
+
const gh = callerOf(ghModule);
|
|
191
|
+
const runs = await gh<RunList>(["gh", "run", "list"], { repo: "owner/name" });
|
|
192
|
+
```
|
|
193
|
+
|
|
182
194
|
## API Reference
|
|
183
195
|
|
|
184
196
|
### Client
|
|
@@ -289,7 +301,7 @@ await toClient.call({ service: "ui", operation: "notify" }, { text: "Build done"
|
|
|
289
301
|
// function gives the unsubscribe. After a drop, the transport resubscribes on the new connection.
|
|
290
302
|
const unsubscribe = transport.subscribe("orders.created", (order, event) => console.log(event.seq, order), {
|
|
291
303
|
onSubscribed: (topics) => console.log("subscribed", topics),
|
|
292
|
-
onError: (error) => console.error(error.code), // TOPIC_NOT_FOUND, FORBIDDEN, NOT_FOUND
|
|
304
|
+
onError: (error) => console.error(error.code), // TOPIC_NOT_FOUND, FORBIDDEN, NOT_FOUND, TOPIC_CLEARED
|
|
293
305
|
});
|
|
294
306
|
await unsubscribe.ready;
|
|
295
307
|
unsubscribe();
|
|
@@ -531,7 +543,10 @@ const server = new ProcedureServer({
|
|
|
531
543
|
server.publish("orders.created", { order: 1 }) // the same as bus.emit()
|
|
532
544
|
|
|
533
545
|
// The subscriber, over each transport
|
|
534
|
-
const unsubscribe = subscribeRemote(transport, "orders.created", (order, event) => console.log(order, event.seq)
|
|
546
|
+
const unsubscribe = subscribeRemote(transport, "orders.created", (order, event) => console.log(order, event.seq), {
|
|
547
|
+
maxAttempts: 10, // the resubscribe tries without a confirmation (default 10; WebSocket: reconnect.maxAttempts)
|
|
548
|
+
backoff: { initialDelay: ms(250), maxDelay: seconds(30), backoffMultiplier: 2, jitter: 1 }, // Durations: a plain number throws a TypeError
|
|
549
|
+
})
|
|
535
550
|
for await (const event of remoteEvents(transport, ["orders.created"], { signal })) console.log(event.data)
|
|
536
551
|
|
|
537
552
|
// The server subscribes to the bus of a client: the client serves it with `serve`
|
|
@@ -541,7 +556,9 @@ wsServer.subscribe(connectionId, "ui.clicked", (click) => console.log(click))
|
|
|
541
556
|
|
|
542
557
|
- 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
558
|
- 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.
|
|
559
|
+
- `ProcedureServer.stop()` ends each subscription with `UNAVAILABLE`, and the subscriber resubscribes later, at most `maxAttempts` times without a confirmation.
|
|
560
|
+
- `clear()` or `clearAll()` of the bus ends each subscription of a cleared topic with `TOPIC_CLEARED`. The subscriber does not resubscribe. `bus.on(channel, handler, { onClear })` gives the same notice to a local handler.
|
|
561
|
+
- The subscribers of the same topics with the same options on one transport share one call. The last unsubscribe sends the cancel. A late subscriber gets no replay: its `ready` resolves at once, and its `seq` starts at 1.
|
|
545
562
|
- `events.subscribe` is not in a registry: it is not an MCP tool.
|
|
546
563
|
|
|
547
564
|
### Server
|
|
@@ -567,13 +584,52 @@ await server.stop()
|
|
|
567
584
|
new HttpServerTransport(server: Server, options: HttpServerTransportOptions)
|
|
568
585
|
|
|
569
586
|
interface HttpServerTransportOptions {
|
|
570
|
-
|
|
571
|
-
|
|
587
|
+
app: Express; // required
|
|
588
|
+
httpServer?: HttpServer; // an existing server: the transport does not listen
|
|
589
|
+
port?: number; // default: 3000
|
|
590
|
+
host?: string; // default: "127.0.0.1"
|
|
591
|
+
basePath?: string; // default: "/api"
|
|
572
592
|
urlStrategy?: HttpUrlStrategy;
|
|
573
|
-
cors?: boolean
|
|
593
|
+
cors?: boolean;
|
|
594
|
+
corsOptions?: { origin?: string | string[]; methods?: string[]; allowedHeaders?: string[]; credentials?: boolean };
|
|
595
|
+
bodyLimit?: number; // default: 1 MiB
|
|
596
|
+
keepAlive?: Duration | false; // default: seconds(15)
|
|
597
|
+
clock?: Clock; // the clock of the keep-alive timers
|
|
574
598
|
}
|
|
575
599
|
```
|
|
576
600
|
|
|
601
|
+
A client that accepts `application/x-ndjson` gets a stream as NDJSON lines ([WIRE-PROTOCOL.md](../documentation/WIRE-PROTOCOL.md)).
|
|
602
|
+
|
|
603
|
+
- After each quiet time of `keepAlive`, the server writes a keep-alive line: a blank line. Thus a proxy or a client with an idle timeout does not close a long, quiet stream.
|
|
604
|
+
- Each line starts the quiet time again. No keep-alive line comes after the end, an error, an abort or a closed connection.
|
|
605
|
+
- `keepAlive: false` turns the keep-alive lines off.
|
|
606
|
+
- The HTTP client skips each blank line. Each item that it reads from an NDJSON stream has the metadata key `stream: true` (`STREAM_ITEM_METADATA_KEY`).
|
|
607
|
+
|
|
608
|
+
A server without Express writes the same lines with `writeNdjsonStream` of `@procedures/client/node`:
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
import { createServer } from "node:http";
|
|
612
|
+
import { seconds } from "@procedures/client";
|
|
613
|
+
import { writeNdjsonStream } from "@procedures/client/node";
|
|
614
|
+
|
|
615
|
+
async function* progress() {
|
|
616
|
+
for (let step = 1; step <= 3; step++) {
|
|
617
|
+
await new Promise((resolve) => setTimeout(resolve, 60_000)); // a quiet minute: 4 keep-alive lines
|
|
618
|
+
yield { step };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
createServer((req, res) => {
|
|
623
|
+
// The headers, the item, done and error lines with seq, the keep-alive lines and the backpressure
|
|
624
|
+
void writeNdjsonStream(res, progress(), {
|
|
625
|
+
keepAlive: seconds(15),
|
|
626
|
+
errorOf: (error) => ({ code: "UNAVAILABLE", message: String(error), retryable: true }),
|
|
627
|
+
});
|
|
628
|
+
}).listen(3000);
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
The promise of `writeNdjsonStream` resolves when the response ends. A closed connection or an abort of `signal` stops the stream, and the iterator of the items gets `return()`.
|
|
632
|
+
|
|
577
633
|
#### WebSocket Server Transport
|
|
578
634
|
```typescript
|
|
579
635
|
new WebSocketServerTransport(server: Server, options: WebSocketServerTransportOptions)
|
|
@@ -642,7 +698,7 @@ import { ... } from "@procedures/client/events";
|
|
|
642
698
|
|---|---|
|
|
643
699
|
| `@procedures/client` | Node. The browser entry and the server transports. Its `WebSocketTransport` uses the global `WebSocket`. |
|
|
644
700
|
| `@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. |
|
|
701
|
+
| `@procedures/client/node` | Node. A `WebSocketTransport` whose sockets are of the `ws` package: a drop ends a socket at once. `writeNdjsonStream`: the NDJSON stream response of `HttpServerTransport`, for a `ServerResponse` of `node:http`. |
|
|
646
702
|
| `@procedures/client/register` | It registers the core procedures in the global registry. |
|
|
647
703
|
|
|
648
704
|
## Procedure Discovery
|