@rayfold/server 0.1.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 (73) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +10 -0
  3. package/README.md +70 -0
  4. package/args.d.ts +15 -0
  5. package/args.js +226 -0
  6. package/args.js.map +1 -0
  7. package/batch.d.ts +54 -0
  8. package/batch.js +483 -0
  9. package/batch.js.map +1 -0
  10. package/bindings.d.ts +37 -0
  11. package/bindings.js +284 -0
  12. package/bindings.js.map +1 -0
  13. package/capability-scope.d.ts +8 -0
  14. package/capability-scope.js +19 -0
  15. package/capability-scope.js.map +1 -0
  16. package/capability.d.ts +56 -0
  17. package/capability.js +112 -0
  18. package/capability.js.map +1 -0
  19. package/context.d.ts +74 -0
  20. package/context.js +109 -0
  21. package/context.js.map +1 -0
  22. package/core.d.ts +18 -0
  23. package/core.js +18 -0
  24. package/core.js.map +1 -0
  25. package/cost.d.ts +15 -0
  26. package/cost.js +110 -0
  27. package/cost.js.map +1 -0
  28. package/executor.d.ts +89 -0
  29. package/executor.js +695 -0
  30. package/executor.js.map +1 -0
  31. package/guard.d.ts +33 -0
  32. package/guard.js +65 -0
  33. package/guard.js.map +1 -0
  34. package/http.d.ts +33 -0
  35. package/http.js +379 -0
  36. package/http.js.map +1 -0
  37. package/index.d.ts +8 -0
  38. package/index.js +9 -0
  39. package/index.js.map +1 -0
  40. package/instrumentation.d.ts +36 -0
  41. package/instrumentation.js +2 -0
  42. package/instrumentation.js.map +1 -0
  43. package/live.d.ts +37 -0
  44. package/live.js +240 -0
  45. package/live.js.map +1 -0
  46. package/mcp.d.ts +55 -0
  47. package/mcp.js +314 -0
  48. package/mcp.js.map +1 -0
  49. package/openapi.d.ts +11 -0
  50. package/openapi.js +124 -0
  51. package/openapi.js.map +1 -0
  52. package/package.json +53 -0
  53. package/policy.d.ts +17 -0
  54. package/policy.js +64 -0
  55. package/policy.js.map +1 -0
  56. package/protocol.d.ts +139 -0
  57. package/protocol.js +98 -0
  58. package/protocol.js.map +1 -0
  59. package/server.d.ts +58 -0
  60. package/server.js +79 -0
  61. package/server.js.map +1 -0
  62. package/usage.d.ts +39 -0
  63. package/usage.js +44 -0
  64. package/usage.js.map +1 -0
  65. package/views.d.ts +33 -0
  66. package/views.js +108 -0
  67. package/views.js.map +1 -0
  68. package/wiring.d.ts +16 -0
  69. package/wiring.js +57 -0
  70. package/wiring.js.map +1 -0
  71. package/ws.d.ts +21 -0
  72. package/ws.js +209 -0
  73. package/ws.js.map +1 -0
