@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
@@ -146,7 +146,8 @@ export interface ProcedureServerOptions extends ServerOptions {
146
146
  * the server answers `UNAUTHORIZED`. Only a local request (`LOCAL_CREDENTIALS`) skips it. The
147
147
  * server refuses a request without credentials (CLI-1). Before, only the Express middleware of
148
148
  * the peer examined the token, so a `ProcedureServer` with the HTTP or WebSocket transport of the
149
- * core and the token of a host policy accepted each request.
149
+ * core and the token of a host policy accepted each request. An empty token means "refuse each
150
+ * network request": no presented token matches it, not even an empty one.
150
151
  */
151
152
  token?: string | undefined;
152
153
 
@@ -235,16 +236,18 @@ export interface RegisterProcedureOptions {
235
236
  }
236
237
 
237
238
  /**
238
- * True when the presented token is the expected one. The comparison reads each character of the
239
- * expected token for each presented token, so its time depends only on the expected token. The
240
- * time tells neither the token nor its length. The function runs in a browser too, so it uses
241
- * no `node:crypto`.
239
+ * True when the presented token is the expected one. An empty expected token matches nothing, not
240
+ * even an empty presented token, so a server with an empty token refuses each network request.
241
+ * The comparison reads each character of the presented token one time, so its time depends only
242
+ * on what the caller sent. The time tells neither the expected token nor its length. The function
243
+ * runs in a browser too, so it uses no `node:crypto`.
242
244
  */
243
245
  export function tokenMatches(expected: string, presented: string | undefined): boolean {
244
246
  if (presented === undefined) return false;
245
- // A difference of the lengths makes the result false, after the same loop as an equal length
246
- let difference = expected.length ^ presented.length;
247
- for (let index = 0; index < expected.length; index++) difference |= expected.charCodeAt(index) ^ (presented.charCodeAt(index) | 0);
247
+ // An empty expected token and a difference of the lengths make the result false, after the same
248
+ // loop as an equal length. The modulo reuses the expected characters for a longer token.
249
+ let difference = (expected.length === 0 ? 1 : 0) | (expected.length ^ presented.length);
250
+ for (let index = 0; index < presented.length; index++) difference |= presented.charCodeAt(index) ^ expected.charCodeAt(index % expected.length);
248
251
  return difference === 0;
249
252
  }
250
253
 
@@ -835,6 +838,16 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
835
838
  return procedure ? (request) => this.invoke(procedure, request) : null;
836
839
  }
837
840
 
841
+ /**
842
+ * A miss of a lazy registry loads the packages of the path, then the server looks up the
843
+ * handler again (roadmap O93). An eager registry, or a server that does not serve its registry,
844
+ * finds no handler here.
845
+ */
846
+ protected override findLateHandler(method: Method, request: ServerRequest<unknown>): Promise<ServerHandler | null> | null {
847
+ if (!this.servesRegistry || !this.procedureRegistry.lazy) return null;
848
+ return this.procedureRegistry.resolve(methodToPath(method)).then(() => this.findHandler(method, request));
849
+ }
850
+
838
851
  /**
839
852
  * Run a procedure for a request. A keyed call to a procedure that is not repeatable runs one
840
853
  * time for its key (R1).
@@ -1207,6 +1220,14 @@ export class ProcedureServer extends Server implements RepositoryProvider, StopP
1207
1220
  return this.servedKeys().size;
1208
1221
  }
1209
1222
 
1223
+ /**
1224
+ * This method loads each package of a lazy registry that the server serves (roadmap O93). Then
1225
+ * `procedureCount` and the lists count each procedure. For an eager registry it does nothing.
1226
+ */
1227
+ async loadAll(): Promise<void> {
1228
+ if (this.servesRegistry && this.procedureRegistry.lazy) await this.procedureRegistry.loadAll();
1229
+ }
1230
+
1210
1231
  /**
1211
1232
  * Check if the server serves a procedure now.
1212
1233
  *
@@ -309,7 +309,8 @@ export class Server {
309
309
 
310
310
  try {
311
311
  // Find handler
312
- const handler = this.findHandler(request.method, request);
312
+ // A miss can load the procedure first (a lazy registry, roadmap O93). A hit does not wait.
313
+ const handler = this.findHandler(request.method, request) ?? (await this.findLateHandler(request.method, request));
313
314
  if (!handler) {
314
315
  throw new HandlerNotFoundError(request.method);
315
316
  }
@@ -363,6 +364,14 @@ export class Server {
363
364
  return null;
364
365
  }
365
366
 
367
+ /**
368
+ * This method finds a handler after `findHandler()` found none. The default finds none at once.
369
+ * `ProcedureServer` loads the procedure of a lazy registry here (roadmap O93).
370
+ */
371
+ protected findLateHandler(_method: Method, _request: ServerRequest<unknown>): Promise<ServerHandler | null> | null {
372
+ return null;
373
+ }
374
+
366
375
  /**
367
376
  * Check if a method matches a matcher.
368
377
  */
