@substrat-run/contract-tests 0.143.0 → 0.145.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 (65) hide show
  1. package/dist/attachment-refusal-suite.d.ts +3 -0
  2. package/dist/attachment-refusal-suite.d.ts.map +1 -0
  3. package/dist/attachment-refusal-suite.js +128 -0
  4. package/dist/attachment-refusal-suite.js.map +1 -0
  5. package/dist/bind-module.d.ts +220 -0
  6. package/dist/bind-module.d.ts.map +1 -0
  7. package/dist/bind-module.js +135 -0
  8. package/dist/bind-module.js.map +1 -0
  9. package/dist/binding-invite-suite.d.ts +12 -0
  10. package/dist/binding-invite-suite.d.ts.map +1 -0
  11. package/dist/binding-invite-suite.js +560 -0
  12. package/dist/binding-invite-suite.js.map +1 -0
  13. package/dist/derived-module.d.ts +414 -3
  14. package/dist/derived-module.d.ts.map +1 -1
  15. package/dist/derived-module.js +201 -6
  16. package/dist/derived-module.js.map +1 -1
  17. package/dist/derived-suite.d.ts.map +1 -1
  18. package/dist/derived-suite.js +351 -4
  19. package/dist/derived-suite.js.map +1 -1
  20. package/dist/entity-state-module.d.ts +16 -0
  21. package/dist/entity-state-module.d.ts.map +1 -1
  22. package/dist/entity-trash-module.d.ts +80 -0
  23. package/dist/entity-trash-module.d.ts.map +1 -1
  24. package/dist/entity-trash-module.js +73 -0
  25. package/dist/entity-trash-module.js.map +1 -1
  26. package/dist/entity-trash-suite.d.ts.map +1 -1
  27. package/dist/entity-trash-suite.js +94 -2
  28. package/dist/entity-trash-suite.js.map +1 -1
  29. package/dist/erasure-module.d.ts +8 -0
  30. package/dist/erasure-module.d.ts.map +1 -1
  31. package/dist/findings-suite.d.ts.map +1 -1
  32. package/dist/findings-suite.js +55 -1
  33. package/dist/findings-suite.js.map +1 -1
  34. package/dist/index.d.ts +5 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +5 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/modules.d.ts +248 -0
  39. package/dist/modules.d.ts.map +1 -1
  40. package/dist/modules.js +11 -1
  41. package/dist/modules.js.map +1 -1
  42. package/dist/scope-host-suite.d.ts.map +1 -1
  43. package/dist/scope-host-suite.js +501 -0
  44. package/dist/scope-host-suite.js.map +1 -1
  45. package/dist/shape-backfill-ties.d.ts +29 -0
  46. package/dist/shape-backfill-ties.d.ts.map +1 -0
  47. package/dist/shape-backfill-ties.js +113 -0
  48. package/dist/shape-backfill-ties.js.map +1 -0
  49. package/dist/shape-reconcile-plan.d.ts +19 -5
  50. package/dist/shape-reconcile-plan.d.ts.map +1 -1
  51. package/dist/shape-reconcile-plan.js +76 -20
  52. package/dist/shape-reconcile-plan.js.map +1 -1
  53. package/dist/stamps-module.d.ts +462 -0
  54. package/dist/stamps-module.d.ts.map +1 -0
  55. package/dist/stamps-module.js +242 -0
  56. package/dist/stamps-module.js.map +1 -0
  57. package/dist/stamps-suite.d.ts +3 -0
  58. package/dist/stamps-suite.d.ts.map +1 -0
  59. package/dist/stamps-suite.js +170 -0
  60. package/dist/stamps-suite.js.map +1 -0
  61. package/dist/vertical-events-suite.d.ts +16 -0
  62. package/dist/vertical-events-suite.d.ts.map +1 -1
  63. package/dist/vertical-events-suite.js +30 -3
  64. package/dist/vertical-events-suite.js.map +1 -1
  65. package/package.json +3 -3
