@quereus/sync 3.3.0 → 4.1.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 (92) hide show
  1. package/README.md +17 -7
  2. package/dist/src/clock/hlc.d.ts +43 -3
  3. package/dist/src/clock/hlc.d.ts.map +1 -1
  4. package/dist/src/clock/hlc.js +60 -10
  5. package/dist/src/clock/hlc.js.map +1 -1
  6. package/dist/src/create-sync-module.d.ts +46 -9
  7. package/dist/src/create-sync-module.d.ts.map +1 -1
  8. package/dist/src/create-sync-module.js +11 -8
  9. package/dist/src/create-sync-module.js.map +1 -1
  10. package/dist/src/index.d.ts +10 -8
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js +8 -5
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/metadata/basis-lifecycle.d.ts +180 -0
  15. package/dist/src/metadata/basis-lifecycle.d.ts.map +1 -0
  16. package/dist/src/metadata/basis-lifecycle.js +211 -0
  17. package/dist/src/metadata/basis-lifecycle.js.map +1 -0
  18. package/dist/src/metadata/change-log.d.ts +1 -1
  19. package/dist/src/metadata/change-log.d.ts.map +1 -1
  20. package/dist/src/metadata/change-log.js +6 -6
  21. package/dist/src/metadata/change-log.js.map +1 -1
  22. package/dist/src/metadata/column-version.d.ts +14 -5
  23. package/dist/src/metadata/column-version.d.ts.map +1 -1
  24. package/dist/src/metadata/column-version.js +22 -12
  25. package/dist/src/metadata/column-version.js.map +1 -1
  26. package/dist/src/metadata/index.d.ts +2 -0
  27. package/dist/src/metadata/index.d.ts.map +1 -1
  28. package/dist/src/metadata/index.js +2 -0
  29. package/dist/src/metadata/index.js.map +1 -1
  30. package/dist/src/metadata/keys.d.ts +52 -2
  31. package/dist/src/metadata/keys.d.ts.map +1 -1
  32. package/dist/src/metadata/keys.js +99 -14
  33. package/dist/src/metadata/keys.js.map +1 -1
  34. package/dist/src/metadata/peer-state.d.ts +1 -1
  35. package/dist/src/metadata/peer-state.js +5 -5
  36. package/dist/src/metadata/quarantine.d.ts +90 -0
  37. package/dist/src/metadata/quarantine.d.ts.map +1 -0
  38. package/dist/src/metadata/quarantine.js +167 -0
  39. package/dist/src/metadata/quarantine.js.map +1 -0
  40. package/dist/src/metadata/schema-migration.d.ts +1 -1
  41. package/dist/src/metadata/schema-migration.js +7 -7
  42. package/dist/src/metadata/schema-version.d.ts +1 -1
  43. package/dist/src/metadata/schema-version.js +7 -7
  44. package/dist/src/metadata/tombstones.d.ts +26 -9
  45. package/dist/src/metadata/tombstones.d.ts.map +1 -1
  46. package/dist/src/metadata/tombstones.js +42 -19
  47. package/dist/src/metadata/tombstones.js.map +1 -1
  48. package/dist/src/sql/basis-lifecycle-tvf.d.ts +34 -0
  49. package/dist/src/sql/basis-lifecycle-tvf.d.ts.map +1 -0
  50. package/dist/src/sql/basis-lifecycle-tvf.js +101 -0
  51. package/dist/src/sql/basis-lifecycle-tvf.js.map +1 -0
  52. package/dist/src/sync/admission.d.ts +81 -0
  53. package/dist/src/sync/admission.d.ts.map +1 -0
  54. package/dist/src/sync/admission.js +94 -0
  55. package/dist/src/sync/admission.js.map +1 -0
  56. package/dist/src/sync/change-applicator.d.ts +65 -2
  57. package/dist/src/sync/change-applicator.d.ts.map +1 -1
  58. package/dist/src/sync/change-applicator.js +493 -68
  59. package/dist/src/sync/change-applicator.js.map +1 -1
  60. package/dist/src/sync/change-grouping.d.ts +43 -0
  61. package/dist/src/sync/change-grouping.d.ts.map +1 -0
  62. package/dist/src/sync/change-grouping.js +103 -0
  63. package/dist/src/sync/change-grouping.js.map +1 -0
  64. package/dist/src/sync/events.d.ts +159 -1
  65. package/dist/src/sync/events.d.ts.map +1 -1
  66. package/dist/src/sync/events.js +50 -0
  67. package/dist/src/sync/events.js.map +1 -1
  68. package/dist/src/sync/manager.d.ts +87 -0
  69. package/dist/src/sync/manager.d.ts.map +1 -1
  70. package/dist/src/sync/protocol.d.ts +174 -11
  71. package/dist/src/sync/protocol.d.ts.map +1 -1
  72. package/dist/src/sync/protocol.js +4 -1
  73. package/dist/src/sync/protocol.js.map +1 -1
  74. package/dist/src/sync/snapshot-stream.d.ts.map +1 -1
  75. package/dist/src/sync/snapshot-stream.js +89 -19
  76. package/dist/src/sync/snapshot-stream.js.map +1 -1
  77. package/dist/src/sync/snapshot.d.ts.map +1 -1
  78. package/dist/src/sync/snapshot.js +63 -44
  79. package/dist/src/sync/snapshot.js.map +1 -1
  80. package/dist/src/sync/store-adapter.d.ts +117 -15
  81. package/dist/src/sync/store-adapter.d.ts.map +1 -1
  82. package/dist/src/sync/store-adapter.js +247 -91
  83. package/dist/src/sync/store-adapter.js.map +1 -1
  84. package/dist/src/sync/sync-context.d.ts +44 -1
  85. package/dist/src/sync/sync-context.d.ts.map +1 -1
  86. package/dist/src/sync/sync-context.js +26 -0
  87. package/dist/src/sync/sync-context.js.map +1 -1
  88. package/dist/src/sync/sync-manager-impl.d.ts +247 -13
  89. package/dist/src/sync/sync-manager-impl.d.ts.map +1 -1
  90. package/dist/src/sync/sync-manager-impl.js +788 -193
  91. package/dist/src/sync/sync-manager-impl.js.map +1 -1
  92. package/package.json +9 -9