package/batch.js ADDED
@@ -0,0 +1,483 @@
1
+ import { annotation, baseName, hashJson, isShapeId } from "@rayfold/schema";
2
+ import { coerceArgs, collectRefs, getPath, resolveRefs } from "./args.js";
3
+ import { EventBus } from "./context.js";
4
+ import { estimateCost } from "./cost.js";
5
+ import { compactQueryFrame } from "./executor.js";
6
+ import { RayfoldError, VersionConflict, toWireError } from "./protocol.js";
7
+ import { resolveRequestShape } from "./views.js";
8
+ import { capabilityAllows } from "./capability-scope.js";
9
+ import { ChangeBus, changeFromPatch, diffResults, foldFrames, readSetOf } from "./live.js";
10
+ /** Unbounded async queue of frames; `close()` ends iteration once drained. */
11
+ export class FrameSink {
12
+ queue = [];
13
+ waiting = null;
14
+ closed = false;
15
+ frames = [];
16
+ push(f) {
17
+ if (this.closed)
18
+ return;
19
+ this.frames.push(f);
20
+ if (this.waiting) {
21
+ const w = this.waiting;
22
+ this.waiting = null;
23
+ w({ value: f, done: false });
24
+ }
25
+ else
26
+ this.queue.push(f);
27
+ }
28
+ close() {
29
+ if (this.closed)
30
+ return;
31
+ this.closed = true;
32
+ if (this.waiting) {
33
+ const w = this.waiting;
34
+ this.waiting = null;
35
+ w({ value: undefined, done: true });
36
+ }
37
+ }
38
+ [Symbol.asyncIterator]() {
39
+ return {
40
+ next: () => {
41
+ if (this.queue.length)
42
+ return Promise.resolve({ value: this.queue.shift(), done: false });
43
+ if (this.closed)
44
+ return Promise.resolve({ value: undefined, done: true });
45
+ return new Promise((res) => (this.waiting = res));
46
+ },
47
+ return: () => {
48
+ this.close();
49
+ return Promise.resolve({ value: undefined, done: true });
50
+ },
51
+ };
52
+ }
53
+ }
54
+ export function executeBatch(rt, envelope, opts = {}) {
55
+ const sink = new FrameSink();
56
+ void run(rt, envelope, opts, sink).catch((e) => {
57
+ sink.push({ error: toWireError(e), fin: true });
58
+ sink.close();
59
+ });
60
+ return sink;
61
+ }
62
+ async function run(rt, envelope, opts, sink) {
63
+ const hook = rt.instrumentation?.batch;
64
+ if (!hook) {
65
+ await runBatch(rt, envelope, opts, sink);
66
+ return;
67
+ }
68
+ const meta = envelope && typeof envelope === "object" && envelope.meta && typeof envelope.meta === "object" ? envelope.meta : {};
69
+ await hook({ ops: Array.isArray(envelope?.ops) ? envelope.ops.length : 0, meta }, () => runBatch(rt, envelope, opts, sink));
70
+ }
71
+ async function runBatch(rt, envelope, opts, sink) {
72
+ const batchError = (e) => {
73
+ sink.push({ error: e, fin: true });
74
+ sink.close();
75
+ return { error: e };
76
+ };
77
+ // ---- envelope validation (batch-level failures, spec 05 §6)
78
+ const v = validateEnvelope(rt, envelope);
79
+ if (v)
80
+ return batchError(v);
81
+ // ---- plan every op up front: shape, cost, deps
82
+ const planned = [];
83
+ let total = 0;
84
+ for (const req of envelope.ops) {
85
+ const op = rt.ir.ops[req.op];
86
+ const deps = [...collectRefs(req.args ?? {})].filter((d) => Number.isFinite(d));
87
+ const p = { req, op, shape: { items: [] }, explicit: req.shape !== undefined, deps, cost: 0 };
88
+ try {
89
+ p.shape = resolveRequestShape(rt.ir, req.shape, op.returns, rt.registry, rt.options.trustedShapes);
90
+ // Estimate on the arguments the op would run with. Arguments that fail validation mean the op never runs, so it
91
+ // costs nothing (and reports its error when its turn comes). Only args with $ref, known after earlier ops, are
92
+ // estimated from the raw request, where any page size the model cannot trust counts as the largest page.
93
+ const raw = (req.args ?? {});
94
+ const planArgs = deps.length ? raw : coerceArgs(rt.ir, op.args, raw, `${op.name}()`);
95
+ const est = estimateCost(rt.ir, op, planArgs, p.shape, req.vars ?? {});
96
+ if (est.depth > rt.options.maxDepth)
97
+ throw new RayfoldError("resource_exhausted", `Shape depth ${est.depth} exceeds ${rt.options.maxDepth}`);
98
+ if (est.fields > rt.options.maxFields)
99
+ throw new RayfoldError("resource_exhausted", `Shape selects ${est.fields} fields, max ${rt.options.maxFields}`);
100
+ p.cost = est.cost;
101
+ total += est.cost;
102
+ // Only a shape that passed every check is remembered, so rejected shapes cannot fill the registry.
103
+ if (req.shape !== undefined && !isShapeId(req.shape))
104
+ rt.registry.register(p.shape);
105
+ }
106
+ catch (e) {
107
+ p.failure = e instanceof RayfoldError ? e : new RayfoldError("internal", "Planning failed");
108
+ }
109
+ planned.push(p);
110
+ }
111
+ if (total > rt.options.budget) {
112
+ return batchError({ code: "resource_exhausted", message: `Batch cost ${total} exceeds budget ${rt.options.budget}`, data: { cost: total, budget: rt.options.budget } });
113
+ }
114
+ // ---- deadline / cancellation
115
+ const batchAbort = new AbortController();
116
+ const onOuterAbort = () => batchAbort.abort(new RayfoldError("canceled", "Canceled"));
117
+ opts.signal?.addEventListener("abort", onOuterAbort, { once: true });
118
+ if (opts.signal?.aborted)
119
+ onOuterAbort();
120
+ let timer;
121
+ if (envelope.meta?.deadline !== undefined) {
122
+ timer = setTimeout(() => batchAbort.abort(new RayfoldError("deadline_exceeded", "Batch deadline exceeded")), envelope.meta.deadline);
123
+ }
124
+ // One memo for the whole batch: a field loaded for an entity by one op is not loaded again by another.
125
+ const scoped = { ...opts, batchState: new Map() };
126
+ const results = new Map();
127
+ const status = new Map();
128
+ const done = new Map();
129
+ const resolvers = new Map();
130
+ for (const p of planned)
131
+ done.set(p.req.id, new Promise((res) => resolvers.set(p.req.id, res)));
132
+ // Commands run one at a time in ascending id order (spec 03 §3); everything else runs as soon as its refs resolve.
133
+ let prevCommand = Promise.resolve();
134
+ const viewerScope = hashJson(opts.viewer ?? null);
135
+ const tasks = [...planned]
136
+ .sort((a, b) => a.req.id - b.req.id)
137
+ .map((p) => {
138
+ const gate = p.op.kind === "command" ? prevCommand : Promise.resolve();
139
+ const task = (async () => {
140
+ await Promise.all(p.deps.map((d) => done.get(d) ?? Promise.resolve()));
141
+ await gate;
142
+ await runOne(rt, p, envelope.meta ?? {}, scoped, batchAbort.signal, viewerScope, results, status, sink);
143
+ resolvers.get(p.req.id)();
144
+ })();
145
+ if (p.op.kind === "command")
146
+ prevCommand = task;
147
+ return task;
148
+ });
149
+ await Promise.all(tasks);
150
+ if (timer)
151
+ clearTimeout(timer);
152
+ opts.signal?.removeEventListener("abort", onOuterAbort);
153
+ sink.close();
154
+ return {};
155
+ }
156
+ function validateEnvelope(rt, envelope) {
157
+ const bad = (message) => ({ code: "invalid_argument", message });
158
+ if (!envelope || typeof envelope !== "object" || !Array.isArray(envelope.ops))
159
+ return bad("Body must be { ops: [...] }");
160
+ if (envelope.ops.length === 0)
161
+ return bad("ops must not be empty");
162
+ if (envelope.ops.length > rt.options.maxOps)
163
+ return { code: "resource_exhausted", message: `At most ${rt.options.maxOps} ops per batch` };
164
+ if (envelope.meta?.deadline !== undefined && !validDeadline(envelope.meta.deadline))
165
+ return bad(`meta.deadline: expected whole milliseconds from 0 to ${MAX_DEADLINE_MS}`);
166
+ const ids = new Set();
167
+ for (const [i, req] of envelope.ops.entries()) {
168
+ if (!req || typeof req !== "object")
169
+ return bad(`ops[${i}]: expected an object`);
170
+ if (!Number.isInteger(req.id) || req.id <= 0)
171
+ return bad(`ops[${i}].id: expected a positive integer`);
172
+ if (ids.has(req.id))
173
+ return bad(`ops[${i}].id: duplicate id ${req.id}`);
174
+ ids.add(req.id);
175
+ if (typeof req.op !== "string" || !rt.ir.ops[req.op])
176
+ return bad(`ops[${i}].op: unknown operation ${JSON.stringify(req.op)}`);
177
+ if (req.args !== undefined && (req.args === null || typeof req.args !== "object" || Array.isArray(req.args)))
178
+ return bad(`ops[${i}].args: expected an object`);
179
+ if (req.shape !== undefined && typeof req.shape !== "string")
180
+ return bad(`ops[${i}].shape: expected a string`);
181
+ // Checked before anything walks the values recursively.
182
+ if (tooDeep(req.args, MAX_NESTING))
183
+ return bad(`ops[${i}].args: nested deeper than ${MAX_NESTING} levels`);
184
+ if (tooDeep(req.vars, MAX_NESTING))
185
+ return bad(`ops[${i}].vars: nested deeper than ${MAX_NESTING} levels`);
186
+ for (const d of collectRefs(req.args ?? {})) {
187
+ if (!Number.isInteger(d) || d <= 0)
188
+ return bad(`ops[${i}].args: bad $ref`);
189
+ if (d >= req.id)
190
+ return bad(`ops[${i}].args: $ref to op ${d} must point to an earlier op`);
191
+ if (!ids.has(d))
192
+ return bad(`ops[${i}].args: $ref to unknown op ${d}`);
193
+ }
194
+ if (req.live && rt.ir.ops[req.op].kind !== "query")
195
+ return bad(`ops[${i}].live: only queries can be live`);
196
+ }
197
+ return null;
198
+ }
199
+ async function runOne(rt, p, meta, opts, batchSignal, viewerScope, results, status, sink) {
200
+ const hook = rt.instrumentation?.op;
201
+ const run = () => runOp(rt, p, meta, opts, batchSignal, viewerScope, results, status, sink);
202
+ if (!hook) {
203
+ await run();
204
+ return;
205
+ }
206
+ await hook({ id: p.req.id, name: p.op.name, kind: p.op.kind, cost: p.cost }, async () => {
207
+ const error = await run();
208
+ return error ? { error } : {};
209
+ });
210
+ }
211
+ /** Runs one op and sends its frames; resolves to the error it failed with, if it failed. */
212
+ async function runOp(rt, p, meta, opts, batchSignal, viewerScope, results, status, sink) {
213
+ const id = p.req.id;
214
+ const started = rt.options.now();
215
+ const fail = (e) => {
216
+ let w = toWireError(e);
217
+ if (batchSignal.aborted && batchSignal.reason instanceof RayfoldError)
218
+ w = batchSignal.reason.toWire();
219
+ sink.push({ id, error: w, fin: true });
220
+ status.set(id, "failed");
221
+ return w;
222
+ };
223
+ if (p.failure)
224
+ return fail(p.failure);
225
+ for (const d of p.deps) {
226
+ if (status.get(d) !== "ok") {
227
+ return fail(new RayfoldError("failed_precondition", `Depends on op ${d}, which failed`, { type: "DependencyFailed", data: { op: d } }));
228
+ }
229
+ }
230
+ if (batchSignal.aborted)
231
+ return fail(batchSignal.reason);
232
+ if (p.req.deadline !== undefined && !validDeadline(p.req.deadline))
233
+ return fail(new RayfoldError("invalid_argument", `deadline: expected whole milliseconds from 0 to ${MAX_DEADLINE_MS}`));
234
+ const opAbort = new AbortController();
235
+ const relay = () => opAbort.abort(batchSignal.reason);
236
+ batchSignal.addEventListener("abort", relay, { once: true });
237
+ let opTimer;
238
+ if (p.req.deadline !== undefined)
239
+ opTimer = setTimeout(() => opAbort.abort(new RayfoldError("deadline_exceeded", "Deadline exceeded")), p.req.deadline);
240
+ const stamp = (f) => {
241
+ if (rt.options.timing && "meta" in f && f.meta)
242
+ f.meta.ms = rt.options.now() - started;
243
+ return f;
244
+ };
245
+ try {
246
+ const rawArgs = resolveRefs(p.req.args ?? {}, (opId, path) => getPath(results.get(opId), path), `ops.${id}.args`);
247
+ const args = coerceArgs(rt.ir, p.op.args, rawArgs, `${p.op.name}()`);
248
+ const ctx = {
249
+ viewer: opts.viewer ?? null,
250
+ signal: opAbort.signal,
251
+ simulate: !!p.req.simulate,
252
+ events: rt.events,
253
+ meta,
254
+ opId: id,
255
+ opName: p.op.name,
256
+ policy: {},
257
+ shape: p.shape,
258
+ state: new Map(),
259
+ batch: opts.batchState ?? new Map(),
260
+ now: rt.options.now,
261
+ checkVersion(key, actual, current) {
262
+ const want = ctx.ifVersion;
263
+ if (want === undefined)
264
+ return;
265
+ if (String(actual) !== String(want))
266
+ throw new VersionConflict(key, want, actual, current);
267
+ },
268
+ };
269
+ // A capability token may call only the operations it names (spec 06 section 6); any other viewer is left to
270
+ // the schema's own policies.
271
+ if (!capabilityAllows(opts.viewer, p.op.name)) {
272
+ throw new RayfoldError("permission_denied", `This capability does not allow ${p.op.name}()`);
273
+ }
274
+ rt.usage?.record({ op: p.op.name, path: "", client: String(meta.client ?? "") }, rt.options.now());
275
+ if (p.req.ifVersion !== undefined)
276
+ ctx.ifVersion = p.req.ifVersion;
277
+ if (p.req.vars)
278
+ ctx.vars = p.req.vars;
279
+ if (p.req.compact)
280
+ ctx.compact = true;
281
+ switch (p.op.kind) {
282
+ case "query": {
283
+ if (p.req.live) {
284
+ await runLive(rt, p, args, ctx, sink, stamp, results);
285
+ break;
286
+ }
287
+ const data = await rt.executor.runQuery(p.op, args, p.shape, p.explicit, p.cost, ctx, (f) => sink.push(stamp(f)));
288
+ results.set(id, data);
289
+ break;
290
+ }
291
+ case "stream":
292
+ await rt.executor.runStream(p.op, args, p.shape, p.explicit, ctx, (f) => sink.push(stamp(f)));
293
+ break;
294
+ case "command": {
295
+ const idem = annotation(p.op, "idempotent");
296
+ const optedOut = idem !== undefined && idem.args["value"] === false;
297
+ const key = p.req.key;
298
+ if (!optedOut && !(opts.keyOptional && key === undefined) && (typeof key !== "string" || key.length < 16 || key.length > 128)) {
299
+ throw new RayfoldError("invalid_argument", `${p.op.name}(): commands require an idempotency key of 16-128 characters`);
300
+ }
301
+ rt.executor.authorize(p.op, args, ctx); // the write policy holds before anything is replayed
302
+ if (ctx.simulate && !annotation(p.op, "simulate"))
303
+ throw new RayfoldError("failed_precondition", `${p.op.name}() does not support dry runs`);
304
+ if (key && !ctx.simulate && (opts.viewer === null || opts.viewer === undefined)) {
305
+ // Anonymous callers cannot be told apart, so they would share one replay scope (spec 12 section 4).
306
+ throw new RayfoldError("unauthenticated", `${p.op.name}(): idempotency keys need an identified caller`);
307
+ }
308
+ // Bound to the operation as well as the arguments: a key can never replay another command's result.
309
+ const argsHash = hashJson({ op: p.op.name, args });
310
+ const claim = key && !ctx.simulate ? `${viewerScope}\u0000${key}` : undefined;
311
+ let release;
312
+ if (claim) {
313
+ // One execution per key even when retries arrive together: later callers wait for the first, then replay.
314
+ for (let running = rt.inflight.get(claim); running; running = rt.inflight.get(claim))
315
+ await running;
316
+ rt.inflight.set(claim, new Promise((r) => (release = r)));
317
+ }
318
+ try {
319
+ if (claim) {
320
+ const prior = await rt.idempotency.get(viewerScope, key);
321
+ if (prior) {
322
+ if (prior.argsHash !== argsHash)
323
+ throw new RayfoldError("already_exists", `Idempotency key ${key} was used for another operation or other arguments`);
324
+ const stored = p.req.compact && prior.compactFrame !== undefined ? prior.compactFrame : prior.frame;
325
+ const replay = structuredClone(stored);
326
+ replay.meta = { ...(replay.meta ?? {}), replay: true };
327
+ replay.id = id; // a retry may use another op id; the answer belongs to this op
328
+ sink.push(stamp(replay));
329
+ results.set(id, replay.ok);
330
+ break;
331
+ }
332
+ }
333
+ const { result, full, compact, patch } = await rt.executor.runCommand(p.op, args, p.shape, p.explicit, p.cost, ctx, (f) => sink.push(stamp(f)));
334
+ results.set(id, result);
335
+ if (claim)
336
+ await rt.idempotency.put(viewerScope, key, { argsHash, frame: structuredClone(full), compactFrame: structuredClone(compact), at: rt.options.now() });
337
+ if (!ctx.simulate)
338
+ rt.changes.publish(changeFromPatch(patch));
339
+ }
340
+ finally {
341
+ if (claim) {
342
+ rt.inflight.delete(claim);
343
+ release?.();
344
+ }
345
+ }
346
+ break;
347
+ }
348
+ }
349
+ status.set(id, "ok");
350
+ }
351
+ catch (e) {
352
+ return fail(e);
353
+ }
354
+ finally {
355
+ if (opTimer)
356
+ clearTimeout(opTimer);
357
+ batchSignal.removeEventListener("abort", relay);
358
+ }
359
+ return undefined;
360
+ }
361
+ /**
362
+ * Live query loop: first result, then re-run on intersecting changes until the op is aborted.
363
+ * Frames: data (no fin) -> [patch | data]* -> error(canceled)/fin.
364
+ */
365
+ async function runLive(rt, p, args, ctx, sink, stamp, results) {
366
+ const id = p.req.id;
367
+ // Read sets and diffs need `$type`, so the query always runs in full form; compaction happens on the way out.
368
+ const runCtx = ctx.compact ? { ...ctx, compact: false } : ctx;
369
+ const wire = (f) => (ctx.compact ? compactQueryFrame(f) : f);
370
+ const collect = async () => {
371
+ const frames = [];
372
+ await rt.executor.runQuery(p.op, args, p.shape, p.explicit, p.cost, runCtx, (f) => frames.push(f));
373
+ return { frames, data: foldFrames(frames) };
374
+ };
375
+ const first = await collect();
376
+ let current = first.data;
377
+ let readSet = readSetOf(current);
378
+ // Entity types reachable from the result type: a new entity of such a type may change membership.
379
+ const typeSet = reachableEntityTypes(rt.ir, p.op.returns);
380
+ for (const f0 of first.frames) {
381
+ const f = wire(f0);
382
+ if ("fin" in f && f.fin && !("data" in f) && !("error" in f))
383
+ continue; // keep the op open
384
+ if ("data" in f && !("at" in f)) {
385
+ const { fin: _fin, ...rest } = f;
386
+ sink.push(stamp(rest));
387
+ }
388
+ else
389
+ sink.push(stamp(f));
390
+ }
391
+ results.set(id, current);
392
+ let dirty = false;
393
+ let running = false;
394
+ let wake = null;
395
+ const off = rt.changes.subscribe((c) => {
396
+ const hit = c.ops.has(p.op.name) || [...c.keys].some((k) => readSet.has(k) || typeSet.has(k.slice(0, k.indexOf(":"))));
397
+ if (!hit)
398
+ return;
399
+ dirty = true;
400
+ wake?.();
401
+ });
402
+ const onAbort = () => wake?.();
403
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
404
+ try {
405
+ while (!ctx.signal.aborted) {
406
+ if (!dirty)
407
+ await new Promise((res) => (wake = res));
408
+ wake = null;
409
+ if (ctx.signal.aborted)
410
+ break;
411
+ if (!dirty || running)
412
+ continue;
413
+ dirty = false;
414
+ running = true;
415
+ try {
416
+ const next = await collect();
417
+ const d = diffResults(current, next.data);
418
+ current = next.data;
419
+ readSet = readSetOf(current);
420
+ results.set(id, current);
421
+ if (d && "patch" in d)
422
+ sink.push(stamp({ id, patch: d.patch }));
423
+ else if (d)
424
+ sink.push(stamp(wire({ id, data: d.data, meta: { cost: p.cost } })));
425
+ }
426
+ finally {
427
+ running = false;
428
+ }
429
+ }
430
+ }
431
+ finally {
432
+ off();
433
+ ctx.signal.removeEventListener("abort", onAbort);
434
+ }
435
+ throw ctx.signal.reason instanceof RayfoldError ? ctx.signal.reason : new RayfoldError("canceled", "Canceled");
436
+ }
437
+ function reachableEntityTypes(ir, root, maxDepth = 4) {
438
+ const out = new Set();
439
+ const visit = (t, depth) => {
440
+ const name = baseName(t);
441
+ const def = ir.types[name];
442
+ if (!def || depth > maxDepth)
443
+ return;
444
+ if (def.kind === "entity") {
445
+ if (out.has(name))
446
+ return;
447
+ out.add(name);
448
+ }
449
+ if ("fields" in def)
450
+ for (const f of def.fields)
451
+ visit(f.type, depth + 1);
452
+ if (def.kind === "union")
453
+ for (const m of def.members)
454
+ visit({ kind: "named", name: m, nullable: false }, depth + 1);
455
+ if (t.kind === "named" && t.args)
456
+ for (const a of t.args)
457
+ visit(a, depth);
458
+ };
459
+ visit(root, 0);
460
+ return out;
461
+ }
462
+ /** Nesting limit for args and vars, checked before anything walks them recursively. */
463
+ const MAX_NESTING = 64;
464
+ /** Longest deadline a client may ask for. */
465
+ const MAX_DEADLINE_MS = 600_000;
466
+ function validDeadline(v) {
467
+ return typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= MAX_DEADLINE_MS;
468
+ }
469
+ /** Iterative, so hostile nesting cannot overflow the stack while it is being measured. */
470
+ function tooDeep(value, max) {
471
+ const stack = [[value, 0]];
472
+ while (stack.length) {
473
+ const [v, d] = stack.pop();
474
+ if (v === null || typeof v !== "object")
475
+ continue;
476
+ if (d >= max)
477
+ return true;
478
+ for (const x of Array.isArray(v) ? v : Object.values(v))
479
+ stack.push([x, d + 1]);
480
+ }
481
+ return false;
482
+ }
483
+ //# sourceMappingURL=batch.js.map
package/batch.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.js","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"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","sourcesContent":["/** Batch scheduling: dependency waves, serial commands, deadlines, idempotency. Spec: spec/03. */\nimport type { Instrumentation, Outcome } from \"./instrumentation.ts\";\nimport { annotation, baseName, hashJson, isShapeId, type OpDef, type RayfoldSchemaIR, type Shape, type TypeRef } from \"@rayfold/schema\";\nimport { coerceArgs, collectRefs, getPath, resolveRefs } from \"./args.ts\";\nimport { EventBus, type IdempotencyStore, type RayfoldContext } from \"./context.ts\";\nimport { estimateCost } from \"./cost.ts\";\nimport { compactQueryFrame, type Executor } from \"./executor.ts\";\nimport { RayfoldError, VersionConflict, toWireError, type Frame, type RequestEnvelope, type RequestMeta, type RequestOp, type WireError } from \"./protocol.ts\";\nimport { resolveRequestShape, type ShapeRegistry } from \"./views.ts\";\nimport type { UsageSink } from \"./usage.ts\";\nimport { capabilityAllows } from \"./capability-scope.ts\";\nimport { ChangeBus, changeFromPatch, diffResults, foldFrames, readSetOf } from \"./live.ts\";\n\nexport interface BatchOptions {\n trustedShapes: boolean;\n budget: number;\n maxOps: number;\n maxDepth: number;\n maxFields: number;\n timing: boolean;\n now: () => number;\n}\n\nexport interface BatchRuntime {\n ir: RayfoldSchemaIR;\n executor: Executor;\n registry: ShapeRegistry;\n idempotency: IdempotencyStore;\n events: EventBus;\n changes: ChangeBus;\n options: BatchOptions;\n instrumentation?: Instrumentation;\n /** Records which operations each client called (spec 11). */\n usage?: UsageSink;\n /** Commands running right now, by idempotency scope and key: a concurrent retry waits for the first, then replays. */\n inflight: Map<string, Promise<void>>;\n}\n\nexport interface ExecuteOptions {\n viewer?: unknown;\n signal?: AbortSignal;\n /**\n * Set by transports whose HTTP method is itself idempotent (PUT, PATCH, DELETE bindings): commands may run\n * without an idempotency key. Never settable from the wire envelope.\n */\n keyOptional?: boolean;\n /** @internal Set by the batch itself: the memo every op of this request shares. Never read from the wire. */\n batchState?: Map<string, unknown>;\n}\n\n/** Unbounded async queue of frames; `close()` ends iteration once drained. */\nexport class FrameSink implements AsyncIterable<Frame> {\n private readonly queue: Frame[] = [];\n private waiting: ((r: IteratorResult<Frame>) => void) | null = null;\n private closed = false;\n readonly frames: Frame[] = [];\n\n push(f: Frame): void {\n if (this.closed) return;\n this.frames.push(f);\n if (this.waiting) {\n const w = this.waiting;\n this.waiting = null;\n w({ value: f, done: false });\n } else this.queue.push(f);\n }\n close(): void {\n if (this.closed) return;\n this.closed = true;\n if (this.waiting) {\n const w = this.waiting;\n this.waiting = null;\n w({ value: undefined as never, done: true });\n }\n }\n [Symbol.asyncIterator](): AsyncIterator<Frame> {\n return {\n next: (): Promise<IteratorResult<Frame>> => {\n if (this.queue.length) return Promise.resolve({ value: this.queue.shift()!, done: false });\n if (this.closed) return Promise.resolve({ value: undefined as never, done: true });\n return new Promise((res) => (this.waiting = res));\n },\n return: (): Promise<IteratorResult<Frame>> => {\n this.close();\n return Promise.resolve({ value: undefined as never, done: true });\n },\n };\n }\n}\n\ninterface Planned {\n req: RequestOp;\n op: OpDef;\n shape: Shape;\n explicit: boolean;\n deps: number[];\n cost: number;\n /** pre-execution failure, reported when the op's turn comes */\n failure?: RayfoldError;\n}\n\nexport function executeBatch(rt: BatchRuntime, envelope: RequestEnvelope, opts: ExecuteOptions = {}): AsyncIterable<Frame> {\n const sink = new FrameSink();\n void run(rt, envelope, opts, sink).catch((e) => {\n sink.push({ error: toWireError(e), fin: true });\n sink.close();\n });\n return sink;\n}\n\nasync function run(rt: BatchRuntime, envelope: RequestEnvelope, opts: ExecuteOptions, sink: FrameSink): Promise<void> {\n const hook = rt.instrumentation?.batch;\n if (!hook) {\n await runBatch(rt, envelope, opts, sink);\n return;\n }\n const meta = envelope && typeof envelope === \"object\" && envelope.meta && typeof envelope.meta === \"object\" ? envelope.meta : {};\n await hook({ ops: Array.isArray(envelope?.ops) ? envelope.ops.length : 0, meta }, () => runBatch(rt, envelope, opts, sink));\n}\n\nasync function runBatch(rt: BatchRuntime, envelope: RequestEnvelope, opts: ExecuteOptions, sink: FrameSink): Promise<Outcome> {\n const batchError = (e: WireError): Outcome => {\n sink.push({ error: e, fin: true });\n sink.close();\n return { error: e };\n };\n\n // ---- envelope validation (batch-level failures, spec 05 §6)\n const v = validateEnvelope(rt, envelope);\n if (v) return batchError(v);\n\n // ---- plan every op up front: shape, cost, deps\n const planned: Planned[] = [];\n let total = 0;\n for (const req of envelope.ops) {\n const op = rt.ir.ops[req.op]!;\n const deps = [...collectRefs(req.args ?? {})].filter((d) => Number.isFinite(d));\n const p: Planned = { req, op, shape: { items: [] }, explicit: req.shape !== undefined, deps, cost: 0 };\n try {\n p.shape = resolveRequestShape(rt.ir, req.shape, op.returns, rt.registry, rt.options.trustedShapes);\n // Estimate on the arguments the op would run with. Arguments that fail validation mean the op never runs, so it\n // costs nothing (and reports its error when its turn comes). Only args with $ref, known after earlier ops, are\n // estimated from the raw request, where any page size the model cannot trust counts as the largest page.\n const raw = (req.args ?? {}) as Record<string, unknown>;\n const planArgs = deps.length ? raw : coerceArgs(rt.ir, op.args, raw, `${op.name}()`);\n const est = estimateCost(rt.ir, op, planArgs, p.shape, req.vars ?? {});\n if (est.depth > rt.options.maxDepth) throw new RayfoldError(\"resource_exhausted\", `Shape depth ${est.depth} exceeds ${rt.options.maxDepth}`);\n if (est.fields > rt.options.maxFields) throw new RayfoldError(\"resource_exhausted\", `Shape selects ${est.fields} fields, max ${rt.options.maxFields}`);\n p.cost = est.cost;\n total += est.cost;\n // Only a shape that passed every check is remembered, so rejected shapes cannot fill the registry.\n if (req.shape !== undefined && !isShapeId(req.shape)) rt.registry.register(p.shape);\n } catch (e) {\n p.failure = e instanceof RayfoldError ? e : new RayfoldError(\"internal\", \"Planning failed\");\n }\n planned.push(p);\n }\n if (total > rt.options.budget) {\n return batchError({ code: \"resource_exhausted\", message: `Batch cost ${total} exceeds budget ${rt.options.budget}`, data: { cost: total, budget: rt.options.budget } });\n }\n\n // ---- deadline / cancellation\n const batchAbort = new AbortController();\n const onOuterAbort = () => batchAbort.abort(new RayfoldError(\"canceled\", \"Canceled\"));\n opts.signal?.addEventListener(\"abort\", onOuterAbort, { once: true });\n if (opts.signal?.aborted) onOuterAbort();\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (envelope.meta?.deadline !== undefined) {\n timer = setTimeout(() => batchAbort.abort(new RayfoldError(\"deadline_exceeded\", \"Batch deadline exceeded\")), envelope.meta.deadline);\n }\n\n // One memo for the whole batch: a field loaded for an entity by one op is not loaded again by another.\n const scoped: ExecuteOptions = { ...opts, batchState: new Map<string, unknown>() };\n const results = new Map<number, unknown>();\n const status = new Map<number, \"ok\" | \"failed\">();\n const done = new Map<number, Promise<void>>();\n const resolvers = new Map<number, () => void>();\n for (const p of planned) done.set(p.req.id, new Promise<void>((res) => resolvers.set(p.req.id, res)));\n\n // Commands run one at a time in ascending id order (spec 03 §3); everything else runs as soon as its refs resolve.\n let prevCommand: Promise<void> = Promise.resolve();\n const viewerScope = hashJson(opts.viewer ?? null);\n\n const tasks = [...planned]\n .sort((a, b) => a.req.id - b.req.id)\n .map((p) => {\n const gate = p.op.kind === \"command\" ? prevCommand : Promise.resolve();\n const task = (async () => {\n await Promise.all(p.deps.map((d) => done.get(d) ?? Promise.resolve()));\n await gate;\n await runOne(rt, p, envelope.meta ?? {}, scoped, batchAbort.signal, viewerScope, results, status, sink);\n resolvers.get(p.req.id)!();\n })();\n if (p.op.kind === \"command\") prevCommand = task;\n return task;\n });\n await Promise.all(tasks);\n if (timer) clearTimeout(timer);\n opts.signal?.removeEventListener(\"abort\", onOuterAbort);\n sink.close();\n return {};\n}\n\nfunction validateEnvelope(rt: BatchRuntime, envelope: RequestEnvelope): WireError | null {\n const bad = (message: string): WireError => ({ code: \"invalid_argument\", message });\n if (!envelope || typeof envelope !== \"object\" || !Array.isArray(envelope.ops)) return bad(\"Body must be { ops: [...] }\");\n if (envelope.ops.length === 0) return bad(\"ops must not be empty\");\n if (envelope.ops.length > rt.options.maxOps) return { code: \"resource_exhausted\", message: `At most ${rt.options.maxOps} ops per batch` };\n if (envelope.meta?.deadline !== undefined && !validDeadline(envelope.meta.deadline)) return bad(`meta.deadline: expected whole milliseconds from 0 to ${MAX_DEADLINE_MS}`);\n const ids = new Set<number>();\n for (const [i, req] of envelope.ops.entries()) {\n if (!req || typeof req !== \"object\") return bad(`ops[${i}]: expected an object`);\n if (!Number.isInteger(req.id) || req.id <= 0) return bad(`ops[${i}].id: expected a positive integer`);\n if (ids.has(req.id)) return bad(`ops[${i}].id: duplicate id ${req.id}`);\n ids.add(req.id);\n if (typeof req.op !== \"string\" || !rt.ir.ops[req.op]) return bad(`ops[${i}].op: unknown operation ${JSON.stringify(req.op)}`);\n if (req.args !== undefined && (req.args === null || typeof req.args !== \"object\" || Array.isArray(req.args))) return bad(`ops[${i}].args: expected an object`);\n if (req.shape !== undefined && typeof req.shape !== \"string\") return bad(`ops[${i}].shape: expected a string`);\n // Checked before anything walks the values recursively.\n if (tooDeep(req.args, MAX_NESTING)) return bad(`ops[${i}].args: nested deeper than ${MAX_NESTING} levels`);\n if (tooDeep(req.vars, MAX_NESTING)) return bad(`ops[${i}].vars: nested deeper than ${MAX_NESTING} levels`);\n for (const d of collectRefs(req.args ?? {})) {\n if (!Number.isInteger(d) || d <= 0) return bad(`ops[${i}].args: bad $ref`);\n if (d >= req.id) return bad(`ops[${i}].args: $ref to op ${d} must point to an earlier op`);\n if (!ids.has(d)) return bad(`ops[${i}].args: $ref to unknown op ${d}`);\n }\n if (req.live && rt.ir.ops[req.op]!.kind !== \"query\") return bad(`ops[${i}].live: only queries can be live`);\n }\n return null;\n}\n\nasync function runOne(\n rt: BatchRuntime,\n p: Planned,\n meta: RequestMeta,\n opts: ExecuteOptions,\n batchSignal: AbortSignal,\n viewerScope: string,\n results: Map<number, unknown>,\n status: Map<number, \"ok\" | \"failed\">,\n sink: FrameSink,\n): Promise<void> {\n const hook = rt.instrumentation?.op;\n const run = () => runOp(rt, p, meta, opts, batchSignal, viewerScope, results, status, sink);\n if (!hook) {\n await run();\n return;\n }\n await hook({ id: p.req.id, name: p.op.name, kind: p.op.kind, cost: p.cost }, async () => {\n const error = await run();\n return error ? { error } : {};\n });\n}\n\n/** Runs one op and sends its frames; resolves to the error it failed with, if it failed. */\nasync function runOp(\n rt: BatchRuntime,\n p: Planned,\n meta: RequestMeta,\n opts: ExecuteOptions,\n batchSignal: AbortSignal,\n viewerScope: string,\n results: Map<number, unknown>,\n status: Map<number, \"ok\" | \"failed\">,\n sink: FrameSink,\n): Promise<WireError | undefined> {\n const id = p.req.id;\n const started = rt.options.now();\n const fail = (e: unknown): WireError => {\n let w = toWireError(e);\n if (batchSignal.aborted && batchSignal.reason instanceof RayfoldError) w = batchSignal.reason.toWire();\n sink.push({ id, error: w, fin: true });\n status.set(id, \"failed\");\n return w;\n };\n if (p.failure) return fail(p.failure);\n for (const d of p.deps) {\n if (status.get(d) !== \"ok\") {\n return fail(new RayfoldError(\"failed_precondition\", `Depends on op ${d}, which failed`, { type: \"DependencyFailed\", data: { op: d } }));\n }\n }\n if (batchSignal.aborted) return fail(batchSignal.reason);\n if (p.req.deadline !== undefined && !validDeadline(p.req.deadline)) return fail(new RayfoldError(\"invalid_argument\", `deadline: expected whole milliseconds from 0 to ${MAX_DEADLINE_MS}`));\n\n const opAbort = new AbortController();\n const relay = () => opAbort.abort(batchSignal.reason);\n batchSignal.addEventListener(\"abort\", relay, { once: true });\n let opTimer: ReturnType<typeof setTimeout> | undefined;\n if (p.req.deadline !== undefined) opTimer = setTimeout(() => opAbort.abort(new RayfoldError(\"deadline_exceeded\", \"Deadline exceeded\")), p.req.deadline);\n\n const stamp = (f: Frame): Frame => {\n if (rt.options.timing && \"meta\" in f && f.meta) f.meta.ms = rt.options.now() - started;\n return f;\n };\n\n try {\n const rawArgs = resolveRefs(p.req.args ?? {}, (opId, path) => getPath(results.get(opId), path), `ops.${id}.args`);\n const args = coerceArgs(rt.ir, p.op.args, rawArgs, `${p.op.name}()`);\n const ctx: RayfoldContext = {\n viewer: opts.viewer ?? null,\n signal: opAbort.signal,\n simulate: !!p.req.simulate,\n events: rt.events,\n meta,\n opId: id,\n opName: p.op.name,\n policy: {},\n shape: p.shape,\n state: new Map(),\n batch: opts.batchState ?? new Map(),\n now: rt.options.now,\n checkVersion(key, actual, current) {\n const want = ctx.ifVersion;\n if (want === undefined) return;\n if (String(actual) !== String(want)) throw new VersionConflict(key, want, actual, current);\n },\n };\n // A capability token may call only the operations it names (spec 06 section 6); any other viewer is left to\n // the schema's own policies.\n if (!capabilityAllows(opts.viewer, p.op.name)) {\n throw new RayfoldError(\"permission_denied\", `This capability does not allow ${p.op.name}()`);\n }\n rt.usage?.record({ op: p.op.name, path: \"\", client: String(meta.client ?? \"\") }, rt.options.now());\n if (p.req.ifVersion !== undefined) ctx.ifVersion = p.req.ifVersion;\n if (p.req.vars) ctx.vars = p.req.vars;\n if (p.req.compact) ctx.compact = true;\n\n switch (p.op.kind) {\n case \"query\": {\n if (p.req.live) {\n await runLive(rt, p, args, ctx, sink, stamp, results);\n break;\n }\n const data = await rt.executor.runQuery(p.op, args, p.shape, p.explicit, p.cost, ctx, (f) => sink.push(stamp(f)));\n results.set(id, data);\n break;\n }\n case \"stream\":\n await rt.executor.runStream(p.op, args, p.shape, p.explicit, ctx, (f) => sink.push(stamp(f)));\n break;\n case \"command\": {\n const idem = annotation(p.op, \"idempotent\");\n const optedOut = idem !== undefined && idem.args[\"value\"] === false;\n const key = p.req.key;\n if (!optedOut && !(opts.keyOptional && key === undefined) && (typeof key !== \"string\" || key.length < 16 || key.length > 128)) {\n throw new RayfoldError(\"invalid_argument\", `${p.op.name}(): commands require an idempotency key of 16-128 characters`);\n }\n rt.executor.authorize(p.op, args, ctx); // the write policy holds before anything is replayed\n if (ctx.simulate && !annotation(p.op, \"simulate\")) throw new RayfoldError(\"failed_precondition\", `${p.op.name}() does not support dry runs`);\n if (key && !ctx.simulate && (opts.viewer === null || opts.viewer === undefined)) {\n // Anonymous callers cannot be told apart, so they would share one replay scope (spec 12 section 4).\n throw new RayfoldError(\"unauthenticated\", `${p.op.name}(): idempotency keys need an identified caller`);\n }\n // Bound to the operation as well as the arguments: a key can never replay another command's result.\n const argsHash = hashJson({ op: p.op.name, args });\n const claim = key && !ctx.simulate ? `${viewerScope}\\u0000${key}` : undefined;\n let release: (() => void) | undefined;\n if (claim) {\n // One execution per key even when retries arrive together: later callers wait for the first, then replay.\n for (let running = rt.inflight.get(claim); running; running = rt.inflight.get(claim)) await running;\n rt.inflight.set(claim, new Promise<void>((r) => (release = r)));\n }\n try {\n if (claim) {\n const prior = await rt.idempotency.get(viewerScope, key!);\n if (prior) {\n if (prior.argsHash !== argsHash) throw new RayfoldError(\"already_exists\", `Idempotency key ${key} was used for another operation or other arguments`);\n const stored = p.req.compact && prior.compactFrame !== undefined ? prior.compactFrame : prior.frame;\n const replay = structuredClone(stored) as Frame & { meta?: Record<string, unknown> };\n replay.meta = { ...(replay.meta ?? {}), replay: true };\n (replay as { id: number }).id = id; // a retry may use another op id; the answer belongs to this op\n sink.push(stamp(replay));\n results.set(id, (replay as { ok?: unknown }).ok);\n break;\n }\n }\n const { result, full, compact, patch } = await rt.executor.runCommand(p.op, args, p.shape, p.explicit, p.cost, ctx, (f) => sink.push(stamp(f)));\n results.set(id, result);\n if (claim) await rt.idempotency.put(viewerScope, key!, { argsHash, frame: structuredClone(full), compactFrame: structuredClone(compact), at: rt.options.now() });\n if (!ctx.simulate) rt.changes.publish(changeFromPatch(patch));\n } finally {\n if (claim) {\n rt.inflight.delete(claim);\n release?.();\n }\n }\n break;\n }\n }\n status.set(id, \"ok\");\n } catch (e) {\n return fail(e);\n } finally {\n if (opTimer) clearTimeout(opTimer);\n batchSignal.removeEventListener(\"abort\", relay);\n }\n return undefined;\n}\n\n\n/**\n * Live query loop: first result, then re-run on intersecting changes until the op is aborted.\n * Frames: data (no fin) -> [patch | data]* -> error(canceled)/fin.\n */\nasync function runLive(\n rt: BatchRuntime,\n p: Planned,\n args: Record<string, unknown>,\n ctx: RayfoldContext,\n sink: FrameSink,\n stamp: (f: Frame) => Frame,\n results: Map<number, unknown>,\n): Promise<void> {\n const id = p.req.id;\n // Read sets and diffs need `$type`, so the query always runs in full form; compaction happens on the way out.\n const runCtx: RayfoldContext = ctx.compact ? { ...ctx, compact: false } : ctx;\n const wire = (f: Frame): Frame => (ctx.compact ? compactQueryFrame(f) : f);\n const collect = async (): Promise<{ frames: Frame[]; data: unknown }> => {\n const frames: Frame[] = [];\n await rt.executor.runQuery(p.op, args, p.shape, p.explicit, p.cost, runCtx, (f) => frames.push(f));\n return { frames, data: foldFrames(frames) };\n };\n const first = await collect();\n let current = first.data;\n let readSet = readSetOf(current);\n // Entity types reachable from the result type: a new entity of such a type may change membership.\n const typeSet = reachableEntityTypes(rt.ir, p.op.returns);\n for (const f0 of first.frames) {\n const f = wire(f0);\n if (\"fin\" in f && f.fin && !(\"data\" in f) && !(\"error\" in f)) continue; // keep the op open\n if (\"data\" in f && !(\"at\" in f)) {\n const { fin: _fin, ...rest } = f as { fin?: boolean } & Record<string, unknown>;\n sink.push(stamp(rest as Frame));\n } else sink.push(stamp(f));\n }\n results.set(id, current);\n\n let dirty = false;\n let running = false;\n let wake: (() => void) | null = null;\n const off = rt.changes.subscribe((c) => {\n const hit = c.ops.has(p.op.name) || [...c.keys].some((k) => readSet.has(k) || typeSet.has(k.slice(0, k.indexOf(\":\"))));\n if (!hit) return;\n dirty = true;\n wake?.();\n });\n const onAbort = () => wake?.();\n ctx.signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n while (!ctx.signal.aborted) {\n if (!dirty) await new Promise<void>((res) => (wake = res));\n wake = null;\n if (ctx.signal.aborted) break;\n if (!dirty || running) continue;\n dirty = false;\n running = true;\n try {\n const next = await collect();\n const d = diffResults(current, next.data);\n current = next.data;\n readSet = readSetOf(current);\n results.set(id, current);\n if (d && \"patch\" in d) sink.push(stamp({ id, patch: d.patch }));\n else if (d) sink.push(stamp(wire({ id, data: d.data, meta: { cost: p.cost } })));\n } finally {\n running = false;\n }\n }\n } finally {\n off();\n ctx.signal.removeEventListener(\"abort\", onAbort);\n }\n throw ctx.signal.reason instanceof RayfoldError ? ctx.signal.reason : new RayfoldError(\"canceled\", \"Canceled\");\n}\n\nfunction reachableEntityTypes(ir: RayfoldSchemaIR, root: TypeRef, maxDepth = 4): Set<string> {\n const out = new Set<string>();\n const visit = (t: TypeRef, depth: number): void => {\n const name = baseName(t);\n const def = ir.types[name];\n if (!def || depth > maxDepth) return;\n if (def.kind === \"entity\") {\n if (out.has(name)) return;\n out.add(name);\n }\n if (\"fields\" in def) for (const f of def.fields) visit(f.type, depth + 1);\n if (def.kind === \"union\") for (const m of def.members) visit({ kind: \"named\", name: m, nullable: false }, depth + 1);\n if (t.kind === \"named\" && t.args) for (const a of t.args) visit(a, depth);\n };\n visit(root, 0);\n return out;\n}\n\n/** Nesting limit for args and vars, checked before anything walks them recursively. */\nconst MAX_NESTING = 64;\n/** Longest deadline a client may ask for. */\nconst MAX_DEADLINE_MS = 600_000;\n\nfunction validDeadline(v: unknown): boolean {\n return typeof v === \"number\" && Number.isInteger(v) && v >= 0 && v <= MAX_DEADLINE_MS;\n}\n\n/** Iterative, so hostile nesting cannot overflow the stack while it is being measured. */\nfunction tooDeep(value: unknown, max: number): boolean {\n const stack: Array<[unknown, number]> = [[value, 0]];\n while (stack.length) {\n const [v, d] = stack.pop()!;\n if (v === null || typeof v !== \"object\") continue;\n if (d >= max) return true;\n for (const x of Array.isArray(v) ? v : Object.values(v as Record<string, unknown>)) stack.push([x, d + 1]);\n }\n return false;\n}\n"]}
package/bindings.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * HTTP bindings (extension `http`, spec 04 section 8): expose queries and commands on REST-shaped routes with
3
+ * their natural methods, backed by the same contract (validation, policies, typed errors, idempotency, patches).
4
+ *
5
+ * query book(id: ID): Book? @http(method: GET, path: "/books/{id}")
6
+ * command editReview(id: ID, input: ...) @http(method: PUT, path: "/reviews/{id}", body: input)
7
+ * command updateBook(id: ID, patch: ...) @http(method: PATCH, path: "/books/{id}", body: patch)
8
+ * command deleteReview(id: ID) @http(method: DELETE, path: "/reviews/{id}")
9
+ * command placeOrder(input: ...) @http(method: POST, path: "/orders", body: input, location: "/orders/{id}")
10
+ * query books(filter: ..., page: ...) @http(method: QUERY, path: "/books", body: "*")
11
+ */
12
+ import type { IncomingMessage, ServerResponse } from "node:http";
13
+ import { type OpDef, type RayfoldSchemaIR } from "@rayfold/schema";
14
+ import type { RayfoldServer } from "./server.js";
15
+ import { type OriginOptions } from "./guard.js";
16
+ export declare const QUERY_METHODS: readonly ["GET", "QUERY"];
17
+ export declare const COMMAND_METHODS: readonly ["POST", "PUT", "PATCH", "DELETE"];
18
+ export interface Binding {
19
+ op: OpDef;
20
+ method: string;
21
+ path: string;
22
+ /** name of the argument that receives the JSON body, or "*" to spread the body into the arguments */
23
+ body?: string;
24
+ /** Location template for 201 responses, filled from the result, e.g. "/orders/{id}" */
25
+ location?: string;
26
+ params: string[];
27
+ regex: RegExp;
28
+ }
29
+ export declare function bindingsOf(ir: RayfoldSchemaIR): Binding[];
30
+ export interface BindingOptions extends OriginOptions {
31
+ /** Mount prefix, default "" (routes are served exactly as declared). */
32
+ prefix?: string;
33
+ viewer?: (req: IncomingMessage) => unknown | Promise<unknown>;
34
+ maxBody?: number;
35
+ }
36
+ /** Returns a handler that answers bound routes and returns false for anything else. */
37
+ export declare function createBindingHandler(server: RayfoldServer, opts?: BindingOptions): (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;