@microdelta/history 0.0.0 → 0.2.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 (79) hide show
  1. package/README.md +30 -4
  2. package/dist/api/history.alpha.d.ts +3 -0
  3. package/dist/api/history.beta.d.ts +3 -0
  4. package/dist/api/history.conformance.store.alpha.d.ts +3 -0
  5. package/dist/api/history.conformance.store.beta.d.ts +3 -0
  6. package/dist/api/history.conformance.store.public.d.ts +3 -0
  7. package/dist/api/history.conformance.store.raw.alpha.d.ts +215 -0
  8. package/dist/api/history.conformance.store.raw.beta.d.ts +215 -0
  9. package/dist/api/history.conformance.store.raw.public.d.ts +215 -0
  10. package/dist/api/history.conformance.store.raw.untrimmed.d.ts +215 -0
  11. package/dist/api/history.conformance.store.untrimmed.d.ts +3 -0
  12. package/dist/api/history.public.d.ts +3 -0
  13. package/dist/api/history.root.raw.alpha.d.ts +1371 -0
  14. package/dist/api/history.root.raw.beta.d.ts +425 -0
  15. package/dist/api/history.root.raw.public.d.ts +425 -0
  16. package/dist/api/history.root.raw.untrimmed.d.ts +1372 -0
  17. package/dist/api/history.shared.alpha.d.ts +1402 -0
  18. package/dist/api/history.shared.beta.d.ts +456 -0
  19. package/dist/api/history.shared.public.d.ts +456 -0
  20. package/dist/api/history.shared.untrimmed.d.ts +1403 -0
  21. package/dist/api/history.untrimmed.d.ts +3 -0
  22. package/dist/api/tsdoc-metadata.json +11 -0
  23. package/dist/src/completed-results.d.ts +99 -0
  24. package/dist/src/completed-results.d.ts.map +1 -0
  25. package/dist/src/completed-results.js +2 -0
  26. package/dist/src/completed-results.js.map +1 -0
  27. package/dist/src/durable/contracts.d.ts +612 -0
  28. package/dist/src/durable/contracts.d.ts.map +1 -0
  29. package/dist/src/durable/contracts.js +2 -0
  30. package/dist/src/durable/contracts.js.map +1 -0
  31. package/dist/src/durable/errors.d.ts +94 -0
  32. package/dist/src/durable/errors.d.ts.map +1 -0
  33. package/dist/src/durable/errors.js +118 -0
  34. package/dist/src/durable/errors.js.map +1 -0
  35. package/dist/src/durable/index.d.ts +9 -0
  36. package/dist/src/durable/index.d.ts.map +1 -0
  37. package/dist/src/durable/index.js +770 -0
  38. package/dist/src/durable/index.js.map +1 -0
  39. package/dist/src/durable/journal.d.ts +53 -0
  40. package/dist/src/durable/journal.d.ts.map +1 -0
  41. package/dist/src/durable/journal.js +174 -0
  42. package/dist/src/durable/journal.js.map +1 -0
  43. package/dist/src/durable/locator.d.ts +31 -0
  44. package/dist/src/durable/locator.d.ts.map +1 -0
  45. package/dist/src/durable/locator.js +50 -0
  46. package/dist/src/durable/locator.js.map +1 -0
  47. package/dist/src/durable/records.d.ts +40 -0
  48. package/dist/src/durable/records.d.ts.map +1 -0
  49. package/dist/src/durable/records.js +74 -0
  50. package/dist/src/durable/records.js.map +1 -0
  51. package/dist/src/durable/schema.d.ts +37 -0
  52. package/dist/src/durable/schema.d.ts.map +1 -0
  53. package/dist/src/durable/schema.js +335 -0
  54. package/dist/src/durable/schema.js.map +1 -0
  55. package/dist/src/durable/selected-index.d.ts +94 -0
  56. package/dist/src/durable/selected-index.d.ts.map +1 -0
  57. package/dist/src/durable/selected-index.js +594 -0
  58. package/dist/src/durable/selected-index.js.map +1 -0
  59. package/dist/src/index.d.ts +28 -0
  60. package/dist/src/index.d.ts.map +1 -0
  61. package/dist/src/index.js +15 -0
  62. package/dist/src/index.js.map +1 -0
  63. package/dist/src/store/index.d.ts +128 -0
  64. package/dist/src/store/index.d.ts.map +1 -0
  65. package/dist/src/store/index.js +33 -0
  66. package/dist/src/store/index.js.map +1 -0
  67. package/dist/src/store/memory/index.d.ts +24 -0
  68. package/dist/src/store/memory/index.d.ts.map +1 -0
  69. package/dist/src/store/memory/index.js +162 -0
  70. package/dist/src/store/memory/index.js.map +1 -0
  71. package/dist/src/types.d.ts +100 -0
  72. package/dist/src/types.d.ts.map +1 -0
  73. package/dist/src/types.js +2 -0
  74. package/dist/src/types.js.map +1 -0
  75. package/dist/test/conformance/store/index.d.ts +34 -0
  76. package/dist/test/conformance/store/index.d.ts.map +1 -0
  77. package/dist/test/conformance/store/index.js +368 -0
  78. package/dist/test/conformance/store/index.js.map +1 -0
  79. package/package.json +48 -8