@@ -1,11 +1,104 @@
1
1
  /**
2
2
  * Store adapter for applying remote sync changes.
3
3
  *
4
- * This module provides adapters that implement the ApplyToStoreCallback
5
- * interface for LevelDB and IndexedDB stores, enabling the SyncManager
6
- * to apply remote changes to the actual data store.
4
+ * Implements the ApplyToStoreCallback interface over the store module's
5
+ * external row-write entry point (`StoreTable.applyExternalRowChanges`
6
+ * table-owned key encoding, secondary-index and stats maintenance) plus the
7
+ * engine's batch ingestion seam (`Database.ingestExternalRowChanges` — change
8
+ * capture for `Database.watch` + global assertions, materialized-view
9
+ * maintenance, opt-in parent-side FK actions).
10
+ *
11
+ * Per `applyToStore(dataChanges, schemaChanges, options)` invocation:
12
+ * 1. Schema changes execute first via `db.exec` (DDL before DML), with the
13
+ * resulting module schema events pre-marked remote.
14
+ * 2. Data changes are grouped per table, then per row; each row group
15
+ * collapses to ONE `ExternalRowOp` (a delete in the group wins over
16
+ * column updates; column updates merge onto the pre-read existing row,
17
+ * or onto a PK+nulls partial row when absent — UPSERT semantics).
18
+ * 3. `StoreTable.applyExternalRowChanges(ops)` applies the table's ops to
19
+ * committed storage and returns the EFFECTIVE changes (no-ops — absent
20
+ * delete, value-identical upsert — are suppressed: no storage write, no
21
+ * module event, no seam report).
22
+ * 4. Module data events are emitted from the effective changes with
23
+ * `remote: true` (so the SyncManager never re-records inbound changes),
24
+ * carrying accurate `oldRow` before-images and derived `changedColumns`.
25
+ * 5. After all tables' storage writes, ONE seam call reports the
26
+ * accumulated effective changes: `db.ingestExternalRowChanges(batch,
27
+ * { applyForeignKeyActions, assertionFailureMode: 'report' })` — capture +
28
+ * MV facets default on, commit-time global assertions in REPORT mode (a
29
+ * violation is collected into the result and the batch still commits).
30
+ * Changes recorded in `result.errors` are excluded from the batch.
31
+ *
32
+ * Snapshot bootstrap (per-call, not sticky): a known-complete wholesale load
33
+ * defers MV maintenance and watch capture for the whole transfer and converges
34
+ * once at the end.
35
+ * - `bootstrap` flush: steps 1–4 run unchanged (storage rows applied, remote
36
+ * module events emitted) but step 5 — the seam call — is SKIPPED. MV
37
+ * maintenance + capture are deferred to the finalize and FK actions are off
38
+ * for a wholesale load, so the only seam facet skipping the call drops
39
+ * outright is commit-time GLOBAL ASSERTION evaluation over the bootstrapped
40
+ * rows — deliberate under the seam's trust-the-origin contract. The
41
+ * incremental path enforces global assertions because it **merges** deltas
42
+ * from possibly many origins into the receiver's existing state, and a
43
+ * cross-origin merge can produce a global-invariant violation no single
44
+ * origin ever saw. Bootstrap does something different: it installs **one**
45
+ * origin's already-converged state **wholesale (replace, not merge)** — no
46
+ * merge means no merge-introduced violation, so a complete snapshot already
47
+ * satisfied the origin's assertions and re-checking is redundant. The
48
+ * `bootstrapFinalize` therefore does NOT evaluate any global assertion —
49
+ * not even a no-dependency one. This uniform skip is consistent with the
50
+ * seam's general trust-the-origin posture for every other constraint type
51
+ * (see `docs/materialized-views.md` § Trust boundary). MV-backed assertions
52
+ * would see the MV only after `refreshAllMaterializedViews()`; under
53
+ * trust-the-origin they are not evaluated at finalize at all, so MV-refresh
54
+ * ordering is moot for assertions. Residual risk (a corrupt/hostile snapshot
55
+ * installs invariant-violating data) is already unguarded for every other
56
+ * constraint type; a one-off assertion sweep would be inconsistent
57
+ * defense-in-depth — a separate integrity layer is the right fix if origins
58
+ * are ever distrusted. Per-flush evaluation also could not serve bootstrap
59
+ * correctly: it could spuriously fail a valid snapshot whose cross-table
60
+ * assertion sees children before parents. The remaining seam work for a
61
+ * wholesale load is otherwise deferred, so skipping the call also removes
62
+ * the per-flush transaction/savepoint and the per-flush full-rebuild.
63
+ * - `bootstrapFinalize` call (empty data/schema): converges every MV in
64
+ * dependency order via `db.refreshAllMaterializedViews()`, then fires a
65
+ * coarse `db.notifyExternalChange` per bootstrapped base table and per
66
+ * refreshed MV — one whole-table watch invalidation instead of per-row
67
+ * capture. The caller issues it before clearing the snapshot checkpoint, so
68
+ * a finalize throw leaves the checkpoint in place and the transfer retries.
69
+ *
70
+ * Inbound assertion violations are DETECT-AND-NOTIFY, not throw. Under the
71
+ * seam's trust-the-origin contract the merged data must land regardless, so a
72
+ * **local** commit-time global assertion can only usefully *notify*: the seam
73
+ * runs in report mode, so a violation is collected and RETURNED in
74
+ * `result.assertionViolations` while the batch commits — the derived effects
75
+ * (MV backing deltas, capture entries) for the violating row land on the FIRST
76
+ * attempt, so an incremental MV / `Database.watch` subscriber stays consistent
77
+ * with the base table and there is no divergence and no retry. The consumer
78
+ * (admission.ts) surfaces each returned violation to the host as an
79
+ * `onAssertionViolation` event; the host decides policy.
80
+ *
81
+ * A genuine per-change STORAGE failure is different and still aborts: the
82
+ * adapter collects it in `result.errors` (it keeps applying other tables), and
83
+ * the consumer treats any non-empty `errors` like a whole-batch throw — emit
84
+ * `status:'error'`, leave CRDT metadata uncommitted, re-resolve next sync.
85
+ * Re-application is idempotent (value-identical upserts suppress). "Orthogonal"
86
+ * (above) means the two outcomes are independent facts, NOT that a co-occurring
87
+ * violation is dropped: when ONE batch carries both a per-change error and a
88
+ * reported violation, the violation's row already committed in report mode, so
89
+ * the consumer surfaces the `onAssertionViolation` event BEFORE the per-change
90
+ * abort throws (see admission.ts `applyDataToStore`). The abort still blocks the
91
+ * metadata commit and the batch still re-resolves.
92
+ *
93
+ * Constraints (see docs/materialized-views.md § External row-change
94
+ * ingestion): the callback is host-driven — never invoke it from within
95
+ * statement execution or vtab callbacks (exec-mutex deadlock), and hosts
96
+ * should not drive it while holding an open explicit transaction on `db`
97
+ * (the seam would join that transaction, so a later rollback diverges
98
+ * MV/capture state from the already-committed storage rows; recoverable via
99
+ * MV refresh).
7
100
  */
