1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
import assert from "node:assert/strict";
import { test } from "node:test";
import { decodeCbor, decodeFrame, encodeCbor, encodeFrame, runModule, subscribeXrpc, wsUrl } from "./index.js";
const READ = "com.atproto.repo.getRecord";
const SUB = "com.atproto.sync.subscribeRepos";
const DELETE = "com.atproto.repo.deleteRecord";
const mod = (extra) => ({ module: new Uint8Array(), calls: [READ, SUB, DELETE], ...extra });
// The handler stands in for the wasm export: ctx.wasm.run(entry, input) -> handler(input).
const ctxOf = (handler, storage) => ({ storage, wasm: { run: async ({ input }) => handler(input) } });
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// A subscription stand-in: push() a message, end() it, fail() it; records sends and close.
function channel() {
const queue = [];
const sent = [];
let wake;
let finished = false;
let failure;
const self = {
sent,
closed: false,
push: (m) => (queue.push(m), wake?.()),
end: () => ((finished = true), wake?.()),
fail: (e) => ((failure = e), (finished = true), wake?.()),
send: async (data) => void sent.push(data),
close: () => ((self.closed = true), self.end()),
async *[Symbol.asyncIterator]() {
for (;;) {
while (!queue.length && !finished) await new Promise((r) => (wake = r));
if (queue.length) yield queue.shift();
else if (failure) throw failure;
else return;
}
},
};
return self;
}
test("results arrive one by one as calls finish, with state carried and no barrier", async () => {
const seen = [];
const handler = ({ event, state }) => {
seen.push(event.type === "result" ? event.id : event.type);
if (event.type === "start") return { calls: [{ id: "slow", nsid: READ, params: { ms: 30 } }, { id: "fast", nsid: READ, params: { ms: 1 } }], state: { n: 0 } };
return { state: { n: state.n + 1 }, result: { last: event.id, n: state.n + 1, output: event.output } };
};
const call = async (nsid, { params }) => (await sleep(Number(params.ms)), { ms: params.ms });
const ran = await runModule(ctxOf(handler), mod(), "go", {}, call);
assert.deepEqual(seen, ["start", "fast", "slow"]);
assert.deepEqual(ran.result, { last: "slow", n: 2, output: { ms: "30" } });
assert.equal(ran.events, 3);
assert.deepEqual(ran.calls.map((c) => c.id), ["fast", "slow"]);
});
test("calls past inFlight queue FIFO; refusals (allowlist, rkey guard, bad call, budget) are result events", async () => {
let live = 0;
let peak = 0;
const order = [];
const call = async (nsid, { params }) => {
peak = Math.max(peak, ++live);
order.push(params.i);
await sleep(5);
live--;
return {};
};
const errors = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
...[0, 1, 2, 3, 4].map((i) => ({ id: `c${i}`, nsid: READ, params: { i } })),
{ id: "nope", nsid: "com.example.not.allowed" },
{ id: "del", nsid: DELETE, input: { repo: "did:plc:a", collection: "x.y.z", rkey: "k" } },
{ nsid: READ },
{ id: "c0", nsid: READ },
{ id: "over", nsid: READ },
],
};
}
if (!event.ok) errors[event.id ?? "noid"] = event.error;
return {};
};
const ran = await runModule(ctxOf(handler), mod(), "go", {}, call, { budget: { inFlight: 2, calls: 6 } });
assert.equal(peak, 2);
assert.deepEqual(order, ["0", "1", "2", "3", "4"]);
assert.match(errors.nope, /does not allow/);
assert.match(errors.del, /was not created by this module/);
assert.match(errors.noid, /needs an id/);
assert.match(errors.c0, /already pending/);
assert.match(errors.over, /exceeded 6 calls/);
assert.equal(ran.error, undefined);
});
test("a subscription yields messages, takes sends in order, and ends with closed", async () => {
const feed = channel();
const got = [];
let request;
const call = async (nsid, req) => ((request = req), feed);
const handler = ({ event, state }) => {
if (event.type === "start") return { calls: [{ id: "feed", nsid: SUB, params: { cursor: 5 }, subscribe: true, framing: "cbor" }], send: [{ id: "feed", data: "hello" }], state: { count: 0 } };
if (event.type === "message") {
got.push(event.data);
return { state: { count: state.count + 1 }, send: [{ id: "feed", data: { ack: event.data.seq } }] };
}
if (event.type === "closed") return { result: { count: state.count, closed: event } };
return {};
};
const running = runModule(ctxOf(handler), mod(), "watch", {}, call);
await sleep(5);
feed.push({ seq: 1 });
feed.push({ seq: 2 });
await sleep(5);
feed.end();
const ran = await running;
assert.equal(request.subscribe, true);
assert.equal(request.framing, "cbor");
assert.deepEqual(request.params, { cursor: "5" });
assert.ok(request.signal instanceof AbortSignal);
assert.deepEqual(got, [{ seq: 1 }, { seq: 2 }]);
assert.deepEqual(feed.sent, ["hello", { ack: 1 }, { ack: 2 }]);
assert.deepEqual(ran.result, { count: 2, closed: { type: "closed", id: "feed" } });
assert.deepEqual(ran.calls, [{ id: "feed", nsid: SUB, subscribe: true, messages: 2, ok: true }]);
});
test("cancel closes a subscription, timers fire, send to a closed id is refused, done ends the run", async () => {
const feed = channel();
const seen = [];
const handler = ({ event }) => {
seen.push(event.type === "result" ? `result:${event.ok}` : event.type);
if (event.type === "start") return { calls: [{ id: "feed", nsid: SUB, subscribe: true }], timers: [{ id: "stop", ms: 10 }] };
if (event.type === "timer" && event.id === "stop") return { cancel: ["feed"] };
if (event.type === "closed") return { result: event.error, timers: [{ id: "again", ms: 1 }], send: [{ id: "feed", data: "late" }] };
if (event.type === "result") return {};
return { done: true };
};
const ran = await runModule(ctxOf(handler), mod(), "watch", {}, async () => feed);
assert.deepEqual(seen, ["start", "timer", "closed", "result:false", "timer"]);
assert.equal(ran.result, "cancelled");
assert.equal(feed.closed, true);
assert.deepEqual(feed.sent, []);
});
test("a subscription that fails closes with its error; a stream without send refuses sends", async () => {
const feed = channel();
const plain = { async *[Symbol.asyncIterator]() { yield 1; } };
const events = [];
const handler = ({ event }) => {
events.push(event);
if (event.type === "start") return { calls: [{ id: "a", nsid: SUB, subscribe: true }, { id: "b", nsid: SUB, subscribe: true }], send: [{ id: "b", data: 1 }] };
return {};
};
let n = 0;
const running = runModule(ctxOf(handler), mod(), "watch", {}, async () => (n++ ? plain : feed));
await sleep(5);
feed.fail(new Error("relay went away"));
await running;
const byType = (t, id) => events.find((e) => e.type === t && e.id === id);
assert.equal(byType("closed", "a").error, "relay went away");
assert.match(byType("result", "b").error, /cannot send/);
assert.equal(byType("message", "b").data, 1);
});
test("abort stops a run: in-flight calls get the signal and subscriptions close", async () => {
const feed = channel();
let callSignal;
const call = async (nsid, req) => {
if (req.subscribe) return feed;
callSignal = req.signal;
return new Promise(() => {});
};
const handler = ({ event }) => (event.type === "start" ? { calls: [{ id: "hang", nsid: READ }, { id: "feed", nsid: SUB, subscribe: true }] } : {});
const stop = new AbortController();
setTimeout(() => stop.abort(), 10);
const ran = await runModule(ctxOf(handler), mod(), "go", {}, call, { signal: stop.signal });
assert.equal(ran.error, "aborted");
assert.equal(callSignal.aborted, true);
assert.equal(feed.closed, true);
});
test("per-call timeout fails the call; events and wall budgets end the run with an error", async () => {
const timeout = await runModule(
ctxOf(({ event }) => (event.type === "start" ? { calls: [{ id: "t", nsid: READ, timeoutMs: 5 }] } : { result: event.error })),
mod(), "go", {}, () => new Promise(() => {}),
);
assert.match(timeout.result, /timed out after 5 ms/);
// a huge timeoutMs is clamped to wallMs: no setTimeout overflow (fires at once) nor a Rust panic
const huge = await runModule(
ctxOf(({ event }) => (event.type === "start" ? { calls: [{ id: "t", nsid: READ, timeoutMs: 1e300 }] } : { result: event.ok ? "ok" : event.error })),
mod(), "go", {}, () => new Promise((r) => setTimeout(() => r({}), 30)), { budget: { wallMs: 2000 } },
);
assert.equal(huge.result, "ok");
const timers = await runModule(ctxOf(({ event }) => (event.type === "start" ? { timers: [{ id: "x", ms: 1e300 }] } : { result: "fired" })), mod(), "go", {}, async () => ({}), { budget: { wallMs: 30 } });
assert.equal(timers.error, "Module exceeded 30 ms");
const feed = channel();
for (let i = 0; i < 10; i++) feed.push(i);
const ignored = await runModule(ctxOf(({ event }) => (event.type === "start" ? { timers: [{ id: "x", ms: 20 }] } : { result: "fired", done: true })), mod(), "go", {}, async () => ({}), { budget: { wallMs: "5", events: -1, calls: Number.NaN } });
assert.equal(ignored.result, "fired", "a budget value that is not a positive finite number leaves the default");
const chatty = await runModule(ctxOf(({ event }) => (event.type === "start" ? { calls: [{ id: "f", nsid: SUB, subscribe: true }] } : {})), mod(), "go", {}, async () => feed, { budget: { events: 4 } });
assert.equal(chatty.error, "Module exceeded 4 events");
assert.equal(feed.closed, true);
const slow = await runModule(ctxOf(({ event }) => (event.type === "start" ? { timers: [{ id: "x", ms: 1000 }] } : {})), mod(), "go", {}, async () => ({}), { budget: { wallMs: 10 } });
assert.equal(slow.error, "Module exceeded 10 ms");
});
test("a host may set calls, events and wallMs unlimited; callsPerMinute refuses past the rate", async () => {
// 150 calls, past the default 100, each answered; done after the last
const many = ({ event, state }) => {
if (event.type === "start") return { calls: Array.from({ length: 150 }, (_, i) => ({ id: `c${i}`, nsid: READ })) };
const ok = (state ?? 0) + (event.ok ? 1 : 0);
return ok === 150 ? { result: ok, done: true } : { state: ok };
};
const unlimited = await runModule(ctxOf(many), mod(), "go", {}, async () => ({}), { budget: { calls: Infinity, events: Infinity, wallMs: Infinity } });
assert.equal(unlimited.error, undefined);
assert.equal(unlimited.result, 150);
// no deadline: a timer and a per-call timeout still fire (min(ms, wallMs)), a huge timer is clamped and only
// the signal ends the run
const timed = await runModule(
ctxOf(({ event }) => (event.type === "start" ? { timers: [{ id: "x", ms: 30 }], calls: [{ id: "t", nsid: READ, timeoutMs: 5 }] } : event.type === "timer" ? { result: "fired", done: true } : {})),
mod(), "go", {}, () => new Promise(() => {}), { budget: { wallMs: Infinity } },
);
assert.equal(timed.error, undefined);
assert.equal(timed.result, "fired");
assert.match(timed.calls[0].error, /timed out after 5 ms/);
const stop = new AbortController();
setTimeout(() => stop.abort(), 50);
const served = await runModule(ctxOf(({ event }) => (event.type === "start" ? { timers: [{ id: "x", ms: 1e300 }] } : { result: "fired" })), mod(), "go", {}, async () => ({}), { budget: { wallMs: Infinity }, signal: stop.signal });
assert.equal(served.error, "aborted");
assert.equal(served.result, null);
// Infinity is only for those keys: an unlimited inFlight leaves the default
const capped = await runModule(ctxOf(({ event }) => (event.type === "start" ? { calls: [{ id: "a", nsid: READ }, { id: "b", nsid: READ }] } : {})), mod(), "go", {}, async () => ({}), { budget: { calls: 1, inFlight: Infinity } });
assert.match(capped.calls.find((c) => c.id === "b").error, /^Module exceeded 1 calls$/);
// past callsPerMinute: refused like the calls cap, and the run goes on
const rate = ({ event, state }) => {
if (event.type === "start") return { calls: Array.from({ length: 5 }, (_, i) => ({ id: `c${i}`, nsid: READ })) };
const seen = [...(state ?? []), event.ok ? "ok" : event.error];
return seen.length === 5 ? { result: seen, done: true } : { state: seen };
};
const limited = await runModule(ctxOf(rate), mod(), "go", {}, async () => ({}), { budget: { calls: Infinity, callsPerMinute: 3 } });
assert.equal(limited.error, undefined);
assert.deepEqual(limited.result.filter((r) => r === "ok").length, 3);
assert.deepEqual(limited.result.filter((r) => r !== "ok"), ["Module exceeded 3 calls per minute", "Module exceeded 3 calls per minute"]);
});
test("a full queue drops the oldest messages and counts them on the next one", async () => {
const feed = channel();
const seen = [];
let release;
const gate = new Promise((r) => (release = r));
const ctx = {
wasm: {
run: async ({ input: { event } }) => {
if (event.type === "start") return { calls: [{ id: "f", nsid: SUB, subscribe: true }] };
if (event.type === "message") {
await gate; // the handler is slow, so messages pile up
seen.push([event.data, event.dropped]);
}
return {};
},
},
};
const running = runModule(ctx, mod(), "go", {}, async () => feed, { budget: { queue: 3 } });
await sleep(5);
for (let i = 0; i < 8; i++) feed.push(i);
await sleep(5);
feed.end();
await sleep(5);
release();
await running;
// 0 is being handled; 1..7 race for 3 slots: 1..4 dropped, 5, 6, 7 kept; closed queues behind them.
assert.deepEqual(seen, [[0, undefined], [5, 4], [6, undefined], [7, undefined]]);
});
test("inputs become input events and keep the run open until they end", async () => {
async function* source() {
yield "a";
await sleep(5);
yield "b";
}
const ran = await runModule(ctxOf(({ event, state }) => (event.type === "input" ? { state: [...(state ?? []), event.data], result: [...(state ?? []), event.data] } : {})), mod(), "go", {}, async () => ({}), { inputs: source() });
assert.deepEqual(ran.result, ["a", "b"]);
});
test("handler errors end the run as data", async () => {
const failed = await runModule(ctxOf(() => ({ error: "bad input" })), mod(), "go", {}, async () => ({}));
assert.equal(failed.error, "bad input");
});
test("event-stream frames: CBOR round trip, error frames, CIDs and bytes in the atproto JSON model", () => {
const cid = "bafyreie5737gdxlw5i64vzichcalba3z2v5n6icifvx5xytvske7mr3hpm";
const frame = encodeCbor({ op: 1, t: "#commit" }, { seq: 7, repo: "did:plc:a", blocks: { $bytes: "AQID" }, commit: { $link: cid }, tooBig: false, ops: [] });
assert.deepEqual(decodeFrame(frame.buffer, SUB), { $type: `${SUB}#commit`, seq: 7, repo: "did:plc:a", blocks: { $bytes: "AQID" }, commit: { $link: cid }, tooBig: false, ops: [] });
assert.throws(() => decodeFrame(encodeCbor({ op: -1 }, { error: "FutureCursor", message: "too far" }).buffer), /FutureCursor: too far/);
assert.deepEqual(decodeFrame('{"kind":"commit"}'), { kind: "commit" });
assert.deepEqual(decodeCbor(encodeCbor(-500, 70000, "é", [null, true])), [-500, 70000, "é", [null, true]]);
assert.deepEqual(decodeCbor(encodeFrame({ $type: "com.fedproxy.temp.xrpc.subscribe#response", id: 1 }, "cbor")), [{ op: 1, t: "#response" }, { id: 1 }]);
assert.equal(encodeFrame({ a: 1 }), '{"a":1}');
assert.deepEqual([...encodeFrame({ $bytes: "AQID" })], [1, 2, 3]);
assert.equal(wsUrl("https://pds.example/", SUB, { cursor: "1" }), `wss://pds.example/xrpc/${SUB}?cursor=1`);
});
test("CBOR: bounded nesting, 1 MB byte strings, ints past 2^32, $type without #", () => {
// ~1 MB of 0x81 (a one-item array, nested) errors instead of overflowing the stack
assert.throws(() => decodeCbor(new Uint8Array(1 << 20).fill(0x81)), /nested deeper than 128/);
const deep = (n) => Uint8Array.from([...new Array(n).fill(0x81), 0]);
assert.equal(decodeCbor(deep(128)).length, 1);
assert.throws(() => decodeCbor(deep(129)), /nested deeper/);
assert.throws(() => decodeCbor(Uint8Array.from(new Array(200).fill([0xa1, 0x00]).flat())), /nested deeper/);
assert.throws(() => decodeCbor(Uint8Array.from([0x5a, 0xff, 0xff, 0xff, 0xff])), /truncated/);
const mb = { $bytes: Buffer.alloc(1 << 20, 7).toString("base64").replace(/=+$/, "") };
assert.deepEqual(decodeCbor(encodeCbor(mb, "x".repeat(1 << 20))), [mb, "x".repeat(1 << 20)]);
const big = { time: 1731000000000, neg: -5000000000 };
assert.deepEqual(decodeCbor(encodeCbor(big)), [big]);
const noHash = encodeFrame({ $type: SUB, x: 1 }, "cbor");
assert.deepEqual(decodeCbor(noHash), [{ op: 1 }, { x: 1 }]);
assert.deepEqual(decodeFrame(noHash.buffer, SUB), { $type: SUB, x: 1 });
});
test("subscribeXrpc decodes frames from a WebSocket, sends, and throws on an abnormal close", async () => {
const sockets = [];
class FakeSocket {
constructor(url, options) {
Object.assign(this, { url, options, readyState: 0, bufferedAmount: 0, sent: [] });
sockets.push(this);
setTimeout(() => ((this.readyState = 1), this.onopen?.()));
}
send(data) {
this.sent.push(data);
}
close(code = 1000) {
this.readyState = 3;
this.onclose?.({ code });
}
}
const stream = subscribeXrpc("wss://x/xrpc/a.b.c", { nsid: SUB, framing: "cbor", WebSocket: FakeSocket });
const ws = sockets[0];
await stream.send({ $type: "#response", ok: true });
assert.deepEqual(decodeCbor(ws.sent[0]), [{ op: 1, t: "#response" }, { ok: true }]);
ws.onmessage({ data: encodeCbor({ op: 1, t: "#identity" }, { seq: 1 }).buffer });
ws.onmessage({ data: '{"kind":"account"}' });
setTimeout(() => ws.onclose({ code: 1006, reason: "gone" }), 5);
const got = [];
await assert.rejects(async () => {
for await (const m of stream) got.push(m);
}, /closed: 1006 gone/);
assert.deepEqual(got, [{ $type: `${SUB}#identity`, seq: 1 }, { kind: "account" }]);
subscribeXrpc("wss://x/", { headers: { authorization: "Bearer t" }, WebSocket: FakeSocket });
assert.deepEqual(sockets[1].options, { headers: { authorization: "Bearer t" } });
});
test("frames sent with done go out before the stream closes", async () => {
const order = [];
const feed = channel();
feed.send = async (data) => (await sleep(5), order.push(`send ${data}`));
feed.close = () => (order.push("close"), feed.end());
const handler = ({ event }) => (event.type === "start" ? { calls: [{ id: "f", nsid: SUB, subscribe: true }], timers: [{ id: "t", ms: 1 }] } : { send: [{ id: "f", data: "bye" }], done: true });
await runModule(ctxOf(handler), mod(), "go", {}, async () => feed);
assert.deepEqual(order, ["send bye", "close"]);
});
test("a cancel after sends flushes them, then closes; no messages after closed", async () => {
const order = [];
const feed = channel();
feed.send = async (data) => (await sleep(5), order.push(`send ${data}`));
feed.close = () => (order.push("close"), feed.end());
const seen = [];
const handler = ({ event }) => {
seen.push(event.type);
if (event.type === "start") return { calls: [{ id: "f", nsid: SUB, subscribe: true }], send: [{ id: "f", data: "a" }], timers: [{ id: "t", ms: 1 }] };
if (event.type === "timer" && event.id === "t") return { send: [{ id: "f", data: "b" }], cancel: ["f"], timers: [{ id: "after", ms: 30 }] };
return {};
};
const running = runModule(ctxOf(handler), mod(), "go", {}, async () => feed);
await sleep(8);
feed.push("late"); // arrives while the cancel is flushing
assert.equal((await running).error, undefined);
assert.deepEqual(order, ["send a", "send b", "close"]);
assert.ok(seen.indexOf("closed") > -1 && !seen.slice(seen.indexOf("closed")).includes("message"));
});
test("overflow close: a full queue closes the subscription instead of dropping its messages", async () => {
const feed = channel();
const other = channel();
const seen = [];
let release, entered;
const gate = new Promise((r) => (release = r));
const inside = new Promise((r) => (entered = r));
const ctx = {
wasm: {
run: async ({ input: { event } }) => {
if (event.type === "start") return { calls: [{ id: "req", nsid: SUB, subscribe: true, overflow: "close" }, { id: "fire", nsid: SUB, subscribe: true }] };
if (event.type === "message" && event.data === "r0") {
entered();
await gate;
}
seen.push(event.type === "message" ? `${event.id}:${event.data}${event.dropped ? `+${event.dropped}` : ""}` : `${event.type}:${event.id}:${event.error ?? ""}`);
return {};
},
},
};
let n = 0;
const running = runModule(ctx, mod(), "go", {}, async () => (n++ ? other : feed), { budget: { queue: 3 } });
feed.push("r0");
await inside; // r0 is being handled while the rest pile up
feed.push("r1");
other.push("f1");
other.push("f2");
other.push("f3"); // full: drops f1 (a drop subscription), never r1
// all four queued before r2 (under swap.mjs each subscription reaches the queue on its own task, so the
// order across the two is not kept: only each one's own order is asserted)
await sleep(20);
feed.push("r2"); // full again: req closes rather than lose r2
await sleep(20);
other.end();
release();
const ran = await running;
assert.equal(feed.closed, true);
const of = (id) => seen.filter((s) => s.split(":").includes(id));
assert.deepEqual(of("req"), ["req:r0", "req:r1", "closed:req:overflow"]);
assert.deepEqual(of("fire"), ["fire:f2+1", "fire:f3", "closed:fire:"]);
assert.deepEqual(ran.calls.find((c) => c.id === "req"), { id: "req", nsid: SUB, subscribe: true, messages: 3, ok: false, error: "overflow" });
await assert.doesNotReject(async () => assert.match((await runModule(ctxOf(({ event }) => (event.type === "start" ? { calls: [{ id: "x", nsid: SUB, subscribe: true, overflow: "maybe" }] } : { result: event.error })), mod(), "go", {}, async () => feed)).result, /overflow must be drop or close/));
});
test("budget.log keeps the last call records; the result counts the rest", async () => {
const handler = ({ event }) => (event.type === "start" ? { calls: [0, 1, 2, 3, 4].map((n) => ({ id: `c${n}`, nsid: READ })) } : {});
const ran = await runModule(ctxOf(handler), mod(), "go", {}, async () => ({}), { budget: { log: 2 } });
assert.deepEqual(ran.calls.map((c) => c.id), ["c3", "c4"]);
assert.equal(ran.callsDropped, 3);
const all = await runModule(ctxOf(handler), mod(), "go", {}, async () => ({}));
assert.equal(all.calls.length, 5);
assert.equal(all.callsDropped, undefined);
});
test("subscribe serviceAuth reaches call(); input only for host pseudo-call sessions; bad shapes are refused", async () => {
const IROH = "com.fedfork.exec.iroh.connect";
const requests = [];
const errors = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "hdr", nsid: SUB, service: "https://relay.example", subscribe: true, serviceAuth: {} },
{ id: "qry", nsid: SUB, service: "https://relay.example", subscribe: true, serviceAuth: { param: "service_auth" } },
{ id: "iroh", nsid: IROH, input: { ticket: "abc", command: "id" }, subscribe: true },
{ id: "in", nsid: SUB, input: { x: 1 }, subscribe: true },
{ id: "nosvc", nsid: SUB, subscribe: true, serviceAuth: {} },
{ id: "odd", nsid: SUB, service: "https://relay.example", subscribe: true, serviceAuth: { param: "a b" } },
{ id: "extra", nsid: SUB, service: "https://relay.example", subscribe: true, serviceAuth: { header: "x" } },
],
};
}
if (event.type === "result" && !event.ok) errors[event.id] = event.error;
return {};
};
await runModule(ctxOf(handler), mod({ calls: [SUB, IROH] }), "go", {}, async (nsid, request) => (requests.push([nsid, request]), []), { budget: { subscriptions: 10 } });
const byNsid = (n, key) => requests.filter(([nsid]) => nsid === n).map(([, r]) => r[key]);
assert.deepEqual(byNsid(SUB, "serviceAuth"), [{}, { param: "service_auth" }]);
assert.deepEqual(byNsid(IROH, "input"), [{ ticket: "abc", command: "id" }]);
assert.match(errors.in, /subscribe takes params, not input/);
assert.match(errors.nosvc, /serviceAuth needs an https service/);
assert.match(errors.odd, /serviceAuth must be \{\} or \{ param \}/);
assert.match(errors.extra, /serviceAuth must be \{\} or \{ param \}/);
});
test("withServiceAuth: a header where the WebSocket takes one, else the token in the named query param", async () => {
const { withServiceAuth } = await import("./index.js");
const mint = async (service, nsid) => `tok:${new URL(service).host}:${nsid}`;
const req = { subscribe: true, service: "https://relay.example", params: { cursor: "1" } };
assert.deepEqual(await withServiceAuth(SUB, { ...req, serviceAuth: {} }, mint), { ...req, headers: { authorization: `Bearer tok:relay.example:${SUB}` } });
assert.deepEqual(await withServiceAuth(SUB, { ...req, serviceAuth: { param: "service_auth" } }, mint, { canHeader: false }), { ...req, params: { cursor: "1", service_auth: `tok:relay.example:${SUB}` } });
assert.deepEqual(await withServiceAuth(SUB, req, mint, { canHeader: false }), req, "no serviceAuth: untouched, nothing minted");
await assert.rejects(withServiceAuth(SUB, { ...req, serviceAuth: {} }, mint, { canHeader: false }), /browsers cannot set WebSocket headers; serviceAuth needs \{ param \}/);
await assert.rejects(withServiceAuth(SUB, { ...req, headers: { a: "b" } }, mint, { canHeader: false }), /browsers cannot set WebSocket headers/);
});
// review 3b #1: what a cancel or a re-armed timer replaces must not arrive later, even when its event
// is already queued (the handler is slow, so events queue behind it).
test("cancel drops a queued result and queued messages; a re-armed timer fires once", async () => {
const slowAfter = (handler, id) => ({ wasm: { run: async ({ input }) => {
const out = handler(input);
if (input.event.id === id && input.event.type !== "start") await sleep(80);
return out;
} } });
const late = async (nsid, { params }) => (await sleep(Number(params.ms)), {});
const seen = [];
const note = ({ event }) => seen.push(`${event.type}:${event.id ?? ""}`);
let ran = await runModule(slowAfter((input) => {
note(input);
if (input.event.type === "start") return { calls: [{ id: "a", nsid: READ, params: { ms: 5 } }, { id: "b", nsid: READ, params: { ms: 30 } }] };
if (input.event.id === "a") return { cancel: ["b"] };
return {};
}, "a"), mod(), "go", {}, late);
assert.deepEqual(seen, ["start:", "result:a"], "b's result was queued behind a; the cancel drops it");
assert.equal(ran.error, undefined);
seen.length = 0;
await runModule(slowAfter((input) => {
note(input);
if (input.event.type === "start") return { calls: [{ id: "a", nsid: READ, params: { ms: 5 } }], timers: [{ id: "t", ms: 30 }] };
if (input.event.id === "a") return { timers: [{ id: "t", ms: 100 }] };
return {};
}, "a"), mod(), "go", {}, late);
assert.deepEqual(seen, ["start:", "result:a", "timer:t"], "t fired while a ran; re-armed, only the new firing arrives");
seen.length = 0;
ran = await runModule({ wasm: { run: async ({ input: { event } }) => {
seen.push(`${event.type}:${event.id ?? ""}${event.data ? `:${event.data.n}` : ""}`);
if (event.type === "start") return { calls: [{ id: "s", nsid: SUB, subscribe: true }] };
if (event.type === "message" && event.data.n === 0) return await sleep(80), { cancel: ["s"] };
return {};
} } }, mod(), "go", {}, async () => ({ async *[Symbol.asyncIterator]() {
for (let n = 0; n < 3; n++) yield { n }, await sleep(10);
await sleep(1e4);
} }));
assert.deepEqual(seen, ["start:", "message:s:0", "closed:s"]);
});
// review 3b #4: a ledger read that fails in the rkey guard refuses that call; the run does not throw.
test("a failing ledger read refuses the write instead of throwing", async () => {
const PUT = "com.atproto.repo.putRecord";
const storage = { managed_records: { get: async () => { throw new Error("database is locked"); }, put: async () => {}, delete: async () => {} } };
const ran = await runModule(ctxOf(({ event }) => (event.type === "start" ? { calls: [{ id: "p", nsid: PUT, input: { repo: "did:plc:a", collection: "x.y.z", rkey: "k", record: {} } }] } : { result: event, done: true }), storage), mod({ calls: [PUT] }), "go", {}, async () => ({}));
assert.equal(ran.error, undefined);
assert.equal(ran.result.ok, false);
assert.match(ran.result.error, /database is locked/);
});
// review 3c: map keys sort by their UTF-8 length, then bytewise (subscribe.js sorted UTF-16 lengths).
test("encodeCbor orders non-ASCII map keys by encoded length", () => {
// "é" is 1 UTF-16 unit but 2 bytes: z (1 byte), then ab and é (2 bytes; 0x61 < 0xc3).
assert.deepEqual([...encodeCbor({ é: 1, ab: 2, z: 3 })], [0xa3, 0x61, 0x7a, 0x03, 0x62, 0x61, 0x62, 0x02, 0x62, 0xc3, 0xa9, 0x01]);
assert.deepEqual(decodeCbor(encodeCbor({ é: 1, ab: 2, z: 3 })), [{ z: 3, ab: 2, é: 1 }]);
});
test("http.fetch: declared ∩ host-allowed origins, host headers injected and replacing the module's, refusals are call errors", async () => {
const HTTP = "com.fedfork.exec.http.fetch";
const seen = [];
const fetch = async (url, init) => (seen.push([url, init.method, init.headers, init.body ?? null, init.redirect]), new Response(`hi ${init.method}`, { status: 201, headers: { "content-type": "text/plain", "mcp-session-id": "s1" } }));
const req = (id, input) => ({ id, nsid: HTTP, input });
const results = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
req("ok", { method: "POST", url: "https://api.example/v1/messages?x=1", headers: { "content-type": "application/json", "X-Api-Key": "module-guess" }, body: "{}" }),
req("get", { url: "https://Mcp.Example:443/mcp" }),
req("undeclared", { url: "https://other.example/" }),
req("unallowed", { url: "https://declared.example/" }),
req("creds", { url: "https://u:p@api.example/" }),
req("ftp", { url: "ftp://api.example/" }),
req("method", { method: "TRACE", url: "https://api.example/" }),
req("hdrs", { url: "https://api.example/", headers: { "bad name": "x" } }),
req("body", { method: "POST", url: "https://api.example/", body: { a: 1 } }),
req("getbody", { url: "https://api.example/", body: "x" }),
req("noinput", undefined),
{ id: "sub", nsid: HTTP, input: { url: "https://api.example/" }, subscribe: true },
],
};
}
results[event.id] = event.ok ? event.output : event.error;
return { result: results };
};
const ctx = { ...ctxOf(handler), http: { fetch } };
const http = { "https://api.example": { headers: { "x-api-key": "SECRET" } }, "https://mcp.example": {}, "https://unlisted.example": {} };
const ran = await runModule(ctx, mod({ calls: [HTTP], http: ["https://api.example", "https://mcp.example", "https://declared.example"] }), "go", {}, async () => assert.fail("never the XRPC call"), { http });
assert.equal(ran.error, undefined);
const r = ran.result;
assert.deepEqual(r.ok, { status: 201, headers: { "content-type": "text/plain", "mcp-session-id": "s1" }, body: "hi POST" });
assert.equal(r.get.body, "hi GET");
assert.deepEqual(seen.sort(), [
["https://Mcp.Example:443/mcp", "GET", {}, null, "manual"],
// a JSON body goes as its JSON
["https://api.example/", "POST", { "x-api-key": "SECRET" }, '{"a":1}', "manual"],
["https://api.example/v1/messages?x=1", "POST", { "content-type": "application/json", "x-api-key": "SECRET" }, "{}", "manual"],
]);
const P = `${HTTP}: `;
assert.equal(r.undeclared, `${P}https://other.example is not in the module's http list`);
assert.equal(r.unallowed, `${P}this host does not allow https://declared.example`);
assert.equal(r.creds, `${P}url must be a URL without credentials`);
assert.equal(r.ftp, `${P}ftp://* is not in the module's http list`);
assert.equal(r.method, `${P}method must be one of GET, HEAD, POST, PUT, PATCH, DELETE`);
assert.equal(r.hdrs, `${P}headers must map header names to strings`);
assert.equal(r.getbody, `${P}GET takes no body`);
assert.equal(r.noinput, `${P}input must be { method?, url, headers?, body? }`);
assert.equal(r.sub, `${P}cannot be subscribed to (a response arrives whole)`);
// "*" reaches any origin the host allows, and none without the host's allowlist
const star = await runModule(ctx, mod({ calls: [HTTP], http: ["*"] }), "go", {}, async () => {}, { http: { "https://mcp.example": {} } });
assert.equal(star.result.get.body, "hi GET");
assert.equal(star.result.ok, `${P}this host does not allow https://api.example`);
const none = await runModule(ctx, mod({ calls: [HTTP], http: ["*"] }), "go", {}, async () => {});
assert.equal(none.result.get, `${P}this host does not allow https://mcp.example`);
const undeclared = await runModule(ctx, mod({ calls: [HTTP] }), "go", {}, async () => {}, { http });
assert.equal(undeclared.result.get, `${P}https://mcp.example is not in the module's http list`);
});
test("http.fetch: another scheme goes to the host's fetcher for it, allowed as <scheme>://* in both lists, without host headers", async () => {
const HTTP = "com.fedfork.exec.http.fetch";
const seen = [];
// a stub transport fetcher (atlibs-webrtc's host.fetchers()): what it got, and a response with mixed-case headers
const fetchers = { "webrtc:": async ({ method, url, headers, body, signal }) => (seen.push([method, url, headers, body ?? null, signal instanceof AbortSignal]), { status: 200, headers: { "Content-Type": "application/json", "Mcp-Session-Id": "s1" }, body: `{"via":"webrtc"}` }) };
const url = "webrtc:///eyJyZWNlaXB0Ijp7fX0/shell/mcp";
const req = (id, input) => ({ id, nsid: HTTP, input });
const results = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
req("post", { method: "POST", url, headers: { "content-type": "application/json", "x-api-key": "module's own" }, body: "{}" }),
req("iroh", { url: "iroh:///e30/shell/mcp" }),
req("creds", { url: "webrtc://u:p@x/y" }),
req("https", { url: "https://api.example/" }),
],
};
}
results[event.id] = event.ok ? event.output : event.error;
return { result: results };
};
const ctx = { ...ctxOf(handler), http: { fetch: async () => assert.fail("never ctx.http.fetch for webrtc:") } };
// the host's headers and serviceAuth never ride a scheme fetch
const http = { "webrtc://*": { headers: { "x-api-key": "SECRET" } }, "iroh://*": {} };
const run = (declared, options) => runModule(ctx, mod({ calls: [HTTP], http: declared }), "go", {}, async () => assert.fail("never the XRPC call"), options);
const ran = await run(["webrtc://*", "iroh://*"], { http, fetchers });
assert.equal(ran.error, undefined);
const P = `${HTTP}: `;
assert.deepEqual(ran.result.post, { status: 200, headers: { "content-type": "application/json", "mcp-session-id": "s1" }, body: `{"via":"webrtc"}` });
assert.deepEqual(seen, [["POST", url, { "content-type": "application/json", "x-api-key": "module's own" }, "{}", true]]);
assert.equal(ran.result.iroh, `${P}no fetcher for iroh:`);
assert.equal(ran.result.creds, `${P}url must be a URL without credentials`);
assert.equal(ran.result.https, `${P}https://api.example is not in the module's http list`);
// a module's "*" is http(s) only; a scheme the host does not allow is refused before any fetcher
const star = await run(["*", "webrtc://host.example"], { http, fetchers });
assert.equal(star.result.post, `${P}webrtc://* is not in the module's http list`);
const unallowed = await run(["webrtc://*"], { http: { "iroh://*": {} }, fetchers });
assert.equal(unallowed.result.post, `${P}this host does not allow webrtc://*`);
assert.equal(seen.length, 1);
});
test("a call's service may be a did#service (a public read of that service); bad ones and input are refused", async () => {
const seen = [];
const errors = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "ok", nsid: READ, service: "did:web:api.example%3A8443#svc_1", params: { a: "1" } },
{ id: "empty", nsid: READ, service: "did:web:api.example#" },
{ id: "odd", nsid: READ, service: "did:web:api.example#a b" },
{ id: "input", nsid: READ, service: "did:web:api.example#svc", input: {} },
],
};
}
if (!event.ok) errors[event.id] = event.error;
return {};
};
await runModule(ctxOf(handler), mod(), "go", {}, async (nsid, request) => (seen.push(request.service), {}));
assert.deepEqual(seen, ["did:web:api.example%3A8443#svc_1"]);
assert.deepEqual(errors, {
empty: "Module call service must be an https origin, a DID or a did#service",
odd: "Module call service must be an https origin, a DID or a did#service",
input: "Module call to a DID service is a read: no input",
});
});
test("a did#service call may name its endpoints (then it takes input); they reach call() as given; bad ones are refused", async () => {
const seen = [];
const errors = {};
const endpoints = [{ kind: "iroh", address: "endpointabc234" }, { kind: "https", address: "https://bidder.example" }];
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "ok", nsid: READ, service: "did:plc:bidder#pdr_temp_market", endpoints, input: { a: 1 } },
{ id: "bare", nsid: READ, service: "did:plc:bidder", endpoints },
{ id: "https", nsid: READ, service: "https://bidder.example", endpoints },
{ id: "none", nsid: READ, service: "did:plc:bidder#pdr_temp_market", endpoints: [] },
{ id: "extra", nsid: READ, service: "did:plc:bidder#pdr_temp_market", endpoints: [{ kind: "iroh", address: "e", x: 1 }] },
{ id: "kind", nsid: READ, service: "did:plc:bidder#pdr_temp_market", endpoints: [{ kind: "Iroh!", address: "e" }] },
{ id: "space", nsid: READ, service: "did:plc:bidder#pdr_temp_market", endpoints: [{ kind: "iroh", address: "a b" }] },
],
};
}
if (!event.ok) errors[event.id] = event.error;
return {};
};
await runModule(ctxOf(handler), mod(), "go", {}, async (nsid, request) => (seen.push([request.service, request.endpoints, request.input]), {}));
assert.deepEqual(seen, [["did:plc:bidder#pdr_temp_market", endpoints, { a: 1 }]]);
const bad = "Module call endpoints must be 1 to 8 { kind, address } (kind a URL scheme name, address up to 2048 printable ASCII)";
assert.deepEqual(errors, {
bare: "Module call endpoints need a did#service service",
https: "Module call endpoints need a did#service service",
none: bad,
extra: bad,
kind: bad,
space: bad,
});
});
test("ioBytes: the host's per-event wasm I/O cap reaches ctx.wasm.run, 1 MiB by default, never past 16 MiB", async () => {
for (const [budget, want] of [[undefined, 1 << 20], [{ ioBytes: 8 << 20 }, 8 << 20], [{ ioBytes: 1 << 30 }, 16 << 20], [{ ioBytes: Infinity }, 1 << 20]]) {
const seen = [];
const ctx = { wasm: { run: async ({ ioBytes }) => (seen.push(ioBytes), { done: true }) } };
await runModule(ctx, mod(), "go", {}, async () => ({}), budget && { budget });
assert.deepEqual(seen, [want], JSON.stringify(budget));
}
});
test("a call's encoding names the content-type of its { $bytes } input and reaches call(); bad ones are refused", async () => {
const seen = [];
const errors = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "ok", nsid: READ, input: { $bytes: "AAEC" }, encoding: "application/x-git-packed-objects" },
{ id: "plain", nsid: READ, input: { $bytes: "AAEC" } },
{ id: "odd", nsid: READ, input: { $bytes: "AAEC" }, encoding: "not a type" },
{ id: "json", nsid: READ, input: { a: 1 }, encoding: "application/json" },
],
};
}
if (!event.ok) errors[event.id] = event.error;
return {};
};
await runModule(ctxOf(handler), mod(), "go", {}, async (nsid, request) => (seen.push([request.input, request.encoding]), {}));
assert.deepEqual(seen, [[{ $bytes: "AAEC" }, "application/x-git-packed-objects"], [{ $bytes: "AAEC" }, undefined]]);
assert.deepEqual(errors, { odd: "Module call encoding must be a MIME type", json: "Module call encoding needs a $bytes input" });
});
test("held values: put, append, get, delete in order at plan time; spliced into an http.fetch body and the result; bounded by heldBytes", async () => {
const H = "com.fedfork.exec.held.";
const HTTP = "com.fedfork.exec.http.fetch";
const errors = {};
const outputs = {};
const sent = [];
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "a1", nsid: `${H}append`, input: { name: "t", items: [{ role: "user", content: "hi" }] } },
{ id: "a2", nsid: `${H}append`, input: { name: "t", items: [{ role: "assistant", content: "yo" }] } },
{ id: "send", nsid: HTTP, input: { method: "POST", url: "https://api.example/v1", body: { model: "m", messages: { $held: "t" } } } },
{ id: "p", nsid: `${H}put`, input: { name: "n", value: 7 } },
{ id: "g", nsid: `${H}get`, input: { name: "n" } },
{ id: "d", nsid: `${H}delete`, input: { name: "n" } },
{ id: "g2", nsid: `${H}get`, input: { name: "n" } },
{ id: "big", nsid: `${H}put`, input: { name: "b", value: "x".repeat(200) } },
{ id: "notlist", nsid: `${H}append`, input: { name: "t2", items: 1 } },
{ id: "missing", nsid: HTTP, input: { method: "POST", url: "https://api.example/v1", body: { m: { $held: "nope" } } } },
{ id: "odd", nsid: `${H}grow`, input: { name: "t" } },
],
result: { messages: { $held: "t" } },
};
}
if (event.ok) outputs[event.id] = event.output;
else errors[event.id] = event.error;
return {};
};
const ctx = { ...ctxOf(handler), http: { fetch: async (url, init) => (sent.push(init.body), new Response("{}")) } };
const calls = [`${H}put`, `${H}append`, `${H}get`, `${H}delete`, `${H}grow`, HTTP];
const ran = await runModule(ctx, mod({ calls, http: ["https://api.example"] }), "go", {}, async () => ({}), { http: { "https://api.example": {} }, budget: { heldBytes: 150 } });
assert.equal(ran.error, undefined);
assert.deepEqual(sent, ['{"model":"m","messages":[{"role":"user","content":"hi"},{"role":"assistant","content":"yo"}]}']);
assert.deepEqual(ran.result, { messages: [{ role: "user", content: "hi" }, { role: "assistant", content: "yo" }] });
assert.deepEqual({ a1: outputs.a1, a2: outputs.a2, p: outputs.p, g: outputs.g, d: outputs.d, g2: outputs.g2 }, { a1: { length: 1, bytes: 32 }, a2: { length: 2, bytes: 68 }, p: {}, g: { value: 7 }, d: {}, g2: { value: null } });
assert.deepEqual(errors, {
big: "com.fedfork.exec.held.put: held values past 150 bytes",
notlist: "com.fedfork.exec.held.append: items must be a list",
missing: "com.fedfork.exec.http.fetch: no held value nope",
odd: "com.fedfork.exec.held.grow is not a held-value call (put, append, get, delete)",
});
});
test("held values are copies: a later append grows neither the module's put value (onEvent's output) nor an earlier get's answer", async () => {
const H = "com.fedfork.exec.held.";
const outputs = {};
let seen;
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "p", nsid: `${H}put`, input: { name: "t", value: [{ n: 1 }] } },
{ id: "g1", nsid: `${H}get`, input: { name: "t" } },
{ id: "a", nsid: `${H}append`, input: { name: "t", items: [{ n: 2 }] } },
{ id: "g2", nsid: `${H}get`, input: { name: "t" } },
{ id: "a2", nsid: `${H}append`, input: { name: "t", items: [{ n: 3 }] } },
],
};
}
outputs[event.id] = event.output;
return {};
};
const onEvent = (event, output) => event.type === "start" && (seen = output);
const ran = await runModule(ctxOf(handler), mod({ calls: [`${H}put`, `${H}get`, `${H}append`] }), "go", {}, async () => ({}), { onEvent });
assert.equal(ran.error, undefined);
assert.deepEqual(outputs.g1, { value: [{ n: 1 }] });
assert.deepEqual(outputs.g2, { value: [{ n: 1 }, { n: 2 }] });
assert.deepEqual(seen.calls[0].input.value, [{ n: 1 }]);
assert.deepEqual(seen.calls[2].input.items, [{ n: 2 }]);
});
test("a host starts a run with held values (counted against heldBytes); { $held: [names] } concatenates held lists", async () => {
const H = "com.fedfork.exec.held.";
const HTTP = "com.fedfork.exec.http.fetch";
const sent = [];
const errors = {};
const handler = ({ event }) => {
if (event.type === "start") {
return {
calls: [
{ id: "a", nsid: `${H}append`, input: { name: "t", items: [{ n: 2 }] } },
{ id: "send", nsid: HTTP, input: { method: "POST", url: "https://api.example/v1", body: { messages: { $held: ["s", "t"] } } } },
{ id: "notlist", nsid: HTTP, input: { method: "POST", url: "https://api.example/v1", body: { m: { $held: ["s", "n"] } } } },
{ id: "missing", nsid: HTTP, input: { method: "POST", url: "https://api.example/v1", body: { m: { $held: ["nope"] } } } },
],
result: { t: { $held: "t" }, n: { $held: "n" } },
};
}
if (!event.ok) errors[event.id] = event.error;
return {};
};
const ctx = { ...ctxOf(handler), http: { fetch: async (url, init) => (sent.push(init.body), new Response("{}")) } };
const m = mod({ calls: [`${H}append`, HTTP], http: ["https://api.example"] });
const held = { s: [{ n: 0 }], t: [{ n: 1 }], n: 7 };
const ran = await runModule(ctx, m, "go", {}, async () => ({}), { http: { "https://api.example": {} }, held });
assert.equal(ran.error, undefined);
assert.deepEqual(ran.result, { t: [{ n: 1 }, { n: 2 }], n: 7 });
assert.deepEqual(held.t, [{ n: 1 }], "the host's value is copied, not grown");
assert.deepEqual(sent, ['{"messages":[{"n":0},{"n":1},{"n":2}]}']);
assert.deepEqual(errors, { notlist: `${HTTP}: held value n is not a list`, missing: `${HTTP}: no held value nope` });
const big = await runModule(ctx, m, "go", {}, async () => ({}), { held: { t: "x".repeat(50) }, budget: { heldBytes: 20 } });
assert.equal(big.error, "held: com.fedfork.exec.held.put: held values past 20 bytes");
assert.equal((await runModule(ctx, m, "go", {}, async () => ({}), { held: { "bad name": 1 } })).error, "held: com.fedfork.exec.held.put: input needs a name");
});
test("http.fetch serviceAuth: the host mints a token per request with the run's session; no session or no token is a call error", async () => {
const HTTP = "com.fedfork.exec.http.fetch";
const sent = [];
const handler = ({ event, state }) => {
if (event.type === "start") {
const req = (id) => ({ id, nsid: HTTP, input: { method: "POST", url: "https://ai.example/v1/chat", headers: { Authorization: "Bearer module-guess", "content-type": "application/json" }, body: "{}" } });
return { calls: [req("a"), req("b")] };
}
const seen = { ...state, [event.id]: event.ok ? event.output.body : event.error };
return { result: seen, state: seen };
};
const ctx = { ...ctxOf(handler), http: { fetch: async (url, init) => (sent.push(init.headers), new Response("ok")) } };
const m = mod({ calls: [HTTP], http: ["https://ai.example"] });
const http = { "https://ai.example": { headers: { "x-extra": "1" }, serviceAuth: { aud: "did:web:console.ai.example", lxm: "example.ai.dispatch" } } };
const minted = [];
const session = async (nsid, request) => (minted.push([nsid, request.params]), { token: `tok-${minted.length}` });
const ran = await runModule(ctx, m, "go", {}, session, { http });
assert.equal(ran.error, undefined);
assert.deepEqual(minted, [["com.atproto.server.getServiceAuth", { aud: "did:web:console.ai.example", lxm: "example.ai.dispatch" }], ["com.atproto.server.getServiceAuth", { aud: "did:web:console.ai.example", lxm: "example.ai.dispatch" }]]);
assert.deepEqual(sent.map((h) => h.authorization).sort(), ["Bearer tok-1", "Bearer tok-2"]);
assert.ok(sent.every((h) => h["x-extra"] === "1" && h["content-type"] === "application/json" && !("Authorization" in h)));
const signedOut = await runModule(ctx, m, "go", {}, async () => Promise.reject(new Error("no session")), { http });
assert.equal(signedOut.result.a, `${HTTP}: https://ai.example needs a service-auth token (aud did:web:console.ai.example, lxm example.ai.dispatch) from the run's session: no session (an OAuth session's scope must include rpc:example.ai.dispatch?aud=did:web:console.ai.example)`);
const empty = await runModule(ctx, m, "go", {}, async () => ({}), { http });
assert.equal(empty.result.a, `${HTTP}: com.atproto.server.getServiceAuth answered no service-auth token`);
});