@@ -0,0 +1,29 @@
1
+ import { type SwitchSql } from '@substrat-run/kernel';
2
+ interface TieCase {
3
+ name: string;
4
+ /** The pairs the backfill marked, and the ones it should have. */
5
+ marked: string[];
6
+ expected: string[];
7
+ /** Whether the run-once record was written once the reconcile finished. */
8
+ recorded: boolean;
9
+ }
10
+ /**
11
+ * Three cases, each its own table and entity type, each reconciled at `limit` 1 (one mark, a window
12
+ * of ten rows) until the reconcile finishes:
13
+ *
14
+ * - `duplicate id`: `(e1, A)`, `(e1, B)`, `(e2, C)` on an index of the id alone. The write budget
15
+ * ends the first pass on A, mid-id.
16
+ * - `null principal at the edge`: nine rows with no principal, then `(e1, NULL)` closing the first
17
+ * window, then `(e1, A)`. A principal-keyed seek past `(e1, NULL)` compares NULL for every e1 row.
18
+ * - `without rowid`: `(e1, A)`, `(e1, B)`, `(e2, C)` in a `WITHOUT ROWID` table keyed on another
19
+ * column, with an index on the id. Then the same table declared `WITHOUT ROWID, STRICT`, and
20
+ * `WITHOUT ROWID` followed by a comment: DDL text that does not end in the clause.
21
+ * - `a column named rowid`: the duplicate-id case in a table whose own `rowid` column, the same on
22
+ * every row, would stand where the row's identity should.
23
+ */
24
+ export declare function shapeBackfillTies(raw: SwitchSql, ids: {
25
+ tenantId: string;
26
+ scopeId: string;
27
+ }): TieCase[];
28
+ export {};
29
+ //# sourceMappingURL=shape-backfill-ties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-backfill-ties.d.ts","sourceRoot":"","sources":["../src/shape-backfill-ties.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkD,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtG,UAAU,OAAO;IACf,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,EAAE,CA+EvG"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * A table holder's rows that share an `idColumn` are all read, whichever row a window ends on
3
+ * (#2154, #2159 review). Nothing makes `idColumn` unique or the principal non-NULL, so a read keyed
4
+ * on the id alone, or on the id and the principal, skipped the rest of an id's rows at a window's
5
+ * edge, and the run-once record then closed the backfill over them for good. Pure, with no runner:
6
+ * each adapter hands it its scope's own SQLite as the kernel's `SwitchSql`, so the cases run on the
7
+ * engine it actually ships, and asserts every case marked what it expected.
8
+ *
9
+ * Harness code: it writes `_substrat_*` rows directly, which is exactly what `ctx.sql` refuses.
10
+ */
11
+ import { entityGrantShape } from '@substrat-run/contracts';
12
+ import { topUpEntityGrantShapes, ulid } from '@substrat-run/kernel';
13
+ const KEY = 'shape-tie:use';
14
+ /**
15
+ * Three cases, each its own table and entity type, each reconciled at `limit` 1 (one mark, a window
16
+ * of ten rows) until the reconcile finishes:
17
+ *
18
+ * - `duplicate id`: `(e1, A)`, `(e1, B)`, `(e2, C)` on an index of the id alone. The write budget
19
+ * ends the first pass on A, mid-id.
20
+ * - `null principal at the edge`: nine rows with no principal, then `(e1, NULL)` closing the first
21
+ * window, then `(e1, A)`. A principal-keyed seek past `(e1, NULL)` compares NULL for every e1 row.
22
+ * - `without rowid`: `(e1, A)`, `(e1, B)`, `(e2, C)` in a `WITHOUT ROWID` table keyed on another
23
+ * column, with an index on the id. Then the same table declared `WITHOUT ROWID, STRICT`, and
24
+ * `WITHOUT ROWID` followed by a comment: DDL text that does not end in the clause.
25
+ * - `a column named rowid`: the duplicate-id case in a table whose own `rowid` column, the same on
26
+ * every row, would stand where the row's identity should.
27
+ */
28
+ export function shapeBackfillTies(raw, ids) {
29
+ const cases = [
30
+ {
31
+ name: 'duplicate id',
32
+ type: 'tieseat',
33
+ ddl: ['CREATE TABLE shape_tie_seat (entity_id TEXT, holder TEXT)', 'CREATE INDEX shape_tie_seat_id ON shape_tie_seat (entity_id)'],
34
+ rows: [['e1', 'A'], ['e1', 'B'], ['e2', 'C']],
35
+ },
36
+ {
37
+ name: 'null principal at the edge',
38
+ type: 'tiedesk',
39
+ ddl: ['CREATE TABLE shape_tie_desk (entity_id TEXT, holder TEXT)', 'CREATE INDEX shape_tie_desk_id ON shape_tie_desk (entity_id)'],
40
+ rows: [...Array.from({ length: 9 }, (_, i) => [`a${i}`, null]), ['e1', null], ['e1', 'A']],
41
+ },
42
+ {
43
+ name: 'without rowid',
44
+ type: 'tiebadge',
45
+ ddl: [
46
+ 'CREATE TABLE shape_tie_badge (k TEXT PRIMARY KEY, entity_id TEXT, holder TEXT) WITHOUT ROWID',
47
+ 'CREATE INDEX shape_tie_badge_id ON shape_tie_badge (entity_id)',
48
+ ],
49
+ rows: [['e1', 'A'], ['e1', 'B'], ['e2', 'C']],
50
+ },
51
+ {
52
+ name: 'without rowid, strict',
53
+ type: 'tiestrict',
54
+ ddl: [
55
+ 'CREATE TABLE shape_tie_strict (k TEXT PRIMARY KEY, entity_id TEXT, holder TEXT) WITHOUT ROWID, STRICT',
56
+ 'CREATE INDEX shape_tie_strict_id ON shape_tie_strict (entity_id)',
57
+ ],
58
+ rows: [['e1', 'A'], ['e1', 'B'], ['e2', 'C']],
59
+ },
60
+ {
61
+ name: 'without rowid, then a comment',
62
+ type: 'tienote',
63
+ ddl: [
64
+ 'CREATE TABLE shape_tie_note (k TEXT PRIMARY KEY, entity_id TEXT, holder TEXT) WITHOUT ROWID -- keyed on k',
65
+ 'CREATE INDEX shape_tie_note_id ON shape_tie_note (entity_id)',
66
+ ],
67
+ rows: [['e1', 'A'], ['e1', 'B'], ['e2', 'C']],
68
+ },
69
+ {
70
+ name: 'a column named rowid',
71
+ type: 'tieshadow',
72
+ ddl: [
73
+ "CREATE TABLE shape_tie_shadow (rowid TEXT DEFAULT 'same', entity_id TEXT, holder TEXT)",
74
+ 'CREATE INDEX shape_tie_shadow_id ON shape_tie_shadow (entity_id)',
75
+ ],
76
+ rows: [['e1', 'A'], ['e1', 'B'], ['e2', 'C']],
77
+ },
78
+ ];
79
+ return cases.map(({ name, type, ddl, rows }) => {
80
+ const table = ddl[0].split(' ')[2];
81
+ for (const st of ddl)
82
+ raw.run(st);
83
+ const expected = [];
84
+ const keyed = ddl[0].includes('WITHOUT ROWID'); // its own key column, `k`
85
+ rows.forEach(([id, who], i) => {
86
+ const holder = who && `${who}-${ulid()}`;
87
+ if (keyed)
88
+ raw.run(`INSERT INTO ${table} (k, entity_id, holder) VALUES (?, ?, ?)`, `k${i}`, id, holder);
89
+ else
90
+ raw.run(`INSERT INTO ${table} (entity_id, holder) VALUES (?, ?)`, id, holder);
91
+ if (!holder)
92
+ return;
93
+ raw.run('INSERT INTO _substrat_tuples (subject, relation, object) VALUES (?, ?, ?)', `principal:${holder}`, `granted:${KEY}`, `${type}:${id}`);
94
+ expected.push(`principal:${holder} ${type}:${id}`);
95
+ });
96
+ const shapes = [entityGrantShape.parse({ entityType: type, permissions: [KEY], bootstrap: true, holder: { table, idColumn: 'entity_id', principalColumn: 'holder' } })];
97
+ let after = null;
98
+ let passes = 0;
99
+ do {
100
+ after = topUpEntityGrantShapes(raw, { ...ids, shapes, now: new Date().toISOString(), limit: 1, after, mintEventId: () => ulid(), version: null }).next;
101
+ passes++;
102
+ } while (after && passes < 100);
103
+ // A cursor that never ends is a failure of its own, whatever happened to be marked by the cap.
104
+ if (after)
105
+ throw new Error(`${name}: the reconcile did not finish in ${passes} passes`);
106
+ const marked = raw
107
+ .all("SELECT subject || ' ' || object AS pair FROM _substrat_tuples WHERE relation = 'bootstrap' AND object >= ? AND object < ? ORDER BY 1", `${type}:`, `${type};`)
108
+ .map((r) => String(r['pair']));
109
+ const recorded = raw.all("SELECT 1 FROM _substrat_tuples WHERE subject = ? AND relation = 'shape-backfilled'", `shape:${type}`).length === 1;
110
+ return { name, marked, expected: expected.sort(), recorded };
111
+ });
112
+ }
113
+ //# sourceMappingURL=shape-backfill-ties.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-backfill-ties.js","sourceRoot":"","sources":["../src/shape-backfill-ties.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,37 @@
1
1
  import { type SwitchSql } from '@substrat-run/kernel';
2
+ type HolderKind = 'self' | 'grantee' | 'table';
2
3
  interface ShapeReconcilePlanReport {
3
4
  /** The plan of each marker walk the passes sent (one per retire and top-up walk). */
4
5
  walks: string[];
5
- /** The plan of each grantee backfill read. */
6
- backfills: string[];
6
+ /** The plan of each backfill read, by the holder kind it read for. */
7
+ backfills: Record<HolderKind, string[]>;
7
8
  /** Every planned table scan of `_substrat_tuples`, with the statement that planned it. */
8
9
  scans: string[];
9
10
  /** Passes the reconcile took, and whether every holder ended up with the whole shape. */
10
11
  passes: number;
11
12
  complete: boolean;
13
+ /** Markers the backfill wrote, and how many legacy holders there were to mark. */
14
+ marks: number;
15
+ holders: number;
16
+ /** The most rows any one pass's backfill reads fetched, and the read budget of a pass. */
17
+ maxFetched: number;
18
+ window: number;
19
+ /** The most rows the engine read in one pass, every statement included: given `rowsRead` only. */
20
+ maxRowsRead: number | null;
12
21
  }
13
22
  /**
14
- * Seed one shape's world on `raw` — holders of the old shape, legacy grantees with no marker, a
15
- * retired key, and another type's markers beside them — then reconcile it in small passes, each
16
- * resuming from the last, planning every statement. Run it on a scope nothing else is writing.
23
+ * Seed three shapes' worlds on `raw` — holders of the old shape, legacy holders with no marker for
24
+ * each holder kind, a retired key, and another type's markers beside them — then reconcile them in
25
+ * small passes, each resuming from the last, planning every statement. `legacy` legacy holders per
26
+ * kind (default 5); `rowsRead`, when the engine counts them, the running total of rows it has read.
27
+ * Run it on a scope nothing else is writing.
17
28
  */
18
29
  export declare function shapeReconcilePlans(raw: SwitchSql, ids: {
19
30
  tenantId: string;
20
31
  scopeId: string;
32
+ }, opts?: {
33
+ legacy?: number;
34
+ rowsRead?: () => number;
21
35
  }): ShapeReconcilePlanReport;
22
36
  export {};
23
37
  //# sourceMappingURL=shape-reconcile-plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shape-reconcile-plan.d.ts","sourceRoot":"","sources":["../src/shape-reconcile-plan.ts"],"names":[],"mappings":"AAeA,OAAO,EAAkD,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAItG,UAAU,wBAAwB;IAChC,qFAAqF;IACrF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0FAA0F;IAC1F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,wBAAwB,CA2DxH"}
1
+ {"version":3,"file":"shape-reconcile-plan.d.ts","sourceRoot":"","sources":["../src/shape-reconcile-plan.ts"],"names":[],"mappings":"AAsBA,OAAO,EAA8E,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAIlI,KAAK,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE/C,UAAU,wBAAwB;IAChC,qFAAqF;IACrF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,0FAA0F;IAC1F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,kGAAkG;IAClG,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAQD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,SAAS,EACd,GAAG,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC1C,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GACtD,wBAAwB,CA0G1B"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The shape reconcile's reads, planned by the SQLite each adapter actually runs (#2083).
2
+ * The shape reconcile's reads, planned by the SQLite each adapter actually runs (#2083, #2154).
3
3
  *
4
4
  * A pass walks a shape's markers on `_substrat_tuples_shape_marker`, a partial index SQLite uses
5
5
  * only where it can prove the index's `relation = 'bootstrap'` from the query. A relation term it
@@ -10,22 +10,33 @@
10
10
  * kernel's `SwitchSql` (over a `better-sqlite3` file, a Durable Object's `storage.sql`) and
11
11
  * asserts on the report.
12
12
  *
13
+ * The backfill reads in windows too (#2154), for each holder kind: the probe seeds legacy holders
14
+ * of a `'self'`, a `'grantee'` and a table-holder shape, and reports each kind's read plans, how
15
+ * many rows each pass fetched, and how many markers the backfill wrote, so an adapter can assert
16
+ * every holder was marked exactly once. Given the engine's own count of rows read (a Durable
17
+ * Object's `cursor.rowsRead`), it reports the most any one pass read, every statement included,
18
+ * which is the bound a scope actually pays.
19
+ *
13
20
  * Harness code: it writes `_substrat_*` rows directly, which is exactly what `ctx.sql` refuses.
14
21
  */
15
22
  import { entityGrantShape } from '@substrat-run/contracts';
16
- import { topUpEntityGrantShapes, ulid } from '@substrat-run/kernel';
23
+ import { SHAPE_MARKER_READS_PER_ROW, topUpEntityGrantShapes, ulid } from '@substrat-run/kernel';
17
24
  const MARKERS = 30;
18
- const LEGACY = 5;
25
+ const LIMIT = 4;
19
26
  const KEYS = ['shape-plan:read', 'shape-plan:use'];
20
27
  const RETIRED = 'shape-plan:old';
28
+ const BADGES = 'shape_plan_badge';
21
29
  /**
22
- * Seed one shape's world on `raw` — holders of the old shape, legacy grantees with no marker, a
23
- * retired key, and another type's markers beside them — then reconcile it in small passes, each
24
- * resuming from the last, planning every statement. Run it on a scope nothing else is writing.
30
+ * Seed three shapes' worlds on `raw` — holders of the old shape, legacy holders with no marker for
31
+ * each holder kind, a retired key, and another type's markers beside them — then reconcile them in
32
+ * small passes, each resuming from the last, planning every statement. `legacy` legacy holders per
33
+ * kind (default 5); `rowsRead`, when the engine counts them, the running total of rows it has read.
34
+ * Run it on a scope nothing else is writing.
25
35
  */
26
- export function shapeReconcilePlans(raw, ids) {
36
+ export function shapeReconcilePlans(raw, ids, opts = {}) {
37
+ const legacy = opts.legacy ?? 5;
27
38
  const tuple = (subject, relation, object) => raw.run('INSERT OR REPLACE INTO _substrat_tuples (subject, relation, object) VALUES (?, ?, ?)', subject, relation, object);
28
- const pad = (i) => String(i).padStart(3, '0');
39
+ const pad = (i) => String(i).padStart(5, '0');
29
40
  for (let i = 0; i < MARKERS; i++) {
30
41
  const [who, desk] = [`principal:${ulid()}`, `desk:d${pad(i)}`];
31
42
  tuple(who, 'bootstrap', desk);
@@ -34,11 +45,21 @@ export function shapeReconcilePlans(raw, ids) {
34
45
  tuple(who, `granted:${RETIRED}`, desk);
35
46
  tuple(who, 'bootstrap', `room:d${pad(i)}`); // another type's marker: never read by this shape
36
47
  }
37
- for (let i = 0; i < LEGACY; i++)
38
- tuple(`principal:${ulid()}`, `granted:${KEYS[0]}`, `desk:l${pad(i)}`);
48
+ raw.run(`CREATE TABLE IF NOT EXISTS ${BADGES} (id TEXT PRIMARY KEY, holder TEXT)`);
49
+ for (let i = 0; i < legacy; i++) {
50
+ const [grantee, owner, badged] = [ulid(), ulid(), ulid()];
51
+ tuple(`principal:${grantee}`, `granted:${KEYS[0]}`, `desk:l${pad(i)}`);
52
+ tuple(`principal:${owner}`, `granted:${KEYS[0]}`, `seat:${owner}`);
53
+ tuple(`principal:${ulid()}`, `granted:${KEYS[0]}`, `seat:${owner}`); // a sharee on it: never marked
54
+ raw.run(`INSERT INTO ${BADGES} (id, holder) VALUES (?, ?)`, `b${pad(i)}`, badged);
55
+ tuple(`principal:${badged}`, `granted:${KEYS[0]}`, `badge:b${pad(i)}`);
56
+ }
39
57
  const walks = [];
40
- const backfills = [];
58
+ const backfills = { self: [], grantee: [], table: [] };
41
59
  const scans = [];
60
+ let [marks, fetched] = [0, 0];
61
+ /** Which holder kind a statement is the backfill read of, if it is one. */
62
+ const backfillOf = (sql, params) => sql.includes(`FROM "${BADGES}" e`) ? 'table' : sql.includes('(t.object, t.relation, t.subject) > (?, ?, ?)') ? (params.includes('seat;') ? 'self' : 'grantee') : null;
42
63
  const plan = (sql, params) => {
43
64
  if (!/^\s*(SELECT|UPDATE|DELETE)/i.test(sql))
44
65
  return;
@@ -46,42 +67,77 @@ export function shapeReconcilePlans(raw, ids) {
46
67
  const joined = detail.join(' | ');
47
68
  if (sql.includes('(m.object, m.subject) > (?, ?)'))
48
69
  walks.push(joined);
49
- if (sql.includes('SELECT DISTINCT t.subject, t.object'))
50
- backfills.push(joined);
51
- // A virtual table (`json_each`) is scanned by design; a scan of the tuple table never is.
70
+ const kind = backfillOf(sql, params);
71
+ if (kind)
72
+ backfills[kind].push(joined);
73
+ // A virtual table (`json_each`, `pragma_table_list`) is scanned by design; a scan of the tuple
74
+ // table, or of the holder's table, never is.
52
75
  for (const d of detail)
53
76
  if (/^SCAN /.test(d) && !/VIRTUAL TABLE/.test(d))
54
77
  scans.push(`${d} — ${sql.replace(/\s+/g, ' ').slice(0, 160)}`);
55
78
  };
56
79
  const sql = {
57
80
  all: (q, ...p) => {
81
+ if (q.startsWith('EXPLAIN'))
82
+ return raw.all(q, ...p); // the kernel's own plan check: not a read of rows
58
83
  plan(q, p);
59
- return raw.all(q, ...p);
84
+ const rows = raw.all(q, ...p);
85
+ if (backfillOf(q, p))
86
+ fetched += rows.length;
87
+ return rows;
60
88
  },
61
89
  run: (q, ...p) => {
62
90
  plan(q, p);
91
+ if (q.startsWith('INSERT OR IGNORE') && p[1] === 'bootstrap')
92
+ marks++;
63
93
  raw.run(q, ...p);
64
94
  },
65
95
  };
66
- const shapes = [entityGrantShape.parse({ entityType: 'desk', permissions: KEYS, retired: [RETIRED], bootstrap: true, holder: 'grantee' })];
96
+ const shapes = [
97
+ entityGrantShape.parse({ entityType: 'desk', permissions: KEYS, retired: [RETIRED], bootstrap: true, holder: 'grantee' }),
98
+ entityGrantShape.parse({ entityType: 'seat', permissions: KEYS, bootstrap: true, holder: 'self' }),
99
+ entityGrantShape.parse({ entityType: 'badge', permissions: KEYS, bootstrap: true, holder: { table: BADGES, idColumn: 'id', principalColumn: 'holder' } }),
100
+ ];
67
101
  let after = null;
68
102
  let passes = 0;
103
+ let maxFetched = 0;
104
+ let maxRowsRead = 0;
69
105
  do {
106
+ fetched = 0;
107
+ const before = opts.rowsRead?.() ?? 0;
70
108
  after = topUpEntityGrantShapes(sql, {
71
109
  ...ids,
72
110
  shapes,
73
111
  now: new Date().toISOString(),
74
- limit: 4,
112
+ limit: LIMIT,
75
113
  after,
76
114
  mintEventId: () => ulid(),
77
115
  version: null,
78
116
  }).next;
117
+ maxFetched = Math.max(maxFetched, fetched);
118
+ // `EXPLAIN QUERY PLAN` executes nothing, so the probe's own planning adds no rows read.
119
+ maxRowsRead = Math.max(maxRowsRead, (opts.rowsRead?.() ?? 0) - before);
79
120
  passes++;
80
- } while (after && passes < 100);
121
+ } while (after && passes < 100_000);
81
122
  const short = raw.all(`SELECT count(*) AS n FROM _substrat_tuples m
82
- WHERE m.relation = 'bootstrap' AND m.object >= 'desk:' AND m.object < 'desk;'
123
+ WHERE m.relation = 'bootstrap' AND ((m.object >= 'desk:' AND m.object < 'desk;') OR (m.object >= 'seat:' AND m.object < 'seat;')
124
+ OR (m.object >= 'badge:' AND m.object < 'badge;'))
83
125
  AND NOT EXISTS (SELECT 1 FROM _substrat_tuples t WHERE t.subject = m.subject AND t.object = m.object
84
126
  AND t.relation = 'granted:${KEYS[1]}' AND t.revoked_at IS NULL)`)[0]['n'];
85
- return { walks, backfills, scans, passes, complete: after === null && Number(short) === 0 };
127
+ const markers = raw.all(`SELECT count(*) AS n FROM _substrat_tuples WHERE relation = 'bootstrap'
128
+ AND ((object >= 'desk:l' AND object < 'desk:m') OR (object >= 'seat:' AND object < 'seat;') OR (object >= 'badge:' AND object < 'badge;'))`)[0]['n'];
129
+ const holders = legacy * 3;
130
+ return {
131
+ walks,
132
+ backfills,
133
+ scans,
134
+ passes,
135
+ complete: after === null && Number(short) === 0 && Number(markers) === holders,
136
+ marks,
137
+ holders,
138
+ maxFetched,
139
+ window: LIMIT * SHAPE_MARKER_READS_PER_ROW,
140
+ maxRowsRead: opts.rowsRead ? maxRowsRead : null,
141
+ };
86
142
  }
87
143
  //# sourceMappingURL=shape-reconcile-plan.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shape-reconcile-plan.js","sourceRoot":"","sources":["../src/shape-reconcile-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,IAAI,EAAoC,MAAM,sBAAsB,CAAC;AAgBtG,MAAM,OAAO,GAAG,EAAE,CAAC;AACnB,MAAM,MAAM,GAAG,CAAC,CAAC;AACjB,MAAM,IAAI,GAAG,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AACnD,MAAM,OAAO,GAAG,gBAAgB,CAAC;AAEjC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAc,EAAE,GAA0C;IAC5F,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,QAAgB,EAAE,MAAc,EAAE,EAAE,CAClE,GAAG,CAAC,GAAG,CAAC,sFAAsF,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7H,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,EAAE,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/D,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QAC9B,KAAK,CAAC,GAAG,EAAE,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,KAAK,CAAC,GAAG,EAAE,WAAW,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QACxD,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kDAAkD;IAChG,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE;QAAE,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,EAAE,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEvG,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,MAAwB,EAAE,EAAE;QACrD,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO;QACrD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAsB,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC/F,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,GAAG,CAAC,QAAQ,CAAC,gCAAgC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvE,IAAI,GAAG,CAAC,QAAQ,CAAC,qCAAqC,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChF,0FAA0F;QAC1F,KAAK,MAAM,CAAC,IAAI,MAAM;YAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3I,CAAC,CAAC;IACF,MAAM,GAAG,GAAc;QACrB,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE;YACf,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACX,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC1B,CAAC;QACD,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE;YACf,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACX,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACnB,CAAC;KACF,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IAC3I,IAAI,KAAK,GAAuB,IAAI,CAAC;IACrC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,GAAG,CAAC;QACF,KAAK,GAAG,sBAAsB,CAAC,GAAG,EAAE;YAClC,GAAG,GAAG;YACN,MAAM;YACN,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC7B,KAAK,EAAE,CAAC;YACR,KAAK;YACL,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE;YACzB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,IAAI,CAAC;QACR,MAAM,EAAE,CAAC;IACX,CAAC,QAAQ,KAAK,IAAI,MAAM,GAAG,GAAG,EAAE;IAEhC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CACnB;;;qDAGiD,IAAI,CAAC,CAAC,CAAC,6BAA6B,CACtF,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,CAAC;IACX,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;AAC9F,CAAC"}
1
+ {"version":3,"file":"shape-reconcile-plan.js","sourceRoot":"","sources":["../src/shape-reconcile-plan.ts"],"names":[],"mappings":"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"}