@suss/datalog 0.31.0 → 0.31.2

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.
package/dist/index.cjs CHANGED
@@ -20,8 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ BudgetExhausted: () => BudgetExhausted,
23
24
  Database: () => Database,
24
25
  Witness: () => Witness,
26
+ chargeAbandoned: () => chargeAbandoned,
27
+ chargeQuestion: () => chargeQuestion,
25
28
  clearRelations: () => clearRelations,
26
29
  confidence: () => confidence,
27
30
  confidenceWith: () => confidenceWith,
@@ -34,6 +37,7 @@ __export(index_exports, {
34
37
  profileEvaluation: () => profileEvaluation,
35
38
  profileEvaluationAsync: () => profileEvaluationAsync,
36
39
  proofOf: () => proofOf,
40
+ rowBudget: () => rowBudget,
37
41
  rule: () => rule,
38
42
  ruleLabel: () => ruleLabel,
39
43
  stratify: () => stratify,
@@ -73,6 +77,7 @@ function deriveOnDemand(rules, complete) {
73
77
  }
74
78
  }
75
79
  const derives = (relation) => rulesByHead.has(relation);
80
+ const labels = labelColumns(rules, derives);
76
81
  const asked = /* @__PURE__ */ new Map();
77
82
  const pending = [];
78
83
  const want = (adorned) => {
@@ -94,7 +99,7 @@ function deriveOnDemand(rules, complete) {
94
99
  while (pending.length > 0) {
95
100
  const adorned = pending.pop();
96
101
  for (const written of rulesByHead.get(adorned.relation) ?? []) {
97
- const r = boundFirst(written, adorned.adornment);
102
+ const r = boundFirst(written, adorned.adornment, labels);
98
103
  const adornments = bodyAdornments(r, adorned.adornment, derives);
99
104
  r.body.forEach((literal, at) => {
100
105
  const adornment = adornments[at];
@@ -137,7 +142,7 @@ function deriveOnDemand(rules, complete) {
137
142
  });
138
143
  }
139
144
  for (const written of rulesByHead.get(adorned.relation) ?? []) {
140
- const r = boundFirst(written, adorned.adornment);
145
+ const r = boundFirst(written, adorned.adornment, labels);
141
146
  const guard = anyBound(adorned.adornment) ? [
142
147
  positiveLiteral(
143
148
  demandOn(adorned.relation, adorned.adornment),
@@ -178,7 +183,30 @@ function deriveOnDemand(rules, complete) {
178
183
  return { rules: rewritten, demandDriven: [...demandDriven], demands };
179
184
  }
180
185
  var sameLiteral = (premises, conclusion) => premises.length === 1 && JSON.stringify(premises[0]) === JSON.stringify(conclusion);
181
- function boundFirst(r, headAdornment) {
186
+ function labelColumns(rules, derives) {
187
+ const labels = /* @__PURE__ */ new Map();
188
+ const note = ({
189
+ relation,
190
+ terms
191
+ }) => {
192
+ if (!derives(relation)) {
193
+ return;
194
+ }
195
+ terms.forEach((term, column) => {
196
+ if (term.type === "constant") {
197
+ const columns = labels.get(relation) ?? /* @__PURE__ */ new Set();
198
+ columns.add(column);
199
+ labels.set(relation, columns);
200
+ }
201
+ });
202
+ };
203
+ for (const r of rules) {
204
+ note(r.head);
205
+ r.body.forEach(note);
206
+ }
207
+ return labels;
208
+ }
209
+ function boundFirst(r, headAdornment, labels) {
182
210
  const bound = /* @__PURE__ */ new Set();
183
211
  r.head.terms.forEach((term, column) => {
184
212
  if (headAdornment[column] && term.type === "variable") {
@@ -186,7 +214,7 @@ function boundFirst(r, headAdornment) {
186
214
  }
187
215
  });
188
216
  const boundColumns = (literal) => literal.terms.filter(
189
- (term) => term.type === "constant" || bound.has(term.name)
217
+ (term, column) => !labels.get(literal.relation)?.has(column) && (term.type === "constant" || bound.has(term.name))
190
218
  ).length;
191
219
  const remaining = [...r.body];
192
220
  const body = [];
@@ -231,21 +259,43 @@ function bodyAdornments(r, headAdornment, derives) {
231
259
  var open = [];
232
260
  var isProfiling = () => open.length > 0;
233
261
  var ruleKey = (head, body) => `${head} <- ${body.join(", ")}`;
234
- function addCost(into, key, head, body, ms, derived) {
262
+ function addCost(into, key, head, body, ms, derived, examined) {
235
263
  const existing = into.get(key);
236
264
  if (existing === void 0) {
237
- into.set(key, { head, body, ms, derived, attempts: 1 });
265
+ into.set(key, { head, body, ms, derived, examined, attempts: 1 });
238
266
  return;
239
267
  }
240
268
  existing.ms += ms;
241
269
  existing.derived += derived;
270
+ existing.examined += examined;
242
271
  existing.attempts += 1;
243
272
  }
244
- function chargeRule(ruleSet, head, body, ms, derived) {
273
+ function chargeRule(ruleSet, head, body, ms, derived, examined) {
245
274
  const key = ruleKey(head, body);
246
275
  for (const collector of open) {
247
- addCost(collector.rules, key, head, body, ms, derived);
248
- addCost(setIn(collector, ruleSet).rules, key, head, body, ms, derived);
276
+ addCost(collector.rules, key, head, body, ms, derived, examined);
277
+ addCost(
278
+ setIn(collector, ruleSet).rules,
279
+ key,
280
+ head,
281
+ body,
282
+ ms,
283
+ derived,
284
+ examined
285
+ );
286
+ }
287
+ }
288
+ function chargeAbandoned(question, examined) {
289
+ for (const collector of open) {
290
+ collector.abandoned.push({ question, examined });
291
+ }
292
+ }
293
+ function chargeQuestion(outcome) {
294
+ for (const collector of open) {
295
+ collector.questions.asked += 1;
296
+ if (outcome !== void 0) {
297
+ collector.questions[outcome] += 1;
298
+ }
249
299
  }
250
300
  }
251
301
  function chargeEvaluation(ruleSet) {
@@ -254,6 +304,13 @@ function chargeEvaluation(ruleSet) {
254
304
  setIn(collector, ruleSet).evaluations += 1;
255
305
  }
256
306
  }
307
+ function chargeEvaluationRows(examined) {
308
+ for (const collector of open) {
309
+ if (examined > collector.largestEvaluation) {
310
+ collector.largestEvaluation = examined;
311
+ }
312
+ }
313
+ }
257
314
  function chargeRound(ruleSet) {
258
315
  for (const collector of open) {
259
316
  collector.rounds += 1;
@@ -313,7 +370,10 @@ function openScope() {
313
370
  rules: /* @__PURE__ */ new Map(),
314
371
  relations: /* @__PURE__ */ new Map(),
315
372
  derivedRelations: /* @__PURE__ */ new Set(),
316
- ruleSets: /* @__PURE__ */ new Map()
373
+ ruleSets: /* @__PURE__ */ new Map(),
374
+ abandoned: [],
375
+ largestEvaluation: 0,
376
+ questions: { asked: 0, abandoned: 0, skipped: 0 }
317
377
  };
318
378
  open.push(mine);
319
379
  return mine;
@@ -331,6 +391,13 @@ var totalMs = (rules) => {
331
391
  }
332
392
  return ms;
333
393
  };
394
+ var totalExamined = (rules) => {
395
+ let rows = 0;
396
+ for (const cost of rules) {
397
+ rows += cost.examined;
398
+ }
399
+ return rows;
400
+ };
334
401
  var byCost = (a, b) => b.ms - a.ms;
335
402
  function summarise(mine) {
336
403
  return {
@@ -338,6 +405,10 @@ function summarise(mine) {
338
405
  datalogMs: totalMs(mine.rules.values()),
339
406
  rounds: mine.rounds,
340
407
  evaluations: mine.evaluations,
408
+ examined: totalExamined(mine.rules.values()),
409
+ largestEvaluation: mine.largestEvaluation,
410
+ abandoned: [...mine.abandoned],
411
+ questions: { ...mine.questions },
341
412
  relations: [...mine.relations].map(([relation, tuples]) => ({
342
413
  relation,
343
414
  tuples,
@@ -358,14 +429,26 @@ function ruleLines(rules, datalogMs) {
358
429
  return rules.slice(0, 20).map((r) => {
359
430
  const ms = `${r.ms.toFixed(0)}ms`.padStart(8);
360
431
  const derived = String(r.derived).padStart(7);
361
- return ` ${ms} ${share(r.ms, datalogMs)} ${derived} tuples ${r.attempts} attempts ${ruleKey(r.head, r.body)}`;
432
+ const examined = String(r.examined).padStart(9);
433
+ return ` ${ms} ${share(r.ms, datalogMs)} ${derived} tuples ${examined} rows read ${r.attempts} attempts ${ruleKey(r.head, r.body)}`;
362
434
  });
363
435
  }
364
436
  function formatProfile(profile) {
365
437
  const lines = [];
366
438
  lines.push(
367
- `datalog: ${profile.datalogMs.toFixed(0)}ms (${share(profile.datalogMs, profile.wallMs).trim()} of ${profile.wallMs.toFixed(0)}ms wall), ${profile.evaluations} evaluations, ${profile.rounds} rounds`
439
+ `datalog: ${profile.datalogMs.toFixed(0)}ms (${share(profile.datalogMs, profile.wallMs).trim()} of ${profile.wallMs.toFixed(0)}ms wall), ${profile.evaluations} evaluations, ${profile.rounds} rounds, ${profile.examined} rows read (${profile.largestEvaluation} in the biggest evaluation)`
368
440
  );
441
+ const { asked, abandoned, skipped } = profile.questions;
442
+ if (asked > 0) {
443
+ lines.push(
444
+ ` ${asked} questions under a site: ${abandoned} given up part way, ${skipped} never put once the run's rows were spent`
445
+ );
446
+ }
447
+ for (const given of profile.abandoned) {
448
+ lines.push(
449
+ ` gave up on ${given.question} after reading ${given.examined} rows`
450
+ );
451
+ }
369
452
  const derivedTuples = profile.relations.filter((r) => r.derived).reduce((sum, r) => sum + r.tuples, 0);
370
453
  lines.push(" tuples by relation (d = derived by a rule):");
371
454
  for (const { relation, tuples, derived } of profile.relations.slice(0, 20)) {
@@ -779,93 +862,109 @@ function headTuple(head, bindings) {
779
862
  return bound;
780
863
  });
781
864
  }
782
- var variablesOf = (literal) => literal.terms.filter((term) => term.type === "variable").map((term) => term.name);
783
- var plans = /* @__PURE__ */ new WeakMap();
784
- function bodyPlan(r, deltaIndex) {
785
- let byDelta = plans.get(r);
786
- if (byDelta === void 0) {
787
- byDelta = /* @__PURE__ */ new Map();
788
- plans.set(r, byDelta);
789
- }
790
- const known = byDelta.get(deltaIndex);
791
- if (known !== void 0) {
792
- return known;
793
- }
794
- const positives = r.body.map((literal, index) => literal.negated ? -1 : index).filter((index) => index !== -1);
795
- const deltaAt = positives[deltaIndex] ?? -1;
796
- const computed = { deltaAt, order: planBodyOrder(r, deltaAt) };
797
- byDelta.set(deltaIndex, computed);
798
- return computed;
865
+ var positiveLiterals = /* @__PURE__ */ new WeakMap();
866
+ function deltaLiteral(r, deltaIndex) {
867
+ let positives = positiveLiterals.get(r);
868
+ if (positives === void 0) {
869
+ positives = r.body.map((literal, index) => literal.negated ? -1 : index).filter((index) => index !== -1);
870
+ positiveLiterals.set(r, positives);
871
+ }
872
+ return positives[deltaIndex] ?? -1;
799
873
  }
800
- function planBodyOrder(r, deltaAt) {
801
- const natural = r.body.map((_, i) => i);
802
- if (deltaAt <= 0) {
803
- return natural;
804
- }
805
- const taken = r.body.map(() => false);
806
- const bound = /* @__PURE__ */ new Set();
807
- const order = [];
808
- const take = (index) => {
809
- taken[index] = true;
810
- order.push(index);
811
- for (const name of variablesOf(r.body[index])) {
812
- bound.add(name);
813
- }
814
- };
815
- const firstWhere = (want) => natural.find((index) => !taken[index] && want(index)) ?? -1;
816
- const nextLiteral = () => {
817
- const ready = firstWhere(
818
- (i) => r.body[i].negated && variablesOf(r.body[i]).every((name) => bound.has(name))
819
- );
820
- if (ready !== -1) {
821
- return ready;
822
- }
823
- const joined = firstWhere(
824
- (i) => !r.body[i].negated && variablesOf(r.body[i]).some((name) => bound.has(name))
825
- );
826
- if (joined !== -1) {
827
- return joined;
828
- }
829
- const disconnected = firstWhere((i) => !r.body[i].negated);
830
- if (disconnected !== -1) {
831
- return disconnected;
874
+ var allBound = (literal, bindings) => literal.terms.every(
875
+ (term) => term.type === "constant" || boundValue(bindings, term.name) !== void 0
876
+ );
877
+ var rowBudget = (limit) => ({
878
+ examined: 0,
879
+ limit
880
+ });
881
+ var OUT_OF_BUDGET = /* @__PURE__ */ Symbol("datalog row budget exhausted");
882
+ var BudgetExhausted = class extends Error {
883
+ constructor(ruleSet, examined) {
884
+ super(`gave up after reading ${examined} rows without settling`);
885
+ this.ruleSet = ruleSet;
886
+ this.examined = examined;
887
+ this.name = "BudgetExhausted";
888
+ }
889
+ ruleSet;
890
+ examined;
891
+ };
892
+ function evaluateRule(db, deltas, r, deltaIndex, budget) {
893
+ const results = [];
894
+ const body = r.body;
895
+ const deltaAt = deltaLiteral(r, deltaIndex);
896
+ const whole = 2 ** body.length - 1;
897
+ const walk2 = (index, source, taken, bindings) => {
898
+ const literal = body[index];
899
+ for (const tuple of source) {
900
+ budget.examined++;
901
+ if (budget.examined > budget.limit) {
902
+ throw OUT_OF_BUDGET;
903
+ }
904
+ const next = unify(literal, tuple, bindings);
905
+ if (next !== NO_MATCH) {
906
+ step(taken | 1 << index, next);
907
+ }
832
908
  }
833
- return firstWhere(() => true);
834
909
  };
835
- take(deltaAt);
836
- while (order.length < r.body.length) {
837
- take(nextLiteral());
838
- }
839
- return order;
840
- }
841
- function evaluateRule(db, deltas, r, deltaIndex) {
842
- const results = [];
843
- const { deltaAt, order } = bodyPlan(r, deltaIndex);
844
- const step = (orderIndex, bindings) => {
845
- if (orderIndex === order.length) {
910
+ const step = (taken, bindings) => {
911
+ if (taken === whole) {
846
912
  results.push(headTuple(r.head, bindings));
847
913
  return;
848
914
  }
849
- const literalIndex = order[orderIndex];
850
- const literal = r.body[literalIndex];
851
- if (literal.negated) {
852
- if (!db.has(literal.relation, groundNegated(literal, bindings))) {
853
- step(orderIndex + 1, bindings);
915
+ let pick = -1;
916
+ let narrowest = null;
917
+ for (let index = 0; index < body.length; index++) {
918
+ if (taken & 1 << index) {
919
+ continue;
920
+ }
921
+ const literal = body[index];
922
+ if (literal.negated) {
923
+ if (allBound(literal, bindings)) {
924
+ if (!db.has(literal.relation, groundNegated(literal, bindings))) {
925
+ step(taken | 1 << index, bindings);
926
+ }
927
+ return;
928
+ }
929
+ continue;
930
+ }
931
+ const source = narrowedSource(db, literal, bindings);
932
+ if (source === null) {
933
+ continue;
934
+ }
935
+ if (source.length === 0) {
936
+ return;
937
+ }
938
+ if (narrowest === null || source.length < narrowest.length) {
939
+ pick = index;
940
+ narrowest = source;
854
941
  }
942
+ }
943
+ if (narrowest !== null) {
944
+ walk2(pick, narrowest, taken, bindings);
855
945
  return;
856
946
  }
857
- const source = literalIndex === deltaAt ? deltas.get(literal.relation) ?? [] : boundSource(db, literal, bindings);
858
- for (const tuple of source) {
859
- const next = unify(literal, tuple, bindings);
860
- if (next !== NO_MATCH) {
861
- step(orderIndex + 1, next);
947
+ for (let index = 0; index < body.length; index++) {
948
+ if (!(taken & 1 << index) && !body[index].negated) {
949
+ walk2(index, db.facts(body[index].relation), taken, bindings);
950
+ return;
951
+ }
952
+ }
953
+ for (let index = 0; index < body.length; index++) {
954
+ if (!(taken & 1 << index)) {
955
+ groundNegated(body[index], bindings);
862
956
  }
863
957
  }
864
958
  };
865
- step(0, null);
959
+ if (deltaAt === -1) {
960
+ step(0, null);
961
+ } else {
962
+ walk2(deltaAt, deltas.get(body[deltaAt].relation) ?? [], 0, null);
963
+ }
866
964
  return results;
867
965
  }
868
- function boundSource(db, literal, bindings) {
966
+ var boundSource = (db, literal, bindings) => narrowedSource(db, literal, bindings) ?? db.facts(literal.relation);
967
+ function narrowedSource(db, literal, bindings) {
869
968
  let narrowest = null;
870
969
  for (let column = 0; column < literal.terms.length; column++) {
871
970
  const term = literal.terms[column];
@@ -881,10 +980,10 @@ function boundSource(db, literal, bindings) {
881
980
  narrowest = bucket;
882
981
  }
883
982
  }
884
- return narrowest ?? db.facts(literal.relation);
983
+ return narrowest;
885
984
  }
886
985
  var EMPTY_TAGS = [];
887
- function evaluateRuleTagged(db, deltas, r, deltaIndex, algebra) {
986
+ function evaluateRuleTagged(db, deltas, r, deltaIndex, algebra, budget) {
888
987
  const results = [];
889
988
  const bodyTags = [];
890
989
  const bodyMatches = [];
@@ -922,6 +1021,7 @@ function evaluateRuleTagged(db, deltas, r, deltaIndex, algebra) {
922
1021
  }
923
1022
  const source = positiveIndex === deltaIndex ? deltas.get(literal.relation) ?? [] : boundSource(db, literal, bindings);
924
1023
  for (const tuple of source) {
1024
+ budget.examined++;
925
1025
  const next = unify(literal, tuple, bindings);
926
1026
  if (next !== NO_MATCH) {
927
1027
  if (readsTags) {
@@ -1005,15 +1105,18 @@ function currentMarks(db) {
1005
1105
  }
1006
1106
  return marks;
1007
1107
  }
1008
- function evaluate(db, rules, algebra) {
1108
+ function evaluate(db, rules, algebra, budget) {
1009
1109
  if (algebra !== void 0 && isDemandRewritten(rules)) {
1010
1110
  throw new Error(
1011
1111
  "cannot evaluate demand-rewritten rules with a tag algebra: tags would follow the demand-transformed rules rather than the ones the caller wrote"
1012
1112
  );
1013
1113
  }
1114
+ if (algebra !== void 0 && budget !== void 0) {
1115
+ throw new Error("cannot evaluate with both a tag algebra and a row budget");
1116
+ }
1014
1117
  deriving.set(db, (deriving.get(db) ?? 0) + 1);
1015
1118
  try {
1016
- return runRules(db, rules, algebra);
1119
+ return runRules(db, rules, algebra, budget);
1017
1120
  } finally {
1018
1121
  const depth = (deriving.get(db) ?? 1) - 1;
1019
1122
  if (depth === 0) {
@@ -1063,13 +1166,19 @@ function storedMergeFor(algebra) {
1063
1166
  return stored === void 0 && merged === algebra.asserted ? void 0 : merged;
1064
1167
  };
1065
1168
  }
1066
- function runRules(db, rules, algebra) {
1169
+ function runRules(db, rules, algebra, given) {
1067
1170
  const {
1068
1171
  signature,
1069
1172
  name: ruleSetName,
1070
1173
  derivedRelations,
1071
1174
  strata
1072
1175
  } = shapeOf(rules);
1176
+ const budget = given ?? {
1177
+ examined: 0,
1178
+ limit: Number.POSITIVE_INFINITY
1179
+ };
1180
+ const startedAt = budget.examined;
1181
+ const addedHere = given === void 0 ? null : [];
1073
1182
  chargeEvaluation(ruleSetName);
1074
1183
  const states = statesFor(db);
1075
1184
  const state = states.get(signature) ?? {
@@ -1091,10 +1200,11 @@ function runRules(db, rules, algebra) {
1091
1200
  derived.set(relation, ledger);
1092
1201
  }
1093
1202
  ledger.set(keyOf2(tuple), tuple);
1203
+ addedHere?.push([relation, tuple]);
1094
1204
  };
1095
1205
  const mergeStored = algebra === void 0 ? void 0 : storedMergeFor(algebra);
1096
1206
  const marks = canResume(rules, state) ? state.marks : void 0;
1097
- for (const stratum of strata) {
1207
+ const runStratum = (stratum) => {
1098
1208
  let delta = /* @__PURE__ */ new Map();
1099
1209
  const derivedHere = new Set(stratum.map((r) => r.head.relation));
1100
1210
  const record = (relation, tuple) => {
@@ -1120,13 +1230,14 @@ function runRules(db, rules, algebra) {
1120
1230
  seed,
1121
1231
  r,
1122
1232
  deltaIndex,
1123
- algebra
1233
+ algebra,
1234
+ budget
1124
1235
  )) {
1125
1236
  recordTagged(r.head.relation, found.tuple, found.tag);
1126
1237
  }
1127
1238
  return;
1128
1239
  }
1129
- for (const tuple of evaluateRule(db, seed, r, deltaIndex)) {
1240
+ for (const tuple of evaluateRule(db, seed, r, deltaIndex, budget)) {
1130
1241
  record(r.head.relation, tuple);
1131
1242
  }
1132
1243
  };
@@ -1135,15 +1246,17 @@ function runRules(db, rules, algebra) {
1135
1246
  deriveInto(r, seed, deltaIndex);
1136
1247
  return;
1137
1248
  }
1138
- const startedAt = performance.now();
1249
+ const startedAt2 = performance.now();
1139
1250
  const before = db.size(r.head.relation);
1251
+ const readBefore = budget.examined;
1140
1252
  deriveInto(r, seed, deltaIndex);
1141
1253
  chargeRule(
1142
1254
  ruleSetName,
1143
1255
  r.head.relation,
1144
1256
  r.body.map(literalName),
1145
- performance.now() - startedAt,
1146
- db.size(r.head.relation) - before
1257
+ performance.now() - startedAt2,
1258
+ db.size(r.head.relation) - before,
1259
+ budget.examined - readBefore
1147
1260
  );
1148
1261
  };
1149
1262
  const applyDelta = (seed, derivedOnly) => {
@@ -1188,7 +1301,24 @@ function runRules(db, rules, algebra) {
1188
1301
  applyDelta(lastDelta, true);
1189
1302
  chargeRound(ruleSetName);
1190
1303
  }
1304
+ };
1305
+ const readHere = () => budget.examined - startedAt;
1306
+ try {
1307
+ for (const stratum of strata) {
1308
+ runStratum(stratum);
1309
+ }
1310
+ } catch (error) {
1311
+ if (error !== OUT_OF_BUDGET) {
1312
+ throw error;
1313
+ }
1314
+ for (const [relation, tuples] of byRelation(addedHere ?? [])) {
1315
+ db.retract(relation, tuples);
1316
+ }
1317
+ state.marks = null;
1318
+ chargeEvaluationRows(readHere());
1319
+ throw new BudgetExhausted(ruleSetName, readHere());
1191
1320
  }
1321
+ chargeEvaluationRows(readHere());
1192
1322
  state.marks = currentMarks(db);
1193
1323
  if (isProfiling()) {
1194
1324
  chargeRelationSizes(
@@ -1198,6 +1328,13 @@ function runRules(db, rules, algebra) {
1198
1328
  }
1199
1329
  return db;
1200
1330
  }
1331
+ function byRelation(added) {
1332
+ const buckets = /* @__PURE__ */ new Map();
1333
+ for (const [relation, tuple] of added) {
1334
+ addTo(buckets, relation, tuple);
1335
+ }
1336
+ return buckets;
1337
+ }
1201
1338
  function addTo(buckets, relation, tuple) {
1202
1339
  const bucket = buckets.get(relation);
1203
1340
  if (bucket === void 0) {
@@ -1208,8 +1345,11 @@ function addTo(buckets, relation, tuple) {
1208
1345
  }
1209
1346
  // Annotate the CommonJS export names for ESM import in node:
1210
1347
  0 && (module.exports = {
1348
+ BudgetExhausted,
1211
1349
  Database,
1212
1350
  Witness,
1351
+ chargeAbandoned,
1352
+ chargeQuestion,
1213
1353
  clearRelations,
1214
1354
  confidence,
1215
1355
  confidenceWith,
@@ -1222,6 +1362,7 @@ function addTo(buckets, relation, tuple) {
1222
1362
  profileEvaluation,
1223
1363
  profileEvaluationAsync,
1224
1364
  proofOf,
1365
+ rowBudget,
1225
1366
  rule,
1226
1367
  ruleLabel,
1227
1368
  stratify,