@bjornpagen/bumbledb 0.15.0 → 0.17.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 (103) hide show
  1. package/COOKBOOK.md +33 -49
  2. package/README.md +3 -3
  3. package/dist/capacity.d.ts +24 -136
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +18 -40
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/closed.d.ts +0 -156
  8. package/dist/closed.d.ts.map +1 -1
  9. package/dist/closed.js +0 -104
  10. package/dist/closed.js.map +1 -1
  11. package/dist/db.d.ts +7 -223
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +147 -396
  14. package/dist/db.js.map +1 -1
  15. package/dist/face.d.ts +0 -133
  16. package/dist/face.d.ts.map +1 -1
  17. package/dist/face.js +0 -33
  18. package/dist/face.js.map +1 -1
  19. package/dist/fields.d.ts +1 -145
  20. package/dist/fields.d.ts.map +1 -1
  21. package/dist/fields.js +2 -91
  22. package/dist/fields.js.map +1 -1
  23. package/dist/index.d.ts +11 -15
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +9 -13
  26. package/dist/index.js.map +1 -1
  27. package/dist/law.d.ts +111 -93
  28. package/dist/law.d.ts.map +1 -1
  29. package/dist/law.js +23 -27
  30. package/dist/law.js.map +1 -1
  31. package/dist/lower.d.ts +9 -35
  32. package/dist/lower.d.ts.map +1 -1
  33. package/dist/lower.js +8 -53
  34. package/dist/lower.js.map +1 -1
  35. package/dist/marshal.d.ts +0 -65
  36. package/dist/marshal.d.ts.map +1 -1
  37. package/dist/marshal.js +0 -72
  38. package/dist/marshal.js.map +1 -1
  39. package/dist/native.d.ts +25 -290
  40. package/dist/native.d.ts.map +1 -1
  41. package/dist/native.js +6 -66
  42. package/dist/native.js.map +1 -1
  43. package/dist/query/atom.d.ts +10 -276
  44. package/dist/query/atom.d.ts.map +1 -1
  45. package/dist/query/atom.js +1 -96
  46. package/dist/query/atom.js.map +1 -1
  47. package/dist/query/find.d.ts +10 -76
  48. package/dist/query/find.d.ts.map +1 -1
  49. package/dist/query/find.js +0 -30
  50. package/dist/query/find.js.map +1 -1
  51. package/dist/query/lower.d.ts +64 -146
  52. package/dist/query/lower.d.ts.map +1 -1
  53. package/dist/query/lower.js +19 -256
  54. package/dist/query/lower.js.map +1 -1
  55. package/dist/query/parse-ir.d.ts +0 -7
  56. package/dist/query/parse-ir.d.ts.map +1 -1
  57. package/dist/query/parse-ir.js +1 -13
  58. package/dist/query/parse-ir.js.map +1 -1
  59. package/dist/query/run.d.ts +0 -36
  60. package/dist/query/run.d.ts.map +1 -1
  61. package/dist/query/run.js +0 -44
  62. package/dist/query/run.js.map +1 -1
  63. package/dist/query/scope.d.ts +24 -180
  64. package/dist/query/scope.d.ts.map +1 -1
  65. package/dist/query/scope.js +2 -66
  66. package/dist/query/scope.js.map +1 -1
  67. package/dist/relation.d.ts +2 -50
  68. package/dist/relation.d.ts.map +1 -1
  69. package/dist/relation.js +2 -37
  70. package/dist/relation.js.map +1 -1
  71. package/dist/schema.d.ts +13 -63
  72. package/dist/schema.d.ts.map +1 -1
  73. package/dist/schema.js +118 -92
  74. package/dist/schema.js.map +1 -1
  75. package/dist/spec.d.ts +1 -140
  76. package/dist/spec.d.ts.map +1 -1
  77. package/dist/spec.js +1 -68
  78. package/dist/spec.js.map +1 -1
  79. package/dist/statements.d.ts +6 -137
  80. package/dist/statements.d.ts.map +1 -1
  81. package/dist/statements.js +16 -119
  82. package/dist/statements.js.map +1 -1
  83. package/package.json +2 -2
  84. package/src/capacity.ts +26 -140
  85. package/src/closed.ts +5 -206
  86. package/src/db.ts +203 -692
  87. package/src/face.ts +0 -142
  88. package/src/fields.ts +4 -172
  89. package/src/index.ts +9 -15
  90. package/src/law.ts +201 -129
  91. package/src/lower.ts +8 -53
  92. package/src/marshal.ts +1 -85
  93. package/src/native.ts +47 -323
  94. package/src/query/atom.ts +26 -313
  95. package/src/query/find.ts +24 -110
  96. package/src/query/lower.ts +132 -377
  97. package/src/query/parse-ir.ts +1 -14
  98. package/src/query/run.ts +0 -45
  99. package/src/query/scope.ts +25 -186
  100. package/src/relation.ts +2 -66
  101. package/src/schema.ts +143 -122
  102. package/src/spec.ts +1 -160
  103. package/src/statements.ts +22 -174
