@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.
Files changed (148) hide show
  1. package/CHANGELOG.md +99 -68
  2. package/README.md +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/adapters/http/server/transport.d.ts +5 -0
  5. package/dist/adapters/http/server/transport.d.ts.map +1 -1
  6. package/dist/adapters/http/server/transport.js +7 -0
  7. package/dist/adapters/http/server/transport.js.map +1 -1
  8. package/dist/adapters/local/client/transport.d.ts.map +1 -1
  9. package/dist/adapters/local/client/transport.js +12 -1
  10. package/dist/adapters/local/client/transport.js.map +1 -1
  11. package/dist/adapters/websocket/client/node.d.ts.map +1 -1
  12. package/dist/adapters/websocket/client/node.js +9 -2
  13. package/dist/adapters/websocket/client/node.js.map +1 -1
  14. package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
  15. package/dist/adapters/websocket/server/transport.js +14 -5
  16. package/dist/adapters/websocket/server/transport.js.map +1 -1
  17. package/dist/browser.d.ts +2 -2
  18. package/dist/browser.d.ts.map +1 -1
  19. package/dist/browser.js +7 -3
  20. package/dist/browser.js.map +1 -1
  21. package/dist/client/client.d.ts +10 -1
  22. package/dist/client/client.d.ts.map +1 -1
  23. package/dist/client/client.js +46 -5
  24. package/dist/client/client.js.map +1 -1
  25. package/dist/client/errors/known-codes.d.ts +16 -0
  26. package/dist/client/errors/known-codes.d.ts.map +1 -0
  27. package/dist/client/errors/known-codes.js +20 -0
  28. package/dist/client/errors/known-codes.js.map +1 -0
  29. package/dist/client/validation/index.d.ts +1 -1
  30. package/dist/client/validation/index.js +1 -1
  31. package/dist/events/publisher.js +1 -1
  32. package/dist/events/publisher.js.map +1 -1
  33. package/dist/procedures/caller.d.ts +1 -1
  34. package/dist/procedures/caller.js +1 -1
  35. package/dist/procedures/collection/admission.d.ts +1 -1
  36. package/dist/procedures/collection/admission.js +1 -1
  37. package/dist/procedures/core/array.js +1 -1
  38. package/dist/procedures/core/array.js.map +1 -1
  39. package/dist/procedures/core/comparison.js +1 -1
  40. package/dist/procedures/core/comparison.js.map +1 -1
  41. package/dist/procedures/core/index.d.ts.map +1 -1
  42. package/dist/procedures/core/index.js +4 -2
  43. package/dist/procedures/core/index.js.map +1 -1
  44. package/dist/procedures/core/math.js +1 -1
  45. package/dist/procedures/core/math.js.map +1 -1
  46. package/dist/procedures/core/meta.js +1 -1
  47. package/dist/procedures/core/meta.js.map +1 -1
  48. package/dist/procedures/core/object.js +1 -1
  49. package/dist/procedures/core/object.js.map +1 -1
  50. package/dist/procedures/core/operands.d.ts.map +1 -1
  51. package/dist/procedures/core/operands.js +7 -2
  52. package/dist/procedures/core/operands.js.map +1 -1
  53. package/dist/procedures/core/outputs.d.ts +1 -1
  54. package/dist/procedures/core/outputs.d.ts.map +1 -1
  55. package/dist/procedures/core/outputs.js +1 -1
  56. package/dist/procedures/core/outputs.js.map +1 -1
  57. package/dist/procedures/core/string.js +1 -1
  58. package/dist/procedures/core/string.js.map +1 -1
  59. package/dist/procedures/core/type.js +1 -1
  60. package/dist/procedures/core/type.js.map +1 -1
  61. package/dist/procedures/define-procedure.d.ts.map +1 -1
  62. package/dist/procedures/define-procedure.js +28 -12
  63. package/dist/procedures/define-procedure.js.map +1 -1
  64. package/dist/procedures/index.d.ts +6 -5
  65. package/dist/procedures/index.d.ts.map +1 -1
  66. package/dist/procedures/index.js +6 -4
  67. package/dist/procedures/index.js.map +1 -1
  68. package/dist/procedures/invoke.d.ts.map +1 -1
  69. package/dist/procedures/invoke.js +14 -1
  70. package/dist/procedures/invoke.js.map +1 -1
  71. package/dist/procedures/module.d.ts +34 -0
  72. package/dist/procedures/module.d.ts.map +1 -1
  73. package/dist/procedures/module.js +90 -0
  74. package/dist/procedures/module.js.map +1 -1
  75. package/dist/procedures/ref.d.ts +10 -1
  76. package/dist/procedures/ref.d.ts.map +1 -1
  77. package/dist/procedures/ref.js +64 -0
  78. package/dist/procedures/ref.js.map +1 -1
  79. package/dist/procedures/registry.d.ts +64 -0
  80. package/dist/procedures/registry.d.ts.map +1 -1
  81. package/dist/procedures/registry.js +60 -0
  82. package/dist/procedures/registry.js.map +1 -1
  83. package/dist/procedures/segments.d.ts.map +1 -1
  84. package/dist/procedures/segments.js +13 -3
  85. package/dist/procedures/segments.js.map +1 -1
  86. package/dist/procedures/types.d.ts +6 -0
  87. package/dist/procedures/types.d.ts.map +1 -1
  88. package/dist/procedures/types.js.map +1 -1
  89. package/dist/server/error-policy.d.ts.map +1 -1
  90. package/dist/server/error-policy.js +2 -1
  91. package/dist/server/error-policy.js.map +1 -1
  92. package/dist/server/face-rules.d.ts +1 -1
  93. package/dist/server/face-rules.js +1 -1
  94. package/dist/server/host-procedures.d.ts.map +1 -1
  95. package/dist/server/host-procedures.js +12 -8
  96. package/dist/server/host-procedures.js.map +1 -1
  97. package/dist/server/procedure-server.d.ts +18 -5
  98. package/dist/server/procedure-server.d.ts.map +1 -1
  99. package/dist/server/procedure-server.js +28 -8
  100. package/dist/server/procedure-server.js.map +1 -1
  101. package/dist/server/server.d.ts +5 -0
  102. package/dist/server/server.d.ts.map +1 -1
  103. package/dist/server/server.js +9 -1
  104. package/dist/server/server.js.map +1 -1
  105. package/dist/testing/transport-contract.d.ts +1 -1
  106. package/dist/testing/transport-contract.js +2 -2
  107. package/dist/testing/transport-contract.js.map +1 -1
  108. package/dist/tracing/traceparent.d.ts +6 -0
  109. package/dist/tracing/traceparent.d.ts.map +1 -1
  110. package/dist/tracing/traceparent.js +27 -6
  111. package/dist/tracing/traceparent.js.map +1 -1
  112. package/package.json +5 -4
  113. package/src/adapters/http/server/transport.ts +8 -0
  114. package/src/adapters/local/client/transport.ts +11 -1
  115. package/src/adapters/websocket/client/node.ts +11 -2
  116. package/src/adapters/websocket/server/transport.ts +17 -5
  117. package/src/browser.ts +11 -0
  118. package/src/client/client.ts +46 -4
  119. package/src/client/errors/known-codes.ts +22 -0
  120. package/src/client/validation/index.ts +1 -1
  121. package/src/events/publisher.ts +1 -1
  122. package/src/procedures/caller.ts +1 -1
  123. package/src/procedures/collection/admission.ts +1 -1
  124. package/src/procedures/core/array.ts +1 -1
  125. package/src/procedures/core/comparison.ts +1 -1
  126. package/src/procedures/core/index.ts +4 -1
  127. package/src/procedures/core/math.ts +1 -1
  128. package/src/procedures/core/meta.ts +1 -1
  129. package/src/procedures/core/object.ts +1 -1
  130. package/src/procedures/core/operands.ts +8 -1
  131. package/src/procedures/core/outputs.ts +1 -1
  132. package/src/procedures/core/string.ts +1 -1
  133. package/src/procedures/core/type.ts +1 -1
  134. package/src/procedures/define-procedure.ts +25 -10
  135. package/src/procedures/index.ts +7 -2
  136. package/src/procedures/invoke.ts +15 -1
  137. package/src/procedures/module.ts +113 -2
  138. package/src/procedures/ref.ts +58 -1
  139. package/src/procedures/registry.ts +99 -0
  140. package/src/procedures/segments.ts +12 -3
  141. package/src/procedures/types.ts +6 -0
  142. package/src/server/error-policy.ts +2 -1
  143. package/src/server/face-rules.ts +1 -1
  144. package/src/server/host-procedures.ts +12 -8
  145. package/src/server/procedure-server.ts +29 -8
  146. package/src/server/server.ts +10 -1
  147. package/src/testing/transport-contract.ts +2 -2
  148. 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