@@ -0,0 +1,335 @@
1
+ import { HistorySchemaError } from './errors.js';
2
+ /** Identity tag for embedded SQL; the text is passed through unchanged. */
3
+ const sql = String.raw;
4
+ /** The schema identity recorded in every durable History file. */
5
+ export const schemaName = 'microdelta.history.durable';
6
+ /** The only schema version this implementation reads or writes. */
7
+ export const schemaVersion = 2;
8
+ /** Tables whose rows may never be updated or deleted once written. */
9
+ const immutableTables = [
10
+ 'history_identity',
11
+ 'history_results',
12
+ 'history_dependencies',
13
+ 'history_nodes',
14
+ 'history_edges',
15
+ 'history_addresses',
16
+ 'history_acceptances',
17
+ 'history_acceptance_dependencies',
18
+ 'history_promotions',
19
+ 'history_promotion_results',
20
+ 'history_journal',
21
+ ];
22
+ /** Tables whose rows may change state but are never deleted. */
23
+ const retainedTables = ['history_writer', 'history_sequences', 'history_attempts', 'history_current'];
24
+ /**
25
+ * Every schema object, one statement each, in creation order. Stored
26
+ * definitions are compared with these texts after whitespace normalization.
27
+ */
28
+ const schemaObjects = [
29
+ // One row naming the schema, its version and the logical store this file holds.
30
+ sql `CREATE TABLE history_identity (
31
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
32
+ schema_name TEXT NOT NULL,
33
+ schema_version INTEGER NOT NULL,
34
+ logical_store TEXT NOT NULL CHECK (length(logical_store) > 0)
35
+ ) STRICT`,
36
+ // The single logical writer: holder, monotonically increasing fence, lease
37
+ // expiry and the persisted clock high-water, all in History's clock domain.
38
+ sql `CREATE TABLE history_writer (
39
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
40
+ last_fence INTEGER NOT NULL CHECK (last_fence >= 0),
41
+ holder TEXT CHECK (holder IS NULL OR length(holder) > 0),
42
+ expires_at INTEGER NOT NULL CHECK (expires_at >= 0),
43
+ time_high_water INTEGER NOT NULL CHECK (time_high_water >= 0)
44
+ ) STRICT`,
45
+ // Store-wide counters; issued values are never reissued (PUB-003).
46
+ sql `CREATE TABLE history_sequences (
47
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
48
+ last_attempt INTEGER NOT NULL CHECK (last_attempt >= 0),
49
+ last_publication INTEGER NOT NULL CHECK (last_publication >= 0),
50
+ last_acceptance INTEGER NOT NULL CHECK (last_acceptance >= 0),
51
+ last_promotion INTEGER NOT NULL CHECK (last_promotion >= 0),
52
+ last_journal INTEGER NOT NULL CHECK (last_journal >= 0)
53
+ ) STRICT`,
54
+ // Durable attempts with their stable key, intent digest, staged candidate
55
+ // content and failure evidence. Staged content is never a completed result.
56
+ sql `CREATE TABLE history_attempts (
57
+ attempt_id INTEGER PRIMARY KEY CHECK (attempt_id > 0),
58
+ analysis TEXT NOT NULL CHECK (length(analysis) > 0),
59
+ environment TEXT NOT NULL CHECK (length(environment) > 0),
60
+ subject TEXT NOT NULL CHECK (length(subject) > 0),
61
+ version INTEGER NOT NULL CHECK (version > 0),
62
+ attempt_key TEXT NOT NULL CHECK (length(attempt_key) > 0),
63
+ intent_digest TEXT NOT NULL CHECK (length(intent_digest) > 0),
64
+ state TEXT NOT NULL CHECK (state IN ('allocated', 'staged', 'completed', 'failed', 'interrupted')),
65
+ allocated_fence INTEGER NOT NULL CHECK (allocated_fence > 0),
66
+ ended_fence INTEGER CHECK (ended_fence IS NULL OR ended_fence > 0),
67
+ staged_payload TEXT,
68
+ staged_provenance_format TEXT,
69
+ staged_provenance_version INTEGER,
70
+ staged_provenance TEXT,
71
+ outcome_format TEXT,
72
+ outcome_version INTEGER,
73
+ outcome TEXT,
74
+ UNIQUE (analysis, environment, subject, attempt_key),
75
+ UNIQUE (attempt_id, analysis, environment, subject, version),
76
+ CHECK ((staged_payload IS NULL) = (staged_provenance IS NULL)
77
+ AND (staged_payload IS NULL) = (staged_provenance_format IS NULL)
78
+ AND (staged_payload IS NULL) = (staged_provenance_version IS NULL)),
79
+ CHECK ((outcome IS NULL) = (outcome_format IS NULL) AND (outcome IS NULL) = (outcome_version IS NULL)),
80
+ CHECK ((state = 'allocated' AND staged_payload IS NULL AND ended_fence IS NULL AND outcome IS NULL)
81
+ OR (state = 'staged' AND staged_payload IS NOT NULL AND ended_fence IS NULL AND outcome IS NULL)
82
+ OR (state = 'completed' AND staged_payload IS NULL AND ended_fence IS NOT NULL AND outcome IS NULL)
83
+ OR (state IN ('failed', 'interrupted') AND ended_fence IS NOT NULL AND outcome IS NOT NULL))
84
+ ) STRICT`,
85
+ // Immutable completed results. A result's identity is the attempt that
86
+ // produced it; its scope and version must equal that attempt's.
87
+ sql `CREATE TABLE history_results (
88
+ result_id INTEGER PRIMARY KEY,
89
+ analysis TEXT NOT NULL,
90
+ environment TEXT NOT NULL,
91
+ subject TEXT NOT NULL,
92
+ version INTEGER NOT NULL,
93
+ publication INTEGER NOT NULL UNIQUE CHECK (publication > 0),
94
+ published_fence INTEGER NOT NULL CHECK (published_fence > 0),
95
+ encoding TEXT NOT NULL,
96
+ index_version INTEGER NOT NULL,
97
+ payload TEXT NOT NULL,
98
+ provenance_format TEXT NOT NULL,
99
+ provenance_version INTEGER NOT NULL,
100
+ provenance TEXT NOT NULL,
101
+ UNIQUE (result_id, analysis, environment, subject),
102
+ FOREIGN KEY (result_id, analysis, environment, subject, version)
103
+ REFERENCES history_attempts (attempt_id, analysis, environment, subject, version)
104
+ ) STRICT`,
105
+ sql `CREATE INDEX history_results_by_subject ON history_results (analysis, environment, subject, version, publication)`,
106
+ // Explicit exact dependencies recorded with staged content, in caller order.
107
+ sql `CREATE TABLE history_dependencies (
108
+ attempt_id INTEGER NOT NULL REFERENCES history_attempts (attempt_id),
109
+ position INTEGER NOT NULL CHECK (position >= 0),
110
+ dependency_id INTEGER NOT NULL REFERENCES history_results (result_id),
111
+ PRIMARY KEY (attempt_id, position)
112
+ ) STRICT`,
113
+ // The generated selected index: one row per node of the canonical payload.
114
+ // Only `scalar` holds author data; everything else is shape or digest.
115
+ sql `CREATE TABLE history_nodes (
116
+ result_id INTEGER NOT NULL REFERENCES history_results (result_id),
117
+ node_id INTEGER NOT NULL CHECK (node_id >= 0),
118
+ kind TEXT NOT NULL CHECK (kind IN ('scalar', 'record', 'array')),
119
+ scalar TEXT,
120
+ array_length INTEGER,
121
+ own_keys TEXT,
122
+ prototype TEXT CHECK (prototype IS NULL OR prototype IN ('null', 'object', 'custom')),
123
+ prototype_node INTEGER,
124
+ terminal TEXT CHECK (terminal IS NULL OR terminal IN ('null', 'object')),
125
+ snapshot_fingerprint TEXT NOT NULL,
126
+ PRIMARY KEY (result_id, node_id),
127
+ CHECK ((kind = 'scalar' AND scalar IS NOT NULL AND array_length IS NULL AND own_keys IS NULL
128
+ AND prototype IS NULL AND prototype_node IS NULL AND terminal IS NULL)
129
+ OR (kind = 'array' AND scalar IS NULL AND array_length IS NOT NULL AND array_length >= 0 AND own_keys IS NULL
130
+ AND prototype IS NULL AND prototype_node IS NULL AND terminal IS NULL)
131
+ OR (kind = 'record' AND scalar IS NULL AND array_length IS NULL AND own_keys IS NOT NULL
132
+ AND prototype IS NOT NULL AND terminal IS NOT NULL AND (prototype = 'custom') = (prototype_node IS NOT NULL)))
133
+ ) STRICT`,
134
+ // Structural slots: own record members and present array elements.
135
+ sql `CREATE TABLE history_edges (
136
+ result_id INTEGER NOT NULL REFERENCES history_results (result_id),
137
+ parent_node INTEGER NOT NULL,
138
+ edge_kind TEXT NOT NULL CHECK (edge_kind IN ('property', 'index')),
139
+ edge_key TEXT NOT NULL,
140
+ position INTEGER NOT NULL,
141
+ child_node INTEGER NOT NULL,
142
+ PRIMARY KEY (result_id, parent_node, edge_kind, edge_key)
143
+ ) STRICT`,
144
+ // Every reachable structured address, with its node and MDO1 value digest.
145
+ sql `CREATE TABLE history_addresses (
146
+ result_id INTEGER NOT NULL REFERENCES history_results (result_id),
147
+ address TEXT NOT NULL,
148
+ node_id INTEGER NOT NULL,
149
+ own INTEGER NOT NULL CHECK (own IN (0, 1)),
150
+ value_fingerprint TEXT,
151
+ PRIMARY KEY (result_id, address),
152
+ CHECK ((address = '[]') = (value_fingerprint IS NULL))
153
+ ) STRICT`,
154
+ // The latest publication of each scoped subject; acceptance never moves it.
155
+ sql `CREATE TABLE history_current (
156
+ analysis TEXT NOT NULL,
157
+ environment TEXT NOT NULL,
158
+ subject TEXT NOT NULL,
159
+ result_id INTEGER NOT NULL,
160
+ PRIMARY KEY (analysis, environment, subject),
161
+ FOREIGN KEY (result_id, analysis, environment, subject)
162
+ REFERENCES history_results (result_id, analysis, environment, subject)
163
+ ) STRICT`,
164
+ // Current acceptance evidence, separate from the result's provenance, in
165
+ // the namespace of the environment whose verification recorded it.
166
+ sql `CREATE TABLE history_acceptances (
167
+ acceptance_id INTEGER PRIMARY KEY CHECK (acceptance_id > 0),
168
+ result_id INTEGER NOT NULL REFERENCES history_results (result_id),
169
+ environment TEXT NOT NULL CHECK (length(environment) > 0),
170
+ fence INTEGER NOT NULL CHECK (fence > 0),
171
+ evidence_format TEXT NOT NULL,
172
+ evidence_version INTEGER NOT NULL,
173
+ evidence TEXT NOT NULL
174
+ ) STRICT`,
175
+ sql `CREATE INDEX history_acceptances_by_result ON history_acceptances (result_id, environment, acceptance_id)`,
176
+ sql `CREATE TABLE history_acceptance_dependencies (
177
+ acceptance_id INTEGER NOT NULL REFERENCES history_acceptances (acceptance_id),
178
+ position INTEGER NOT NULL CHECK (position >= 0),
179
+ dependency_id INTEGER NOT NULL REFERENCES history_results (result_id),
180
+ PRIMARY KEY (acceptance_id, position)
181
+ ) STRICT`,
182
+ // Recorded promotions (RUN-017): each admits exact results of other
183
+ // environments into its target environment, with the promoter's evidence.
184
+ sql `CREATE TABLE history_promotions (
185
+ promotion_id INTEGER PRIMARY KEY CHECK (promotion_id > 0),
186
+ analysis TEXT NOT NULL CHECK (length(analysis) > 0),
187
+ environment TEXT NOT NULL CHECK (length(environment) > 0),
188
+ fence INTEGER NOT NULL CHECK (fence > 0),
189
+ evidence_format TEXT NOT NULL,
190
+ evidence_version INTEGER NOT NULL,
191
+ evidence TEXT NOT NULL
192
+ ) STRICT`,
193
+ sql `CREATE INDEX history_promotions_by_target ON history_promotions (analysis, environment, promotion_id)`,
194
+ sql `CREATE TABLE history_promotion_results (
195
+ promotion_id INTEGER NOT NULL REFERENCES history_promotions (promotion_id),
196
+ position INTEGER NOT NULL CHECK (position >= 0),
197
+ result_id INTEGER NOT NULL REFERENCES history_results (result_id),
198
+ PRIMARY KEY (promotion_id, position),
199
+ UNIQUE (promotion_id, result_id)
200
+ ) STRICT`,
201
+ sql `CREATE INDEX history_promotion_results_by_result ON history_promotion_results (result_id, promotion_id)`,
202
+ // Run Supervision's operation journal: immutable revisions of opaque owner
203
+ // records per environment namespace, owner-named collection and key. The
204
+ // current record is an address's highest revision; the sequence orders
205
+ // commits. The format and version are each revision's tag, never part of
206
+ // the record's identity, so two formats can never hold parallel records.
207
+ sql `CREATE TABLE history_journal (
208
+ analysis TEXT NOT NULL CHECK (length(analysis) > 0),
209
+ environment TEXT NOT NULL CHECK (length(environment) > 0),
210
+ collection TEXT NOT NULL CHECK (length(collection) > 0),
211
+ journal_key TEXT NOT NULL CHECK (length(journal_key) > 0),
212
+ revision INTEGER NOT NULL CHECK (revision > 0),
213
+ sequence INTEGER NOT NULL UNIQUE CHECK (sequence > 0),
214
+ fence INTEGER NOT NULL CHECK (fence > 0),
215
+ format TEXT NOT NULL CHECK (length(format) > 0),
216
+ format_version INTEGER NOT NULL CHECK (format_version > 0),
217
+ content TEXT NOT NULL,
218
+ PRIMARY KEY (analysis, environment, collection, journal_key, revision)
219
+ ) STRICT`,
220
+ ...immutableTables.flatMap((table) => [
221
+ sql `CREATE TRIGGER ${table}_immutable_update BEFORE UPDATE ON ${table}
222
+ BEGIN SELECT RAISE(ABORT, '${table} rows are immutable history'); END`,
223
+ sql `CREATE TRIGGER ${table}_immutable_delete BEFORE DELETE ON ${table}
224
+ BEGIN SELECT RAISE(ABORT, '${table} rows are immutable history'); END`,
225
+ ]),
226
+ ...retainedTables.map((table) => sql `CREATE TRIGGER ${table}_immutable_delete BEFORE DELETE ON ${table}
227
+ BEGIN SELECT RAISE(ABORT, '${table} rows are immutable history and are never deleted'); END`),
228
+ // An attempt's identity, scope, key, intent and allocating fence never change.
229
+ sql `CREATE TRIGGER history_attempts_identity_immutable
230
+ BEFORE UPDATE OF attempt_id, analysis, environment, subject, version, attempt_key, intent_digest, allocated_fence
231
+ ON history_attempts
232
+ BEGIN SELECT RAISE(ABORT, 'history_attempts identity is immutable history'); END`,
233
+ ];
234
+ /** Collapse whitespace so stored definitions compare by meaning-preserving text. */
235
+ function normalize(text) {
236
+ return text.replace(/\s+/gu, ' ').trim();
237
+ }
238
+ /** The object name declared by one schema statement. */
239
+ function objectName(statement) {
240
+ const match = /^CREATE (?:TABLE|INDEX|TRIGGER) (\S+)/u.exec(normalize(statement));
241
+ if (match?.[1] === undefined) {
242
+ throw new Error('History schema statement has no object name');
243
+ }
244
+ return match[1];
245
+ }
246
+ /** Expected normalized definitions keyed by object name. */
247
+ const expectedObjects = new Map(schemaObjects.map((statement) => [objectName(statement), normalize(statement)]));
248
+ /** Every user-visible object currently in the database, keyed by name. */
249
+ function presentObjects(connection) {
250
+ const rows = connection.prepare(sql `/* schema */ SELECT name, sql FROM sqlite_master WHERE name NOT LIKE 'sqlite\_%' ESCAPE '\'`).all();
251
+ return new Map(rows.map((row) => [String(row.name), typeof row.sql === 'string' ? normalize(row.sql) : null]));
252
+ }
253
+ /**
254
+ * Create the schema in one transaction when the database has no objects,
255
+ * then validate it. Validation requires exactly the expected objects with
256
+ * their exact definitions and a single identity row naming this schema,
257
+ * version and the requested logical store.
258
+ */
259
+ export function initializeSchema(connection, logicalStore) {
260
+ // An existing store is validated without taking the write lock; only an
261
+ // apparently empty file enters the IMMEDIATE transaction, which rechecks.
262
+ if (presentObjects(connection).size > 0) {
263
+ validateSchema(connection, logicalStore);
264
+ return;
265
+ }
266
+ connection.transaction(() => {
267
+ if (presentObjects(connection).size > 0) {
268
+ return undefined;
269
+ }
270
+ for (const statement of schemaObjects) {
271
+ connection.exec(statement);
272
+ }
273
+ connection.prepare(sql `/* schema */ INSERT INTO history_identity (singleton, schema_name, schema_version, logical_store) VALUES (1, ?, ?, ?)`)
274
+ .run(schemaName, schemaVersion, logicalStore);
275
+ connection.prepare(sql `/* schema */ INSERT INTO history_writer (singleton, last_fence, holder, expires_at, time_high_water) VALUES (1, 0, NULL, 0, 0)`).run();
276
+ connection.prepare(sql `/* schema */ INSERT INTO history_sequences (singleton, last_attempt, last_publication, last_acceptance, last_promotion, last_journal)
277
+ VALUES (1, 0, 0, 0, 0, 0)`).run();
278
+ return undefined;
279
+ });
280
+ validateSchema(connection, logicalStore);
281
+ }
282
+ /**
283
+ * Reject a file that records this schema under another version before
284
+ * comparing objects, so an earlier or later History layout is diagnosed by its
285
+ * version rather than as an incomplete schema. A file whose identity cannot be
286
+ * read this way is left to the exact object comparison.
287
+ */
288
+ function rejectOtherVersion(connection, present) {
289
+ if (!present.has('history_identity')) {
290
+ return;
291
+ }
292
+ let identities;
293
+ try {
294
+ identities = connection.prepare(sql `/* schema */ SELECT schema_name, schema_version FROM history_identity`).all();
295
+ }
296
+ catch {
297
+ return;
298
+ }
299
+ const identity = identities[0];
300
+ if (identities.length === 1 && identity?.schema_name === schemaName && identity.schema_version !== schemaVersion) {
301
+ throw new HistorySchemaError(`Unsupported History schema ${schemaName} version ${String(identity.schema_version)}; this implementation reads only version ${String(schemaVersion)} and never migrates stored data by guessing`);
302
+ }
303
+ }
304
+ /** Reject any database that is not exactly this schema for this logical store. */
305
+ function validateSchema(connection, logicalStore) {
306
+ const present = presentObjects(connection);
307
+ rejectOtherVersion(connection, present);
308
+ const missing = [...expectedObjects.keys()].filter((name) => !present.has(name));
309
+ const unexpected = [...present.keys()].filter((name) => !expectedObjects.has(name));
310
+ if (missing.length > 0 || unexpected.length > 0) {
311
+ throw new HistorySchemaError(`Unsupported or incomplete History schema: missing [${missing.join(', ')}], unexpected [${unexpected.join(', ')}]`);
312
+ }
313
+ const altered = [...expectedObjects].filter(([name, definition]) => present.get(name) !== definition).map(([name]) => name);
314
+ if (altered.length > 0) {
315
+ throw new HistorySchemaError(`History schema objects differ from version ${String(schemaVersion)}: ${altered.join(', ')}`);
316
+ }
317
+ const identities = connection.prepare(sql `/* schema */ SELECT schema_name, schema_version, logical_store FROM history_identity`).all();
318
+ const identity = identities[0];
319
+ if (identities.length !== 1 || identity === undefined) {
320
+ throw new HistorySchemaError('History schema has no single identity row');
321
+ }
322
+ if (identity.schema_name !== schemaName || identity.schema_version !== schemaVersion) {
323
+ throw new HistorySchemaError(`Unsupported History schema ${String(identity.schema_name)} version ${String(identity.schema_version)}`);
324
+ }
325
+ if (identity.logical_store !== logicalStore) {
326
+ throw new HistorySchemaError('The SQLite file holds a different logical History store');
327
+ }
328
+ for (const table of ['history_writer', 'history_sequences']) {
329
+ const count = connection.prepare(sql `/* schema */ SELECT count(*) AS n FROM ${table}`).get();
330
+ if (count?.n !== 1) {
331
+ throw new HistorySchemaError(`History schema table ${table} does not have exactly one row`);
332
+ }
333
+ }
334
+ }
335
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/durable/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * History's generated selected index over one immutable completed result,
3
+ * adopted from the accepted nested selected-read gate (issue #54, index
4
+ * requirements 1–7). History alone generates the index, inside the publish
5
+ * transaction, from the canonical MDS1 payload it stores; callers never supply
6
+ * index metadata. The index answers navigation, selected leaves and
7
+ * fingerprint metadata without decoding the root payload:
8
+ *
9
+ * - one node row per payload node (kind, MDV1 scalar leaf, array length, own
10
+ * keys in snapshot order, prototype link and chain terminal, MDS1 digest);
11
+ * - one edge row per own record member or present array element;
12
+ * - one address row per reachable structured address (including members
13
+ * inherited through supported prototypes) with its MDO1 `value` digest.
14
+ *
15
+ * Absent members are answered by evaluating Value on a payload-free stand-in of
16
+ * the longest indexed prefix. Reads verify what they return: a scalar leaf must
17
+ * match its address's `value` digest and a subtree its node's MDS1 digest, so
18
+ * corrupted storage fails as an integrity error instead of returning different
19
+ * data. Storage-shape failures never surface as `TypeError`, which is reserved
20
+ * for Value rejecting a selection's shape.
21
+ * @packageDocumentation
22
+ */
23
+ import type { ISha256Capability, ISqliteConnection } from '@microdelta/machine';
24
+ import type { ICompletedNavigationReader, ICompletedResultReader, ICompletedResultReference } from '../completed-results.js';
25
+ import type { IResultVerification } from './contracts.js';
26
+ /** The index layout version recorded with every result. */
27
+ export declare const indexVersion = 1;
28
+ /** One indexed node; only `scalar` holds author payload. */
29
+ interface INodeRow {
30
+ readonly nodeId: number;
31
+ readonly kind: 'scalar' | 'record' | 'array';
32
+ /** MDV1 encoding of a scalar leaf. */
33
+ readonly scalar: string | null;
34
+ readonly arrayLength: number | null;
35
+ /** A record's own enumerable keys in snapshot order, as JSON. */
36
+ readonly ownKeys: string | null;
37
+ readonly prototype: 'null' | 'object' | 'custom' | null;
38
+ readonly prototypeNode: number | null;
39
+ /** Where a record's lookup chain ends; decides intrinsic-member semantics for absent keys. */
40
+ readonly terminal: 'null' | 'object' | null;
41
+ /** SHA-256 of the node's MDS1 snapshot. */
42
+ readonly snapshotFingerprint: string;
43
+ }
44
+ /** One structural slot: an own record member or a present array element. */
45
+ interface IEdgeRow {
46
+ readonly parentNode: number;
47
+ readonly edgeKind: 'property' | 'index';
48
+ readonly edgeKey: string;
49
+ /** Own enumeration position for records; the index for arrays. */
50
+ readonly position: number;
51
+ readonly childNode: number;
52
+ }
53
+ /** One reachable structured address. */
54
+ interface IAddressRow {
55
+ readonly address: string;
56
+ readonly nodeId: number;
57
+ /** Whether the final Property segment names an own member rather than an inherited one. */
58
+ readonly own: 0 | 1;
59
+ /** SHA-256 of the MDO1 `value` fact here; null for the root, which has no member address. */
60
+ readonly valueFingerprint: string | null;
61
+ }
62
+ /** The complete generated index for one result. */
63
+ export interface IIndexRows {
64
+ readonly nodes: readonly INodeRow[];
65
+ readonly edges: readonly IEdgeRow[];
66
+ readonly addresses: readonly IAddressRow[];
67
+ }
68
+ /**
69
+ * Generate the index for one decoded canonical snapshot with a record or
70
+ * array root. Every container has exactly one structural parent because Value
71
+ * rejects shared references. Reachable addresses follow Value's lookup order:
72
+ * own members first, then members inherited through each custom prototype
73
+ * that an earlier level does not shadow.
74
+ */
75
+ export declare function buildIndex(root: object, host: ISha256Capability): IIndexRows;
76
+ /** Resolves an exact reference to a verified result identity in this store and scope. */
77
+ export type IResultResolver = (reference: ICompletedResultReference) => number;
78
+ /** The generated index's write, read and verification operations for one connection. */
79
+ export interface ISelectedIndex {
80
+ /** Insert a result's generated index rows inside the caller's publish transaction. */
81
+ insert(resultId: number, rows: IIndexRows): void;
82
+ /** Exact scalar and navigation reader over completed results. */
83
+ readonly reader: ICompletedResultReader & ICompletedNavigationReader;
84
+ /** Regenerate a result's index from its stored canonical payload and compare. */
85
+ verify(resultId: number): IResultVerification;
86
+ }
87
+ /**
88
+ * Create the index operations over an owned connection. Exact references
89
+ * are resolved by the authority's `resolve`, which validates locator, store,
90
+ * scope, completion and stored encoding before any index row is read.
91
+ */
92
+ export declare function createSelectedIndex(connection: ISqliteConnection, host: ISha256Capability, resolve: IResultResolver): ISelectedIndex;
93
+ export {};
94
+ //# sourceMappingURL=selected-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selected-index.d.ts","sourceRoot":"","sources":["../../../src/durable/selected-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAA4B,MAAM,qBAAqB,CAAC;AAc1G,OAAO,KAAK,EACV,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EAI1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAM1D,2DAA2D;AAC3D,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,4DAA4D;AAC5D,UAAU,QAAQ;IAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC7C,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC5C,2CAA2C;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED,4EAA4E;AAC5E,UAAU,QAAQ;IAChB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wCAAwC;AACxC,UAAU,WAAW;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,WAAW,EAAE,CAAC;CAC5C;AAwBD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,UAAU,CA+F5E;AAgGD,yFAAyF;AACzF,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,yBAAyB,KAAK,MAAM,CAAC;AAE/E,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,sBAAsB,GAAG,0BAA0B,CAAC;IACrE,iFAAiF;IACjF,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAuYpI"}