@procedures/client 0.37.0 → 0.39.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/adapters/http/server/handler.js +3 -2
  4. package/dist/adapters/http/server/handler.js.map +1 -1
  5. package/dist/adapters/websocket/client/transport.d.ts +1 -1
  6. package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
  7. package/dist/adapters/websocket/client/transport.js +18 -6
  8. package/dist/adapters/websocket/client/transport.js.map +1 -1
  9. package/dist/browser.d.ts +2 -1
  10. package/dist/browser.d.ts.map +1 -1
  11. package/dist/browser.js +2 -1
  12. package/dist/browser.js.map +1 -1
  13. package/dist/client/validation/types.d.ts.map +1 -1
  14. package/dist/client/validation/types.js +1 -0
  15. package/dist/client/validation/types.js.map +1 -1
  16. package/dist/procedures/collection/schemas.d.ts.map +1 -1
  17. package/dist/procedures/collection/schemas.js +1 -0
  18. package/dist/procedures/collection/schemas.js.map +1 -1
  19. package/dist/procedures/envelope.d.ts.map +1 -1
  20. package/dist/procedures/envelope.js +2 -0
  21. package/dist/procedures/envelope.js.map +1 -1
  22. package/dist/procedures/policy.js +2 -2
  23. package/dist/procedures/policy.js.map +1 -1
  24. package/dist/procedures/program.d.ts.map +1 -1
  25. package/dist/procedures/program.js +3 -0
  26. package/dist/procedures/program.js.map +1 -1
  27. package/dist/procedures/ref.d.ts.map +1 -1
  28. package/dist/procedures/ref.js +1 -0
  29. package/dist/procedures/ref.js.map +1 -1
  30. package/dist/procedures/types.d.ts +1 -1
  31. package/dist/procedures/types.d.ts.map +1 -1
  32. package/dist/procedures/types.js.map +1 -1
  33. package/dist/server/index.d.ts +2 -1
  34. package/dist/server/index.d.ts.map +1 -1
  35. package/dist/server/index.js +2 -1
  36. package/dist/server/index.js.map +1 -1
  37. package/dist/server/procedure-server.d.ts +0 -9
  38. package/dist/server/procedure-server.d.ts.map +1 -1
  39. package/dist/server/procedure-server.js +2 -9
  40. package/dist/server/procedure-server.js.map +1 -1
  41. package/dist/server/stream-metadata.d.ts +15 -0
  42. package/dist/server/stream-metadata.d.ts.map +1 -0
  43. package/dist/server/stream-metadata.js +15 -0
  44. package/dist/server/stream-metadata.js.map +1 -0
  45. package/dist/testing/index.d.ts +13 -0
  46. package/dist/testing/index.d.ts.map +1 -0
  47. package/dist/testing/index.js +11 -0
  48. package/dist/testing/index.js.map +1 -0
  49. package/dist/testing/log-storage.d.ts +28 -0
  50. package/dist/testing/log-storage.d.ts.map +1 -0
  51. package/dist/testing/log-storage.js +75 -0
  52. package/dist/testing/log-storage.js.map +1 -0
  53. package/dist/testing/mcp-caller.d.ts +33 -0
  54. package/dist/testing/mcp-caller.d.ts.map +1 -0
  55. package/dist/testing/mcp-caller.js +97 -0
  56. package/dist/testing/mcp-caller.js.map +1 -0
  57. package/dist/testing/transport-contract.d.ts +167 -0
  58. package/dist/testing/transport-contract.d.ts.map +1 -0
  59. package/dist/testing/transport-contract.js +387 -0
  60. package/dist/testing/transport-contract.js.map +1 -0
  61. package/package.json +5 -1
  62. package/src/adapters/http/server/handler.ts +3 -2
  63. package/src/adapters/websocket/client/transport.ts +20 -7
  64. package/src/browser.ts +2 -1
  65. package/src/client/validation/types.ts +1 -0
  66. package/src/procedures/collection/schemas.ts +1 -0
  67. package/src/procedures/envelope.ts +2 -0
  68. package/src/procedures/policy.ts +2 -2
  69. package/src/procedures/program.ts +3 -0
  70. package/src/procedures/ref.ts +1 -0
  71. package/src/procedures/types.ts +1 -1
  72. package/src/server/index.ts +2 -1
  73. package/src/server/procedure-server.ts +2 -11
  74. package/src/server/stream-metadata.ts +16 -0
  75. package/src/testing/index.ts +31 -0
  76. package/src/testing/log-storage.ts +72 -0
  77. package/src/testing/mcp-caller.ts +100 -0
  78. package/src/testing/transport-contract.ts +560 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * The transport contract (roadmap CLI-85, invariant I4 of ARCHITECTURE.md): one suite of wire
