@dispatch-foo/protocol 0.0.0-stage → 0.2.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.
@@ -0,0 +1,672 @@
1
+ import { d as SYSTEM_ADDRESS, o as GATE_TYPES, r as DELIVERY_STATES } from "./constants-CRXF4f7g.js";
2
+ import { C as parseAddress, c as createUlidFactory, g as gateOf, i as openMessagesDb, l as renderDigestLine, r as SqliteMessageStore, s as DeliveryEngine, t as PROTOCOL_VERSION, u as renderForAgent, v as validateSendInput, w as MessagingError } from "./version-Ds8ZFZLh.js";
3
+ import { queryAll } from "@dispatch-foo/core";
4
+
5
+ //#region src/conformance/errors.ts
6
+ var UnsupportedOp = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "UnsupportedOp";
10
+ }
11
+ };
12
+
13
+ //#endregion
14
+ //#region src/conformance/fields.ts
15
+ function malformed(where, why) {
16
+ throw new Error(`malformed vector field ${where}: ${why}`);
17
+ }
18
+ function isObject(value) {
19
+ return value !== null && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+ function asObject(value, where) {
22
+ return isObject(value) ? value : malformed(where, "expected an object");
23
+ }
24
+ function text(obj, key, where) {
25
+ const value = obj[key];
26
+ return typeof value === "string" ? value : malformed(`${where}.${key}`, "expected a string");
27
+ }
28
+ function optionalText(obj, key, where) {
29
+ return obj[key] === void 0 ? void 0 : text(obj, key, where);
30
+ }
31
+ function texts(value, where) {
32
+ if (!Array.isArray(value)) return malformed(where, "expected a list");
33
+ return value.map((v, i) => typeof v === "string" ? v : malformed(`${where}[${i}]`, "expected a string"));
34
+ }
35
+ function bare(session) {
36
+ return session.startsWith("run:") ? session.slice(4) : session;
37
+ }
38
+
39
+ //#endregion
40
+ //#region src/conformance/host.ts
41
+ const DEFAULT_CLOCK = "2026-09-23T10:00:00.000Z";
42
+ const RULINGS = [
43
+ "allow",
44
+ "ask",
45
+ "deny"
46
+ ];
47
+ function worldFrom(given) {
48
+ const world = {
49
+ clockMs: Date.parse(given.clock ?? DEFAULT_CLOCK),
50
+ liveRuns: /* @__PURE__ */ new Map(),
51
+ runTasks: /* @__PURE__ */ new Map(),
52
+ auxRuns: new Set((given.auxSessions ?? []).map(bare)),
53
+ implicit: new Map(Object.entries(given.implicit ?? {})),
54
+ owner: given.owner ?? "human:owner",
55
+ rulings: new Map(Object.entries(given.rulings ?? {})),
56
+ wakeResults: new Map(Object.entries(given.wakeResults ?? {}).map(([target, r]) => [target, r.ok ? {
57
+ ok: true,
58
+ runId: bare(r.session)
59
+ } : r])),
60
+ failPush: new Set((given.failPush ?? []).map(bare)),
61
+ failOnAnswered: given.failOnAnswered === true,
62
+ external: new Map(Object.entries(given.external ?? {}))
63
+ };
64
+ for (const item of given.workItems ?? []) {
65
+ for (const s of item.sessions ?? []) world.runTasks.set(bare(s), item.id);
66
+ if (item.liveSession !== void 0) {
67
+ world.liveRuns.set(item.id, bare(item.liveSession));
68
+ world.runTasks.set(bare(item.liveSession), item.id);
69
+ }
70
+ }
71
+ return world;
72
+ }
73
+ function rulingOf(value, where) {
74
+ return RULINGS.find((r) => r === value) ?? malformed(where, `expected one of ${RULINGS.join(", ")}`);
75
+ }
76
+ function wakeResultOf(value, where) {
77
+ const r = asObject(value, where);
78
+ if (r["ok"] === true) return {
79
+ ok: true,
80
+ runId: bare(text(r, "session", where))
81
+ };
82
+ if (r["ok"] === false) return {
83
+ ok: false,
84
+ reason: text(r, "reason", where)
85
+ };
86
+ return malformed(`${where}.ok`, "expected a boolean");
87
+ }
88
+ function applyWorld(world, change) {
89
+ const keys = Object.keys(change);
90
+ const key = keys[0];
91
+ if (keys.length !== 1 || key === void 0) throw new UnsupportedOp(`a world change names exactly one key, not ${keys.length}`);
92
+ const value = change[key];
93
+ const where = `world.${key}`;
94
+ switch (key) {
95
+ case "startSession": {
96
+ const s = asObject(value, where);
97
+ const item = text(s, "workItem", where);
98
+ const run = bare(text(s, "session", where));
99
+ world.liveRuns.set(item, run);
100
+ world.runTasks.set(run, item);
101
+ return;
102
+ }
103
+ case "endSession":
104
+ world.liveRuns.delete(text(asObject(value, where), "workItem", where));
105
+ return;
106
+ case "ruling": {
107
+ const r = asObject(value, where);
108
+ world.rulings.set(text(r, "target", where), rulingOf(r["ruling"], `${where}.ruling`));
109
+ return;
110
+ }
111
+ case "wakeResult": {
112
+ const r = asObject(value, where);
113
+ world.wakeResults.set(text(r, "target", where), wakeResultOf(r["result"], `${where}.result`));
114
+ return;
115
+ }
116
+ case "failPush":
117
+ world.failPush = new Set(texts(value, where).map(bare));
118
+ return;
119
+ case "failOnAnswered":
120
+ if (typeof value !== "boolean") malformed(where, "expected a boolean");
121
+ world.failOnAnswered = value;
122
+ return;
123
+ case "advanceMs":
124
+ if (typeof value !== "number" || !Number.isFinite(value)) malformed(where, "expected a number");
125
+ world.clockMs += value;
126
+ return;
127
+ default: throw new UnsupportedOp(`world change ${key} is not implemented`);
128
+ }
129
+ }
130
+ var ConformanceHost = class {
131
+ calls = [];
132
+ constructor(world) {
133
+ this.world = world;
134
+ }
135
+ liveRunFor(taskId) {
136
+ return this.world.liveRuns.get(taskId) ?? null;
137
+ }
138
+ isLiveRun(runId) {
139
+ return this.world.auxRuns.has(runId) || [...this.world.liveRuns.values()].includes(runId);
140
+ }
141
+ taskOfRun(runId) {
142
+ return this.world.runTasks.get(runId) ?? null;
143
+ }
144
+ push(runId, _rendered, message) {
145
+ this.calls.push({
146
+ hook: "push",
147
+ session: `run:${runId}`,
148
+ message: message.id
149
+ });
150
+ return this.settle(runId, "push failed");
151
+ }
152
+ notify(runId, _digest, message) {
153
+ this.calls.push({
154
+ hook: "notify",
155
+ session: `run:${runId}`,
156
+ message: message.id
157
+ });
158
+ return this.settle(runId, "notify failed");
159
+ }
160
+ notifyHuman(actor, message) {
161
+ this.calls.push({
162
+ hook: "notifyHuman",
163
+ actor,
164
+ message: message.id
165
+ });
166
+ }
167
+ wake(target, message) {
168
+ this.calls.push({
169
+ hook: "wake",
170
+ target,
171
+ message: message.id
172
+ });
173
+ return Promise.resolve(this.world.wakeResults.get(target) ?? {
174
+ ok: true,
175
+ runId: "r-00000f"
176
+ });
177
+ }
178
+ decide(request) {
179
+ this.calls.push({
180
+ hook: "decide",
181
+ target: request.target,
182
+ message: request.message.id
183
+ });
184
+ return this.world.rulings.get(request.target) ?? "deny";
185
+ }
186
+ owner() {
187
+ return this.world.owner;
188
+ }
189
+ implicitMembers(channel) {
190
+ return this.world.implicit.get(channel) ?? [];
191
+ }
192
+ onAnswered(question, answer) {
193
+ this.calls.push({
194
+ hook: "onAnswered",
195
+ question: question.id,
196
+ answer: answer.id
197
+ });
198
+ if (this.world.failOnAnswered) return Promise.reject(/* @__PURE__ */ new Error("effect failed"));
199
+ const gate = gateOf(question);
200
+ if (gate?.type === "wake" && answer.choice === "approve") this.calls.push({
201
+ hook: "wake",
202
+ target: gate.target,
203
+ message: gate.message
204
+ });
205
+ return Promise.resolve();
206
+ }
207
+ now() {
208
+ return new Date(this.world.clockMs);
209
+ }
210
+ external(address) {
211
+ return this.world.external.get(address) ?? null;
212
+ }
213
+ admitExternal(target, _sender, _replyTarget, message) {
214
+ this.calls.push({
215
+ hook: "admitExternal",
216
+ recipient: target.recipient,
217
+ message: message.id
218
+ });
219
+ return "deliver";
220
+ }
221
+ settle(runId, why) {
222
+ return this.world.failPush.has(runId) ? Promise.reject(new Error(why)) : Promise.resolve();
223
+ }
224
+ };
225
+
226
+ //#endregion
227
+ //#region src/conformance/seed.ts
228
+ function seededBytes(seed) {
229
+ let state = seed >>> 0;
230
+ return (n) => {
231
+ const out$1 = new Uint8Array(n);
232
+ for (let i = 0; i < n; i++) {
233
+ state = state + 1831565813 >>> 0;
234
+ let t = state;
235
+ t = Math.imul(t ^ t >>> 15, t | 1);
236
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
237
+ out$1[i] = (t ^ t >>> 14) & 255;
238
+ }
239
+ return out$1;
240
+ };
241
+ }
242
+
243
+ //#endregion
244
+ //#region src/conformance/seedStore.ts
245
+ const VIAS = ["direct", "channel"];
246
+ function nullableText(obj, key, where) {
247
+ return obj[key] === null ? null : optionalText(obj, key, where) ?? null;
248
+ }
249
+ function refsOf(value, where) {
250
+ if (value === void 0) return [];
251
+ if (!Array.isArray(value)) return malformed(where, "expected a list");
252
+ return value.map((raw, i) => {
253
+ const at = `${where}[${i}]`;
254
+ const r = asObject(raw, at);
255
+ const ref = {
256
+ type: text(r, "type", at),
257
+ id: text(r, "id", at)
258
+ };
259
+ const commit = optionalText(r, "at", at);
260
+ if (commit !== void 0) ref.at = commit;
261
+ return ref;
262
+ });
263
+ }
264
+ function seededMessage(row, at, where) {
265
+ const id = text(row, "id", where);
266
+ const message = {
267
+ id,
268
+ thread: optionalText(row, "thread", where) ?? id,
269
+ replyTo: nullableText(row, "replyTo", where),
270
+ from: text(row, "from", where),
271
+ to: texts(row["to"], `${where}.to`),
272
+ kind: text(row, "kind", where),
273
+ body: text(row, "body", where),
274
+ refs: refsOf(row["refs"], `${where}.refs`),
275
+ urgent: row["urgent"] === true,
276
+ blocking: row["blocking"] === true,
277
+ wake: row["wake"] === "request" ? "request" : "none",
278
+ createdAt: optionalText(row, "createdAt", where) ?? at
279
+ };
280
+ const session = optionalText(row, "session", where);
281
+ if (session !== void 0) message.session = session;
282
+ const data = row["data"];
283
+ if (data !== void 0) message.data = data;
284
+ if (row["choices"] !== void 0) message.choices = texts(row["choices"], `${where}.choices`);
285
+ const choice = optionalText(row, "choice", where);
286
+ if (choice !== void 0) message.choice = choice;
287
+ return message;
288
+ }
289
+ function seededDelivery(row, at, where) {
290
+ const session = nullableText(row, "session", where);
291
+ const via = row["via"] === void 0 ? "direct" : row["via"];
292
+ return {
293
+ id: text(row, "id", where),
294
+ messageId: text(row, "message", where),
295
+ recipient: text(row, "recipient", where),
296
+ runId: session === null ? null : bare(session),
297
+ via: VIAS.find((v) => v === via) ?? malformed(`${where}.via`, `expected one of ${VIAS.join(", ")}`),
298
+ state: DELIVERY_STATES.find((s) => s === row["state"]) ?? malformed(`${where}.state`, `expected one of ${DELIVERY_STATES.join(", ")}`),
299
+ updatedAt: at
300
+ };
301
+ }
302
+ function seedStore(store, given, world) {
303
+ const at = new Date(world.clockMs).toISOString();
304
+ store.transaction(() => {
305
+ for (const agent of given.agents ?? []) store.putAgent({
306
+ address: agent.address,
307
+ displayName: agent.address,
308
+ client: "conformance",
309
+ tokenHash: `seed:${agent.address}`,
310
+ status: agent.status,
311
+ muted: agent.muted === true,
312
+ approvedBy: world.owner,
313
+ createdAt: at
314
+ });
315
+ for (const channel of given.channels ?? []) {
316
+ store.ensureChannel(channel.name, at, false);
317
+ for (const member of channel.members) store.addMember(channel.name, member, at);
318
+ }
319
+ const rows = given.store ?? {};
320
+ (rows.messages ?? []).forEach((row, i) => store.insertMessage(seededMessage(row, at, `given.store.messages[${i}]`)));
321
+ (rows.deliveries ?? []).forEach((row, i) => store.insertDelivery(seededDelivery(row, at, `given.store.deliveries[${i}]`)));
322
+ for (const id of rows.appliedGates ?? []) store.markGateApplied(id, at);
323
+ });
324
+ }
325
+
326
+ //#endregion
327
+ //#region src/conformance/adapter.ts
328
+ const CREATES = new Set([
329
+ "send",
330
+ "reply",
331
+ "close",
332
+ "a2a.inbound"
333
+ ]);
334
+ const ROLE = /\$(?:s|gate|notice)[0-9]+\b/g;
335
+ const ORIGINS = ["local", "received"];
336
+ const AGENT_STATUSES = [
337
+ "pending",
338
+ "approved",
339
+ "revoked"
340
+ ];
341
+ const REFERENCE_HELLO = {
342
+ dmp: "hello",
343
+ implementation: {
344
+ name: "dispatch-reference",
345
+ version: PROTOCOL_VERSION
346
+ },
347
+ classes: ["envelope", "host-core"],
348
+ profiles: ["core", "dispatch"],
349
+ capabilities: ["implicit-members", "muted-senders"],
350
+ systemAddress: SYSTEM_ADDRESS,
351
+ gateTypes: [...GATE_TYPES],
352
+ render: {
353
+ quotePrefix: "│ ",
354
+ header: "^\\[message from ",
355
+ hostLines: [
356
+ "^\\(in reply to ",
357
+ "^choices: ",
358
+ "^choice: ",
359
+ "^refs: ",
360
+ "^The sender is waiting\\. "
361
+ ],
362
+ digestLead: "^📬(?: #[^ ]+ ·)? [^ ]+ from [^ ]+(?: \\(external\\))?: "
363
+ }
364
+ };
365
+ function mapStrings(value, f) {
366
+ if (typeof value === "string") return f(value);
367
+ if (Array.isArray(value)) return value.map((v) => mapStrings(v, f));
368
+ if (isObject(value)) return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, mapStrings(v, f)]));
369
+ return value;
370
+ }
371
+ var RoleBinder = class {
372
+ bound = /* @__PURE__ */ new Map();
373
+ created = /* @__PURE__ */ new Set();
374
+ seeded;
375
+ constructor(db, vector) {
376
+ this.db = db;
377
+ this.seeded = new Set((vector.given.store?.messages ?? []).flatMap((r) => typeof r["id"] === "string" ? [r["id"]] : []));
378
+ }
379
+ bindStep(step, id) {
380
+ this.bound.set(`$s${step}`, id);
381
+ this.created.add(id);
382
+ }
383
+ resolve(step) {
384
+ this.bindRaised();
385
+ return mapStrings(step, (s) => s.replace(ROLE, (token) => this.bound.get(token) ?? token));
386
+ }
387
+ bindRaised() {
388
+ let gates = 0;
389
+ let notices = 0;
390
+ const rows = queryAll(this.db, "SELECT id, kind FROM messages WHERE from_addr = ? ORDER BY rowid", [SYSTEM_ADDRESS]);
391
+ for (const { id, kind } of rows) {
392
+ if (this.created.has(id) || this.seeded.has(id)) continue;
393
+ if (kind === "question") this.bound.set(`$gate${++gates}`, id);
394
+ else if (kind === "notice") this.bound.set(`$notice${++notices}`, id);
395
+ }
396
+ }
397
+ };
398
+ function gateTypesOf(options) {
399
+ return options.gateTypes ?? GATE_TYPES;
400
+ }
401
+ function senderOf(step) {
402
+ const as = asObject(step["as"], "as");
403
+ const canDecide = as["canDecide"];
404
+ if (typeof canDecide !== "boolean") malformed("as.canDecide", "expected a boolean");
405
+ return {
406
+ address: text(as, "address", "as"),
407
+ canDecide
408
+ };
409
+ }
410
+ function inputOf(step) {
411
+ return asObject(step["input"], "input");
412
+ }
413
+ function originOf(step) {
414
+ const origin = step["origin"] ?? "local";
415
+ return ORIGINS.find((o) => o === origin) ?? malformed("origin", `expected one of ${ORIGINS.join(", ")}`);
416
+ }
417
+ function sendResult(r) {
418
+ const result = {
419
+ message: r.message.id,
420
+ downgraded: r.downgraded
421
+ };
422
+ if (r.replayed === true) result["replayed"] = true;
423
+ return result;
424
+ }
425
+ function setAgentStatus(store, value) {
426
+ const where = "world.agentStatus";
427
+ const change = asObject(value, where);
428
+ const address = text(change, "address", where);
429
+ const status = AGENT_STATUSES.find((s) => s === change["status"]) ?? malformed(`${where}.status`, `expected one of ${AGENT_STATUSES.join(", ")}`);
430
+ const agent = store.getAgent(address) ?? malformed(`${where}.address`, "no such agent");
431
+ store.putAgent({
432
+ ...agent,
433
+ status
434
+ });
435
+ }
436
+ function statesOf(step) {
437
+ if (step["states"] === void 0) return void 0;
438
+ return texts(step["states"], "states").map((s) => DELIVERY_STATES.find((d) => d === s) ?? malformed("states", `unknown delivery state ${s}`));
439
+ }
440
+ function createdId(result) {
441
+ if (result === void 0 || !isObject(result)) return null;
442
+ const id = result["message"];
443
+ return typeof id === "string" ? id : null;
444
+ }
445
+ async function runStep(step, ctx, options, record) {
446
+ const custom = options.ops?.[step.op];
447
+ if (custom !== void 0) return custom(step, ctx);
448
+ const { engine, store, host } = ctx;
449
+ switch (step.op) {
450
+ case "send": return sendResult(await engine.send(inputOf(step), senderOf(step), { origin: originOf(step) }));
451
+ case "reply": return sendResult(await engine.reply(text(step, "message", "reply"), inputOf(step), senderOf(step)));
452
+ case "close": return { message: engine.close(text(step, "question", "close"), text(step, "reason", "close")).id };
453
+ case "markRead": {
454
+ const recipient = text(step, "recipient", "markRead");
455
+ const found = store.deliveries({ messageId: text(step, "message", "markRead") }).find((d) => d.recipient === recipient);
456
+ if (found === void 0) throw new MessagingError("not-found", `no delivery to ${recipient}`, "recipient");
457
+ return { state: engine.markRead(found.id).state };
458
+ }
459
+ case "inbox": return { messages: engine.inbox(text(step, "recipient", "inbox"), statesOf(step)).map((x) => x.message.id) };
460
+ case "thread": return { messages: engine.thread(text(step, "thread", "thread")).messages.map((m) => m.id) };
461
+ case "canRead": return { readable: engine.canRead(text(step, "message", "canRead"), senderOf(step)) };
462
+ case "openBlocking": return { messages: engine.openBlocking().map((m) => m.id) };
463
+ case "join":
464
+ engine.join(text(step, "channel", "join"), text(step, "member", "join"));
465
+ return {};
466
+ case "leave": return { removed: engine.leave(text(step, "channel", "leave"), text(step, "member", "leave")) };
467
+ case "deliverHeld": return { deliveries: (await engine.deliverHeld(bare(text(step, "session", "deliverHeld")), text(step, "workItem", "deliverHeld"))).map((d) => ({
468
+ message: d.messageId,
469
+ recipient: d.recipient,
470
+ state: d.state
471
+ })) };
472
+ case "recover": {
473
+ const { retried, reverted, replayed, voided } = await engine.recover();
474
+ return {
475
+ retried,
476
+ reverted,
477
+ replayed,
478
+ voided
479
+ };
480
+ }
481
+ case "parseAddress": return { ...parseAddress(text(step, "input", "parseAddress")) };
482
+ case "validate": {
483
+ const as = senderOf(step);
484
+ const target = optionalText(step, "replyTarget", "validate");
485
+ validateSendInput(inputOf(step), as.address, as.canDecide, target === void 0 ? null : store.getMessage(target), {
486
+ gateTypes: new Set(gateTypesOf(options)),
487
+ origin: originOf(step)
488
+ });
489
+ return {};
490
+ }
491
+ case "render": {
492
+ const id = text(step, "message", "render");
493
+ const message = store.getMessage(id);
494
+ if (message === null) throw new MessagingError("not-found", `no message ${id}`, "message");
495
+ const external = step["external"] === true;
496
+ const rendered = step["form"] === "digest" ? renderDigestLine(message, external) : renderForAgent(message, external);
497
+ record(rendered);
498
+ return { text: rendered };
499
+ }
500
+ case "world": {
501
+ const change = asObject(step["change"], "change");
502
+ const keys = Object.keys(change);
503
+ if (keys.length === 1 && keys[0] === "agentStatus") setAgentStatus(store, change["agentStatus"]);
504
+ else applyWorld(host.world, change);
505
+ return;
506
+ }
507
+ default: throw new UnsupportedOp(`op ${step.op} is not implemented`);
508
+ }
509
+ }
510
+ function observedMessage(m) {
511
+ return {
512
+ id: m.id,
513
+ thread: m.thread,
514
+ replyTo: m.replyTo,
515
+ from: m.from,
516
+ to: m.to,
517
+ kind: m.kind,
518
+ body: m.body,
519
+ refs: m.refs.map((r) => r.at === void 0 ? {
520
+ type: r.type,
521
+ id: r.id
522
+ } : {
523
+ type: r.type,
524
+ id: r.id,
525
+ at: r.at
526
+ }),
527
+ ...m.data === void 0 ? {} : { data: m.data },
528
+ urgent: m.urgent,
529
+ blocking: m.blocking,
530
+ ...m.choices === void 0 ? {} : { choices: m.choices },
531
+ ...m.choice === void 0 ? {} : { choice: m.choice },
532
+ wake: m.wake,
533
+ createdAt: m.createdAt
534
+ };
535
+ }
536
+ function observedDelivery(d) {
537
+ return {
538
+ id: d.id,
539
+ message: d.messageId,
540
+ recipient: d.recipient,
541
+ session: d.runId === null ? null : `run:${d.runId}`,
542
+ via: d.via,
543
+ state: d.state
544
+ };
545
+ }
546
+ function observe(id, db, store, host, steps, render) {
547
+ const messages = queryAll(db, "SELECT id FROM messages ORDER BY rowid").flatMap((row) => {
548
+ const m = store.getMessage(row.id);
549
+ return m === null ? [] : [observedMessage(m)];
550
+ });
551
+ const gateEffects = queryAll(db, "SELECT question_id FROM gate_effects ORDER BY question_id").map((row) => row.question_id);
552
+ return {
553
+ dmp: "observation",
554
+ id,
555
+ steps,
556
+ messages,
557
+ deliveries: store.deliveries({}).map(observedDelivery),
558
+ calls: host.calls,
559
+ gateEffects,
560
+ voided: queryAll(db, "SELECT answer_id FROM voided_answers ORDER BY answer_id").map((row) => row.answer_id),
561
+ channels: store.channels().map((c) => ({
562
+ name: c.name,
563
+ members: store.members(c.name)
564
+ })),
565
+ render
566
+ };
567
+ }
568
+ async function runVector(vector, options = {}) {
569
+ const db = openMessagesDb(":memory:");
570
+ try {
571
+ const store = new SqliteMessageStore(db);
572
+ const host = new ConformanceHost(worldFrom(vector.given));
573
+ seedStore(store, vector.given, host.world);
574
+ const engine = new DeliveryEngine({
575
+ store,
576
+ host,
577
+ limits: vector.given.limits,
578
+ newUlid: createUlidFactory(seededBytes(vector.given.seed ?? 1)),
579
+ gateTypes: gateTypesOf(options)
580
+ });
581
+ engine.subscribe((e) => {
582
+ if (e.type === "message") host.calls.push({
583
+ hook: "published",
584
+ message: e.message.id
585
+ });
586
+ });
587
+ const roles = new RoleBinder(db, vector);
588
+ const steps = [];
589
+ const render = [];
590
+ for (const [i, raw] of vector.when.entries()) {
591
+ const step = roles.resolve(raw);
592
+ try {
593
+ const result = await runStep(step, {
594
+ engine,
595
+ store,
596
+ host
597
+ }, options, (rendered) => render.push({
598
+ step: i + 1,
599
+ text: rendered
600
+ }));
601
+ steps.push(result === void 0 ? { ok: true } : {
602
+ ok: true,
603
+ result
604
+ });
605
+ const created = createdId(result);
606
+ if (CREATES.has(step.op) && created !== null) roles.bindStep(i + 1, created);
607
+ } catch (err) {
608
+ if (!(err instanceof MessagingError)) throw err;
609
+ steps.push({
610
+ ok: false,
611
+ error: err.field === void 0 ? { code: err.code } : {
612
+ code: err.code,
613
+ field: err.field
614
+ }
615
+ });
616
+ }
617
+ }
618
+ return observe(vector.id, db, store, host, steps, render);
619
+ } finally {
620
+ db.close();
621
+ }
622
+ }
623
+
624
+ //#endregion
625
+ //#region src/conformance/serve.ts
626
+ const SEPARATORS = /[\u2028\u2029]/g;
627
+ function out(msg) {
628
+ const text$1 = JSON.stringify(msg).replace(SEPARATORS, (c) => `\\u${c.charCodeAt(0).toString(16)}`);
629
+ process.stdout.write(`${text$1}\n`);
630
+ }
631
+ function serveStdio(hello, run) {
632
+ async function handle(line) {
633
+ const msg = JSON.parse(line);
634
+ if (msg.dmp === "hello") {
635
+ out(hello);
636
+ return;
637
+ }
638
+ if (msg.dmp === "bye") process.exit(0);
639
+ if (msg.dmp !== "run" || msg.vector === void 0) return;
640
+ try {
641
+ out(await run(msg.vector));
642
+ } catch (err) {
643
+ if (!(err instanceof UnsupportedOp)) throw err;
644
+ out({
645
+ dmp: "unsupported",
646
+ id: msg.vector.id,
647
+ reason: err.message
648
+ });
649
+ }
650
+ }
651
+ let queue = Promise.resolve();
652
+ function enqueue(line) {
653
+ queue = queue.then(() => handle(line)).catch((err) => {
654
+ const why = err instanceof Error ? err.stack ?? err.message : String(err);
655
+ process.stderr.write(`${why}\n`, () => process.exit(1));
656
+ });
657
+ }
658
+ let buffered = "";
659
+ process.stdin.setEncoding("utf8");
660
+ process.stdin.on("data", (chunk) => {
661
+ const lines = (buffered + chunk).split("\n");
662
+ buffered = lines.pop() ?? "";
663
+ for (const line of lines) enqueue(line.endsWith("\r") ? line.slice(0, -1) : line);
664
+ });
665
+ process.stdin.on("end", () => {
666
+ if (buffered !== "") enqueue(buffered);
667
+ });
668
+ }
669
+
670
+ //#endregion
671
+ export { applyWorld as a, ConformanceHost as i, REFERENCE_HELLO as n, worldFrom as o, runVector as r, UnsupportedOp as s, serveStdio as t };
672
+ //# sourceMappingURL=serve-CVPZbCpT.js.map