@@ -1,47 +1,13 @@
1
- /**
2
- * `query()` and the IR lowering, REFERENCE-IDENTITY edition. A query is
3
- * built kysely-shaped — variables minted by {@link v} outside the rule and
4
- * reused by REFERENCE to join:
5
- *
6
- * query(S).rule((r) => {
7
- * const acct = v(Account)
8
- * const h = v(Holder)
9
- * return r
10
- * .match(Account, { id: acct.id, holder: acct.holder })
11
- * .match(Holder, { id: acct.holder })
12
- * .where(r.eq(acct.holder, r.param("root")))
13
- * .find({ account: acct.id, holder: acct.holder })
14
- * })
15
- *
16
- * — and is an INERT value: `Query<Rels, Row, Params>` with `Row` inferred
17
- * from each rule's `.find` RECORD (its keys ARE the answer columns) and
18
- * `Params` inferred to be EXACTLY the params the rules use (params are typed
19
- * BY USE; a param no rule uses never registers). Variable IDENTITY is the
20
- * object reference: reusing one value across binding positions IS the join,
21
- * lowering is a pure function of the query value down to the
22
- * bridge's `QueryIr` (`bumbledb/crates/bumbledb/src/ir.rs`): relations by
23
- * declaration ordinal, variables by dense per-rule first-occurrence ids
24
- * (keyed on the object REFERENCE — the discipline is unchanged, only the map
25
- * key moved from name to reference), params by first-use order. Lowering is
26
- * STABLE — the same query value lowers to deeply-equal IR every time, and
27
- * two identically-written queries (fresh mints each) lower identically.
28
- * Construction validates negation safety and boundness (typed by the var's
29
- * label — object identity is invisible to the type tier, so these are
30
- * construction-time walls); everything else (types, aggregate
31
- * rosters, rule caps) is the ENGINE's judge, surfacing at prepare.
32
- */
33
1
  import * as errors from "@superbuilders/errors";
34
2
  import { sealedFieldsOf } from "#closed.ts";
35
3
  import { assertDeclarationOrderKey, isIntervalValue, literalShapeError, rosterOf } from "#fields.ts";
36
4
  import { allen, and, eq, ge, gt, le, lt, ne, not, or, pointIn } from "#query/atom.ts";
37
5
  import { count, max, min, pack, sum } from "#query/find.ts";
38
6
  import { parseQueryIr } from "#query/parse-ir.ts";