3
+ * cases that each face of the procedures server runs. `WIRE-PROTOCOL.md` is the contract.
4
+ *
5
+ * A face gives a factory (`ContractFace`): it starts a server of the face with the procedures of
6
+ * the suite, and it connects callers. The suite then runs the same cases over each face:
7
+ *
8
+ * - a call and its value, on paths of 1, 2 and 4 segments, and segments with "." and "%" (D36, CLI-75)
9
+ * - `NOT_FOUND` for an unknown path and for an empty segment, and `VALIDATION_ERROR`
10
+ * - the code, the message and the retryable flag of a handler error, for a value and for a stream (CLI-58, CLI-68)
11
+ * - the error policy `expose: "coded"` of the server (CLI-80)
12
+ * - `VERSION_CONFLICT` and its conflicts (CLI-16)
13
+ * - the items of a stream and its end, the abort of a call and the cancel of a stream
14
+ * - the deadline of the caller
15
+ * - the token, and the principal of the request hook in the handler (CLI-1, R18)
16
+ * - a subscription to the change topic of a collection: the hint arrives (CLI-15, CLI-75)
17
+ * - the refusal of a request above the size limit of the face
18
+ *
19
+ * A face that does not have a feature by design declares it in its capabilities, and the suite
20
+ * skips the cases of that feature. Each skip thus is data in the test file of the face.
21
+ *
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
24
+ * suite imports no test runner: the caller gives `describe`, `it`, `afterEach` and `expect`
25
+ * (`ContractRunner`). Those of Vitest fit.
26
+ */
27
+ import { z } from "zod";
28
+ import { Client, VersionConflictError, collectionChangeTopic, collectionTopicRule, createCollectionProcedures, createProcedure, createRegistry, ms, pathToMethod, subscribeRemote, versionConflictsOf, } from "../index.js";
29
+ import { createOverallTimeoutMiddleware } from "../client/index.js";
30
+ import { LogStorage } from "./log-storage.js";
31
+ /** The token of the server of a face with `auth`. */
32
+ export const CONTRACT_TOKEN = "contract-token-0123456789";
33
+ /** The principal that the request hook gives for the token. */
34
+ export const CONTRACT_PRINCIPAL = "contract-user";
35
+ /** The collection of the suite. Its name has a dot and a `%`, so the codec must keep it one segment (D36). */
36
+ export const CONTRACT_COLLECTION = "contract.tabs%";
37
+ /** The paths of the path case: 1, 2 and 4 segments, and segments with "." and "%" (CLI-28, CLI-75). */
38
+ export const CONTRACT_PATHS = [
39
+ ["morning"],
40
+ ["contract", "path"],
41
+ ["contract", "a", "b", "path"],
42
+ ["contract", "dot.ted", "path"],
43
+ ["contract", "100%", "path"],
44
+ ["contract", "%2E", "path"],
45
+ ];
46
+ /** The description of a procedure of the suite: a face can find its tool by it. */
47
+ export function contractDescription(path) {
48
+ return `contract ${JSON.stringify(path)}`;
49
+ }
50
+ /** The size of the request of the size case, and the limit that the face gets. */
51
+ const SIZE_LIMIT = 16 * 1024;
52
+ const CONFLICTS = [
53
+ { id: "a", expected: "1", found: true, value: { n: 2 }, version: "2" },
54
+ { id: "b", expected: null, found: false, version: null },
55
+ ];
56
+ /** The procedures of the suite. */
57
+ function registryOf(seen) {
58
+ const read = { reads: [], writes: [] };
59
+ const procedure = (path) => createProcedure().path([...path]).meta({ effects: read, description: contractDescription(path) });
60
+ const failure = (input) => Object.assign(new Error("it failed"), input.code === undefined ? {} : { code: input.code }, input.retryable === undefined ? {} : { retryable: input.retryable });
61
+ return createRegistry({
62
+ name: "transport-contract",
63
+ procedures: [
64
+ procedure(["contract", "echo"])
65
+ .input(z.object({ text: z.string() }))
66
+ .output(z.object({ text: z.string() }))
67
+ .handler(async (input, ctx) => {
68
+ seen.echoes++;
69
+ const deadline = ctx.capability?.deadline;
70
+ if (deadline !== undefined)
71
+ seen.deadlineLeft.push(deadline - Date.now());
72
+ seen.headers.push(ctx.metadata?.["headers"]);
73
+ return { text: input.text };
74
+ })
75
+ .build(),
76
+ procedure(["contract", "whoami"])
77
+ .input(z.object({}))
78
+ .output(z.object({ principal: z.string().nullable() }))
79
+ .handler(async (_input, ctx) => ({ principal: ctx.principal?.id ?? null }))
80
+ .build(),
81
+ ...CONTRACT_PATHS.map((path) => procedure(path)
82
+ .input(z.object({}).passthrough())
83
+ .output(z.object({ path: z.array(z.string()) }))
84
+ .handler(async (_input, ctx) => ({ path: [...ctx.path] }))
85
+ .build()),
86
+ procedure(["contract", "count"])
87
+ .input(z.object({ to: z.number() }))
88
+ .output(z.number())
89
+ .handler(async function* (input) {
90
+ for (let n = 1; n <= input.to; n++)
91
+ yield n;
92
+ })
93
+ .build(),
94
+ procedure(["contract", "endless"])
95
+ .input(z.object({}))
96
+ .output(z.number())
97
+ .handler(async function* () {
98
+ try {
99
+ for (let n = 1;; n++) {
100
+ yield n;
101
+ await new Promise((resolve) => setTimeout(resolve, 5));
102
+ }
103
+ }
104
+ finally {
105
+ seen.streamClosed++;
106
+ }
107
+ })
108
+ .build(),
109
+ procedure(["contract", "wait"])
110
+ .input(z.object({}))
111
+ .output(z.string())
112
+ .handler(async (_input, ctx) => {
113
+ seen.waiting++;
114
+ await new Promise((resolve) => {
115
+ const timer = setTimeout(resolve, 5000);
116
+ ctx.signal?.addEventListener("abort", () => {
117
+ clearTimeout(timer);
118
+ seen.aborted++;
119
+ resolve();
120
+ }, { once: true });
121
+ });
122
+ return "ended";
123
+ })
124
+ .build(),
125
+ procedure(["contract", "fail"])
126
+ .input(z.object({ code: z.string().optional(), retryable: z.boolean().optional() }))
127
+ .output(z.string())
128
+ .handler(async (input) => {
129
+ throw failure(input);
130
+ })
131
+ .build(),
132
+ // A stream that fails after its items: one item, or the count of the input
133
+ procedure(["contract", "failStream"])
134
+ .input(z.object({ code: z.string().optional(), retryable: z.boolean().optional(), items: z.number().optional() }))
135
+ .output(z.number())
136
+ .handler(async function* (input) {
137
+ for (let n = 1; n <= (input.items ?? 1); n++)
138
+ yield n;
139
+ throw failure(input);
140
+ })
141
+ .build(),
142
+ procedure(["contract", "conflict"])
143
+ .input(z.object({}))
144
+ .output(z.string())
145
+ .handler(async () => {
146
+ throw new VersionConflictError(CONFLICTS);
147
+ })
148
+ .build(),
149
+ ...createCollectionProcedures(CONTRACT_COLLECTION),
150
+ ],
151
+ });
152
+ }
153
+ /** The error of a promise, or a failure when it resolves. */
154
+ async function errorOf(promise) {
155
+ try {
156
+ await promise;
157
+ }
158
+ catch (error) {
159
+ return error;
160
+ }
161
+ throw new Error("The call did not fail");
162
+ }
163
+ /** The items of a stream. */
164
+ async function itemsOf(stream) {
165
+ const items = [];
166
+ for await (const item of stream)
167
+ items.push(item);
168
+ return items;
169
+ }
170
+ async function waitFor(check, what, limit = 3000) {
171
+ const end = Date.now() + limit;
172
+ while (!check()) {
173
+ if (Date.now() > end)
174
+ throw new Error(`waitFor: ${what}`);
175
+ await new Promise((resolve) => setTimeout(resolve, 5));
176
+ }
177
+ }
178
+ /**
179
+ * A caller of a `Client` over a transport of the client: HTTP, WebSocket, a message port or the
180
+ * local transport. Each face of the client uses it.
181
+ */
182
+ export function clientCaller(transport, close) {
183
+ const clientOf = (options) => {
184
+ const client = new Client({ transport, throwOnError: true });
185
+ return options?.deadlineMs === undefined ? client : client.use(createOverallTimeoutMiddleware({ overall: ms(options.deadlineMs) }));
186
+ };
187
+ const callOptions = (options) => (options?.signal ? { signal: options.signal } : {});
188
+ return {
189
+ call: (path, input, options) => clientOf(options).call(pathToMethod([...path]), input, callOptions(options)),
190
+ stream: (path, input, options) => clientOf(options).stream(pathToMethod([...path]), input, callOptions(options)),
191
+ events: transport,
192
+ ...(close ? { close } : {}),
193
+ };
194
+ }
195
+ /**
196
+ * This function declares the cases of the transport contract for one face. Call it at the top
197
+ * level of the test file of the face, with the functions of the test runner:
198
+ * `describeTransportContract(face, { describe, it, afterEach, expect })`.
199
+ */
200
+ export function describeTransportContract(face, runner) {
201
+ const caps = face.capabilities;
202
+ const { describe, it, afterEach, expect } = runner;
203
+ let open = [];
204
+ afterEach(async () => {
205
+ for (const close of open.reverse())
206
+ await close().catch(() => undefined);
207
+ open = [];
208
+ });
209
+ const fresh = () => ({ deadlineLeft: [], waiting: 0, aborted: 0, streamClosed: 0, echoes: 0, headers: [] });
210
+ /** A server of the face, with the procedures of the suite. */
211
+ async function serve(seen, more = {}) {
212
+ const storage = new LogStorage();
213
+ const principalOf = (credentials) => credentials.bearer === CONTRACT_TOKEN ? { principal: { id: CONTRACT_PRINCIPAL, via: "contract" } } : undefined;
214
+ const spec = {
215
+ registry: registryOf(seen),
216
+ collections: { [CONTRACT_COLLECTION]: storage },
217
+ events: { topics: collectionTopicRule(CONTRACT_COLLECTION) },
218
+ ...(caps.auth ? { token: CONTRACT_TOKEN, principalOf } : {}),
219
+ ...more,
220
+ };
221
+ const server = await face.start(spec);
222
+ open.push(() => server.stop());
223
+ return { server, storage };
224
+ }
225
+ /** A caller of the face, with the token when the face has `auth`. */
226
+ async function connect(server, bearer = caps.auth ? CONTRACT_TOKEN : null) {
227
+ const caller = await server.connect(bearer ?? undefined);
228
+ if (caller.close)
229
+ open.push(() => caller.close());
230
+ return caller;
231
+ }
232
+ async function start(seen = fresh(), more = {}) {
233
+ return connect((await serve(seen, more)).server);
234
+ }
235
+ describe(`the transport contract over ${face.name} (CLI-85)`, () => {
236
+ it("gives the value of a call", async () => {
237
+ const caller = await start();
238
+ expect(await caller.call(["contract", "echo"], { text: "hi" })).toEqual({ text: "hi" });
239
+ });
240
+ it("calls a path of 1, 2 and 4 segments, and keeps a segment with a dot or a % as one segment (CLI-28, CLI-75)", async () => {
241
+ const caller = await start();
242
+ for (const path of CONTRACT_PATHS)
243
+ expect(await caller.call(path, {}), JSON.stringify(path)).toEqual({ path });
244
+ });
245
+ it("gives NOT_FOUND for an unknown path and for a path with an empty segment", async () => {
246
+ const caller = await start();
247
+ expect(await errorOf(caller.call(["contract", "missing"], {}))).toMatchObject({ code: "NOT_FOUND", retryable: false });
248
+ expect(await errorOf(caller.call(["contract", "dot", "path"], {}))).toMatchObject({ code: "NOT_FOUND" });
249
+ expect(await errorOf(caller.call(["contract", ""], {}))).toMatchObject({ code: "NOT_FOUND" });
250
+ });
251
+ it("gives VALIDATION_ERROR for an input that is not valid, and the handler does not run", async () => {
252
+ const seen = fresh();
253
+ const caller = await start(seen);
254
+ expect(await errorOf(caller.call(["contract", "echo"], { text: 5 }))).toMatchObject({ code: "VALIDATION_ERROR", retryable: false });
255
+ expect(seen.echoes).toBe(0);
256
+ });
257
+ it("keeps the code, the message and the flag of a handler error, for a value and for a stream (CLI-58, CLI-68)", async () => {
258
+ const caller = await start();
259
+ // The flag, on a face that carries it
260
+ const flag = (retryable) => (caps.retryable ? { retryable } : {});
261
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "E_CUSTOM" }))).toMatchObject({ code: "E_CUSTOM", message: "it failed", retryable: false });
262
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "E_CUSTOM", retryable: true }))).toMatchObject({ code: "E_CUSTOM", ...flag(true) });
263
+ expect(await errorOf(caller.call(["contract", "fail"], {}))).toMatchObject({ code: "HANDLER_ERROR", message: "it failed" });
264
+ // Without a flag, an error before the first item gets the flag of its code
265
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "UNAVAILABLE" }))).toMatchObject({ code: "UNAVAILABLE", ...flag(true) });
266
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "UNAVAILABLE", retryable: false }))).toMatchObject({ code: "UNAVAILABLE", retryable: false });
267
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], { code: "E_CUSTOM" })))).toMatchObject({ code: "E_CUSTOM", message: "it failed" });
268
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], {})))).toMatchObject({ code: "HANDLER_ERROR" });
269
+ // After an item, an end without a flag is not retryable: a retry can send the items again
270
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], { code: "UNAVAILABLE" })))).toMatchObject({ code: "UNAVAILABLE", retryable: false });
271
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], { code: "UNAVAILABLE", retryable: true })))).toMatchObject({ code: "UNAVAILABLE", ...flag(true) });
272
+ // An end in the stream before its first item gets the flag of its code, as an error before the stream (CLI-68)
273
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], { code: "UNAVAILABLE", items: 0 })))).toMatchObject({ code: "UNAVAILABLE", ...flag(true) });
274
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], { code: "UNAVAILABLE", retryable: false, items: 0 })))).toMatchObject({ code: "UNAVAILABLE", retryable: false });
275
+ // The reader gets the items before the end
276
+ if (caps.streams === "items") {
277
+ const items = [];
278
+ await errorOf((async () => {
279
+ for await (const item of caller.stream(["contract", "failStream"], { code: "UNAVAILABLE", items: 2 }))
280
+ items.push(item);
281
+ })());
282
+ expect(items).toEqual([1, 2]);
283
+ }
284
+ });
285
+ it.skipIf(!caps.server)('applies the error policy of the server: under "coded", only a known code goes out (CLI-80)', async () => {
286
+ const logged = [];
287
+ const caller = await start(fresh(), { errors: { expose: "coded", codes: ["E_APP"] }, onError: (error) => void logged.push(error) });
288
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "E_APP" }))).toMatchObject({ code: "E_APP", message: "it failed" });
289
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "CONFLICT", retryable: true }))).toMatchObject({ code: "CONFLICT", message: "it failed", retryable: true });
290
+ // Each other error is INTERNAL_ERROR with the message "internal error", and it keeps its flag
291
+ expect(await errorOf(caller.call(["contract", "fail"], {}))).toMatchObject({ code: "INTERNAL_ERROR", message: "internal error", retryable: false });
292
+ expect(await errorOf(caller.call(["contract", "fail"], { code: "ECONNREFUSED", retryable: true }))).toMatchObject({ code: "INTERNAL_ERROR", message: "internal error", retryable: true });
293
+ expect(await errorOf(itemsOf(caller.stream(["contract", "failStream"], { code: "ECONNREFUSED" })))).toMatchObject({ code: "INTERNAL_ERROR", message: "internal error" });
294
+ // The server gives each hidden error to its logger
295
+ expect(logged.length).toBeGreaterThanOrEqual(3);
296
+ });
297
+ it("gives VERSION_CONFLICT, and the conflicts when the caller gets the metadata of an error (CLI-16)", async () => {
298
+ const caller = await start();
299
+ const error = await errorOf(caller.call(["contract", "conflict"], {}));
300
+ expect(error).toMatchObject({ code: "VERSION_CONFLICT", retryable: false });
301
+ if (caps.errorMetadata)
302
+ expect(versionConflictsOf(error)).toEqual(CONFLICTS);
303
+ });
304
+ it("gives the items of a stream in order, and then the end", async () => {
305
+ const caller = await start();
306
+ expect(await itemsOf(caller.stream(["contract", "count"], { to: 3 }))).toEqual([1, 2, 3]);
307
+ expect(await itemsOf(caller.stream(["contract", "count"], { to: 0 }))).toEqual([]);
308
+ });
309
+ it("aborts the handler when the caller aborts the call", async () => {
310
+ const seen = fresh();
311
+ const caller = await start(seen);
312
+ const controller = new AbortController();
313
+ const call = caller.call(["contract", "wait"], {}, { signal: controller.signal });
314
+ // The abort comes when the handler runs: on a slow runner, a call can take more than 50 ms to arrive
315
+ await waitFor(() => seen.waiting === 1, "the start of the handler");
316
+ controller.abort();
317
+ await errorOf(call);
318
+ await waitFor(() => seen.aborted === 1, "the abort of the handler");
319
+ });
320
+ it.skipIf(caps.streams !== "items")("ends the stream of the handler when the reader stops", async () => {
321
+ const seen = fresh();
322
+ const caller = await start(seen);
323
+ const items = [];
324
+ for await (const item of caller.stream(["contract", "endless"], {})) {
325
+ items.push(item);
326
+ if (items.length === 2)
327
+ break;
328
+ }
329
+ expect(items).toEqual([1, 2]);
330
+ await waitFor(() => seen.streamClosed === 1, "the end of the stream of the handler");
331
+ });
332
+ it.skipIf(!caps.deadline)("gives the deadline of the caller to the handler", async () => {
333
+ const seen = fresh();
334
+ const caller = await start(seen);
335
+ await caller.call(["contract", "echo"], { text: "x" }, { deadlineMs: 3000 });
336
+ expect(seen.deadlineLeft[0]).toBeGreaterThan(2000);
337
+ expect(seen.deadlineLeft[0]).toBeLessThanOrEqual(3000);
338
+ });
339
+ it.skipIf(!caps.auth)("refuses a request without the token, and gives the principal of the request hook to the handler (CLI-1, R18)", async () => {
340
+ const seen = fresh();
341
+ const { server } = await serve(seen);
342
+ const refusal = caps.authRefusal === "connect" ? {} : { code: "UNAUTHORIZED" };
343
+ expect(await errorOf((await connect(server, null)).call(["contract", "echo"], { text: "x" }))).toMatchObject(refusal);
344
+ expect(await errorOf((await connect(server, "a-wrong-token-0123456789")).call(["contract", "echo"], { text: "x" }))).toMatchObject(refusal);
345
+ expect(seen.echoes).toBe(0);
346
+ const caller = await connect(server, CONTRACT_TOKEN);
347
+ expect(await caller.call(["contract", "whoami"], {})).toEqual({ principal: CONTRACT_PRINCIPAL });
348
+ expect(await caller.call(["contract", "echo"], { text: "x" })).toEqual({ text: "x" });
349
+ // The secret does not reach the handler
350
+ expect(JSON.stringify(seen.headers)).not.toContain(CONTRACT_TOKEN);
351
+ });
352
+ it.skipIf(!caps.subscriptions)("gives the change hint of a write to a subscriber of the topic of the collection (CLI-15, CLI-75)", async () => {
353
+ const caller = await start();
354
+ const topic = collectionChangeTopic(CONTRACT_COLLECTION);
355
+ const hints = [];
356
+ const topics = [];
357
+ const subscription = subscribeRemote(caller.events, topic, (data, event) => {
358
+ hints.push(data);
359
+ topics.push(event.topic);
360
+ });
361
+ open.push(async () => subscription());
362
+ await subscription.ready;
363
+ const collection = (operation) => ["collections", CONTRACT_COLLECTION, operation];
364
+ // A pull starts the chain of the events
365
+ await caller.call(collection("changes"), {});
366
+ await caller.call(collection("set"), { id: "t1", value: { title: "one" } });
367
+ await waitFor(() => hints.length >= 1, "the change hint");
368
+ expect(hints[0]).toMatchObject({ changed: ["t1"], deleted: [] });
369
+ expect(topics[0]).toBe(topic);
370
+ expect(await caller.call(collection("get"), { id: "t1" })).toEqual({ title: "one" });
371
+ expect(await caller.call(collection("find"), {})).toEqual([{ id: "t1", value: { title: "one" } }]);
372
+ await caller.call(collection("delete"), { id: "t1" });
373
+ await waitFor(() => hints.length >= 2, "the hint of the delete");
374
+ expect(hints[1]).toMatchObject({ changed: [], deleted: ["t1"] });
375
+ });
376
+ it.skipIf(!caps.sizeLimit)("refuses a request above the size limit of the face, and the handler does not run", async () => {
377
+ const seen = fresh();
378
+ const caller = await start(seen, { sizeLimit: SIZE_LIMIT });
379
+ const error = await errorOf(caller.call(["contract", "echo"], { text: "x".repeat(4 * SIZE_LIMIT) }));
380
+ expect(error).toMatchObject({ code: caps.sizeLimit.code, retryable: false });
381
+ expect(seen.echoes).toBe(0);
382
+ // A request below the limit passes
383
+ expect(await (await start(seen, { sizeLimit: SIZE_LIMIT })).call(["contract", "echo"], { text: "small" })).toEqual({ text: "small" });
384
+ });
385
+ });
386
+ }
387
+ //# sourceMappingURL=transport-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport-contract.js","sourceRoot":"","sources":["../../src/testing/transport-contract.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.37.0",
4
+ "version": "0.39.0",
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",
@@ -74,6 +74,10 @@
74
74
  "./query": {
75
75
  "types": "./dist/procedures/collection/query.d.ts",
76
76
  "import": "./dist/procedures/collection/query.js"
77
+ },
78
+ "./testing": {
79
+ "types": "./dist/testing/index.d.ts",
80
+ "import": "./dist/testing/index.js"
77
81
  }