- function randomHex(bytes) {
14
- const values = new Uint8Array(bytes);
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(values);
28
+ crypto.getRandomValues(pool);
18
29
  else
19
- for (let index = 0; index < bytes; index++)
20
- values[index] = Math.floor(Math.random() * 256);
21
- return Array.from(values, (value) => value.toString(16).padStart(2, "0")).join("");
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,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAI,UAAmF,CAAC,MAAM,CAAC;IAC3G,IAAI,MAAM,EAAE,eAAe;QAAE,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;;QACvD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,EAAE;YAAE,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IACjG,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrF,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"}
1
+ {"version":3,"file":"traceparent.js","sourceRoot":"","sources":["../../src/tracing/traceparent.ts"],"names":[],"mappings":"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"}
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.46.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.14.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/cue": "^1.0.0",
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
- const procedure = handler ? undefined : this.procedureFor(message.method);
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 { WebSocket as NodeWebSocket } from "ws";
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 ?? NodeWebSocket });
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 { WebSocketServer, WebSocket } from "ws";
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 WebSocketServer({
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 === WebSocket.OPEN;
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 !== WebSocket.OPEN) return Promise.resolve();
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 === WebSocket.OPEN) {
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,
@@ -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
- const procedure = this.procedureRegistry.get(path);
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 { z } from "zod";
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
+ }
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @example
8
8
  * ```typescript
9
- * import { z } from "zod";
9
+ * import * as z from "zod";
10
10
  * import { Client } from "client";
11
11
  * import { createZodMiddleware, ValidationError } from "client/client/validation";
12
12
  *
@@ -32,7 +32,7 @@
32
32
  * events (`dropped`). The bus never waits for a subscriber.
33
33
  */
34
34
 
35
- import { z } from "zod";
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";
@@ -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. jarvis had four copies of them.
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 Graph, or `accessAdmitItem()` of `@procedures/access`. The option
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 { z } from "zod";
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 { z } from "zod";
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 { z } from "zod";
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 { z } from "zod";
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 { z } from "zod";
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 { z } from "zod";
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
- return runControl(name, input, { executor: executorOf(ctx), rawInput: rawRule(ctx), onRef: ctx.onRef, scope: scopeOf(input), onStep: options.onStep });
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
 
@@ -6,7 +6,7 @@
6
6
  * helpers give each output a real zod 4 schema with a description.
7
7
  */
8
8
 
9
- import { z } from "zod";
9
+ import * as z from "zod";
10
10
 
11
11
  /**
12
12
  * Any JavaScript number, also `NaN` and `Infinity`. zod 4's `z.number()` refuses both, but
@@ -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 { z } from "zod";
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 { z } from "zod";
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";