8
- import { buildDataKey, serializeRow, deserializeRow } from '@quereus/store';
101
+ import { compareSqlValues } from '@quereus/quereus';
9
102
  import { toError } from './sync-context.js';
10
103
  /**
11
104
  * Creates an ApplyToStoreCallback for applying remote sync changes.
@@ -14,13 +107,20 @@ import { toError } from './sync-context.js';
14
107
  * - UPSERT semantics for column changes (insert if row doesn't exist, update if it does)
15
108
  * - Row deletions by primary key
16
109
  * - DDL execution for schema changes
110
+ * - Post-apply reporting through the engine's ingestion seam (MV maintenance,
111
+ * `Database.watch` capture, opt-in FK actions)
17
112
  *
18
113
  * All data change events are emitted with `remote: true` to prevent
19
114
  * the SyncManager from re-recording CRDT metadata.
20
115
  */
21
116
  export function createStoreAdapter(options) {
22
- const { db, getKVStore, events, getTableSchema, collation = 'NOCASE' } = options;
117
+ const { db, storeModule, events, applyForeignKeyActions = false } = options;
23
118
  return async (dataChanges, schemaChanges, applyOptions) => {
119
+ // Bootstrap finalize carries no data/schema changes: converge the MVs whose
120
+ // per-flush maintenance was deferred, then coarse-notify the watchers.
121
+ if (applyOptions.bootstrapFinalize) {
122
+ return finalizeBootstrap(db, applyOptions);
123
+ }
24
124
  const result = {
25
125
  dataChangesApplied: 0,
26
126
  schemaChangesApplied: 0,
@@ -39,23 +139,43 @@ export function createStoreAdapter(options) {
39
139
  });
40
140
  }
41
141
  }
