@korajs/core 0.4.0 → 0.5.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.
package/dist/index.cjs CHANGED
@@ -20,12 +20,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ APPLY_FAILURE_CODES: () => APPLY_FAILURE_CODES,
24
+ APPLY_RESULTS: () => APPLY_RESULTS,
23
25
  ArrayFieldBuilder: () => ArrayFieldBuilder,
24
26
  CONNECTION_QUALITIES: () => CONNECTION_QUALITIES,
27
+ CausalTracker: () => CausalTracker,
25
28
  ClockDriftError: () => ClockDriftError,
26
29
  EnumFieldBuilder: () => EnumFieldBuilder,
27
30
  FieldBuilder: () => FieldBuilder,
28
31
  HybridLogicalClock: () => HybridLogicalClock,
32
+ KORA_ERROR_FIX_SUGGESTIONS: () => KORA_ERROR_FIX_SUGGESTIONS,
29
33
  KoraError: () => KoraError,
30
34
  MERGE_STRATEGIES: () => MERGE_STRATEGIES,
31
35
  MergeConflictError: () => MergeConflictError,
@@ -37,17 +41,22 @@ __export(index_exports, {
37
41
  StorageError: () => StorageError,
38
42
  SyncError: () => SyncError,
39
43
  advanceVector: () => advanceVector,
44
+ applyOperationTransforms: () => applyOperationTransforms,
40
45
  buildScopeMap: () => buildScopeMap,
41
46
  buildStateMachineConstraints: () => buildStateMachineConstraints,
42
47
  canAutoRollback: () => canAutoRollback,
48
+ collectSchemaScopeFields: () => collectSchemaScopeFields,
49
+ collectSchemaScopeValueKeys: () => collectSchemaScopeValueKeys,
43
50
  computeDelta: () => computeDelta,
44
51
  createOperation: () => createOperation,
45
52
  createReversibleMigration: () => createReversibleMigration,
46
53
  createVersionVector: () => createVersionVector,
54
+ defaultApplyFailureReason: () => defaultApplyFailureReason,
47
55
  defaultSequenceFormat: () => defaultSequenceFormat,
48
56
  defineSchema: () => defineSchema,
49
57
  deserializeVector: () => deserializeVector,
50
58
  dominates: () => dominates,
59
+ extractScopeValuesFromClaims: () => extractScopeValuesFromClaims,
51
60
  extractTimestamp: () => extractTimestamp,
52
61
  formatSequenceValue: () => formatSequenceValue,
53
62
  generateFullDDL: () => generateFullDDL,
@@ -55,8 +64,13 @@ __export(index_exports, {
55
64
  generateRollbackSteps: () => generateRollbackSteps,
56
65
  generateSQL: () => generateSQL,
57
66
  generateUUIDv7: () => generateUUIDv7,
67
+ getCollectionScopeBindings: () => getCollectionScopeBindings,
68
+ getKoraErrorFix: () => getKoraErrorFix,
58
69
  getTransitionMap: () => getTransitionMap,
70
+ hasSchemaSyncRules: () => hasSchemaSyncRules,
71
+ isApplyFailure: () => isApplyFailure,
59
72
  isAtomicOp: () => isAtomicOp,
73
+ isCollectionSyncScoped: () => isCollectionSyncScoped,
60
74
  isValidOperation: () => isValidOperation,
61
75
  isValidUUIDv7: () => isValidUUIDv7,
62
76
  mergeVectors: () => mergeVectors,
@@ -85,6 +99,23 @@ var MERGE_STRATEGIES = [
85
99
  ];
86
100
  var CONNECTION_QUALITIES = ["excellent", "good", "fair", "poor", "offline"];
87
101
 
102
+ // src/errors/error-fixes.ts
103
+ var KORA_ERROR_FIX_SUGGESTIONS = {
104
+ SCHEMA_VALIDATION: "Review your defineSchema() definition: every collection needs at least one field and a positive version.",
105
+ OPERATION_ERROR: "Check the operation payload matches your schema field types and required fields.",
106
+ MERGE_CONFLICT: "Add a custom resolver for the conflicting field in defineSchema(), or adjust constraint onConflict rules.",
107
+ SYNC_ERROR: "Verify the sync server URL, auth token, and that the server accepts your schema version.",
108
+ STORAGE_ERROR: "Confirm the storage adapter is supported in this environment and the database path is writable.",
109
+ CLOCK_DRIFT: "Sync device wall-clock time (NTP). Kora refuses new timestamps when drift exceeds five minutes.",
110
+ PERSISTENCE_ERROR: "IndexedDB persistence failed; check browser storage settings and available disk quota.",
111
+ MISSING_WORKER_URL: "Pass store.workerUrl pointing to sqlite-wasm-worker.js (see create-kora-app templates).",
112
+ INVALID_SYNC_URL: "Use ws:// or wss:// for WebSocket transport, or http(s):// for HTTP long-polling.",
113
+ APP_NOT_READY: "Await app.ready before querying or mutating collections, or wrap the tree in <KoraProvider app={app}>."
114
+ };
115
+ function getKoraErrorFix(code) {
116
+ return KORA_ERROR_FIX_SUGGESTIONS[code];
117
+ }
118
+
88
119
  // src/errors/errors.ts
89
120
  var KoraError = class extends Error {
90
121
  constructor(message, code, context) {
@@ -95,6 +126,16 @@ var KoraError = class extends Error {
95
126
  }
96
127
  code;
97
128
  context;
129
+ /**
130
+ * Actionable hint for resolving this error (from registry or error context).
131
+ */
132
+ get fix() {
133
+ const fromContext = this.context?.fix;
134
+ if (typeof fromContext === "string" && fromContext.length > 0) {
135
+ return fromContext;
136
+ }
137
+ return getKoraErrorFix(this.code);
138
+ }
98
139
  };
99
140
  var SchemaValidationError = class extends KoraError {
100
141
  constructor(message, context) {
@@ -192,6 +233,7 @@ var HybridLogicalClock = class {
192
233
  */
193
234
  receive(remote) {
194
235
  const physicalTime = this.timeSource.now();
236
+ const wasColdStart = this.wallTime === 0;
195
237
  if (physicalTime > this.wallTime && physicalTime > remote.wallTime) {
196
238
  this.wallTime = physicalTime;
197
239
  this.logical = 0;
@@ -203,7 +245,9 @@ var HybridLogicalClock = class {
203
245
  } else {
204
246
  this.logical++;
205
247
  }
206
- this.checkDrift(physicalTime);
248
+ if (!wasColdStart) {
249
+ this.checkDrift(physicalTime);
250
+ }
207
251
  return { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId };
208
252
  }
209
253
  /**
@@ -252,6 +296,50 @@ var HybridLogicalClock = class {
252
296
  }
253
297
  };
254
298
 
299
+ // src/causal/causal-tracker.ts
300
+ var CausalTracker = class {
301
+ lastOpIdByCollection = /* @__PURE__ */ new Map();
302
+ lastTransactionOpId = null;
303
+ /**
304
+ * Start a new transaction boundary. Clears in-transaction op ids.
305
+ */
306
+ beginTransaction() {
307
+ this.lastTransactionOpId = null;
308
+ }
309
+ /**
310
+ * Clear the transaction boundary without recording ops (after rollback).
311
+ */
312
+ clearTransaction() {
313
+ this.lastTransactionOpId = null;
314
+ }
315
+ /**
316
+ * Compute causal dependencies for the next operation in a collection.
317
+ * Uses direct parents only: collection head and the previous op in the open transaction.
318
+ */
319
+ nextCausalDeps(collection, inTransaction) {
320
+ const deps = [];
321
+ const lastInCollection = this.lastOpIdByCollection.get(collection);
322
+ if (lastInCollection !== void 0) {
323
+ deps.push(lastInCollection);
324
+ }
325
+ if (inTransaction && this.lastTransactionOpId !== null) {
326
+ if (!deps.includes(this.lastTransactionOpId)) {
327
+ deps.push(this.lastTransactionOpId);
328
+ }
329
+ }
330
+ return deps;
331
+ }
332
+ /**
333
+ * Record an operation after it has been created and assigned an id.
334
+ */
335
+ afterOperation(collection, operationId, inTransaction) {
336
+ this.lastOpIdByCollection.set(collection, operationId);
337
+ if (inTransaction) {
338
+ this.lastTransactionOpId = operationId;
339
+ }
340
+ }
341
+ };
342
+
255
343
  // src/identifiers/uuid-v7.ts
256
344
  var defaultRandom = globalThis.crypto;
257
345
  function generateUUIDv7(timestamp = Date.now(), randomSource = defaultRandom) {
@@ -288,6 +376,37 @@ function formatUUID(bytes) {
288
376
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
289
377
  }
290
378
 
379
+ // src/operations/apply-result.ts
380
+ var APPLY_RESULTS = ["applied", "duplicate", "skipped", "rejected", "deferred"];
381
+ var APPLY_FAILURE_CODES = {
382
+ APPLY_FAILED: "APPLY_FAILED",
383
+ APPLY_SKIPPED: "APPLY_SKIPPED",
384
+ APPLY_REJECTED: "APPLY_REJECTED",
385
+ APPLY_DEFERRED: "APPLY_DEFERRED",
386
+ CLOCK_DRIFT: "CLOCK_DRIFT",
387
+ REFERENTIAL_INTEGRITY: "REFERENTIAL_INTEGRITY",
388
+ SCHEMA_MISMATCH: "SCHEMA_MISMATCH"
389
+ };
390
+ function isApplyFailure(result) {
391
+ return result === "skipped" || result === "rejected" || result === "deferred";
392
+ }
393
+ function defaultApplyFailureReason(result, overrides) {
394
+ const base = result === "deferred" ? {
395
+ code: APPLY_FAILURE_CODES.APPLY_DEFERRED,
396
+ message: "Operation apply was deferred.",
397
+ retriable: true
398
+ } : result === "rejected" ? {
399
+ code: APPLY_FAILURE_CODES.APPLY_REJECTED,
400
+ message: "Operation apply was rejected.",
401
+ retriable: false
402
+ } : {
403
+ code: APPLY_FAILURE_CODES.APPLY_SKIPPED,
404
+ message: "Operation apply was skipped.",
405
+ retriable: false
406
+ };
407
+ return { ...base, ...overrides };
408
+ }
409
+
291
410
  // src/operations/content-hash.ts
292
411
  async function computeOperationId(input, timestamp) {
293
412
  const hashInput = {
@@ -307,8 +426,11 @@ async function computeOperationId(input, timestamp) {
307
426
  return bufferToHex(hashBuffer);
308
427
  }
309
428
  function canonicalize(obj) {
310
- if (obj === null || obj === void 0) {
311
- return JSON.stringify(obj);
429
+ if (obj === null) {
430
+ return "null";
431
+ }
432
+ if (obj === void 0) {
433
+ return "null";
312
434
  }
313
435
  if (typeof obj !== "object") {
314
436
  return JSON.stringify(obj);
@@ -320,7 +442,7 @@ function canonicalize(obj) {
320
442
  const keys = Object.keys(obj).sort();
321
443
  const pairs = keys.map((key) => {
322
444
  const value = obj[key];
323
- return `${JSON.stringify(key)}:${canonicalize(value)}`;
445
+ return `${JSON.stringify(key)}:${canonicalize(value === void 0 ? null : value)}`;
324
446
  });
325
447
  return `{${pairs.join(",")}}`;
326
448
  }
@@ -441,6 +563,7 @@ function isValidOperation(value) {
441
563
  }
442
564
  function deepFreeze(obj) {
443
565
  if (typeof obj !== "object" || obj === null) return obj;
566
+ if (ArrayBuffer.isView(obj)) return obj;
444
567
  Object.freeze(obj);
445
568
  for (const value of Object.values(obj)) {
446
569
  if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
@@ -548,6 +671,69 @@ function toAtomicOp(sentinel) {
548
671
  return { type: sentinel.type, value: sentinel.value };
549
672
  }
550
673
 
674
+ // src/scopes/sync-scope-bindings.ts
675
+ function hasSchemaSyncRules(schema) {
676
+ return schema.sync !== void 0 && Object.keys(schema.sync).length > 0;
677
+ }
678
+ function getCollectionScopeBindings(schema, collectionName) {
679
+ const syncRule = schema.sync?.[collectionName];
680
+ if (syncRule && Object.keys(syncRule.where).length > 0) {
681
+ return { ...syncRule.where };
682
+ }
683
+ const scopeFields = schema.collections[collectionName]?.scope ?? [];
684
+ if (scopeFields.length === 0) {
685
+ return null;
686
+ }
687
+ const bindings = {};
688
+ for (const field of scopeFields) {
689
+ bindings[field] = field;
690
+ }
691
+ return bindings;
692
+ }
693
+ function isCollectionSyncScoped(schema, collectionName) {
694
+ if (hasSchemaSyncRules(schema)) {
695
+ if (schema.sync?.[collectionName]) {
696
+ return true;
697
+ }
698
+ const legacyScope = schema.collections[collectionName]?.scope ?? [];
699
+ return legacyScope.length > 0;
700
+ }
701
+ return getCollectionScopeBindings(schema, collectionName) !== null;
702
+ }
703
+ function collectSchemaScopeValueKeys(schema) {
704
+ const keys = /* @__PURE__ */ new Set();
705
+ for (const collection of Object.values(schema.collections)) {
706
+ for (const field of collection.scope) {
707
+ keys.add(field);
708
+ }
709
+ }
710
+ if (schema.sync) {
711
+ for (const rule of Object.values(schema.sync)) {
712
+ for (const scopeKey of Object.values(rule.where)) {
713
+ keys.add(scopeKey);
714
+ }
715
+ }
716
+ }
717
+ return [...keys];
718
+ }
719
+ function normalizeSyncRuleWhere(collectionName, where) {
720
+ const normalized = {};
721
+ for (const [field, scopeKey] of Object.entries(where)) {
722
+ if (scopeKey === true) {
723
+ normalized[field] = field;
724
+ continue;
725
+ }
726
+ if (typeof scopeKey === "string" && scopeKey.length > 0) {
727
+ normalized[field] = scopeKey;
728
+ continue;
729
+ }
730
+ throw new Error(
731
+ `Invalid sync rule for collection "${collectionName}": where.${field} must be true or a non-empty scope key string`
732
+ );
733
+ }
734
+ return normalized;
735
+ }
736
+
551
737
  // src/state-machine/state-machine.ts
552
738
  function validateTransition(constraint, fromValue, toValue) {
553
739
  const from = String(fromValue);
@@ -637,7 +823,7 @@ function validateStateMachineDefinition(collectionName, sm, fields) {
637
823
  // src/schema/define.ts
638
824
  var COLLECTION_NAME_RE = /^[a-z][a-z0-9_]*$/;
639
825
  var FIELD_NAME_RE = /^[a-z][a-zA-Z0-9_]*$/;
640
- var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "_created_at", "_updated_at", "_deleted"]);
826
+ var RESERVED_FIELDS = /* @__PURE__ */ new Set(["id", "_created_at", "_updated_at", "_version", "_deleted"]);
641
827
  function defineSchema(input) {
642
828
  validateVersion(input.version);
643
829
  const collections = {};
@@ -680,7 +866,15 @@ function defineSchema(input) {
680
866
  migrations[version] = migration;
681
867
  }
682
868
  }
683
- return { version: input.version, collections, relations, migrations };
869
+ const sync = buildSyncRules(input.sync, collections);
870
+ applySyncRulesToCollectionScope(collections, sync);
871
+ return {
872
+ version: input.version,
873
+ collections,
874
+ relations,
875
+ migrations,
876
+ ...sync ? { sync } : {}
877
+ };
684
878
  }
685
879
  function validateVersion(version) {
686
880
  if (typeof version !== "number" || !Number.isInteger(version) || version < 1) {
@@ -761,6 +955,63 @@ function buildCollection(name, input) {
761
955
  }
762
956
  return { fields, indexes, constraints, resolvers, scope, stateMachine };
763
957
  }
958
+ function buildSyncRules(syncInput, collections) {
959
+ if (!syncInput) {
960
+ return void 0;
961
+ }
962
+ const sync = {};
963
+ for (const [collectionName, ruleInput] of Object.entries(syncInput)) {
964
+ if (!(collectionName in collections)) {
965
+ throw new SchemaValidationError(
966
+ `Sync rule references collection "${collectionName}" which does not exist. Available collections: ${Object.keys(collections).join(", ")}`,
967
+ { collection: collectionName }
968
+ );
969
+ }
970
+ if (!ruleInput.where || Object.keys(ruleInput.where).length === 0) {
971
+ throw new SchemaValidationError(
972
+ `Sync rule for collection "${collectionName}" must declare at least one where binding`,
973
+ { collection: collectionName }
974
+ );
975
+ }
976
+ const collection = collections[collectionName];
977
+ if (!collection) {
978
+ continue;
979
+ }
980
+ for (const fieldName of Object.keys(ruleInput.where)) {
981
+ if (!(fieldName in collection.fields)) {
982
+ throw new SchemaValidationError(
983
+ `Sync rule where field "${fieldName}" does not exist in collection "${collectionName}". Available fields: ${Object.keys(collection.fields).join(", ")}`,
984
+ { collection: collectionName, field: fieldName }
985
+ );
986
+ }
987
+ }
988
+ try {
989
+ sync[collectionName] = { where: normalizeSyncRuleWhere(collectionName, ruleInput.where) };
990
+ } catch (error) {
991
+ throw new SchemaValidationError(
992
+ error instanceof Error ? error.message : "Invalid sync rule",
993
+ { collection: collectionName }
994
+ );
995
+ }
996
+ }
997
+ return sync;
998
+ }
999
+ function applySyncRulesToCollectionScope(collections, sync) {
1000
+ if (!sync) {
1001
+ return;
1002
+ }
1003
+ for (const [collectionName, rule] of Object.entries(sync)) {
1004
+ const collection = collections[collectionName];
1005
+ if (!collection) {
1006
+ continue;
1007
+ }
1008
+ for (const fieldName of Object.keys(rule.where)) {
1009
+ if (!collection.scope.includes(fieldName)) {
1010
+ collection.scope.push(fieldName);
1011
+ }
1012
+ }
1013
+ }
1014
+ }
764
1015
  function validateFieldName(collection, fieldName) {
765
1016
  if (RESERVED_FIELDS.has(fieldName)) {
766
1017
  throw new SchemaValidationError(
@@ -848,6 +1099,7 @@ function generateSQL(collectionName, collection, relations) {
848
1099
  }
849
1100
  columns.push("_created_at INTEGER NOT NULL");
850
1101
  columns.push("_updated_at INTEGER NOT NULL");
1102
+ columns.push("_version TEXT NOT NULL DEFAULT ''");
851
1103
  columns.push("_deleted INTEGER NOT NULL DEFAULT 0");
852
1104
  statements.push(`CREATE TABLE IF NOT EXISTS ${collectionName} (
853
1105
  ${columns.join(",\n ")}
@@ -857,6 +1109,10 @@ function generateSQL(collectionName, collection, relations) {
857
1109
  statements.push(`--kora:safe-alter
858
1110
  ALTER TABLE ${collectionName} ADD COLUMN ${colDef}`);
859
1111
  }
1112
+ statements.push(
1113
+ `--kora:safe-alter
1114
+ ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
1115
+ );
860
1116
  for (const indexField of collection.indexes) {
861
1117
  statements.push(
862
1118
  `CREATE INDEX IF NOT EXISTS idx_${collectionName}_${indexField} ON ${collectionName} (${indexField})`
@@ -896,6 +1152,18 @@ function generateFullDDL(schema) {
896
1152
  statements.push(
897
1153
  "CREATE TABLE IF NOT EXISTS _kora_sequences (\n name TEXT NOT NULL,\n scope TEXT NOT NULL DEFAULT '',\n node_id TEXT NOT NULL,\n counter INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (name, scope, node_id)\n)"
898
1154
  );
1155
+ statements.push(
1156
+ "CREATE TABLE IF NOT EXISTS _kora_sync_queue (\n id TEXT PRIMARY KEY NOT NULL,\n payload TEXT NOT NULL\n)"
1157
+ );
1158
+ statements.push(
1159
+ "CREATE TABLE IF NOT EXISTS _kora_audit_traces (\n id TEXT PRIMARY KEY NOT NULL,\n recorded_at INTEGER NOT NULL,\n event_type TEXT NOT NULL,\n collection TEXT NOT NULL,\n record_id TEXT NOT NULL,\n field TEXT NOT NULL,\n strategy TEXT NOT NULL,\n tier INTEGER NOT NULL,\n constraint_name TEXT,\n trace_json TEXT NOT NULL\n)"
1160
+ );
1161
+ statements.push(
1162
+ "CREATE INDEX IF NOT EXISTS idx_kora_audit_traces_recorded_at ON _kora_audit_traces (recorded_at)"
1163
+ );
1164
+ statements.push(
1165
+ "CREATE INDEX IF NOT EXISTS idx_kora_audit_traces_collection ON _kora_audit_traces (collection)"
1166
+ );
899
1167
  for (const [name, collection] of Object.entries(schema.collections)) {
900
1168
  statements.push(...generateSQL(name, collection, schema.relations));
901
1169
  }
@@ -1024,10 +1292,24 @@ var EnumFieldBuilder = class _EnumFieldBuilder extends FieldBuilder {
1024
1292
  );
1025
1293
  }
1026
1294
  default(value) {
1027
- return new _EnumFieldBuilder(this._enumValues, false, value, this._auto, this._mergeStrategy, this._transitions);
1295
+ return new _EnumFieldBuilder(
1296
+ this._enumValues,
1297
+ false,
1298
+ value,
1299
+ this._auto,
1300
+ this._mergeStrategy,
1301
+ this._transitions
1302
+ );
1028
1303
  }
1029
1304
  auto() {
1030
- return new _EnumFieldBuilder(this._enumValues, false, void 0, true, this._mergeStrategy, this._transitions);
1305
+ return new _EnumFieldBuilder(
1306
+ this._enumValues,
1307
+ false,
1308
+ void 0,
1309
+ true,
1310
+ this._mergeStrategy,
1311
+ this._transitions
1312
+ );
1031
1313
  }
1032
1314
  merge(strategy) {
1033
1315
  return new _EnumFieldBuilder(
@@ -1513,12 +1795,13 @@ function vectorsEqual(a, b) {
1513
1795
  }
1514
1796
  return true;
1515
1797
  }
1516
- function computeDelta(localVector, remoteVector, operationLog) {
1798
+ async function computeDelta(localVector, remoteVector, operationLog) {
1517
1799
  const missing = [];
1518
1800
  for (const [nodeId, localSeq] of localVector) {
1519
1801
  const remoteSeq = remoteVector.get(nodeId) ?? 0;
1520
1802
  if (localSeq > remoteSeq) {
1521
- missing.push(...operationLog.getRange(nodeId, remoteSeq + 1, localSeq));
1803
+ const ops = await operationLog.getRange(nodeId, remoteSeq + 1, localSeq);
1804
+ missing.push(...ops);
1522
1805
  }
1523
1806
  }
1524
1807
  return topologicalSort(missing);
@@ -1535,12 +1818,17 @@ function deserializeVector(s) {
1535
1818
  // src/scopes/build-scope-map.ts
1536
1819
  function buildScopeMap(schema, scopeValues) {
1537
1820
  const result = {};
1538
- for (const [collName, collDef] of Object.entries(schema.collections)) {
1539
- if (collDef.scope.length > 0) {
1821
+ const partialSync = hasSchemaSyncRules(schema);
1822
+ for (const collName of Object.keys(schema.collections)) {
1823
+ if (partialSync && !isCollectionSyncScoped(schema, collName)) {
1824
+ continue;
1825
+ }
1826
+ const bindings = getCollectionScopeBindings(schema, collName);
1827
+ if (bindings) {
1540
1828
  const collScope = {};
1541
- for (const field of collDef.scope) {
1542
- if (field in scopeValues) {
1543
- collScope[field] = scopeValues[field];
1829
+ for (const [field, scopeKey] of Object.entries(bindings)) {
1830
+ if (scopeKey in scopeValues) {
1831
+ collScope[field] = scopeValues[scopeKey];
1544
1832
  }
1545
1833
  }
1546
1834
  result[collName] = collScope;
@@ -1551,6 +1839,34 @@ function buildScopeMap(schema, scopeValues) {
1551
1839
  return result;
1552
1840
  }
1553
1841
 
1842
+ // src/scopes/extract-scope-from-claims.ts
1843
+ function collectSchemaScopeFields(schema) {
1844
+ return collectSchemaScopeValueKeys(schema);
1845
+ }
1846
+ function extractScopeValuesFromClaims(schema, claims) {
1847
+ const scopeFields = collectSchemaScopeValueKeys(schema);
1848
+ if (scopeFields.length === 0) {
1849
+ return {};
1850
+ }
1851
+ const nestedScope = claims.scope;
1852
+ const scopeObject = typeof nestedScope === "object" && nestedScope !== null && !Array.isArray(nestedScope) ? nestedScope : {};
1853
+ const result = {};
1854
+ for (const field of scopeFields) {
1855
+ if (field in claims) {
1856
+ result[field] = claims[field];
1857
+ continue;
1858
+ }
1859
+ if (field in scopeObject) {
1860
+ result[field] = scopeObject[field];
1861
+ continue;
1862
+ }
1863
+ if (field === "userId" && typeof claims.sub === "string") {
1864
+ result.userId = claims.sub;
1865
+ }
1866
+ }
1867
+ return result;
1868
+ }
1869
+
1554
1870
  // src/sequences/sequence-format.ts
1555
1871
  function formatSequenceValue(template, counter, nodeId, now) {
1556
1872
  const date = now ?? /* @__PURE__ */ new Date();
@@ -1577,6 +1893,29 @@ function defaultSequenceFormat(name) {
1577
1893
  return `${name}-{seq:4}`;
1578
1894
  }
1579
1895
 
1896
+ // src/migration/apply-operation-transforms.ts
1897
+ var MAX_TRANSFORM_STEPS = 32;
1898
+ function applyOperationTransforms(operation, targetSchemaVersion, transforms) {
1899
+ let current = operation;
1900
+ for (let step = 0; step < MAX_TRANSFORM_STEPS; step++) {
1901
+ if (current.schemaVersion === targetSchemaVersion) {
1902
+ return current;
1903
+ }
1904
+ const transform = transforms.find(
1905
+ (candidate) => candidate.fromVersion === current.schemaVersion
1906
+ );
1907
+ if (!transform) {
1908
+ return null;
1909
+ }
1910
+ const next = transform.transform(current);
1911
+ if (next === null) {
1912
+ return null;
1913
+ }
1914
+ current = next;
1915
+ }
1916
+ return null;
1917
+ }
1918
+
1580
1919
  // src/migrations/migration-builder.ts
1581
1920
  var RollbackBuilder = class {
1582
1921
  _steps = [];
@@ -2121,12 +2460,16 @@ function generateProtoDefinitions(schema) {
2121
2460
  }
2122
2461
  // Annotate the CommonJS export names for ESM import in node:
2123
2462
  0 && (module.exports = {
2463
+ APPLY_FAILURE_CODES,
2464
+ APPLY_RESULTS,
2124
2465
  ArrayFieldBuilder,
2125
2466
  CONNECTION_QUALITIES,
2467
+ CausalTracker,
2126
2468
  ClockDriftError,
2127
2469
  EnumFieldBuilder,
2128
2470
  FieldBuilder,
2129
2471
  HybridLogicalClock,
2472
+ KORA_ERROR_FIX_SUGGESTIONS,
2130
2473
  KoraError,
2131
2474
  MERGE_STRATEGIES,
2132
2475
  MergeConflictError,
@@ -2138,17 +2481,22 @@ function generateProtoDefinitions(schema) {
2138
2481
  StorageError,
2139
2482
  SyncError,
2140
2483
  advanceVector,
2484
+ applyOperationTransforms,
2141
2485
  buildScopeMap,
2142
2486
  buildStateMachineConstraints,
2143
2487
  canAutoRollback,
2488
+ collectSchemaScopeFields,
2489
+ collectSchemaScopeValueKeys,
2144
2490
  computeDelta,
2145
2491
  createOperation,
2146
2492
  createReversibleMigration,
2147
2493
  createVersionVector,
2494
+ defaultApplyFailureReason,
2148
2495
  defaultSequenceFormat,
2149
2496
  defineSchema,
2150
2497
  deserializeVector,
2151
2498
  dominates,
2499
+ extractScopeValuesFromClaims,
2152
2500
  extractTimestamp,
2153
2501
  formatSequenceValue,
2154
2502
  generateFullDDL,
@@ -2156,8 +2504,13 @@ function generateProtoDefinitions(schema) {
2156
2504
  generateRollbackSteps,
2157
2505
  generateSQL,
2158
2506
  generateUUIDv7,
2507
+ getCollectionScopeBindings,
2508
+ getKoraErrorFix,
2159
2509
  getTransitionMap,
2510
+ hasSchemaSyncRules,
2511
+ isApplyFailure,
2160
2512
  isAtomicOp,
2513
+ isCollectionSyncScoped,
2161
2514
  isValidOperation,
2162
2515
  isValidUUIDv7,
2163
2516
  mergeVectors,