78
82
  },
79
83
  "files": [
@@ -23,7 +23,7 @@ import { lastResortError } from "../../../server/last-resort.js";
23
23
  import { decodeMetadataHeader, METADATA_HEADER_LIMIT } from "../shared/index.js";
24
24
  import { ERROR_REGISTRY } from "../../../client/errors/index.js";
25
25
  import { assertNamedOrigins, checkBrowserRequest, needsNamedOrigin, type BrowserGuardOptions } from "../../../server/browser-guard.js";
26
- import { isUnboundedStream } from "../../../server/procedure-server.js";
26
+ import { isUnboundedStream } from "../../../server/stream-metadata.js";
27
27
  import { headerNames, isSecretHeader, queryRecord, requestCredentials, urlParts, withoutSecretHeaders } from "../../../server/request-auth.js";
28
28
  import { PROTOCOL_HEADER, PROTOCOL_VERSION, httpStatusOf, protocolVersionOf } from "../../../protocol.js";
29
29
  import { realClock, type Cancel, type Clock } from "../../../clock.js";
@@ -561,7 +561,8 @@ export function createProcedureHandler(server: RequestHandler, options: Procedur
561
561
  serverRequest = toServerRequest(req, await readBody(req));
562
562
  } catch (error) {
563
563
  const status = error instanceof BodyError ? error.httpStatus : 400;
564
- sendError(res, status, (error as Error).message, "INVALID_REQUEST");
564
+ // A body above the limit is PAYLOAD_TOO_LARGE, as on the MCP face and the WebSocket face (CLI-85)
565
+ sendError(res, status, (error as Error).message, status === 413 ? "PAYLOAD_TOO_LARGE" : "INVALID_REQUEST");
565
566
  return;
566
567
  }
567
568
  if (!serverRequest) {
@@ -5,7 +5,7 @@
5
5
  * Supports automatic reconnection, heartbeats, and streaming.
6
6
  */
7
7
 
8
- import type { Transport, Message, ResponseItem } from "../../../client/types.js";
8
+ import { ClientError, type Transport, type Message, type ResponseItem } from "../../../client/types.js";
9
9
  import type { WebSocketTransportOptions, WebSocketMessage, ServerRequestHandler, EventHandler, WebSocketClass } from "./types.js";
10
10
  import { WebSocketState } from "./types.js";
11
11
  import { withoutInternalKeys } from "../../metadata.js";
@@ -133,6 +133,19 @@ function detach(ws: WebSocket): void {
133
133
  ws.onerror = ignoreError;
134
134
  }
135
135
 
136
+ /** The close code of a message above the limit of the other side (RFC 6455, 1009). */
137
+ const MESSAGE_TOO_BIG = 1009;
138
+
139
+ /**
140
+ * The error of a pending call when the server closes the connection with 1009: a message was
141
+ * above the limit of the server (`maxPayload`). The close drops each pending call of the
142
+ * connection, so each gets this error (CLI-85). Before, each got an error without a code.
143
+ */
144
+ function tooLarge(id: string): ClientError {
145
+ const message = "The server closed the connection: a message was larger than its limit (1009)";
146
+ return new ClientError({ type: "error", code: "PAYLOAD_TOO_LARGE", message, retryable: false }, id);
147
+ }
148
+
136
149
  function errorItem<TRes>(id: string, code: string, message: string, retryable: boolean): ResponseItem<TRes> {
137
150
  return { id, status: { type: "error", code, message, retryable }, payload: null as TRes, metadata: {} };
138
151
  }
@@ -375,7 +388,7 @@ export class WebSocketTransport implements Transport {
375
388
  ws.onclose = (event) => {
376
389
  if (this.ws !== ws) return;
377
390
  this.ws = null;
378
- this.handleClose(event.reason);
391
+ this.handleClose(event.reason, event.code);
379
392
  };
380
393
 
381
394
  // Connection error
@@ -422,9 +435,9 @@ export class WebSocketTransport implements Transport {
422
435
  }
423
436
 
424
437
  /** Fail every request in progress (a stream ends with the error too). */
425
- private failPending(reason: string): void {
426
- for (const pending of this.pendingRequests.values()) {
427
- pending.queue.fail(new Error(reason));
438
+ private failPending(reason: string, closeCode?: number): void {
439
+ for (const [id, pending] of this.pendingRequests) {
440
+ pending.queue.fail(closeCode === MESSAGE_TOO_BIG ? tooLarge(id) : new Error(reason));
428
441
  pending.timeout?.();
429
442
  }
430
443
  this.pendingRequests.clear();
@@ -433,7 +446,7 @@ export class WebSocketTransport implements Transport {
433
446
  /**
434
447
  * Handle WebSocket close.
435
448
  */
436
- private handleClose(reason?: string): void {
449
+ private handleClose(reason?: string, closeCode?: number): void {
437
450
  this.stopHeartbeat();
438
451
  this.incoming?.abortAll();
439
452
  this.incoming = undefined;
@@ -450,7 +463,7 @@ export class WebSocketTransport implements Transport {
450
463
  this.options.onDisconnect(reason);
451
464
  }
452
465
 
453
- this.failPending("WebSocket connection closed");
466
+ this.failPending("WebSocket connection closed", closeCode);
454
467
 
455
468
  if (willReconnect) {
456
469
  this.scheduleReconnect();
package/src/browser.ts CHANGED
@@ -80,7 +80,8 @@ export type { ServerOptions } from "./server/server.js";
80
80
  // The error policy of a server: what an error tells the caller (roadmap CLI-80)
81
81
  export type { ServerErrorPolicy, ExposedError, ErrorCall } from "./server/error-policy.js";
82
82
  export { exposeError, lastResortError, HIDDEN_ERROR_MESSAGE, DEFAULT_KEPT_METADATA } from "./server/error-policy.js";
83
- export { ProcedureServer, createProcedureServer, STREAM_METADATA_KEY, isUnboundedStream, tokenMatches } from "./server/procedure-server.js";
83
+ export { ProcedureServer, createProcedureServer, tokenMatches } from "./server/procedure-server.js";
84
+ export { STREAM_METADATA_KEY, isUnboundedStream } from "./server/stream-metadata.js";
84
85
  // The credentials of a network request (roadmap R18), and the parts that a request hook reads (CLI-2a)
85
86
  export { credentialsOf, bearerOf, withoutSecretHeaders, cookiesOf, requestCredentials, LOCAL_CREDENTIALS, isLocalCredentials } from "./server/request-auth.js";
86
87
  export type { RequestCredentialsOptions } from "./server/request-auth.js";
@@ -77,6 +77,7 @@ export function methodToKey(method: Method): MethodKey {
77
77
  * Parse a method key back to a Method object.
78
78
  */
79
79
  export function keyToMethod(key: MethodKey): Method {
80
+ // path-check: the key of the validation middleware (service, operation and version), a public API of its own.
80
81
  const parts = key.split(".");
81
82
  if (parts.length === 3 && parts[2]) {
82
83
  return { service: parts[0]!, operation: parts[1]!, version: parts[2] };
@@ -93,6 +93,7 @@ const EXPECTED_VERSION_MESSAGE = `expectedVersion must be a string of at most ${
93
93
  */
94
94
  function expectedVersionError(data: Record<string, unknown>, path: (string | number)[] = []): ZodErrorLike | undefined {
95
95
  if (!("expectedVersion" in data) || data["expectedVersion"] === undefined || isExpectedVersion(data["expectedVersion"])) return undefined;
96
+ // path-check: the field path of the input in an error message.
96
97
  const message = path.length === 0 ? EXPECTED_VERSION_MESSAGE : `${path.join(".")}.${EXPECTED_VERSION_MESSAGE}`;
97
98
  return { message, errors: [{ path: [...path, "expectedVersion"], message }] };
98
99
  }
@@ -324,6 +324,7 @@ function fromRef(ref: EnvelopeShape): { $ref: string } {
324
324
  throw new Error(`A ref to the root "names" needs a name: ["names", name, ...fields]. Got ${preview(refPath)}`);
325
325
  }
326
326
  const [name, ...fields] = steps.map((key) => keyText(key, refPath));
327
+ // path-check: the `$ref` text of a program: a name and its fields (PROGRAM-FORMAT.md).
327
328
  return { $ref: [name === LAST_NAME ? NATIVE_LAST : name, ...fields].join(".") };
328
329
  }
329
330
 
@@ -450,6 +451,7 @@ function toRef(ref: OutputRef): EnvelopeRef {
450
451
  `The $ref object ${preview(ref)} has other keys. The envelope form keeps only the path: keep this program in the $proc form`
451
452
  );
452
453
  }
454
+ // path-check: the `$ref` text of a program: a name and its fields (PROGRAM-FORMAT.md).
453
455
  const [name, ...fields] = refText.split(".");
454
456
  if (name === LAST_NAME) {
455
457
  throw new Error(
@@ -21,7 +21,7 @@
21
21
  */
22
22
 
23
23
  import type { AnyProcedure, ProcedurePath } from "./types.js";
24
- import { pathToKey } from "./types.js";
24
+ import { keyToPath, pathToKey } from "./types.js";
25
25
  import { isDataDriven } from "./ref.js";
26
26
 
27
27
  /** The call policy of a host. */
@@ -79,7 +79,7 @@ export function validateCalls(calls: unknown, path: ProcedurePath): readonly str
79
79
  );
80
80
  }
81
81
  const body = entry.endsWith(".*") ? entry.slice(0, -2) : entry;
82
- if (body.length === 0 || body.includes("*") || body.split(".").some((segment) => segment.length === 0)) {
82
+ if (body.length === 0 || body.includes("*") || keyToPath(body).some((segment) => segment.length === 0)) {
83
83
  throw new TypeError(`metadata.calls of ${where} has an invalid entry: "${entry}". Use "ns.name" or "ns.*".`);
84
84
  }
85
85
  }
@@ -509,6 +509,7 @@ function combine(original: object, keys: readonly string[] | undefined, walk: Wa
509
509
 
510
510
  /** This function makes the document of a `$ref` in a scope. */
511
511
  function refDoc(value: OutputRef, walk: Walk): ProgramDocument {
512
+ // path-check: the `$ref` text of a program: a name and its fields (PROGRAM-FORMAT.md).
512
513
  const [first = "", ...fields] = value.$ref.split(".");
513
514
  const name = first === "$last" ? LAST : nameKey(first);
514
515
  // A run reads the origin only in the input of a deferred call. A check reads its pointer.
@@ -1157,7 +1158,9 @@ function unknownName(source: unknown, path: readonly unknown[]): Error {
1157
1158
  : path
1158
1159
  .slice(1)
1159
1160
  .map((key, index) => (index === 0 ? (key === LAST ? "$last" : nameOfKey(String(key))) : String(key)))
1161
+ // path-check: the `$ref` text of a program: a name and its fields (PROGRAM-FORMAT.md).
1160
1162
  .join(".");
1163
+ // path-check: the `$ref` text of a program: a name and its fields (PROGRAM-FORMAT.md).
1161
1164
  const name = text.split(".")[0];
1162
1165
  return new Error(
1163
1166
  `Unknown $ref name "${name}" in "${text}": no step, item or input has this name here. ` +
@@ -219,6 +219,7 @@ export function getPath(obj: unknown, path: string[]): unknown {
219
219
  * `found` is false when no scope has the name. A missing field is `found` with the value undefined.
220
220
  */
221
221
  export function lookupOutputRef(refPath: string, scope: RefScope | undefined): { found: boolean; value?: unknown } {
222
+ // path-check: the `$ref` text of a program: a name and its fields (PROGRAM-FORMAT.md).
222
223
  const [first, ...rest] = refPath.split(".");
223
224
  for (let current = scope; current; current = current.parent) {
224
225
  if (first === "$last") {