@procedures/client 0.46.0 → 0.47.2
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 +99 -68
- package/README.md +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/server/transport.d.ts +5 -0
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +7 -0
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/local/client/transport.d.ts.map +1 -1
- package/dist/adapters/local/client/transport.js +12 -1
- package/dist/adapters/local/client/transport.js.map +1 -1
- package/dist/adapters/websocket/client/node.d.ts.map +1 -1
- package/dist/adapters/websocket/client/node.js +9 -2
- package/dist/adapters/websocket/client/node.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +14 -5
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +7 -3
- package/dist/browser.js.map +1 -1
- package/dist/client/client.d.ts +10 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +46 -5
- package/dist/client/client.js.map +1 -1
- package/dist/client/errors/known-codes.d.ts +16 -0
- package/dist/client/errors/known-codes.d.ts.map +1 -0
- package/dist/client/errors/known-codes.js +20 -0
- package/dist/client/errors/known-codes.js.map +1 -0
- package/dist/client/validation/index.d.ts +1 -1
- package/dist/client/validation/index.js +1 -1
- package/dist/events/publisher.js +1 -1
- package/dist/events/publisher.js.map +1 -1
- package/dist/procedures/caller.d.ts +1 -1
- package/dist/procedures/caller.js +1 -1
- package/dist/procedures/collection/admission.d.ts +1 -1
- package/dist/procedures/collection/admission.js +1 -1
- package/dist/procedures/core/array.js +1 -1
- package/dist/procedures/core/array.js.map +1 -1
- package/dist/procedures/core/comparison.js +1 -1
- package/dist/procedures/core/comparison.js.map +1 -1
- package/dist/procedures/core/index.d.ts.map +1 -1
- package/dist/procedures/core/index.js +4 -2
- package/dist/procedures/core/index.js.map +1 -1
- package/dist/procedures/core/math.js +1 -1
- package/dist/procedures/core/math.js.map +1 -1
- package/dist/procedures/core/meta.js +1 -1
- package/dist/procedures/core/meta.js.map +1 -1
- package/dist/procedures/core/object.js +1 -1
- package/dist/procedures/core/object.js.map +1 -1
- package/dist/procedures/core/operands.d.ts.map +1 -1
- package/dist/procedures/core/operands.js +7 -2
- package/dist/procedures/core/operands.js.map +1 -1
- package/dist/procedures/core/outputs.d.ts +1 -1
- package/dist/procedures/core/outputs.d.ts.map +1 -1
- package/dist/procedures/core/outputs.js +1 -1
- package/dist/procedures/core/outputs.js.map +1 -1
- package/dist/procedures/core/string.js +1 -1
- package/dist/procedures/core/string.js.map +1 -1
- package/dist/procedures/core/type.js +1 -1
- package/dist/procedures/core/type.js.map +1 -1
- package/dist/procedures/define-procedure.d.ts.map +1 -1
- package/dist/procedures/define-procedure.js +28 -12
- package/dist/procedures/define-procedure.js.map +1 -1
- package/dist/procedures/index.d.ts +6 -5
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +6 -4
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +14 -1
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/module.d.ts +34 -0
- package/dist/procedures/module.d.ts.map +1 -1
- package/dist/procedures/module.js +90 -0
- package/dist/procedures/module.js.map +1 -1
- package/dist/procedures/ref.d.ts +10 -1
- package/dist/procedures/ref.d.ts.map +1 -1
- package/dist/procedures/ref.js +64 -0
- package/dist/procedures/ref.js.map +1 -1
- package/dist/procedures/registry.d.ts +64 -0
- package/dist/procedures/registry.d.ts.map +1 -1
- package/dist/procedures/registry.js +60 -0
- package/dist/procedures/registry.js.map +1 -1
- package/dist/procedures/segments.d.ts.map +1 -1
- package/dist/procedures/segments.js +13 -3
- package/dist/procedures/segments.js.map +1 -1
- package/dist/procedures/types.d.ts +6 -0
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/server/error-policy.d.ts.map +1 -1
- package/dist/server/error-policy.js +2 -1
- package/dist/server/error-policy.js.map +1 -1
- package/dist/server/face-rules.d.ts +1 -1
- package/dist/server/face-rules.js +1 -1
- package/dist/server/host-procedures.d.ts.map +1 -1
- package/dist/server/host-procedures.js +12 -8
- package/dist/server/host-procedures.js.map +1 -1
- package/dist/server/procedure-server.d.ts +18 -5
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +28 -8
- package/dist/server/procedure-server.js.map +1 -1
- package/dist/server/server.d.ts +5 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +9 -1
- package/dist/server/server.js.map +1 -1
- package/dist/testing/transport-contract.d.ts +1 -1
- package/dist/testing/transport-contract.js +2 -2
- package/dist/testing/transport-contract.js.map +1 -1
- package/dist/tracing/traceparent.d.ts +6 -0
- package/dist/tracing/traceparent.d.ts.map +1 -1
- package/dist/tracing/traceparent.js +27 -6
- package/dist/tracing/traceparent.js.map +1 -1
- package/package.json +5 -4
- package/src/adapters/http/server/transport.ts +8 -0
- package/src/adapters/local/client/transport.ts +11 -1
- package/src/adapters/websocket/client/node.ts +11 -2
- package/src/adapters/websocket/server/transport.ts +17 -5
- package/src/browser.ts +11 -0
- package/src/client/client.ts +46 -4
- package/src/client/errors/known-codes.ts +22 -0
- package/src/client/validation/index.ts +1 -1
- package/src/events/publisher.ts +1 -1
- package/src/procedures/caller.ts +1 -1
- package/src/procedures/collection/admission.ts +1 -1
- package/src/procedures/core/array.ts +1 -1
- package/src/procedures/core/comparison.ts +1 -1
- package/src/procedures/core/index.ts +4 -1
- package/src/procedures/core/math.ts +1 -1
- package/src/procedures/core/meta.ts +1 -1
- package/src/procedures/core/object.ts +1 -1
- package/src/procedures/core/operands.ts +8 -1
- package/src/procedures/core/outputs.ts +1 -1
- package/src/procedures/core/string.ts +1 -1
- package/src/procedures/core/type.ts +1 -1
- package/src/procedures/define-procedure.ts +25 -10
- package/src/procedures/index.ts +7 -2
- package/src/procedures/invoke.ts +15 -1
- package/src/procedures/module.ts +113 -2
- package/src/procedures/ref.ts +58 -1
- package/src/procedures/registry.ts +99 -0
- package/src/procedures/segments.ts +12 -3
- package/src/procedures/types.ts +6 -0
- package/src/server/error-policy.ts +2 -1
- package/src/server/face-rules.ts +1 -1
- package/src/server/host-procedures.ts +12 -8
- package/src/server/procedure-server.ts +29 -8
- package/src/server/server.ts +10 -1
- package/src/testing/transport-contract.ts +2 -2
- package/src/tracing/traceparent.ts +28 -5
|
@@ -10,15 +10,36 @@ export const TRACEPARENT_METADATA_KEY = "traceparent";
|
|
|
10
10
|
const TRACEPARENT = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
|
|
11
11
|
const ZERO_TRACE = "0".repeat(32);
|
|
12
12
|
const ZERO_SPAN = "0".repeat(16);
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The size of the pool of random bytes. One fill gives 256 trace ids or 512 span ids. Before
|
|
15
|
+
* (roadmap O94), each id called `getRandomValues` for 8 or 16 bytes: in a chain of 200 steps, the
|
|
16
|
+
* random ids took about 10 % of the per-step time of the host.
|
|
17
|
+
*/
|
|
18
|
+
export const RANDOM_POOL_BYTES = 4096;
|
|
19
|
+
/** The two hex digits of each byte value. */
|
|
20
|
+
const HEX = Array.from({ length: 256 }, (_, value) => value.toString(16).padStart(2, "0"));
|
|
21
|
+
const pool = new Uint8Array(RANDOM_POOL_BYTES);
|
|
22
|
+
// The pool starts empty: the first id fills it
|
|
23
|
+
let next = RANDOM_POOL_BYTES;
|
|
24
|
+
/** This function fills the pool with new random bytes. Each byte goes to one id only. */
|
|
25
|
+
function refill() {
|
|
15
26
|
const crypto = globalThis.crypto;
|
|
16
27
|
if (crypto?.getRandomValues)
|
|
17
|
-
crypto.getRandomValues(
|
|
28
|
+
crypto.getRandomValues(pool);
|
|
18
29
|
else
|
|
19
|
-
for (let index = 0; index <
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
for (let index = 0; index < RANDOM_POOL_BYTES; index++)
|
|
31
|
+
pool[index] = Math.floor(Math.random() * 256);
|
|
32
|
+
next = 0;
|
|
33
|
+
}
|
|
34
|
+
/** This function gives `bytes` random bytes of the pool as hex digits. `bytes` is 16 or less. */
|
|
35
|
+
function randomHex(bytes) {
|
|
36
|
+
if (next + bytes > RANDOM_POOL_BYTES)
|
|
37
|
+
refill();
|
|
38
|
+
let hex = "";
|
|
39
|
+
for (let index = next; index < next + bytes; index++)
|
|
40
|
+
hex += HEX[pool[index]];
|
|
41
|
+
next += bytes;
|
|
42
|
+
return hex;
|
|
22
43
|
}
|
|
23
44
|
/** This function makes a new trace id: 32 random hex digits. */
|
|
24
45
|
export function newTraceId() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traceparent.js","sourceRoot":"","sources":["../../src/tracing/traceparent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAEtD,MAAM,WAAW,GAAG,kDAAkD,CAAC;AACvE,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAClC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AASjC,
|
|
1
|
+
{"version":3,"file":"traceparent.js","sourceRoot":"","sources":["../../src/tracing/traceparent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAEtD,MAAM,WAAW,GAAG,kDAAkD,CAAC;AACvE,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAClC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AASjC;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AAE3F,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAC;AAC/C,+CAA+C;AAC/C,IAAI,IAAI,GAAG,iBAAiB,CAAC;AAE7B,yFAAyF;AACzF,SAAS,MAAM;IACb,MAAM,MAAM,GAAI,UAAmF,CAAC,MAAM,CAAC;IAC3G,IAAI,MAAM,EAAE,eAAe;QAAE,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;;QACrD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,iBAAiB,EAAE,KAAK,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC3G,IAAI,GAAG,CAAC,CAAC;AACX,CAAC;AAED,iGAAiG;AACjG,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,IAAI,GAAG,KAAK,GAAG,iBAAiB;QAAE,MAAM,EAAE,CAAC;IAC/C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,EAAE,KAAK,EAAE;QAAE,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAE,CAAE,CAAC;IAChF,IAAI,IAAI,KAAK,CAAC;IACd,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU;IACxB,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IACzB,OAAO,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,SAAS;IACvB,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACxB,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,MAAc;IACtD,OAAO,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,KAAK,UAAU,IAAI,MAAM,KAAK,SAAS,CAAC;AAC3H,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,MAAc,EAAE,OAAO,GAAG,IAAI;IAC/E,OAAO,MAAM,OAAO,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC;IACzC,IAAI,OAAO,KAAK,UAAU,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACrE,OAAO,EAAE,OAAO,EAAE,OAAQ,EAAE,MAAM,EAAE,MAAO,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;AAC3F,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/client",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.47.2",
|
|
5
5
|
"description": "Procedures as data for TypeScript: define typed procedures once and call them in process, over HTTP, WebSocket, the CLI or MCP. Streaming, middleware, retries, caching and a JSON program format.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"rpc",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
},
|
|
113
113
|
"dependencies": {
|
|
114
114
|
"@mark1russell7/algebra": "^0.8.0",
|
|
115
|
-
"@mark1russell7/coeval": "^0.
|
|
115
|
+
"@mark1russell7/coeval": "^0.18.0",
|
|
116
116
|
"@mark1russell7/quantity": "^0.2.0",
|
|
117
117
|
"@mark1russell7/service": "^0.6.0",
|
|
118
118
|
"@types/ws": "^8.18.1",
|
|
@@ -120,7 +120,8 @@
|
|
|
120
120
|
"zod": "^4.6.5"
|
|
121
121
|
},
|
|
122
122
|
"devDependencies": {
|
|
123
|
-
"@mark1russell7/
|
|
123
|
+
"@mark1russell7/api-report": "^0.3.0",
|
|
124
|
+
"@mark1russell7/cue": "^1.1.0",
|
|
124
125
|
"@stryker-mutator/core": "10.0.0",
|
|
125
126
|
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
126
127
|
"@types/express": "^5.0.0",
|
|
@@ -140,7 +141,7 @@
|
|
|
140
141
|
},
|
|
141
142
|
"scripts": {
|
|
142
143
|
"build": "tsc -b",
|
|
143
|
-
"typecheck": "tsc --noEmit",
|
|
144
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
144
145
|
"clean": "rm -rf dist",
|
|
145
146
|
"test": "vitest run",
|
|
146
147
|
"test:watch": "vitest",
|
|
@@ -219,6 +219,14 @@ export class HttpServerTransport implements ServerTransport {
|
|
|
219
219
|
return !this.stopped && this.httpServer !== null && this.httpServer.listening;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* This method gives the number of requests in progress, open streams too. It is the count of
|
|
224
|
+
* `inFlight()` of the procedure handler of the transport.
|
|
225
|
+
*/
|
|
226
|
+
inFlight(): number {
|
|
227
|
+
return this.handler.inFlight();
|
|
228
|
+
}
|
|
229
|
+
|
|
222
230
|
/**
|
|
223
231
|
* The port that the server of the transport listens on. With the option `port` 0, the system
|
|
224
232
|
* gives a free port, and this function returns that port. Before `start()` and after `stop()`,
|
|
@@ -158,7 +158,17 @@ export class LocalTransport implements Transport {
|
|
|
158
158
|
async *send<TReq, TRes>(message: Message<TReq>): AsyncIterable<ResponseItem<TRes>> {
|
|
159
159
|
const key = methodKey(message.method);
|
|
160
160
|
const handler = this.handlers.get(key);
|
|
161
|
-
|
|
161
|
+
let procedure = handler ? undefined : this.procedureFor(message.method);
|
|
162
|
+
// A miss of a lazy registry loads the packages of the path (roadmap O93). A hit does not wait.
|
|
163
|
+
if (!handler && !procedure && this.registry?.lazy) {
|
|
164
|
+
try {
|
|
165
|
+
procedure = await this.registry.resolve(methodToPath(message.method));
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// A package of the path did not load: the item has the load error, not NOT_FOUND
|
|
168
|
+
yield errorItem<TRes>(message.id, errorCodeOf(error), error instanceof Error ? error.message : String(error));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
162
172
|
|
|
163
173
|
// Handler not found
|
|
164
174
|
if (!handler && !procedure) {
|
|
@@ -11,10 +11,19 @@
|
|
|
11
11
|
* import `ws`.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { createRequire } from "node:module";
|
|
15
|
+
import type { WebSocket as NodeWebSocket } from "ws";
|
|
15
16
|
import { WebSocketTransport as PortableWebSocketTransport } from "./transport.js";
|
|
16
17
|
import type { WebSocketTransportOptions } from "./types.js";
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* This function gives the `WebSocket` class of `ws`. The first transport loads the package, not
|
|
21
|
+
* the import of this file: a process without a socket does not load `ws` (roadmap O93).
|
|
22
|
+
*/
|
|
23
|
+
function nodeWebSocket(): typeof NodeWebSocket {
|
|
24
|
+
return createRequire(import.meta.url)("ws") as typeof NodeWebSocket;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
/**
|
|
19
28
|
* The WebSocket client transport with the `WebSocket` class of the `ws` package as the default
|
|
20
29
|
* class of its sockets. The option `webSocket` gives another class.
|
|
@@ -29,6 +38,6 @@ import type { WebSocketTransportOptions } from "./types.js";
|
|
|
29
38
|
*/
|
|
30
39
|
export class WebSocketTransport extends PortableWebSocketTransport {
|
|
31
40
|
constructor(options: WebSocketTransportOptions) {
|
|
32
|
-
super({ ...options, webSocket: options.webSocket ??
|
|
41
|
+
super({ ...options, webSocket: options.webSocket ?? nodeWebSocket() });
|
|
33
42
|
}
|
|
34
43
|
}
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
import type { IncomingMessage } from "http";
|
|
15
15
|
import type { Duplex } from "stream";
|
|
16
|
-
import {
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import type { WebSocketServer, WebSocket } from "ws";
|
|
17
18
|
import type { ServerTransport, ServerRequest } from "../../../server/types.js";
|
|
18
19
|
import type { Message, ResponseItem, Transport } from "../../../client/types.js";
|
|
19
20
|
import { pathToMethod } from "../../../server/method.js";
|
|
@@ -44,6 +45,17 @@ import {
|
|
|
44
45
|
type Subscription,
|
|
45
46
|
} from "../../../events/remote.js";
|
|
46
47
|
|
|
48
|
+
/** The `readyState` of an open socket: `WebSocket.OPEN` of `ws`. */
|
|
49
|
+
const OPEN = 1;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* This function gives the `WebSocketServer` class of `ws`. The start of the first server loads the
|
|
53
|
+
* package, not the import of this file: a process without a socket does not load `ws` (roadmap O93).
|
|
54
|
+
*/
|
|
55
|
+
function webSocketServerClass(): typeof WebSocketServer {
|
|
56
|
+
return (createRequire(import.meta.url)("ws") as { WebSocketServer: typeof WebSocketServer }).WebSocketServer;
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
/**
|
|
48
60
|
* WebSocket server transport adapter.
|
|
49
61
|
*
|
|
@@ -192,7 +204,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
192
204
|
this.intakeStopped = false;
|
|
193
205
|
if (this.wss) return;
|
|
194
206
|
// The "no server" mode: upgrade() takes the upgrades of the path, and leaves the others alone
|
|
195
|
-
this.wss = new
|
|
207
|
+
this.wss = new (webSocketServerClass())({
|
|
196
208
|
noServer: true,
|
|
197
209
|
path: this.options.path,
|
|
198
210
|
perMessageDeflate: this.options.perMessageDeflate,
|
|
@@ -396,7 +408,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
396
408
|
const credentials = requestCredentials({ headers: req.headers as Record<string, unknown>, url: req.url, socket: req.socket }, { tokenQuery: "token" });
|
|
397
409
|
|
|
398
410
|
// The calls of this connection in the two directions (roadmap R24)
|
|
399
|
-
const isOpen = (): boolean => ws.readyState ===
|
|
411
|
+
const isOpen = (): boolean => ws.readyState === OPEN;
|
|
400
412
|
const send = (frame: WebSocketMessage): void => this.send(ws, frame);
|
|
401
413
|
this.channels.set(connectionId, {
|
|
402
414
|
incoming: new IncomingCalls({
|
|
@@ -573,7 +585,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
573
585
|
* the socket has written this frame, or the signal aborts.
|
|
574
586
|
*/
|
|
575
587
|
private sendWithBackpressure(ws: WebSocket, message: WebSocketMessage, signal: AbortSignal): Promise<void> {
|
|
576
|
-
if (ws.readyState !==
|
|
588
|
+
if (ws.readyState !== OPEN) return Promise.resolve();
|
|
577
589
|
return new Promise<void>((resolve) => {
|
|
578
590
|
let waiting = false;
|
|
579
591
|
const onAbort = (): void => resolve();
|
|
@@ -618,7 +630,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
618
630
|
* Send message to client.
|
|
619
631
|
*/
|
|
620
632
|
private send(ws: WebSocket, message: WebSocketMessage): void {
|
|
621
|
-
if (ws.readyState ===
|
|
633
|
+
if (ws.readyState === OPEN) {
|
|
622
634
|
ws.send(JSON.stringify(message));
|
|
623
635
|
}
|
|
624
636
|
}
|
package/src/browser.ts
CHANGED
|
@@ -194,6 +194,10 @@ export {
|
|
|
194
194
|
registerModules,
|
|
195
195
|
createRegistry,
|
|
196
196
|
registerClientProcedures,
|
|
197
|
+
// The lazy registry (roadmap O93)
|
|
198
|
+
createLazyRegistry,
|
|
199
|
+
resolveIn,
|
|
200
|
+
loadAllIn,
|
|
197
201
|
callerOf,
|
|
198
202
|
// Procedure reference system (procedure-as-data)
|
|
199
203
|
PROCEDURE_SYMBOL,
|
|
@@ -224,6 +228,7 @@ export {
|
|
|
224
228
|
RAW_INPUT_TAG,
|
|
225
229
|
takesRawInput,
|
|
226
230
|
rawInputRule,
|
|
231
|
+
preloadRefPaths,
|
|
227
232
|
MAX_DATA_DEPTH,
|
|
228
233
|
LITERAL_KEY,
|
|
229
234
|
isLiteral,
|
|
@@ -261,6 +266,8 @@ export {
|
|
|
261
266
|
// Every core procedure (math, comparison, string, type, object, array, meta too). Not registered by default.
|
|
262
267
|
allCoreProcedures,
|
|
263
268
|
allCoreModules,
|
|
269
|
+
// Only the math procedures: a bundle that imports only these gets no other core procedure (roadmap O95)
|
|
270
|
+
mathProcedures,
|
|
264
271
|
chainProcedure,
|
|
265
272
|
traceProcedure,
|
|
266
273
|
parallelProcedure,
|
|
@@ -361,6 +368,10 @@ export type {
|
|
|
361
368
|
CallerOptions,
|
|
362
369
|
RegistrationOptions,
|
|
363
370
|
ProcedureRegistryLike,
|
|
371
|
+
LazyRegistryOptions,
|
|
372
|
+
RegistryResolver,
|
|
373
|
+
ProcedureRegistryOptions,
|
|
374
|
+
ResolvingRegistry,
|
|
364
375
|
ProcedureDefinition,
|
|
365
376
|
ProcedureStub,
|
|
366
377
|
InferProcedureInput,
|
package/src/client/client.ts
CHANGED
|
@@ -52,7 +52,7 @@ import type {
|
|
|
52
52
|
StreamingCallResponse,
|
|
53
53
|
ProcedureCallResult,
|
|
54
54
|
} from "./call-types.js";
|
|
55
|
-
import { buildResponse, toMiddlewareContext } from "./call-types.js";
|
|
55
|
+
import { buildResponse, flattenRoute, toMiddlewareContext } from "./call-types.js";
|
|
56
56
|
import { RouteResolver, type ResolvedRoute, type RouteResolutionResult } from "./route-resolver.js";
|
|
57
57
|
import { BatchExecutor, type ExecutionContext } from "./batch-executor.js";
|
|
58
58
|
import type { ProcedureRegistry } from "../procedures/registry.js";
|
|
@@ -88,6 +88,7 @@ import {
|
|
|
88
88
|
isAnyProcedureRef,
|
|
89
89
|
isPlainObject,
|
|
90
90
|
normalizeRef,
|
|
91
|
+
preloadRefPaths,
|
|
91
92
|
rawInputRule,
|
|
92
93
|
type ProcedureRef,
|
|
93
94
|
type AnyProcedureRef,
|
|
@@ -566,6 +567,15 @@ export class Client<TContext = {}> {
|
|
|
566
567
|
* `root` starts the root call.
|
|
567
568
|
*/
|
|
568
569
|
private runRef(ref: ProcedureRef, caller: CallerContext, root: (path: ProcedurePath, input: unknown) => unknown): Promise<unknown> {
|
|
570
|
+
// A lazy registry loads the packages of the paths of the program first: the raw-input rule
|
|
571
|
+
// below reads only the procedures that have loaded (roadmap O93). An eager registry does not wait.
|
|
572
|
+
const preload = preloadRefPaths(this.procedureRegistry, ref);
|
|
573
|
+
if (preload) return preload.then(() => this.startProgram(ref, caller, root));
|
|
574
|
+
return this.startProgram(ref, caller, root);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/** This function starts a program on the kernel, after the paths of a lazy registry loaded. */
|
|
578
|
+
private startProgram(ref: ProcedureRef, caller: CallerContext, root: (path: ProcedurePath, input: unknown) => unknown): Promise<unknown> {
|
|
569
579
|
// A procedure that takes its input raw (control flow, the `runs-refs` and `raw-input` tags,
|
|
570
580
|
// `procedure.define`) gets its operand refs RAW: it runs them itself, with correct
|
|
571
581
|
// ordering/laziness/error-scoping. Eagerly hydrating here would run the operands up-front
|
|
@@ -703,7 +713,8 @@ export class Client<TContext = {}> {
|
|
|
703
713
|
input: unknown,
|
|
704
714
|
caller: CallerContext = {}
|
|
705
715
|
): Promise<TOutput> {
|
|
706
|
-
|
|
716
|
+
// A miss of a lazy registry loads the packages of the path (roadmap O93). A hit does not wait.
|
|
717
|
+
const procedure = this.procedureRegistry.get(path) ?? (this.procedureRegistry.lazy ? await this.procedureRegistry.resolve(path) : undefined);
|
|
707
718
|
if (procedure?.handler) {
|
|
708
719
|
const options = this.invokeOptions(caller);
|
|
709
720
|
return outputValue(await invokeProcedure<TOutput>(procedure, input, options), path);
|
|
@@ -726,7 +737,7 @@ export class Client<TContext = {}> {
|
|
|
726
737
|
): AsyncIterable<TOutput> {
|
|
727
738
|
const self = this;
|
|
728
739
|
return (async function* () {
|
|
729
|
-
const procedure = self.procedureRegistry.get(path);
|
|
740
|
+
const procedure = self.procedureRegistry.get(path) ?? (self.procedureRegistry.lazy ? await self.procedureRegistry.resolve(path) : undefined);
|
|
730
741
|
if (procedure?.handler) {
|
|
731
742
|
const options = self.invokeOptions(caller);
|
|
732
743
|
yield* outputItems(await invokeProcedure<TOutput>(procedure, input, options));
|
|
@@ -950,7 +961,7 @@ export class Client<TContext = {}> {
|
|
|
950
961
|
*
|
|
951
962
|
* @example
|
|
952
963
|
* ```typescript
|
|
953
|
-
* import
|
|
964
|
+
* import * as z from "zod";
|
|
954
965
|
*
|
|
955
966
|
* const client = new Client({ transport })
|
|
956
967
|
* .use(createZodMiddleware())
|
|
@@ -1099,6 +1110,8 @@ export class Client<TContext = {}> {
|
|
|
1099
1110
|
async route<TRoute extends Route>(
|
|
1100
1111
|
request: CallRequest<TRoute>
|
|
1101
1112
|
): Promise<CallResponse<TRoute>> {
|
|
1113
|
+
// A lazy registry loads the packages of the paths first: the resolver reads only what has loaded (roadmap O93)
|
|
1114
|
+
if (this.procedureRegistry.lazy) await this.loadRoutePaths(request.route);
|
|
1102
1115
|
const resolver = this.getRouteResolver();
|
|
1103
1116
|
const executor = this.getBatchExecutor();
|
|
1104
1117
|
|
|
@@ -1135,6 +1148,35 @@ export class Client<TContext = {}> {
|
|
|
1135
1148
|
routeStream<TRoute extends Route>(
|
|
1136
1149
|
request: CallRequest<TRoute>
|
|
1137
1150
|
): StreamingCallResponse<TRoute> {
|
|
1151
|
+
// A lazy registry without each path of the route: the stream starts after the packages loaded (roadmap O93)
|
|
1152
|
+
const registry = this.procedureRegistry;
|
|
1153
|
+
if (registry.lazy && flattenRoute(request.route).some((entry) => !registry.has(entry.path))) {
|
|
1154
|
+
const started = this.loadRoutePaths(request.route).then(() => this.routeStreamLoaded(request));
|
|
1155
|
+
const complete = started.then((response) => response.complete);
|
|
1156
|
+
// A caller that reads only the results gets the load error there: the promise is not an unhandled rejection
|
|
1157
|
+
complete.catch(() => undefined);
|
|
1158
|
+
return {
|
|
1159
|
+
results: (async function* () {
|
|
1160
|
+
yield* (await started).results;
|
|
1161
|
+
})(),
|
|
1162
|
+
complete,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
return this.routeStreamLoaded(request);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* This function loads the packages of the paths of a route into a lazy registry. A package that
|
|
1170
|
+
* did not load rejects the promise, with the load error as the `cause`.
|
|
1171
|
+
*/
|
|
1172
|
+
private async loadRoutePaths(route: Route): Promise<void> {
|
|
1173
|
+
for (const entry of flattenRoute(route)) {
|
|
1174
|
+
if (!this.procedureRegistry.has(entry.path)) await this.procedureRegistry.resolve(entry.path);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/** This function starts the stream of a route whose procedures the registry has. */
|
|
1179
|
+
private routeStreamLoaded<TRoute extends Route>(request: CallRequest<TRoute>): StreamingCallResponse<TRoute> {
|
|
1138
1180
|
const resolver = this.getRouteResolver();
|
|
1139
1181
|
const executor = this.getBatchExecutor();
|
|
1140
1182
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The known error codes of the client, without their texts (roadmap O95).
|
|
3
|
+
*
|
|
4
|
+
* The error policy of a server needs only the codes, not the summaries and the suggestions of
|
|
5
|
+
* `ERROR_REGISTRY`. This module gives the codes from `ErrorCode` and `ERROR_CODES`, so a bundle
|
|
6
|
+
* of the server engine gets no copy of the texts. A test makes sure that this set and the keys of
|
|
7
|
+
* `ERROR_REGISTRY` are equal.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ErrorCode } from "./codes.js";
|
|
11
|
+
import { ERROR_CODES } from "../../protocol.js";
|
|
12
|
+
|
|
13
|
+
/** The known error codes: each value of `ErrorCode` and each code of the wire protocol. */
|
|
14
|
+
export const KNOWN_ERROR_CODES: ReadonlySet<string> = new Set<string>([...Object.values(ErrorCode), ...Object.keys(ERROR_CODES)]);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This function gives `true` for a known error code. It gives the same answer as
|
|
18
|
+
* `isKnownErrorCode` of the registry, but it does not import the texts of the registry.
|
|
19
|
+
*/
|
|
20
|
+
export function isKnownCode(code: unknown): code is string {
|
|
21
|
+
return typeof code === "string" && KNOWN_ERROR_CODES.has(code);
|
|
22
|
+
}
|
package/src/events/publisher.ts
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* events (`dropped`). The bus never waits for a subscriber.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
-
import
|
|
35
|
+
import * as z from "zod";
|
|
36
36
|
import { createProcedure } from "../procedures/define.js";
|
|
37
37
|
import { InvocationError, type Invocation } from "../procedures/invoke.js";
|
|
38
38
|
import { capabilityRefusal } from "../procedures/capability.js";
|
package/src/procedures/caller.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A caller of the procedures of some modules (`callerOf`). An app that calls the procedures of a
|
|
3
3
|
* package in its own process repeats three steps: a registry of the module, `invokePath` and
|
|
4
|
-
* `outputValue`. A caller does the three steps in one call.
|
|
4
|
+
* `outputValue`. A caller does the three steps in one call.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ProcedurePath } from "./types.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The rules of the rows of a served collection (roadmap CLI-41). A right can depend on the
|
|
3
|
-
* document: a `when` condition of
|
|
3
|
+
* document: a `when` condition of an app, or `accessAdmitItem()` of `@procedures/access`. The option
|
|
4
4
|
* `admission` of `createCollectionProcedures()` gives the rules as data:
|
|
5
5
|
*
|
|
6
6
|
* - `admit` examines each row `{ id, value }` that a read gives. It has the shape of `AdmitItem`,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The namespace of all these procedures is "client": for example client.first and client.filter.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { anyOutput, arrayOutput, booleanOutput, numberOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The namespace of all these procedures is "client": for example client.eq and client.gt.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { booleanOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
48
|
import { PURE_EFFECTS, REGISTRY_READ_EFFECTS, withEffects } from "../effects.js";
|
|
49
|
-
import
|
|
49
|
+
import * as z from "zod";
|
|
50
50
|
import { anyOutput, booleanOutput } from "./outputs.js";
|
|
51
51
|
import { defineProcedure, namespace } from "../define.js";
|
|
52
52
|
import type {
|
|
@@ -136,6 +136,7 @@ const TryCatchInputSchema = z.looseObject({
|
|
|
136
136
|
import {
|
|
137
137
|
isAnyProcedureRef,
|
|
138
138
|
normalizeRef,
|
|
139
|
+
preloadRefPaths,
|
|
139
140
|
createRefScope,
|
|
140
141
|
scopeOf,
|
|
141
142
|
getRefWhen,
|
|
@@ -349,6 +350,8 @@ const traceProcedure: TraceProcedure = defineProcedure({
|
|
|
349
350
|
if (!ctx?.client) throw new Error("client.trace: a run needs a client context");
|
|
350
351
|
const registry = ctx.registry ?? PROCEDURE_REGISTRY;
|
|
351
352
|
const limit = upTo ?? Number.POSITIVE_INFINITY;
|
|
353
|
+
// A lazy registry loads the paths of the steps first: the facts below read their effects (roadmap O93)
|
|
354
|
+
await preloadRefPaths(registry, steps);
|
|
352
355
|
|
|
353
356
|
// The facts of each step before the run: its name, its path and the declared effects of its procedure
|
|
354
357
|
const rows: TraceRow[] = steps.map((step, index) => {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The namespace of all these procedures is "client": for example client.add and client.multiply.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { numberOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* The namespace of all these procedures is "client": for example client.import and client.eval.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import * as z from "zod";
|
|
10
10
|
import { anyOutput, stringOutput } from "./outputs.js";
|
|
11
11
|
import { defineProcedure, namespace } from "../define.js";
|
|
12
12
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The namespace of all these procedures is "client": for example client.get and client.set.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { anyOutput, arrayOutput, booleanOutput, recordOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isAnyProcedureRef,
|
|
10
10
|
isOutputRef,
|
|
11
11
|
lookupOutputRef,
|
|
12
|
+
preloadRefPaths,
|
|
12
13
|
rawInputRule,
|
|
13
14
|
scopeOf,
|
|
14
15
|
RUNS_REFS_TAG,
|
|
@@ -41,7 +42,11 @@ function executorOf(ctx: ProcedureContext): <TIn, TOut>(path: ProcedurePath, inp
|
|
|
41
42
|
* each step of a chain (`client.trace`).
|
|
42
43
|
*/
|
|
43
44
|
export function runControlFlow(name: string, input: unknown, ctx: ProcedureContext, options: Pick<RunControlOptions, "onStep"> = {}): Promise<unknown> {
|
|
44
|
-
|
|
45
|
+
// A lazy registry loads the paths of the operands before the raw-input rule reads them (roadmap O93)
|
|
46
|
+
const preload = preloadRefPaths(ctx.registry, input);
|
|
47
|
+
const run = (): Promise<unknown> =>
|
|
48
|
+
runControl(name, input, { executor: executorOf(ctx), rawInput: rawRule(ctx), onRef: ctx.onRef, scope: scopeOf(input), onStep: options.onStep });
|
|
49
|
+
return preload ? preload.then(run) : run();
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
/**
|
|
@@ -70,6 +75,7 @@ export function substituteRefs(value: unknown, scope: RefScope): unknown {
|
|
|
70
75
|
* in `scope` and runs its nested refs, as `exec()` does for an input.
|
|
71
76
|
*/
|
|
72
77
|
export async function hydrateOperand(value: unknown, ctx: ProcedureContext, scope?: RefScope): Promise<unknown> {
|
|
78
|
+
await preloadRefPaths(ctx.registry, value);
|
|
73
79
|
return hydrateInput(value, executorOf(ctx), { scope, rawInput: rawRule(ctx), onRef: ctx.onRef });
|
|
74
80
|
}
|
|
75
81
|
|
|
@@ -86,6 +92,7 @@ export async function callOperand(
|
|
|
86
92
|
ctx: ProcedureContext,
|
|
87
93
|
options: { scope?: RefScope | undefined; extra?: Record<string, unknown> | undefined } = {},
|
|
88
94
|
): Promise<unknown> {
|
|
95
|
+
await preloadRefPaths(ctx.registry, ref);
|
|
89
96
|
return runOperand(ref, { executor: executorOf(ctx), rawInput: rawRule(ctx), onRef: ctx.onRef, scope: options.scope, extra: options.extra });
|
|
90
97
|
}
|
|
91
98
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The namespace of all these procedures is "client": for example client.concat and client.split.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { booleanOutput, numberOutput, stringOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The namespace of all these procedures is "client": for example client.typeof and client.isArray.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as z from "zod";
|
|
9
9
|
import { anyOutput, booleanOutput, stringOutput } from "./outputs.js";
|
|
10
10
|
import { defineProcedure, namespace } from "../define.js";
|
|
11
11
|
import type { AnyProcedure, Procedure } from "../types.js";
|