39
- import { fieldJoins, inferred, isTerm, makeDuration, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts";
40
- /** The frozen constructor vocabulary every rule builder spreads. */
7
+ import { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts";
41
8
  const termOps = Object.freeze({
42
9
  param: makeParam,
43
10
  inSet: makeSetParam,
44
- duration: makeDuration,
45
11
  eq,
46
12
  ne,
47
13
  lt,
@@ -59,7 +25,6 @@ const termOps = Object.freeze({
59
25
  max,
60
26
  pack
61
27
  });
62
- /** The empty rule state. */
63
28
  const EMPTY_RULE = Object.freeze({
64
29
  items: Object.freeze([]),
65
30
  bound: new Set(),
@@ -80,11 +45,6 @@ function mintSlotOf(context, ref) {
80
45
  }
81
46
  return { field: ref.field, class: context.classes[ref.owner.name]?.[ref.column] };
82
47
  }
83
- /**
84
- * Judges one membership ARRAY at a binding position — legal exactly at a
85
- * CLOSED-reference field, holding ≥ 2 DISTINCT handle names. The returned
86
- * name is CONTENT-ADDRESSED (vocabulary + the member SET).
87
- */
88
48
  function membershipSet(context, field, value) {
89
49
  const roster = rosterOf(field);
90
50
  if (roster === undefined) {
@@ -110,14 +70,6 @@ function membershipSet(context, field, value) {
110
70
  const key = [...members].sort();
111
71
  return { name: `∈ ${roster.name} ${JSON.stringify(key)}`, members: Object.freeze(members) };
112
72
  }
113
- /**
114
- * Resolves a bindings record against an atom owner's matchable fields, in
115
- * the record's written order: terms classify by their runtime tag,
116
- * everything else is a bare literal. Every VARIABLE binding judges
117
- * `fieldJoins(mintSlot, positionSlot)` and throws on a class-unequal reuse
118
- * (the runtime twin of `CheckBindings`); the bound refs are collected for
119
- * the rule's boundness set.
120
- */
121
73
  function resolveBindings(context, label, relation, bindings) {
122
74
  const entries = [];
123
75
  const vars = [];
@@ -171,8 +123,6 @@ function resolveBindings(context, label, relation, bindings) {
171
123
  }));
172
124
  break;
173
125
  }
174
- case "duration":
175
- throw errors.new(`${label}.${fieldName}: the measure is not a field-typed value — it lives in comparisons and find entries`);
176
126
  }
177
127
  }
178
128
  else if (Array.isArray(value)) {
@@ -193,7 +143,6 @@ function resolveBindings(context, label, relation, bindings) {
193
143
  }
194
144
  return { atom: Object.freeze({ relation, bindings: Object.freeze(entries) }), vars, uses };
195
145
  }
196
- /** Extends a rule state with one positive atom; the bound variable references accumulate into the boundness set. */
197
146
  function advanceMatch(context, state, relation, bindings) {
198
147
  const resolved = resolveBindings(context, `relation ${relation.name}`, relation, bindings);
199
148
  const bound = new Set(state.bound);
@@ -206,7 +155,6 @@ function advanceMatch(context, state, relation, bindings) {
206
155
  paramUses: Object.freeze([...state.paramUses, ...resolved.uses])
207
156
  });
208
157
  }
209
- /** Resolves one comparison side to its runtime term (variables and the measure ride by reference). */
210
158
  function cmpTermDataOf(value) {
211
159
  if (isTerm(value)) {
212
160
  switch (value[term]) {
@@ -216,31 +164,15 @@ function cmpTermDataOf(value) {
216
164
  return Object.freeze({ kind: "param", name: value.name });
217
165
  case "setParam":
218
166
  return Object.freeze({ kind: "setParam", name: value.name });
219
- case "duration":
220
- return Object.freeze({ kind: "measure", ref: value.over });
221
167
  }
222
168
  }
223
169
  return Object.freeze({ kind: "literal", value });
224
170
  }
225
- /**
226
- * One comparison side's contribution to the param census: a param/set side
227
- * anchors to its SIBLING — a variable's field descriptor or the measure; an
228
- * unanchorable use records with no anchor.
229
- */
230
171
  function sideUses(op, side, sibling, uses) {
231
172
  if (side.kind !== "param" && side.kind !== "setParam") {
232
173
  return;
233
174
  }
234
- let anchor;
235
- if (sibling.kind === "var") {
236
- anchor = sibling.ref.field;
237
- }
238
- else if (sibling.kind === "measure") {
239
- anchor = "measure";
240
- }
241
- else {
242
- anchor = undefined;
243
- }
175
+ const anchor = sibling.kind === "var" ? sibling.ref.field : undefined;
244
176
  uses.push(Object.freeze({
245
177
  name: side.name,
246
178
  shape: side.kind === "param" ? "value" : "set",
@@ -249,7 +181,6 @@ function sideUses(op, side, sibling, uses) {
249
181
  members: undefined
250
182
  }));
251
183
  }
252
- /** Lowers one condition VALUE to its runtime data, recording param uses. */
253
184
  function condDataOf(cond, uses) {
254
185
  if (cond.cond === "cmp") {
255
186
  const lhs = cmpTermDataOf(cond.lhs);
@@ -278,7 +209,6 @@ function condDataOf(cond, uses) {
278
209
  }
279
210
  throw errors.new("a negated atom is not a condition-tree node — pass not(...) to where() directly, never inside and()/or()");
280
211
  }
281
- /** Extends a rule state with one `.where` item (a condition or a negated atom). */
282
212
  function advanceWhere(context, state, cond) {
283
213
  if (typeof cond !== "object" || cond === null || !("cond" in cond)) {
284
214
  throw errors.new("where() takes a comparison, an and()/or() tree, or a negated atom");
@@ -309,15 +239,6 @@ function advanceWhere(context, state, cond) {
309
239
  paramUses: Object.freeze([...state.paramUses, ...uses])
310
240
  });
311
241
  }
312
- /**
313
- * Extends a rule state with one interior atom (a named record over head keys;
314
- * vars validated at completion). A POSITIVE interior atom is a positive
315
- * occurrence exactly as the engine represents it (`check_atoms` walks Interior
316
- * and Edb in one loop), so its variables GROUND: they enter the rule's
317
- * boundness set, may ride the head, and satisfy negation safety — the
318
- * interior-only identity projection of a finished table is spellable with no
319
- * re-grounding join. A NEGATED one binds nothing, only rejects.
320
- */
321
242
  function advanceInterior(state, target, bindings, kind) {
322
243
  const resolved = [];
323
244
  for (const [key, value] of Object.entries(bindings)) {
@@ -341,7 +262,6 @@ function advanceInterior(state, target, bindings, kind) {
341
262
  paramUses: state.paramUses
342
263
  });
343
264
  }
344
- /** Narrows a find entry to an aggregate value. */
345
265
  function isAggregateEntry(value) {
346
266
  return typeof value === "object" && value !== null && "agg" in value;
347
267
  }
@@ -352,7 +272,6 @@ function asVarTerm(context, value) {
352
272
  }
353
273
  throw errors.new(`${context}: expected a variable`);
354
274
  }
355
- /** Classifies one aggregate find entry into its runtime data (variables ride by reference). */
356
275
  function aggDataOf(name, entry) {
357
276
  if (entry.agg === "count") {
358
277
  return Object.freeze({ op: "count" });
@@ -365,10 +284,7 @@ function aggDataOf(name, entry) {
365
284
  if (isTerm(over) && over[term] === "var") {
366
285
  return Object.freeze({ op: "fold", fold: entry.agg, over });
367
286
  }
368
- if (isTerm(over) && over[term] === "duration") {
369
- return Object.freeze({ op: "fold", fold: entry.agg, over: Object.freeze({ duration: over.over }) });
370
- }
371
- throw errors.new(`find ${name} (${entry.agg}): takes a variable or r.duration(v)`);
287
+ throw errors.new(`find ${name} (${entry.agg}): takes a variable`);
372
288
  }
373
289
  case "pack":
374
290
  return Object.freeze({ op: "pack", over: asVarTerm(`find ${name} (pack)`, over) });
@@ -376,11 +292,6 @@ function aggDataOf(name, entry) {
376
292
  throw errors.new(`find ${name}: unknown aggregate ${entry.agg}`);
377
293
  }
378
294
  }
379
- /**
380
- * Classifies one find entry into its named answer column (the KEY names the
381
- * column, `count` included). The `slot`/`closed` slices are resolved LATER,
382
- * at rule completion, where boundness and the mint slots are in hand.
383
- */
384
295
  function findColumnOf(name, entry) {
385
296
  if (isTerm(entry)) {
386
297
  if (entry[term] === "var") {
@@ -391,15 +302,7 @@ function findColumnOf(name, entry) {
391
302
  slot: undefined
392
303
  });
393
304
  }
394
- if (entry[term] === "duration") {
395
- return Object.freeze({
396
- name,
397
- entry: Object.freeze({ kind: "measure", over: entry.over }),
398
- closed: undefined,
399
- slot: undefined
400
- });
401
- }
402
- throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables, r.duration(v), or aggregates`);
305
+ throw errors.new(`find ${name}: a ${entry[term]} is not projectable — find takes variables or aggregates`);
403
306
  }
404
307
  if (isAggregateEntry(entry)) {
405
308
  return Object.freeze({
@@ -409,43 +312,39 @@ function findColumnOf(name, entry) {
409
312
  slot: undefined
410
313
  });
411
314
  }
412
- throw errors.new(`find ${name}: not a find entry — find takes variables, r.duration(v), or aggregates`);
315
+ throw errors.new(`find ${name}: not a find entry — find takes variables or aggregates`);
413
316
  }
414
317
  /**
415
- * The orderable ban's pointed refusal (`docs/architecture/10-data-model.md`
318
+ * The orderable ban's pointed refusal
416
319
  * § orderability): a closed reference is equality-and-membership only.
417
320
  */
418
321
  function closedOrderError(context, position, vocabulary) {
419
- return errors.new(`${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (docs/architecture/10-data-model.md; equality, membership, and counting remain)`);
322
+ return errors.new(`${context}: ${position} is a ${vocabulary} reference — declaration order is an accident, not semantics: vocabularies do not order (equality, membership, and counting remain)`);
420
323
  }
421
- /** The comparison ops under the orderable ban (order roster + point membership). */
422
324
  function isOrderOp(op) {
423
325
  return op === "lt" || op === "le" || op === "gt" || op === "ge" || op === "pointIn";
424
326
  }
425
- /** Requires a variable to be bound by a relation atom of the rule (the boundness wall — invisible to the type tier). */
426
327
  function assertBound(where, bound, ref) {
427
328
  if (!bound.has(ref)) {
428
329
  throw errors.new(`${where}: the variable ${ref.label} is not bound by a relation atom of the rule`);
429
330
  }
430
331
  }
431
- /** Requires a variable to be interval-typed (the measure's and pack's domain), off its own descriptor. */
432
332
  function assertInterval(where, ref) {
433
333
  if (ref.field.kind !== "interval") {
434
334
  throw errors.new(`${where}: ${ref.label} is not interval-typed — the measure is defined over interval-typed variables only`);
435
335
  }
436
336
  }
437
- /** Requires a variable's own field to be non-closed (the orderable ban's runtime twin). */
438
337
  function assertNotClosed(where, position, ref) {
439
338
  const roster = rosterOf(ref.field);
440
339
  if (roster !== undefined) {
441
340
  throw closedOrderError(where, `${position} ${ref.label}`, roster.name);
442
341
  }
443
342
  }
444
- /**
445
- * The classed mint slot one answer column's VALUES flow from: a projected
446
- * variable's mint slot. Counts, folds, `pack` and the measure derive
447
- * numbers/intervals, so they resolve no slot.
448
- */
343
+ function assertNumeric(where, position, ref) {
344
+ if (ref.field.kind !== "u64" && ref.field.kind !== "i64") {
345
+ throw errors.new(`${where}: ${position} ${ref.label} is ${ref.field.kind}, not numeric — a fold reads u64/i64 only`);
346
+ }
347
+ }
449
348
  function findColumnSlotOf(context, column) {
450
349
  const entry = column.entry;
451
350
  if (entry.kind === "var") {
@@ -453,7 +352,6 @@ function findColumnSlotOf(context, column) {
453
352
  }
454
353
  return undefined;
455
354
  }
456
- /** Validates one find column's variable references (boundness + the orderable/interval walls, off the var's own field). */
457
355
  function validateColumn(context, bound, column) {
458
356
  const where = `${contextLabel(context)} find ${column.name}`;
459
357
  const entry = column.entry;
@@ -461,23 +359,14 @@ function validateColumn(context, bound, column) {
461
359
  assertBound(where, bound, entry.over);
462
360
  return;
463
361
  }
464
- if (entry.kind === "measure") {
465
- assertBound(where, bound, entry.over);
466
- assertInterval(where, entry.over);
467
- return;
468
- }
469
362
  const agg = entry.agg;
470
363
  switch (agg.op) {
471
364
  case "count":
472
365
  return;
473
366
  case "fold": {
474
- if ("duration" in agg.over) {
475
- assertBound(where, bound, agg.over.duration);
476
- assertInterval(where, agg.over.duration);
477
- return;
478
- }
479
367
  assertBound(where, bound, agg.over);
480
368
  assertNotClosed(where, `the ${agg.fold} input`, agg.over);
369
+ assertNumeric(where, `the ${agg.fold} input`, agg.over);
481
370
  return;
482
371
  }
483
372
  case "pack":
@@ -486,11 +375,6 @@ function validateColumn(context, bound, column) {
486
375
  return;
487
376
  }
488
377
  }
489
- /**
490
- * Validates one condition's variable references against the rule's bound
491
- * set — and, for `eq`/`ne` over two variables, holds the class wall through
492
- * the mint slots (the unification IS a join; bare pairs only with bare).
493
- */
494
378
  function validateCond(context, bound, cond) {
495
379
  const label = contextLabel(context);
496
380
  if (cond.kind === "cmp") {
@@ -502,10 +386,6 @@ function validateCond(context, bound, cond) {
502
386
  throw closedOrderError(label, `the ${cond.op.kind} side ${side.ref.label}`, roster.name);
503
387
  }
504
388
  }
505
- if (side.kind === "measure") {
506
- assertBound(label, bound, side.ref);
507
- assertInterval(label, side.ref);
508
- }
509
389
  }
510
390
  if ((cond.op.kind === "eq" || cond.op.kind === "ne") && cond.lhs.kind === "var" && cond.rhs.kind === "var") {
511
391
  assertBound(label, bound, cond.lhs.ref);
@@ -522,16 +402,6 @@ function validateCond(context, bound, cond) {
522
402
  validateCond(context, bound, child);
523
403
  }
524
404
  }
525
- /**
526
- * Validates one interior item: every head column of the table is bound exactly
527
- * once (a missing or extra key is a pointed error) and each variable joins
528
- * its head column's classed slot. A POSITIVE interior atom GROUNDS its
529
- * variables (a positive occurrence, exactly the engine's representation),
530
- * so no boundness precondition exists; a NEGATED one binds nothing — its
531
- * variables must be positively bound elsewhere in the rule, the same
532
- * safety rule as EDB negation. When the table's own first rule is in flight
533
- * (`finds` empty), the completing rule's OWN find columns ARE the head.
534
- */
535
405
  function validateInterior(context, bound, item, columns) {
536
406
  const label = contextLabel(context);
537
407
  const headColumns = item.target.finds.length > 0 ? item.target.finds : columns;
@@ -567,12 +437,6 @@ function validateInterior(context, bound, item, columns) {
567
437
  }
568
438
  }
569
439
  }
570
- /**
571
- * Completes one rule: enriches the find columns (declaration-order-safe
572
- * keys, boundness validated, each column's classed slot and closed slice
573
- * resolved), then walks the body walls — negated-atom boundness safety, interior
574
- * head pairing, and condition validation.
575
- */
576
440
  function completeRule(context, state, rawColumns) {
577
441
  const label = contextLabel(context);
578
442
  if (rawColumns.length === 0) {
@@ -601,11 +465,9 @@ function completeRule(context, state, rawColumns) {
601
465
  }
602
466
  return Object.freeze({ items: state.items, finds: Object.freeze(columns), paramUses: state.paramUses });
603
467
  }
604
- /** Builds one typed rule value over completed rule data. */
605
468
  function makeRuleValue(rule) {
606
469
  return Object.freeze({ rule });
607
470
  }
608
- /** The diagnostic label of a chain context. */
609
471
  function contextLabel(context) {
610
472
  switch (context.kind) {
611
473
  case "query":
@@ -618,11 +480,9 @@ function contextLabel(context) {
618
480
  return `rec ${context.self.name} rec`;
619
481
  }
620
482
  }
621
- /** RecHandle is name-only staging; RecHead/RecData carry the sealed finds. */
622
483
  function isRecHead(rec) {
623
484
  return Array.isArray(rec.finds);
624
485
  }
625
- /** Resolves a derived-table name against the context's visible tables. */
626
486
  function lookupDerived(context, name) {
627
487
  const interior = context.interiors.find(function byName(candidate) {
628
488
  return candidate.name === name;
@@ -648,22 +508,15 @@ function lookupDerived(context, name) {
648
508
  }
649
509
  throw errors.new(`${contextLabel(context)}: no derived table named ${name} is in scope`);
650
510
  }
651
- /** Validates and records one interior atom per the context's cut. */
652
511
  function interiorAdvance(context, state, name, bindings) {
653
512
  return advanceInterior(state, lookupDerived(context, name), bindings, "interior");
654
513
  }
655
- /**
656
- * Validates and records one NEGATED finished-table atom — main and interior
657
- * rules: a finished set is a set. Rec bodies refuse every negation
658
- * (`NegationInRec` — self is the wall; EDB / earlier-interior is this-cut).
659
- */
660
514
  function notInteriorAdvance(context, state, name, bindings) {
661
515
  if (context.kind === "rec-base" || context.kind === "rec-arm") {
662
516
  throw errors.new(`rec ${context.self.name}: a rec rule negates no table — self-negation is negation through the cycle (a finished set is what keeps the operator monotone), and a finished table's fold belongs in the main rules`);
663
517
  }
664
518
  return advanceInterior(state, lookupDerived(context, name), bindings, "negatedInterior");
665
519
  }
666
- /** Classifies one find record per the context (interior and rec heads project bound variables only). */
667
520
  function findColumns(context, entries) {
668
521
  const columns = [];
669
522
  const derivedHead = context.kind !== "query";
@@ -679,7 +532,6 @@ function findColumns(context, entries) {
679
532
  }
680
533
  return columns;
681
534
  }
682
- /** Builds one runtime chain (immutably — every step is a fresh chain over fresh state). */
683
535
  function makeRawChain(context, state) {
684
536
  const chain = {
685
537
  match(relation, bindings) {
@@ -698,7 +550,6 @@ function makeRawChain(context, state) {
698
550
  Object.freeze(chain);
699
551
  return chain;
700
552
  }
701
- /** Builds one runtime rule-builder over a context. */
702
553
  function makeRawScope(context) {
703
554
  const scope = {
704
555
  ...termOps,
@@ -724,7 +575,6 @@ function makeRawScope(context) {
724
575
  function isTypedScope(scope) {
725
576
  return typeof scope.match === "function";
726
577
  }
727
- /** Builds one query-rule builder (the typed face of the raw builder). */
728
578
  function makeQueryRuleScope(theory, env) {
729
579
  const raw = makeRawScope({ kind: "query", classes: theory.classes, theory, ...env });
730
580
  if (!isTypedScope(raw)) {
@@ -732,7 +582,6 @@ function makeQueryRuleScope(theory, env) {
732
582
  }
733
583
  return raw;
734
584
  }
735
- /** Builds one interior-rule builder. */
736
585
  function makeInteriorRuleScope(theory, env, self) {
737
586
  const raw = makeRawScope({ kind: "interior", self, classes: theory.classes, theory, ...env });
738
587
  if (!isTypedScope(raw)) {
@@ -761,14 +610,12 @@ function makeRecRuleScope(theory, env, self, kind) {
761
610
  }
762
611
  return raw;
763
612
  }
764
- /** Renders one head column's closed slice for the rule-alignment check's diagnostics. */
765
613
  function renderClosedSlice(closed) {
766
614
  return closed === undefined ? "a bare value" : `a ${closed.name} reference`;
767
615
  }
768
- /** Renders one head column's signature for the rule-alignment check. */
769
616
  function headSignature(column) {
770
617
  const entry = column.entry;
771
- if (entry.kind === "var" || entry.kind === "measure") {
618
+ if (entry.kind === "var") {
772
619
  return `${column.name}:var`;
773
620
  }
774
621
  const agg = entry.agg;
@@ -777,11 +624,6 @@ function headSignature(column) {
777
624
  }
778
625
  return `${column.name}:${agg.op}`;
779
626
  }
780
- /** The roster a param anchor carries: present exactly on a closed-reference field anchor. */
781
- function anchorRosterOf(anchor) {
782
- return anchor === "measure" ? undefined : rosterOf(anchor);
783
- }
784
- /** Renders one param anchor's closedness for the registry's coherence diagnostics. */
785
627
  function renderParamAnchor(roster) {
786
628
  return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`;
787
629
  }
@@ -820,8 +662,8 @@ function paramRegistryOf(interiors, rec, rules) {
820
662
  throw errors.new(`query param ${use.name} is used both as a ${existing.shape} param and a ${use.shape} param — one name, one shape`);
821
663
  }
822
664
  if (existing.anchor !== undefined && use.anchor !== undefined) {
823
- const registered = anchorRosterOf(existing.anchor);
824
- const anchored = anchorRosterOf(use.anchor);
665
+ const registered = rosterOf(existing.anchor);
666
+ const anchored = rosterOf(use.anchor);
825
667
  if (registered !== anchored) {
826
668
  throw errors.new(`query param ${use.name} is anchored at ${renderParamAnchor(registered)} and at ${renderParamAnchor(anchored)} — a closed-anchored param translates handle names through ONE roster (one name, one domain); name the params differently`);
827
669
  }
@@ -853,14 +695,6 @@ function paramRegistryOf(interiors, rec, rules) {
853
695
  if (entry === undefined) {
854
696
  throw errors.new(`query param ${name} lost its registry entry`);
855
697
  }
856
- /**
857
- * A membership array's handle names are program constants, so the
858
- * entry stores the resolved IMAGE: each name rides the one
859
- * roster-verification point (`taggedHandleId`, through
860
- * `taggedCmpLiteral`) exactly once, HERE — an out-of-roster name
861
- * fails at build, and every execute returns this frozen value by
862
- * reference.
863
- */
864
698
  let membership;
865
699
  if (entry.members !== undefined) {
866
700
  const anchor = entry.anchor;
@@ -877,7 +711,6 @@ function paramRegistryOf(interiors, rec, rules) {
877
711
  return Object.freeze({ name, shape: entry.shape, anchor: entry.anchor, op: entry.op, membership });
878
712
  }));
879
713
  }
880
- /** Asserts every rule in a list derives the same head (name, aggregate shape, closed slice, class). */
881
714
  function assertAlignedHeads(label, rules) {
882
715
  const first = rules[0];
883
716
  if (first === undefined) {
@@ -906,13 +739,6 @@ function assertAlignedHeads(label, rules) {
906
739
  function afterMainError(what) {
907
740
  return errors.new(`query: ${what} after a main rule is unwritable — declaration order is interiors, then rec, then main`);
908
741
  }
909
- /**
910
- * Assembles the runtime query value over completed rules: every rule must
911
- * derive the SAME head (name and aggregate shape, position for position —
912
- * the decode labels and the engine's alignment rule agree), and the param
913
- * registry folds in query-walk order. CQ lowering does not mention rec;
914
- * Reach carries RecData by value.
915
- */
916
742
  function makeRawQuery(theory, interiors, rec, rules) {
917
743
  assertAlignedHeads("a query", rules);
918
744
  const first = rules[0];
@@ -959,7 +785,6 @@ function makeRawQuery(theory, interiors, rec, rules) {
959
785
  function makeQuery(theory, interiors, rec, rules) {
960
786
  return makeRawQuery(theory, interiors, rec, rules);
961
787
  }
962
- /** Collects one Interior from its builders. */
963
788
  function collectInterior(theory, env, name, builds) {
964
789
  if (builds.length === 0) {
965
790
  throw errors.new(`query: interior ${name} needs at least one rule`);
@@ -974,7 +799,6 @@ function collectInterior(theory, env, name, builds) {
974
799
  }
975
800
  return Object.freeze({ name, finds: first.finds, rules: Object.freeze(rules) });
976
801
  }
977
- /** Collects the Rec from tagged base/rec builder arrays. */
978
802
  function collectRec(theory, interiors, name, baseBuilds, recBuilds) {
979
803
  if (baseBuilds.length === 0) {
980
804
  throw errors.new(`query: rec ${name} has no base arms`);
@@ -1017,7 +841,6 @@ function collectRec(theory, interiors, name, baseBuilds, recBuilds) {
1017
841
  });
1018
842
  return recData;
1019
843
  }
1020
- /** Builds the CQ query start (interiors, then reach or the first main rule). */
1021
844
  function makeQueryStart(theory, interiors) {
1022
845
  const env = { interiors };
1023
846
  const start = {
@@ -1053,7 +876,6 @@ function makeQueryStart(theory, interiors) {
1053
876
  Object.freeze(start);
1054
877
  return start;
1055
878
  }
1056
- /** Builds the Reach query start (rec sealed; only the first main rule remains). */
1057
879
  function makeQueryReachStart(theory, interiors, rec) {
1058
880
  const env = { interiors, rec };
1059
881
  const start = {
@@ -1065,21 +887,9 @@ function makeQueryReachStart(theory, interiors, rec) {
1065
887
  Object.freeze(start);
1066
888
  return start;
1067
889
  }
1068
- /**
1069
- * Opens a query over a schema: `query(S).rule(r => ...)`, optionally with
1070
- * `interior` / `reach` first. Each `.rule` adds one conjunctive rule;
1071
- * multiple rules are the set union. The schema's law-computed class map and
1072
- * theory value ride into every rule builder — the join walls compare
1073
- * against the mint slots off it.
1074
- */
1075
890
  function query(theory) {
1076
891
  return makeQueryStart(theory, []);
1077
892
  }
1078
- /**
1079
- * Tags one closed-reference literal: the handle NAME, verified against the
1080
- * roster and translated to its declaration-order row id, tagged u64. THE
1081
- * single roster-verification point of the query surface.
1082
- */
1083
893
  function taggedHandleId(context, closed, value) {
1084
894
  if (typeof value !== "string") {
1085
895
  throw literalShapeError(context, `a ${closed.name} handle name (string)`, value);
@@ -1090,11 +900,6 @@ function taggedHandleId(context, closed, value) {
1090
900
  }
1091
901
  return { kind: "u64", value: BigInt(id) };
1092
902
  }
1093
- /**
1094
- * Tags one literal in an interval element domain: a bigint tags as the
1095
- * element (the membership typing rule's point side), an interval-shaped
1096
- * value as the interval (value equality).
1097
- */
1098
903
  function taggedAtElementDomain(context, element, value) {
1099
904
  if (typeof value === "bigint") {
1100
905
  if (element === "u64") {
@@ -1110,10 +915,6 @@ function taggedAtElementDomain(context, element, value) {
1110
915
  }
1111
916
  throw literalShapeError(context, "bigint (point) or { start, end } (interval)", value);
1112
917
  }
1113
- /**
1114
- * Tags one host literal at a FIELD position (atom bindings): the field's
1115
- * structural kind directs the tag, never a guess.
1116
- */
1117
918
  function taggedLiteral(context, field, value) {
1118
919
  const roster = rosterOf(field);
1119
920
  if (roster !== undefined) {
@@ -1167,12 +968,6 @@ function taggedLiteral(context, field, value) {
1167
968
  * interval shape against a scalar sibling stays refused.
1168
969
  */
1169
970
  function taggedCmpLiteral(context, sibling, value, op) {
1170
- if (sibling === "measure") {
1171
- if (typeof value !== "bigint") {
1172
- throw literalShapeError(context, "bigint (the measure is u64)", value);
1173
- }
1174
- return { kind: "u64", value };
1175
- }
1176
971
  if (rosterOf(sibling) === undefined && sibling.kind === "interval") {
1177
972
  return taggedAtElementDomain(context, sibling.element, value);
1178
973
  }
@@ -1184,7 +979,6 @@ function taggedCmpLiteral(context, sibling, value, op) {
1184
979
  }
1185
980
  return taggedLiteral(context, sibling, value);
1186
981
  }
1187
- /** Creates one rule-scoped variable numberer. */
1188
982
  function freshVarIds() {
1189
983
  const assigned = new Map();
1190
984
  return {
@@ -1199,7 +993,6 @@ function freshVarIds() {
1199
993
  }
1200
994
  };
1201
995
  }
1202
- /** Resolves a param name to its dense positional id. */
1203
996
  function paramIdOf(ctx, name) {
1204
997
  const id = ctx.paramIds.get(name);
1205
998
  if (id === undefined) {
@@ -1207,10 +1000,6 @@ function paramIdOf(ctx, name) {
1207
1000
  }
1208
1001
  return id;
1209
1002
  }
1210
- /**
1211
- * Lowers one EDB atom (either polarity). A CLOSED owner lowers through the
1212
- * same edb source, with field ordinals over the SEALED shape.
1213
- */
1214
1003
  function lowerAtom(ctx, atom, ids) {
1215
1004
  const member = ctx.theory.relations[atom.relation.name];
1216
1005
  if (member !== atom.relation) {
@@ -1232,7 +1021,6 @@ function lowerAtom(ctx, atom, ids) {
1232
1021
  });
1233
1022
  return { source: { kind: "edb", relation: relationId }, bindings };
1234
1023
  }
1235
- /** Lowers one binding term. A membership ARRAY lowers to the existing param-set term over its content-addressed entry. */
1236
1024
  function lowerBindingTerm(ctx, context, binding, ids) {
1237
1025
  const bound = binding.term;
1238
1026
  switch (bound.kind) {
@@ -1273,7 +1061,6 @@ function lowerInteriorAtom(ctx, target, bindings, ids) {
1273
1061
  });
1274
1062
  return { source: { kind: "interior", interior }, bindings: irBindings };
1275
1063
  }
1276
- /** Lowers one comparison side; literals tag by the sibling's anchor (op-aware at `pointIn`). */
1277
1064
  function lowerCmpTerm(ctx, side, sibling, ids, op) {
1278
1065
  switch (side.kind) {
1279
1066
  case "var":
@@ -1282,31 +1069,24 @@ function lowerCmpTerm(ctx, side, sibling, ids, op) {
1282
1069
  return { kind: "param", param: paramIdOf(ctx, side.name) };
1283
1070
  case "setParam":
1284
1071
  return { kind: "paramSet", param: paramIdOf(ctx, side.name) };
1285
- case "measure":
1286
- return { kind: "measure", var: ids.of(side.ref) };
1287
1072
  case "literal": {
1288
1073
  const anchor = cmpAnchorOf(ctx, sibling);
1289
1074
  if (anchor === undefined) {
1290
- throw errors.new("query lowering: a comparison literal needs a bound-variable, measure, or anchored-param sibling to type it");
1075
+ throw errors.new("query lowering: a comparison literal needs a bound-variable or anchored-param sibling to type it");
1291
1076
  }
1292
1077
  return { kind: "literal", value: taggedCmpLiteral("comparison literal", anchor, side.value, op) };
1293
1078
  }
1294
1079
  }
1295
1080
  }
1296
- /** Resolves the anchor a comparison literal tags by: the sibling variable's field, the measure, or an anchored param. */
1297
1081
  function cmpAnchorOf(ctx, sibling) {
1298
1082
  if (sibling.kind === "var") {
1299
1083
  return sibling.ref.field;
1300
1084
  }
1301
- if (sibling.kind === "measure") {
1302
- return "measure";
1303
- }
1304
1085
  if (sibling.kind === "param" || sibling.kind === "setParam") {
1305
1086
  return ctx.params.get(sibling.name)?.anchor;
1306
1087
  }
1307
1088
  return undefined;
1308
1089
  }
1309
- /** Lowers one comparison. */
1310
1090
  function lowerComparison(ctx, cmp, ids) {
1311
1091
  if (cmp.op.kind === "allen") {
1312
1092
  return {
@@ -1321,7 +1101,6 @@ function lowerComparison(ctx, cmp, ids) {
1321
1101
  rhs: lowerCmpTerm(ctx, cmp.rhs, cmp.lhs, ids, cmp.op.kind)
1322
1102
  };
1323
1103
  }
1324
- /** Lowers one condition node (comparison leaf or and/or tree). */
1325
1104
  function lowerCondition(ctx, cond, ids) {
1326
1105
  if (cond.kind === "cmp") {
1327
1106
  return { kind: "leaf", cmp: lowerComparison(ctx, cond, ids) };
@@ -1333,29 +1112,20 @@ function lowerCondition(ctx, cond, ids) {
1333
1112
  })
1334
1113
  };
1335
1114
  }
1336
- /** Lowers one find entry to its per-rule find term. */
1337
1115
  function lowerFind(entry, ids) {
1338
1116
  if (entry.kind === "var") {
1339
1117
  return { kind: "var", var: ids.of(entry.over) };
1340
1118
  }
1341
- if (entry.kind === "measure") {
1342
- return { kind: "measure", var: ids.of(entry.over) };
1343
- }
1344
1119
  const agg = entry.agg;
1345
1120
  switch (agg.op) {
1346
1121
  case "count":
1347
1122
  return { kind: "count" };
1348
- case "fold": {
1349
- if ("duration" in agg.over) {
1350
- return { kind: "aggregateMeasure", op: { kind: agg.fold }, over: ids.of(agg.over.duration) };
1351
- }
1123
+ case "fold":
1352
1124
  return { kind: "aggregate", op: { kind: agg.fold }, over: ids.of(agg.over) };
1353
- }
1354
1125
  case "pack":
1355
1126
  return { kind: "pack", over: ids.of(agg.over) };
1356
1127
  }
1357
1128
  }
1358
- /** One aggregate's var-free head-op kind (`AggOp::head_op`). */
1359
1129
  function headOpOf(agg) {
1360
1130
  switch (agg.op) {
1361
1131
  case "count":
@@ -1366,15 +1136,13 @@ function headOpOf(agg) {
1366
1136
  return "pack";
1367
1137
  }
1368
1138
  }
1369
- /** One find entry's var-free head shape. */
1370
1139
  function headTermOf(column) {
1371
1140
  const entry = column.entry;
1372
- if (entry.kind === "var" || entry.kind === "measure") {
1141
+ if (entry.kind === "var") {
1373
1142
  return { kind: "var" };
1374
1143
  }
1375
1144
  return { kind: "aggregate", op: headOpOf(entry.agg) };
1376
1145
  }
1377
- /** Lowers one rule: body walked in written order (var ids by first occurrence), finds last. */
1378
1146
  function lowerRule(ctx, rule) {
1379
1147
  const ids = freshVarIds();
1380
1148
  const atoms = [];
@@ -1413,11 +1181,6 @@ function lowerRule(ctx, rule) {
1413
1181
  conditions
1414
1182
  };
1415
1183
  }
1416
- /**
1417
- * Lowers a query value to the bridge's `QueryIr` — pure and stable:
1418
- * interiors in declaration order, then CQ or Reach, then main. Every
1419
- * registered param must carry a field anchor by now.
1420
- */
1421
1184
  function lowerQuery(q) {
1422
1185
  const theory = q.schema;
1423
1186
  const relationIds = new Map();