@suss/datalog 0.38.0 → 0.38.1

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.js CHANGED
@@ -711,7 +711,7 @@ function addToBucket(buckets, key, tuple) {
711
711
  bucket.push(tuple);
712
712
  }
713
713
  }
714
- function bucketIn(relation, column, value) {
714
+ function columnIndex(relation, column) {
715
715
  let buckets = relation.columns[column];
716
716
  if (buckets === void 0) {
717
717
  buckets = /* @__PURE__ */ new Map();
@@ -723,7 +723,279 @@ function bucketIn(relation, column, value) {
723
723
  }
724
724
  relation.columns[column] = buckets;
725
725
  }
726
- return tuplesIn(buckets.get(value));
726
+ return buckets;
727
+ }
728
+ function bucketIn(relation, column, value) {
729
+ return tuplesIn(columnIndex(relation, column).get(value));
730
+ }
731
+ function bucketSize(relation, column, value) {
732
+ const bucket = columnIndex(relation, column).get(value);
733
+ if (bucket === void 0) {
734
+ return 0;
735
+ }
736
+ return isLone(bucket) ? 1 : bucket.length;
737
+ }
738
+
739
+ // src/reads.ts
740
+ function noteIn(reads, column, value) {
741
+ if (reads.whole) {
742
+ return;
743
+ }
744
+ let values = reads.columns[column];
745
+ if (values === void 0) {
746
+ values = /* @__PURE__ */ new Set();
747
+ reads.columns[column] = values;
748
+ }
749
+ values.add(value);
750
+ }
751
+ var ReadLog = class {
752
+ constructor(skipped) {
753
+ this.skipped = skipped;
754
+ }
755
+ skipped;
756
+ /** Null for a relation the caller said to leave out. */
757
+ byRelation = /* @__PURE__ */ new Map();
758
+ lookedUp(relation, column, value) {
759
+ const reads = this.readsOf(relation);
760
+ if (reads !== null) {
761
+ noteIn(reads, column, value);
762
+ }
763
+ }
764
+ readWhole(relation) {
765
+ const reads = this.readsOf(relation);
766
+ if (reads !== null) {
767
+ reads.whole = true;
768
+ }
769
+ }
770
+ /** Note everything an inner log noted, for an answer cached inside another. */
771
+ absorb(inner) {
772
+ for (const [relation, reads] of inner.byRelation) {
773
+ if (reads === null) {
774
+ continue;
775
+ }
776
+ if (reads.whole) {
777
+ this.readWhole(relation);
778
+ continue;
779
+ }
780
+ reads.columns.forEach((values, column) => {
781
+ for (const value of values ?? []) {
782
+ this.lookedUp(relation, column, value);
783
+ }
784
+ });
785
+ }
786
+ }
787
+ /**
788
+ * The reads with what each one finds in `db` now. One question looks
789
+ * up the same values in many columns, so each value is kept once, and
790
+ * each column keeps a bit per value saying whether it was looked up
791
+ * there.
792
+ */
793
+ counted(db) {
794
+ const names = [];
795
+ const groups = [];
796
+ const columns = [];
797
+ const lookedUp = [];
798
+ const positions = /* @__PURE__ */ new Map();
799
+ const values = [];
800
+ const addGroup = (column, seen) => {
801
+ groups.push(names.length - 1);
802
+ columns.push(column);
803
+ lookedUp.push(seen);
804
+ for (const value of seen) {
805
+ if (!positions.has(value)) {
806
+ positions.set(value, values.length);
807
+ values.push(value);
808
+ }
809
+ }
810
+ };
811
+ for (const [relation, reads] of this.byRelation) {
812
+ if (reads === null) {
813
+ continue;
814
+ }
815
+ names.push(relation);
816
+ if (reads.whole) {
817
+ addGroup(WHOLE, NONE);
818
+ continue;
819
+ }
820
+ reads.columns.forEach((seen, column) => {
821
+ if (seen !== void 0) {
822
+ addGroup(column, seen);
823
+ }
824
+ });
825
+ }
826
+ const words = Math.ceil(values.length / 32);
827
+ const bits = new Uint32Array(groups.length * words);
828
+ let found = 0;
829
+ lookedUp.forEach((seen, group) => {
830
+ const relation = db.relationOf(names[groups[group]]);
831
+ const column = columns[group];
832
+ if (column === WHOLE) {
833
+ found += relation?.tuples.length ?? 0;
834
+ }
835
+ for (const value of seen) {
836
+ const at = positions.get(value);
837
+ bits[group * words + (at >>> 5)] |= 1 << (at & 31);
838
+ found += relation === void 0 ? 0 : bucketSize(relation, column, value);
839
+ }
840
+ });
841
+ return new ReadsNoted(
842
+ db,
843
+ this.skipped,
844
+ { names, groups, columns, values, bits },
845
+ found
846
+ );
847
+ }
848
+ /** @internal Null for a relation the caller said to leave out. */
849
+ readsOf(relation) {
850
+ let reads = this.byRelation.get(relation);
851
+ if (reads === void 0) {
852
+ reads = this.skipped.has(relation) ? null : { whole: false, columns: [] };
853
+ this.byRelation.set(relation, reads);
854
+ }
855
+ return reads;
856
+ }
857
+ };
858
+ var WHOLE = -1;
859
+ var NONE = /* @__PURE__ */ new Set();
860
+ var ReadsNoted = class {
861
+ constructor(db, skipped, lookups, found) {
862
+ this.skipped = skipped;
863
+ this.found = found;
864
+ this.names = lookups.names;
865
+ this.groups = lookups.groups;
866
+ this.columns = lookups.columns;
867
+ this.values = lookups.values;
868
+ this.bits = lookups.bits;
869
+ this.words = Math.ceil(lookups.values.length / 32);
870
+ this.relations = lookups.names.map((name) => db.relationOf(name));
871
+ this.removed = this.sumOver(db, removedFrom);
872
+ this.facts = this.sumOver(db, factsIn);
873
+ }
874
+ skipped;
875
+ found;
876
+ names;
877
+ groups;
878
+ columns;
879
+ values;
880
+ bits;
881
+ words;
882
+ removed;
883
+ /** The facts in every relation read, when the counts last matched. */
884
+ facts;
885
+ /** Each relation read, found by name the first time a check needs it. */
886
+ relations;
887
+ /**
888
+ * Whether every read would find what it found then. A fact left in a
889
+ * skipped relation would join a question asked now without anything
890
+ * here having looked it up, so one there makes the answer no. The
891
+ * counts are taken again only when a relation read has grown.
892
+ */
893
+ unchangedIn(db) {
894
+ if (!allEmpty(db, this.skipped)) {
895
+ return false;
896
+ }
897
+ if (this.sumOver(db, removedFrom) !== this.removed) {
898
+ return false;
899
+ }
900
+ const facts = this.sumOver(db, factsIn);
901
+ if (facts === this.facts) {
902
+ return true;
903
+ }
904
+ if (this.countIn(db, this.found) !== this.found) {
905
+ return false;
906
+ }
907
+ this.facts = facts;
908
+ return true;
909
+ }
910
+ relationAt(db, at) {
911
+ let relation = this.relations[at];
912
+ if (relation === void 0) {
913
+ relation = db.relationOf(this.names[at]);
914
+ this.relations[at] = relation;
915
+ }
916
+ return relation;
917
+ }
918
+ sumOver(db, measure) {
919
+ let sum = 0;
920
+ for (let at = 0; at < this.names.length; at++) {
921
+ const relation = this.relationAt(db, at);
922
+ sum += relation === void 0 ? 0 : measure(relation);
923
+ }
924
+ return sum;
925
+ }
926
+ /**
927
+ * The facts the reads find, stopping once past `limit`. With nothing
928
+ * retracted every count only grows, so passing the old total already
929
+ * says that one of them grew.
930
+ */
931
+ countIn(db, limit) {
932
+ let found = 0;
933
+ for (let group = 0; group < this.groups.length && found <= limit; group++) {
934
+ const relation = this.relationAt(db, this.groups[group]);
935
+ const column = this.columns[group];
936
+ if (column === WHOLE) {
937
+ found += relation?.tuples.length ?? 0;
938
+ } else if (relation !== void 0) {
939
+ found += this.countGroup(relation, group, column);
940
+ }
941
+ }
942
+ return found;
943
+ }
944
+ countGroup(relation, group, column) {
945
+ let found = 0;
946
+ const first = group * this.words;
947
+ for (let word = 0; word < this.words; word++) {
948
+ let left = this.bits[first + word];
949
+ while (left !== 0) {
950
+ const bit = 31 - Math.clz32(left & -left);
951
+ left &= left - 1;
952
+ const value = this.values[word * 32 + bit];
953
+ found += bucketSize(relation, column, value);
954
+ }
955
+ }
956
+ return found;
957
+ }
958
+ };
959
+ var removedFrom = (relation) => relation.removed;
960
+ var factsIn = (relation) => relation.tuples.length;
961
+ var skippedIn = /* @__PURE__ */ new WeakMap();
962
+ function allEmpty(db, skipped) {
963
+ let bySet = skippedIn.get(db);
964
+ if (bySet === void 0) {
965
+ bySet = /* @__PURE__ */ new WeakMap();
966
+ skippedIn.set(db, bySet);
967
+ }
968
+ let known = bySet.get(skipped);
969
+ if (known === void 0) {
970
+ known = { found: [], missing: [...skipped] };
971
+ bySet.set(skipped, known);
972
+ }
973
+ if (known.missing.length > 0) {
974
+ const stillMissing = [];
975
+ for (const name of known.missing) {
976
+ const relation = db.relationOf(name);
977
+ if (relation === void 0) {
978
+ stillMissing.push(name);
979
+ } else {
980
+ known.found.push(relation);
981
+ }
982
+ }
983
+ known.missing = stillMissing;
984
+ }
985
+ return known.found.every((relation) => relation.tuples.length === 0);
986
+ }
987
+ function noteReads(db, skipped, work) {
988
+ const outer = db.readLog;
989
+ const log = new ReadLog(skipped);
990
+ const emptyAtStart = allEmpty(db, skipped);
991
+ db.readLog = log;
992
+ try {
993
+ const value = work();
994
+ return { value, reads: emptyAtStart ? log.counted(db) : null };
995
+ } finally {
996
+ db.readLog = outer;
997
+ outer?.absorb(log);
998
+ }
727
999
  }
728
1000
 
729
1001
  // src/stratum.ts
@@ -749,7 +1021,7 @@ function planStratum(rules) {
749
1021
  }
750
1022
  return { rules, derived, readers, marked: new Uint8Array(rules.length) };
751
1023
  }