42
- // Group data changes by table for efficient batch operations
43
- // Each table may have its own KV store (especially in IndexedDB)
142
+ // Effective changes accumulated across all tables, in apply order,
143
+ // for the single end-of-invocation seam call. The order here is
144
+ // table-grouped (first-appearance opSeq); it is NOT a dependency
145
+ // order and the FK-actions facet does not require one — both FK
146
+ // helpers re-read post-write storage applied above.
147
+ const seamBatch = [];
148
+ // Apply data changes per table; the resolved StoreTable owns key
149
+ // encoding, store resolution, and secondary-index maintenance.
44
150
  const changesByTable = groupChangesByTable(dataChanges);
45
- // Apply data changes per table
46
151
  for (const [tableKey, tableChanges] of changesByTable) {
47
152
  const [schemaName, tableName] = tableKey.split('.');
48
153
  try {
49
- // Get the correct KV store for this table
50
- const kv = await getKVStore(schemaName, tableName);
51
- // Group by row within the table
52
- const changesByRow = groupChangesByRow(tableChanges);
53
- for (const [rowKey, rowChanges] of changesByRow) {
54
- await applyRowChanges(kv, events, getTableSchema, collation, rowKey, rowChanges, applyOptions);
55
- result.dataChangesApplied += rowChanges.length;
154
+ const table = storeModule.getTableForExternalWrite(db, schemaName, tableName);
155
+ if (!table) {
156
+ throw new Error(`Table not found for external write: ${schemaName}.${tableName}`);
157
+ }
158
+ // One ExternalRowOp per row group: multiple same-row changes collapse
159
+ // to a single effective op, so the seam's same-row before-image
160
+ // chaining rule is satisfied trivially (oldRow = true pre-batch image).
161
+ const ops = [];
162
+ for (const rowChanges of groupChangesByRow(tableChanges).values()) {
163
+ ops.push(await buildRowOp(table, rowChanges));
56
164
  }
165
+ const effective = await table.applyExternalRowChanges(ops);
166
+ emitEffectiveChanges(events, table.getSchema(), effective);
167
+ // On a bootstrap flush the seam is skipped (deferred to finalize), so
168
+ // there is no batch to accumulate — don't build one.
169
+ if (!applyOptions.bootstrap) {
170
+ for (const change of effective) {
171
+ seamBatch.push({ schemaName, tableName, change });
172
+ }
173
+ }
174
+ result.dataChangesApplied += tableChanges.length;
57
175
  }
58
176
  catch (error) {
177
+ // Errored changes are excluded from the seam batch; earlier tables'
178
+ // applied changes still report below.
59
179
  for (const change of tableChanges) {
60
180
  result.errors.push({
61
181
  change,
@@ -64,9 +184,54 @@ export function createStoreAdapter(options) {
64
184
  }
65
185
  }
66
186
  }
187
+ // One seam call per invocation, after all storage writes. Driven in
188
+ // assertion REPORT mode: a commit-time global-assertion violation over the
189
+ // inbound batch is COLLECTED and returned (not thrown), and the batch still
190
+ // commits — derived effects (MV deltas, watch capture) for the violating row
191
+ // land on this FIRST attempt, so there is no divergence and no retry. The
192
+ // returned violations are copied into the result for the consumer to surface
193
+ // to the host (see admission.ts). Empty on a bootstrap flush (the batch was
194
+ // never built — maintenance is deferred to the end-of-snapshot
195
+ // `bootstrapFinalize` convergence). A genuine per-change storage failure
196
+ // (collected in `result.errors`) still aborts the apply — that path is
197
+ // orthogonal.
198
+ if (seamBatch.length > 0) {
199
+ const seamResult = await db.ingestExternalRowChanges(seamBatch, {
200
+ applyForeignKeyActions,
201
+ assertionFailureMode: 'report',
202
+ });
203
+ if (seamResult.assertionViolations.length > 0) {
204
+ result.assertionViolations = seamResult.assertionViolations;
205
+ }
206
+ }
67
207
  return result;
68
208
  };
69
209
  }
210
+ /**
211
+ * Finalize a snapshot bootstrap: the per-flush MV maintenance and watch capture
212
+ * were deferred (each bootstrap flush skipped the seam), so converge every MV
213
+ * in dependency order and fire a coarse whole-table watch invalidation for each
214
+ * bootstrapped base table and each refreshed MV — base-table and MV watchers
215
+ * re-read once instead of seeing per-row capture.
216
+ *
217
+ * A throw from `refreshAllMaterializedViews` propagates to the caller, which
218
+ * runs the finalize before clearing the snapshot checkpoint — so the checkpoint
219
+ * survives and the transfer retries (the storage rows are already correct, so
220
+ * the retry's finalize rebuilds cleanly).
221
+ */
222
+ async function finalizeBootstrap(db, options) {
223
+ const refreshed = await db.refreshAllMaterializedViews();
224
+ // Coarse base-table invalidation per bootstrapped table (note the
225
+ // table-then-schema argument order of `notifyExternalChange`)...
226
+ for (const { schema, table } of options.bootstrapTables ?? []) {
227
+ await db.notifyExternalChange(table, schema);
228
+ }
229
+ // ...and per refreshed MV, so MV watchers re-read.
230
+ for (const { schemaName, name } of refreshed) {
231
+ await db.notifyExternalChange(name, schemaName);
232
+ }
233
+ return { dataChangesApplied: 0, schemaChangesApplied: 0, errors: [] };
234
+ }
70
235
  /**
71
236
  * Group data changes by table (schema.table).
72
237
  */
@@ -109,8 +274,8 @@ async function applySchemaChange(db, events, change, _options) {
109
274
  const eventType = change.type.startsWith('drop') ? 'drop' : change.type.startsWith('create') ? 'create' : 'alter';
110
275
  const objectType = change.type.includes('table') ? 'table' : 'index';
111
276
  // Register this as an expected remote event BEFORE executing DDL.
112
- // When IndexedDBModule.create() emits the event, it will be automatically
113
- // marked as remote, so SyncManager won't re-record it.
277
+ // When the module emits the event, it will be automatically marked as
278
+ // remote, so SyncManager won't re-record it.
114
279
  // This approach avoids race conditions with concurrent local DDL.
115
280
  events.expectRemoteSchemaEvent({
116
281
  type: eventType,
@@ -137,98 +302,89 @@ async function applySchemaChange(db, events, change, _options) {
137
302
  // The module's event is automatically marked as remote.
138
303
  }
139
304
  /**
140
- * Apply data changes for a single row.
305
+ * Collapse one row group's changes into a single ExternalRowOp.
306
+ * A delete in the group wins over column updates.
141
307
  */
142
- async function applyRowChanges(kv, events, getTableSchema, collation, _rowKey, changes, _options) {
143
- // All changes in the group should be for the same row
144
- const firstChange = changes[0];
145
- const { schema, table, pk } = firstChange;
146
- const tableSchema = getTableSchema(schema, table);
147
- if (!tableSchema) {
148
- throw new Error(`Table schema not found: ${schema}.${table}`);
149
- }
150
- const encodeOptions = { collation };
151
- const pkDirections = tableSchema.primaryKeyDefinition.map(p => !!p.desc);
152
- const dataKey = buildDataKey(pk, encodeOptions, pkDirections);
153
- // Check for delete operations first
154
- const deleteChange = changes.find(c => c.type === 'delete');
155
- if (deleteChange) {
156
- await applyDelete(kv, events, tableSchema, dataKey, pk);
157
- return;
308
+ async function buildRowOp(table, changes) {
309
+ const { pk } = changes[0];
310
+ if (changes.some(c => c.type === 'delete')) {
311
+ return { op: 'delete', pk };
158
312
  }
159
- // Apply column updates (UPSERT semantics)
160
- await applyColumnUpdates(kv, events, tableSchema, dataKey, pk, changes, { serializeRow, deserializeRow });
313
+ return { op: 'upsert', row: await mergeColumnUpdates(table, pk, changes) };
161
314
  }
162
315
  /**
163
- * Apply a delete operation for a row.
316
+ * Merge column updates onto the row's current image (UPSERT semantics):
317
+ * pre-read the existing row by PK, or build a PK+nulls partial row when
318
+ * absent (column changes may arrive before the rest of the row).
164
319
  */
165
- async function applyDelete(kv, events, tableSchema, dataKey, pk) {
166
- await kv.delete(dataKey);
167
- // Emit data change event with remote flag
168
- events.emitDataChange({
169
- type: 'delete',
170
- schemaName: tableSchema.schemaName,
171
- tableName: tableSchema.name,
172
- key: pk,
173
- remote: true,
174
- });
175
- }
176
- /**
177
- * Apply column updates with UPSERT semantics.
178
- */
179
- async function applyColumnUpdates(kv, events, tableSchema, dataKey, pk, changes, serialization) {
180
- const { serializeRow, deserializeRow } = serialization;
181
- // Read existing row if any
182
- const existingData = await kv.get(dataKey);
320
+ async function mergeColumnUpdates(table, pk, changes) {
321
+ const tableSchema = table.getSchema();
322
+ const existing = await table.readRowByPk(pk);
183
323
  let row;
184
- let isInsert = false;
185
- if (existingData) {
186
- row = deserializeRow(existingData);
324
+ if (existing) {
325
+ row = [...existing];
187
326
  }
188
327
  else {
189
- // Create new row with nulls
190
328
  row = new Array(tableSchema.columns.length).fill(null);
191
- // Set PK values
192
329
  for (let i = 0; i < tableSchema.primaryKeyDefinition.length; i++) {
193
- const pkDef = tableSchema.primaryKeyDefinition[i];
194
- row[pkDef.index] = pk[i];
330
+ row[tableSchema.primaryKeyDefinition[i].index] = pk[i];
195
331
  }
196
- isInsert = true;
197
332
  }
198
- // Apply column changes from all changes
199
- const changedColumns = [];
333
+ let columnsApplied = 0;
200
334
  for (const change of changes) {
201
- if (change.columns) {
202
- for (const [colName, value] of Object.entries(change.columns)) {
203
- const colIndex = tableSchema.columnIndexMap.get(colName.toLowerCase());
204
- if (colIndex !== undefined) {
205
- row[colIndex] = value;
206
- changedColumns.push(colName);
207
- }
208
- else {
209
- // Column name not found in schema - this could be a sync bug
210
- console.warn(`[Sync] Column '${colName}' not found in ${tableSchema.schemaName}.${tableSchema.name}. ` +
211
- `Available columns: ${[...tableSchema.columnIndexMap.keys()].join(', ')}`);
212
- }
335
+ if (!change.columns)
336
+ continue;
337
+ for (const [colName, value] of Object.entries(change.columns)) {
338
+ const colIndex = tableSchema.columnIndexMap.get(colName.toLowerCase());
339
+ if (colIndex !== undefined) {
340
+ row[colIndex] = value;
341
+ columnsApplied++;
342
+ }
343
+ else {
344
+ // Column name not found in schema - this could be a sync bug
345
+ console.warn(`[Sync] Column '${colName}' not found in ${tableSchema.schemaName}.${tableSchema.name}. ` +
346
+ `Available columns: ${[...tableSchema.columnIndexMap.keys()].join(', ')}`);
213
347
  }
214
348
  }
215
349
  }
216
- // Check if any columns were actually applied
217
- if (changedColumns.length === 0 && !isInsert) {
350
+ if (columnsApplied === 0 && existing) {
218
351
  console.warn(`[Sync] No columns were applied for ${tableSchema.schemaName}.${tableSchema.name} pk=${JSON.stringify(pk)}. ` +
219
352
  `This may indicate a column name mismatch between source and destination.`);
220
353
  }
221
- // Write updated row
222
- await kv.put(dataKey, serializeRow(row));
223
- // Emit data change event with remote flag
224
- events.emitDataChange({
225
- type: isInsert ? 'insert' : 'update',
226
- schemaName: tableSchema.schemaName,
227
- tableName: tableSchema.name,
228
- key: pk,
229
- newRow: row,
230
- changedColumns: isInsert ? undefined : changedColumns,
231
- remote: true,
232
- });
354
+ return row;
355
+ }
356
+ /**
357
+ * Emit module data change events for the effective changes, with
358
+ * `remote: true` so the SyncManager never re-records them. Suppressed no-ops
359
+ * (absent delete, value-identical upsert) were never reported by the store,
360
+ * so they emit nothing — deliberate.
361
+ */
362
+ function emitEffectiveChanges(events, tableSchema, effective) {
363
+ for (const change of effective) {
364
+ const row = change.newRow ?? change.oldRow;
365
+ const pk = tableSchema.primaryKeyDefinition.map(p => row[p.index]);
366
+ events.emitDataChange({
367
+ type: change.op,
368
+ schemaName: tableSchema.schemaName,
369
+ tableName: tableSchema.name,
370
+ key: pk,
371
+ oldRow: change.oldRow,
372
+ newRow: change.newRow,
373
+ changedColumns: change.op === 'update'
374
+ ? diffChangedColumns(tableSchema, change.oldRow, change.newRow)
375
+ : undefined,
376
+ remote: true,
377
+ });
378
+ }
379
+ }
380
+ /** Column names whose values differ between the effective before/after images. */
381
+ function diffChangedColumns(tableSchema, oldRow, newRow) {
382
+ const changed = [];
383
+ for (let i = 0; i < tableSchema.columns.length; i++) {
384
+ if (compareSqlValues(oldRow[i], newRow[i]) !== 0) {
385
+ changed.push(tableSchema.columns[i].name);
386
+ }
387
+ }
388
+ return changed;
233
389
  }
234
390
  //# sourceMappingURL=store-adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-adapter.js","sourceRoot":"","sources":["../../../src/sync/store-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store-adapter.js","sourceRoot":"","sources":["../../../src/sync/store-adapter.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,9 @@ import type { ColumnVersionStore } from '../metadata/column-version.js';
11
11
  import type { TombstoneStore } from '../metadata/tombstones.js';
12
12
  import type { ChangeLogStore } from '../metadata/change-log.js';
13
13
  import type { SchemaMigrationStore } from '../metadata/schema-migration.js';
14
- import type { SyncConfig, ApplyToStoreCallback } from './protocol.js';
14
+ import type { QuarantineStore } from '../metadata/quarantine.js';
15
+ import type { BasisLifecycleStore } from '../metadata/basis-lifecycle.js';
16
+ import type { SyncConfig, ApplyToStoreCallback, ApplyToStoreResult, UnknownTableDisposition } from './protocol.js';
15
17
  import type { SyncEventEmitterImpl } from './events.js';
16
18
  /**
17
19
  * Context shared across sync sub-modules.
@@ -27,10 +29,34 @@ export interface SyncContext {
27
29
  readonly tombstones: TombstoneStore;
28
30
  readonly changeLog: ChangeLogStore;
29
31
  readonly schemaMigrations: SchemaMigrationStore;
32
+ readonly quarantine: QuarantineStore;
33
+ readonly basisLifecycle: BasisLifecycleStore;
30
34
  readonly syncEvents: SyncEventEmitterImpl;
31
35
  readonly applyToStore?: ApplyToStoreCallback;
32
36
  getSiteId(): SiteId;
33
37
  getCurrentHLC(): HLC;
38
+ /**
39
+ * Whether `(schema, table)` is in the local basis. Backed by the
40
+ * `getTableSchema` oracle; when no oracle was provided detection is inert and
41
+ * this returns `true` for every table (the store adapter's defensive throw
42
+ * remains the fallback for genuinely-retired tables).
43
+ */
44
+ isTableInBasis(schema: string, table: string): boolean;
45
+ /**
46
+ * Current column names for an in-basis table, or `undefined` when the table is
47
+ * outside the local basis (or no `getTableSchema` oracle was wired). Backs both
48
+ * the drain path's basis gate (`undefined` ⇒ the held group's table is not back,
49
+ * skip it) and its schema-drift filter (a held column change for a column absent
50
+ * from this set is drift-dropped). Implemented via `getTableSchema` in
51
+ * `SyncManagerImpl`.
52
+ */
53
+ getTableColumnNames(schema: string, table: string): readonly string[] | undefined;
54
+ /**
55
+ * Record cumulative unknown-table disposition stats (surfaced via
56
+ * `SyncManager.getUnknownTableStats`). Telemetry-only; called once per
57
+ * diverted `(schema, table)` group after successful admission.
58
+ */
59
+ recordUnknownTable(disposition: UnknownTableDisposition, schema: string, table: string, changeCount: number): void;
34
60
  }
35
61
  /**
36
62
  * Persist HLC state to the KV store (standalone put).
@@ -40,6 +66,23 @@ export declare function persistHLCState(ctx: SyncContext): Promise<void>;
40
66
  * Normalize an unknown caught value into an Error instance.
41
67
  */
42
68
  export declare function toError(error: unknown): Error;
69
+ /**
70
+ * Abort the apply if the store reported any per-change errors.
71
+ *
72
+ * The store adapter continues applying other tables when one fails, collecting
73
+ * each failure in `result.errors` rather than throwing — so the maximal set of
74
+ * resolvable rows reaches committed storage. The *consumer* must still treat
75
+ * any non-empty `errors` exactly like the whole-batch throw path: emit an error
76
+ * sync-state event and throw BEFORE committing any CRDT metadata, so the whole
77
+ * batch re-resolves and re-applies idempotently on the next sync attempt.
78
+ *
79
+ * This upholds the write-ordering invariant (docs/sync.md § Transactional
80
+ * Integrity): CRDT metadata must not be committed when the corresponding data
81
+ * write did not land — for per-change failures, not just whole-batch throws.
82
+ *
83
+ * No-op when `result.errors` is empty.
84
+ */
85
+ export declare function throwIfApplyErrors(ctx: SyncContext, result: ApplyToStoreResult): void;
43
86
  /**
44
87
  * Write HLC state into an existing WriteBatch.
45
88
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sync-context.d.ts","sourceRoot":"","sources":["../../../src/sync/sync-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,gBAAgB,EAAE,oBAAoB,CAAC;IAChD,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC;IAE7C,SAAS,IAAI,MAAM,CAAC;IACpB,aAAa,IAAI,GAAG,CAAC;CACrB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAE7C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,OAAO,gBAAgB,EAAE,UAAU,GACxC,IAAI,CAON"}
1
+ {"version":3,"file":"sync-context.d.ts","sourceRoot":"","sources":["../../../src/sync/sync-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACnH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,gBAAgB,EAAE,oBAAoB,CAAC;IAChD,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,mBAAmB,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC;IAE7C,SAAS,IAAI,MAAM,CAAC;IACpB,aAAa,IAAI,GAAG,CAAC;IAErB;;;;;OAKG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAEvD;;;;;;;OAOG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAElF;;;;OAIG;IACH,kBAAkB,CACjB,WAAW,EAAE,uBAAuB,EACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GACjB,IAAI,CAAC;CACR;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAE7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAarF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,OAAO,gBAAgB,EAAE,UAAU,GACxC,IAAI,CAON"}
@@ -22,6 +22,32 @@ export async function persistHLCState(ctx) {
22
22
  export function toError(error) {
23
23
  return error instanceof Error ? error : new Error(String(error));
24
24
  }
25
+ /**
26
+ * Abort the apply if the store reported any per-change errors.
27
+ *
28
+ * The store adapter continues applying other tables when one fails, collecting
29
+ * each failure in `result.errors` rather than throwing — so the maximal set of
30
+ * resolvable rows reaches committed storage. The *consumer* must still treat
31
+ * any non-empty `errors` exactly like the whole-batch throw path: emit an error
32
+ * sync-state event and throw BEFORE committing any CRDT metadata, so the whole
33
+ * batch re-resolves and re-applies idempotently on the next sync attempt.
34
+ *
35
+ * This upholds the write-ordering invariant (docs/sync.md § Transactional
36
+ * Integrity): CRDT metadata must not be committed when the corresponding data
37
+ * write did not land — for per-change failures, not just whole-batch throws.
38
+ *
39
+ * No-op when `result.errors` is empty.
40
+ */
41
+ export function throwIfApplyErrors(ctx, result) {
42
+ if (result.errors.length === 0)
43
+ return;
44
+ const detail = result.errors
45
+ .map(({ change, error }) => `${change.schema}.${change.table} (${change.type}): ${error.message}`)
46
+ .join('; ');
47
+ const error = new Error(`apply-to-store failed for ${result.errors.length} change(s): ${detail}`, { cause: result.errors[0].error });
48
+ ctx.syncEvents.emitSyncStateChange({ status: 'error', error });
49
+ throw error;
50
+ }
25
51
  /**
26
52
  * Write HLC state into an existing WriteBatch.
27
53
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sync-context.js","sourceRoot":"","sources":["../../../src/sync/sync-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAuBtD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAgB;IACrD,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACrC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CACnC,GAAgB,EAChB,KAA0C;IAE1C,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"sync-context.js","sourceRoot":"","sources":["../../../src/sync/sync-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAuDtD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAgB;IACrD,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACrC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAgB,EAAE,MAA0B;IAC9E,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM;SAC1B,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;SACjG,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,6BAA6B,MAAM,CAAC,MAAM,CAAC,MAAM,eAAe,MAAM,EAAE,EACxE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CACjC,CAAC;IAEF,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,MAAM,KAAK,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CACnC,GAAgB,EAChB,KAA0C;IAE1C,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC"}