@@ -20,12 +20,12 @@
20
20
  * skips the cases of that feature. Each skip thus is data in the test file of the face.
21
21
  *
22
22
  * The suite is the entry point `@procedures/client/testing`, so a face in another package (and
23
- * a server of an app, for example Graph) runs the same cases with the client that it uses. The
23
+ * a server of an app) runs the same cases with the client that it uses. The
24
24
  * suite imports no test runner: the caller gives `describe`, `it`, `afterEach` and `expect`
25
25
  * (`ContractRunner`). Those of Vitest fit.
26
26
  */
27
27
 
28
- import { z } from "zod";
28
+ import * as z from "zod";
29
29
  import {
30
30
  Client,
31
31
  VersionConflictError,
@@ -20,12 +20,35 @@ export interface TraceContext {
20
20
  readonly sampled: boolean;
21
21
  }
22
22
 
23
- function randomHex(bytes: number): string {
24
- const values = new Uint8Array(bytes);
23
+ /**
24
+ * The size of the pool of random bytes. One fill gives 256 trace ids or 512 span ids. Before
25
+ * (roadmap O94), each id called `getRandomValues` for 8 or 16 bytes: in a chain of 200 steps, the
26
+ * random ids took about 10 % of the per-step time of the host.
27
+ */
28
+ export const RANDOM_POOL_BYTES = 4096;
29
+
30
+ /** The two hex digits of each byte value. */
31
+ const HEX = Array.from({ length: 256 }, (_, value) => value.toString(16).padStart(2, "0"));
32
+
33
+ const pool = new Uint8Array(RANDOM_POOL_BYTES);
34
+ // The pool starts empty: the first id fills it
35
+ let next = RANDOM_POOL_BYTES;
36
+
37
+ /** This function fills the pool with new random bytes. Each byte goes to one id only. */
38
+ function refill(): void {
25
39
  const crypto = (globalThis as { crypto?: { getRandomValues?: (array: Uint8Array) => Uint8Array } }).crypto;
26
- if (crypto?.getRandomValues) crypto.getRandomValues(values);
27
- else for (let index = 0; index < bytes; index++) values[index] = Math.floor(Math.random() * 256);
28
- return Array.from(values, (value) => value.toString(16).padStart(2, "0")).join("");
40
+ if (crypto?.getRandomValues) crypto.getRandomValues(pool);
41
+ else for (let index = 0; index < RANDOM_POOL_BYTES; index++) pool[index] = Math.floor(Math.random() * 256);
42
+ next = 0;
43
+ }
44
+
45
+ /** This function gives `bytes` random bytes of the pool as hex digits. `bytes` is 16 or less. */
46
+ function randomHex(bytes: number): string {
47
+ if (next + bytes > RANDOM_POOL_BYTES) refill();
48
+ let hex = "";
49
+ for (let index = next; index < next + bytes; index++) hex += HEX[pool[index]!]!;
50
+ next += bytes;
51
+ return hex;
29
52
  }
30
53
 
31
54
  /** This function makes a new trace id: 32 random hex digits. */