752
- var NONE = [];
1024
+ var NONE2 = [];
753
1025
  function rulesReading(stratum, seed, derivedOnly) {
754
1026
  const lists = [];
755
1027
  for (const [relation, tuples] of seed) {
@@ -765,7 +1037,7 @@ function rulesReading(stratum, seed, derivedOnly) {
765
1037
  }
766
1038
  }
767
1039
  if (lists.length === 0) {
768
- return NONE;
1040
+ return NONE2;
769
1041
  }
770
1042
  if (lists.length === 1) {
771
1043
  return lists[0];
@@ -935,6 +1207,8 @@ var ruleLabel = (r) => r.name ?? `${r.head.relation} :- ${r.body.map(literalName
935
1207
  var FEW_REMOVED = 16;
936
1208
  var Database = class {
937
1209
  store = /* @__PURE__ */ new Map();
1210
+ /** @internal Where reads are noted while `noteReads` runs. */
1211
+ readLog = null;
938
1212
  relation(name) {
939
1213
  const existing = this.store.get(name);
940
1214
  if (existing !== void 0) {
@@ -944,7 +1218,8 @@ var Database = class {
944
1218
  index: new FactIndex(),
945
1219
  tuples: [],
946
1220
  columns: [],
947
- combined: /* @__PURE__ */ new Map()
1221
+ combined: /* @__PURE__ */ new Map(),
1222
+ removed: 0
948
1223
  };
949
1224
  this.store.set(name, created);
950
1225
  return created;
@@ -999,6 +1274,7 @@ var Database = class {
999
1274
  }
1000
1275
  /** The tag stored for this fact, or undefined when there is none. */
1001
1276
  tagOf(relationName, tuple) {
1277
+ this.noteTupleRead(relationName, tuple);
1002
1278
  const relation = this.store.get(relationName);
1003
1279
  if (relation === void 0) {
1004
1280
  return void 0;
@@ -1007,9 +1283,20 @@ var Database = class {
1007
1283
  return key === void 0 ? void 0 : relation.index.tagOf(key);
1008
1284
  }
1009
1285
  has(relationName, tuple) {
1286
+ this.noteTupleRead(relationName, tuple);
1010
1287
  return this.store.get(relationName)?.index.find(tuple) !== void 0;
1011
1288
  }
1289
+ /** A fact matching the whole tuple agrees on its first column, so that lookup is enough to note. */
1290
+ noteTupleRead(relationName, tuple) {
1291
+ const first = tuple[0];
1292
+ if (first === void 0) {
1293
+ this.readLog?.readWhole(relationName);
1294
+ return;
1295
+ }
1296
+ this.readLog?.lookedUp(relationName, 0, first);
1297
+ }
1012
1298
  facts(relationName) {
1299
+ this.readLog?.readWhole(relationName);
1013
1300
  return this.store.get(relationName)?.tuples ?? [];
1014
1301
  }
1015
1302
  /**
@@ -1022,6 +1309,7 @@ var Database = class {
1022
1309
  * gets an index.
1023
1310
  */
1024
1311
  lookup(relationName, column, value) {
1312
+ this.readLog?.lookedUp(relationName, column, value);
1025
1313
  const relation = this.store.get(relationName);
1026
1314
  return relation === void 0 ? [] : bucketIn(relation, column, value);
1027
1315
  }
@@ -1057,6 +1345,7 @@ var Database = class {
1057
1345
  for (const key of going) {
1058
1346
  index.remove(key);
1059
1347
  }
1348
+ relation.removed += going.size;
1060
1349
  if (index.size === 0) {
1061
1350
  relation.tuples = [];
1062
1351
  index.clear();
@@ -1092,6 +1381,7 @@ var Database = class {
1092
1381
  return 0;
1093
1382
  }
1094
1383
  const removed = relation.tuples.length;
1384
+ relation.removed += removed;
1095
1385
  relation.tuples = [];
1096
1386
  relation.index.clear();
1097
1387
  relation.columns.length = 0;
@@ -1100,12 +1390,15 @@ var Database = class {
1100
1390
  return removed;
1101
1391
  }
1102
1392
  size(relationName) {
1103
- return this.store.get(relationName)?.tuples.length ?? 0;
1393
+ this.readLog?.readWhole(relationName);
1394
+ return sizeOf(this, relationName);
1104
1395
  }
1105
1396
  relationNames() {
1106
1397
  return [...this.store.keys()];
1107
1398
  }
1108
1399
  };
1400
+ var tuplesOf = (db, relation) => db.relationOf(relation)?.tuples ?? [];
1401
+ var sizeOf = (db, relation) => db.relationOf(relation)?.tuples.length ?? 0;
1109
1402
  function rulesDeriving(rules, relations) {
1110
1403
  const wanted = new Set(relations);
1111
1404
  const pending = [...relations];
@@ -1301,6 +1594,7 @@ function evaluateRule(db, deltas, r, deltaIndex, budget) {
1301
1594
  }
1302
1595
  const byValue = newFactsByColumn(rows, start.column, budget);
1303
1596
  const first = body[start.index];
1597
+ noteSharedLookups(db, first, start, rows);
1304
1598
  for (const tuple of start.source) {
1305
1599
  budget.examined++;
1306
1600
  if (budget.examined > budget.limit) {
@@ -1332,7 +1626,7 @@ function smallerStart(db, body, deltaAt, rows) {
1332
1626
  if (column === -1) {
1333
1627
  continue;
1334
1628
  }
1335
- const source = narrowedSource(db, literal, null) ?? db.facts(literal.relation);
1629
+ const source = narrowedSource(db, literal, null) ?? tuplesOf(db, literal.relation);
1336
1630
  const worthIt = indexed?.has(column) ? source.length < rows.length : source.length * 2 < rows.length;
1337
1631
  if (worthIt && (best === null || source.length < best.source.length)) {
1338
1632
  best = {
@@ -1390,6 +1684,7 @@ function nextLiteral(db, body, taken, bindings) {
1390
1684
  continue;
1391
1685
  }
1392
1686
  if (source.length === 0) {
1687
+ noteLookup(db, literal, bindings);
1393
1688
  return -1;
1394
1689
  }
1395
1690
  if (narrowest === null || source.length < narrowest.length) {
@@ -1398,6 +1693,7 @@ function nextLiteral(db, body, taken, bindings) {
1398
1693
  }
1399
1694
  }
1400
1695
  if (narrowest !== null) {
1696
+ noteLookup(db, body[pick], bindings);
1401
1697
  nextSource = narrowedFurther(db, body[pick], bindings, narrowest);
1402
1698
  return nextSource.length === 0 ? -1 : pick;
1403
1699
  }
@@ -1409,6 +1705,59 @@ function nextLiteral(db, body, taken, bindings) {
1409
1705
  }
1410
1706
  return body.findIndex((_, index) => !(taken & 1 << index));
1411
1707
  }
1708
+ function noteLookup(db, literal, bindings) {
1709
+ const reads = db.readLog?.readsOf(literal.relation) ?? null;
1710
+ if (reads === null) {
1711
+ return;
1712
+ }
1713
+ for (let column2 = 0; column2 < literal.terms.length; column2++) {
1714
+ const term2 = literal.terms[column2];
1715
+ if (term2.type === "variable") {
1716
+ const value = boundValue(bindings, term2.name);
1717
+ if (value !== void 0) {
1718
+ noteIn(reads, column2, value);
1719
+ return;
1720
+ }
1721
+ }
1722
+ }
1723
+ const column = fewestFactsUnderConstant(db, literal);
1724
+ const term = literal.terms[column];
1725
+ if (term?.type === "constant") {
1726
+ noteIn(reads, column, term.value);
1727
+ }
1728
+ }
1729
+ function fewestFactsUnderConstant(db, literal) {
1730
+ const relation = db.relationOf(literal.relation);
1731
+ let best = -1;
1732
+ let bestFacts = Number.POSITIVE_INFINITY;
1733
+ for (let column = 0; column < literal.terms.length; column++) {
1734
+ const term = literal.terms[column];
1735
+ if (term.type !== "constant") {
1736
+ continue;
1737
+ }
1738
+ const facts = relation === void 0 ? 0 : bucketSize(relation, column, term.value);
1739
+ if (facts < bestFacts) {
1740
+ best = column;
1741
+ bestFacts = facts;
1742
+ }
1743
+ }
1744
+ return best;
1745
+ }
1746
+ function noteSharedLookups(db, first, start, rows) {
1747
+ const reads = db.readLog?.readsOf(first.relation) ?? null;
1748
+ if (reads === null) {
1749
+ return;
1750
+ }
1751
+ const column = first.terms.findIndex(
1752
+ (term) => term.type === "variable" && term.name === start.variable
1753
+ );
1754
+ for (const row of rows) {
1755
+ const value = row[start.column];
1756
+ if (value !== void 0) {
1757
+ noteIn(reads, column, value);
1758
+ }
1759
+ }
1760
+ }
1412
1761
  function narrowedSource(db, literal, bindings) {
1413
1762
  const relation = db.relationOf(literal.relation);
1414
1763
  let narrowest = null;
@@ -1460,7 +1809,11 @@ function constantColumnsOf(literal) {
1460
1809
  values.push(term.value);
1461
1810
  }
1462
1811
  });
1463
- known = columns.length === 0 ? null : { columns, values };
1812
+ known = columns.length === 0 ? null : {
1813
+ columns,
1814
+ values,
1815
+ bucketKey: deltaBucketKey(literal.relation, columns[0])
1816
+ };
1464
1817
  constantColumns.set(literal, known);
1465
1818
  }
1466
1819
  return known;
@@ -1473,7 +1826,7 @@ function hasNoNewRows(deltas, literal) {
1473
1826
  if (rows.length === 0 || constants === null) {
1474
1827
  return rows.length === 0;
1475
1828
  }
1476
- const buckets = deltaBuckets.get(deltas)?.get(deltaBucketKey(literal.relation, constants.columns[0]));
1829
+ const buckets = deltaBuckets.get(deltas)?.get(constants.bucketKey);
1477
1830
  return buckets !== void 0 && !buckets.has(constants.values[0]);
1478
1831
  }
1479
1832
  function deltaRows(deltas, literal, budget) {
@@ -1488,7 +1841,7 @@ function deltaRows(deltas, literal, budget) {
1488
1841
  deltaBuckets.set(deltas, byColumn);
1489
1842
  }
1490
1843
  const column = constants.columns[0];
1491
- const key = deltaBucketKey(literal.relation, column);
1844
+ const key = constants.bucketKey;
1492
1845
  let buckets = byColumn.get(key);
1493
1846
  if (buckets === void 0) {
1494
1847
  buckets = /* @__PURE__ */ new Map();
@@ -1633,7 +1986,7 @@ function canResume(rules, state) {
1633
1986
  function factsSince(db, marks) {
1634
1987
  const added = /* @__PURE__ */ new Map();
1635
1988
  for (const name of db.relationNames()) {
1636
- const all = db.facts(name);
1989
+ const all = tuplesOf(db, name);
1637
1990
  const mark = marks.get(name) ?? 0;
1638
1991
  if (all.length > mark) {
1639
1992
  added.set(name, all.slice(mark));
@@ -1644,7 +1997,7 @@ function factsSince(db, marks) {
1644
1997
  function currentMarks(db) {
1645
1998
  const marks = /* @__PURE__ */ new Map();
1646
1999
  for (const name of db.relationNames()) {
1647
- marks.set(name, db.size(name));
2000
+ marks.set(name, sizeOf(db, name));
1648
2001
  }
1649
2002
  return marks;
1650
2003
  }
@@ -1706,9 +2059,14 @@ function shapeOf(rules) {
1706
2059
  return shape;
1707
2060
  }
1708
2061
  function couldProduce(db, rule2) {
1709
- return rule2.body.every(
1710
- (literal) => literal.negated || db.size(literal.relation) > 0
2062
+ const empty = rule2.body.find(
2063
+ (literal) => !literal.negated && sizeOf(db, literal.relation) === 0
1711
2064
  );
2065
+ if (empty === void 0) {
2066
+ return true;
2067
+ }
2068
+ db.readLog?.readWhole(empty.relation);
2069
+ return false;
1712
2070
  }
1713
2071
  function storedMergeFor(algebra) {
1714
2072
  return (stored, incoming) => {
@@ -1757,6 +2115,17 @@ function runRules(db, rules, algebra, given) {
1757
2115
  };
1758
2116
  const mergeStored = algebra === void 0 ? void 0 : storedMergeFor(algebra);
1759
2117
  const marks = canResume(rules, state) ? state.marks : void 0;
2118
+ const producible = /* @__PURE__ */ new Set();
2119
+ const canProduce = (r) => {
2120
+ if (producible.has(r)) {
2121
+ return true;
2122
+ }
2123
+ if (!couldProduce(db, r)) {
2124
+ return false;
2125
+ }
2126
+ producible.add(r);
2127
+ return true;
2128
+ };
1760
2129
  const runStratum = (stratum) => {
1761
2130
  let delta = /* @__PURE__ */ new Map();
1762
2131
  const record = (relation, tuple) => {
@@ -1799,7 +2168,7 @@ function runRules(db, rules, algebra, given) {
1799
2168
  return;
1800
2169
  }
1801
2170
  const startedAt2 = performance.now();
1802
- const before = db.size(r.head.relation);
2171
+ const before = sizeOf(db, r.head.relation);
1803
2172
  const readBefore = budget.examined;
1804
2173
  deriveInto(r, seed, deltaIndex);
1805
2174
  chargeRule(
@@ -1807,14 +2176,14 @@ function runRules(db, rules, algebra, given) {
1807
2176
  r.head.relation,
1808
2177
  r.body.map(literalName),
1809
2178
  performance.now() - startedAt2,
1810
- db.size(r.head.relation) - before,
2179
+ sizeOf(db, r.head.relation) - before,
1811
2180
  budget.examined - readBefore
1812
2181
  );
1813
2182
  };
1814
2183
  const applyDelta = (seed, derivedOnly) => {
1815
2184
  for (const at of rulesReading(stratum, seed, derivedOnly)) {
1816
2185
  const r = stratum.rules[at];
1817
- if (!couldProduce(db, r)) {
2186
+ if (!canProduce(r)) {
1818
2187
  continue;
1819
2188
  }
1820
2189
  const positives = positivesOf(r);
@@ -1832,13 +2201,13 @@ function runRules(db, rules, algebra, given) {
1832
2201
  };
1833
2202
  if (marks === void 0 || marks === null) {
1834
2203
  for (const r of stratum.rules) {
1835
- if (!couldProduce(db, r)) {
2204
+ if (!canProduce(r)) {
1836
2205
  continue;
1837
2206
  }
1838
2207
  const all = /* @__PURE__ */ new Map();
1839
2208
  for (const l of r.body) {
1840
2209
  if (!l.negated) {
1841
- all.set(l.relation, db.facts(l.relation));
2210
+ all.set(l.relation, tuplesOf(db, l.relation));
1842
2211
  }
1843
2212
  }
1844
2213
  runOneRule(r, all, -1);
@@ -1875,7 +2244,7 @@ function runRules(db, rules, algebra, given) {
1875
2244
  state.marks = currentMarks(db);
1876
2245
  if (isProfiling()) {
1877
2246
  chargeRelationSizes(
1878
- db.relationNames().map((name) => [name, db.size(name)]),
2247
+ db.relationNames().map((name) => [name, sizeOf(db, name)]),
1879
2248
  derivedRelations
1880
2249
  );
1881
2250
  }
@@ -1911,6 +2280,7 @@ export {
1911
2280
  formatProfile,
1912
2281
  lit,
1913
2282
  notLit,
2283
+ noteReads,
1914
2284
  profileEvaluation,
1915
2285
  profileEvaluationAsync,
1916
2286
  proofOf,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/datalog",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
4
4
  "type": "module",
5
5
  "description": "A small semi-naive Datalog evaluator with stratified negation, the rules engine suss runs its whole-program analyses on.",
6
6
  "keywords": [