@korajs/core 0.6.0 → 1.0.0-beta.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
@@ -30,21 +30,26 @@ __export(index_exports, {
30
30
  EnumFieldBuilder: () => EnumFieldBuilder,
31
31
  FieldBuilder: () => FieldBuilder,
32
32
  HybridLogicalClock: () => HybridLogicalClock,
33
+ InvalidTimestampError: () => InvalidTimestampError,
33
34
  KORA_ERROR_FIX_SUGGESTIONS: () => KORA_ERROR_FIX_SUGGESTIONS,
34
35
  KoraError: () => KoraError,
36
+ MAX_LOGICAL: () => MAX_LOGICAL,
35
37
  MERGE_STRATEGIES: () => MERGE_STRATEGIES,
36
38
  MergeConflictError: () => MergeConflictError,
37
39
  MigrationBuilder: () => MigrationBuilder,
38
40
  MigrationRollbackError: () => MigrationRollbackError,
39
41
  OperationError: () => OperationError,
42
+ RemoteClockDriftError: () => RemoteClockDriftError,
40
43
  RollbackBuilder: () => RollbackBuilder,
41
44
  SchemaValidationError: () => SchemaValidationError,
42
45
  StorageError: () => StorageError,
43
46
  SyncError: () => SyncError,
44
47
  advanceVector: () => advanceVector,
45
48
  applyOperationTransforms: () => applyOperationTransforms,
49
+ base64ToBytes: () => base64ToBytes,
46
50
  buildScopeMap: () => buildScopeMap,
47
51
  buildStateMachineConstraints: () => buildStateMachineConstraints,
52
+ bytesToBase64: () => bytesToBase64,
48
53
  canAutoRollback: () => canAutoRollback,
49
54
  collectSchemaScopeFields: () => collectSchemaScopeFields,
50
55
  collectSchemaScopeValueKeys: () => collectSchemaScopeValueKeys,
@@ -52,11 +57,13 @@ __export(index_exports, {
52
57
  createOperation: () => createOperation,
53
58
  createReversibleMigration: () => createReversibleMigration,
54
59
  createVersionVector: () => createVersionVector,
60
+ decodeBytesFromOpData: () => decodeBytesFromOpData,
55
61
  defaultApplyFailureReason: () => defaultApplyFailureReason,
56
62
  defaultSequenceFormat: () => defaultSequenceFormat,
57
63
  defineSchema: () => defineSchema,
58
64
  deserializeVector: () => deserializeVector,
59
65
  dominates: () => dominates,
66
+ encodeBytesForOpData: () => encodeBytesForOpData,
60
67
  extractScopeValuesFromClaims: () => extractScopeValuesFromClaims,
61
68
  extractTimestamp: () => extractTimestamp,
62
69
  formatSequenceValue: () => formatSequenceValue,
@@ -72,6 +79,8 @@ __export(index_exports, {
72
79
  isApplyFailure: () => isApplyFailure,
73
80
  isAtomicOp: () => isAtomicOp,
74
81
  isCollectionSyncScoped: () => isCollectionSyncScoped,
82
+ isKoraBytesValue: () => isKoraBytesValue,
83
+ isLegacyNumericByteObject: () => isLegacyNumericByteObject,
75
84
  isValidOperation: () => isValidOperation,
76
85
  isValidUUIDv7: () => isValidUUIDv7,
77
86
  mergeVectors: () => mergeVectors,
@@ -108,6 +117,7 @@ var KORA_ERROR_FIX_SUGGESTIONS = {
108
117
  SYNC_ERROR: "Verify the sync server URL, auth token, and that the server accepts your schema version.",
109
118
  STORAGE_ERROR: "Confirm the storage adapter is supported in this environment and the database path is writable.",
110
119
  CLOCK_DRIFT: "Sync device wall-clock time (NTP). Kora refuses new timestamps when drift exceeds five minutes.",
120
+ INVALID_TIMESTAMP_FIELDS: "HLC timestamps need non-negative integer wallTime (< 10^15) and logical (<= 99999). Generate timestamps through HybridLogicalClock instead of building them by hand.",
111
121
  PERSISTENCE_ERROR: "IndexedDB persistence failed; check browser storage settings and available disk quota.",
112
122
  MISSING_WORKER_URL: "Pass store.workerUrl pointing to sqlite-wasm-worker.js (see create-kora-app templates).",
113
123
  INVALID_SYNC_URL: "Use ws:// or wss:// for WebSocket transport, or http(s):// for HTTP long-polling.",
@@ -186,6 +196,31 @@ var AppNotReadyError = class extends KoraError {
186
196
  this.name = "AppNotReadyError";
187
197
  }
188
198
  };
199
+ var RemoteClockDriftError = class extends KoraError {
200
+ constructor(remoteWallTime, localReferenceTime) {
201
+ const aheadSeconds = Math.round((remoteWallTime - localReferenceTime) / 1e3);
202
+ super(
203
+ `Rejected remote timestamp ${aheadSeconds}s ahead of local reference time. The sending device's clock is set too far in the future.`,
204
+ "REMOTE_CLOCK_DRIFT",
205
+ { remoteWallTime, localReferenceTime, aheadSeconds }
206
+ );
207
+ this.remoteWallTime = remoteWallTime;
208
+ this.localReferenceTime = localReferenceTime;
209
+ this.name = "RemoteClockDriftError";
210
+ }
211
+ remoteWallTime;
212
+ localReferenceTime;
213
+ };
214
+ var InvalidTimestampError = class extends KoraError {
215
+ constructor(message, wallTime, logical) {
216
+ super(message, "INVALID_TIMESTAMP_FIELDS", { wallTime, logical });
217
+ this.wallTime = wallTime;
218
+ this.logical = logical;
219
+ this.name = "InvalidTimestampError";
220
+ }
221
+ wallTime;
222
+ logical;
223
+ };
189
224
  var ClockDriftError = class extends KoraError {
190
225
  constructor(currentHlcTime, physicalTime) {
191
226
  const driftSeconds = Math.round((currentHlcTime - physicalTime) / 1e3);
@@ -204,24 +239,51 @@ var ClockDriftError = class extends KoraError {
204
239
 
205
240
  // src/clock/hlc.ts
206
241
  var systemTimeSource = { now: () => Date.now() };
242
+ var MAX_LOGICAL = 99999;
243
+ var LOGICAL_MODULUS = MAX_LOGICAL + 1;
244
+ var WALL_TIME_LIMIT = 10 ** 15;
207
245
  var DRIFT_WARN_MS = 6e4;
208
246
  var DRIFT_ERROR_MS = 5 * 6e4;
209
- var HybridLogicalClock = class {
210
- constructor(nodeId, timeSource = systemTimeSource, onDriftWarning) {
247
+ var MAX_REMOTE_FUTURE_MS = 5 * 6e4;
248
+ var HybridLogicalClock = class _HybridLogicalClock {
249
+ constructor(nodeId, timeSource = systemTimeSource, onDriftWarning, onDriftError) {
211
250
  this.nodeId = nodeId;
212
251
  this.timeSource = timeSource;
213
252
  this.onDriftWarning = onDriftWarning;
253
+ this.onDriftError = onDriftError;
214
254
  }
215
255
  nodeId;
216
256
  timeSource;
217
257
  onDriftWarning;
258
+ onDriftError;
218
259
  wallTime = 0;
219
260
  logical = 0;
261
+ referenceOffsetMs = null;
262
+ /**
263
+ * Records the known offset between an external time reference (usually the
264
+ * sync server, learned at handshake) and this device's physical clock:
265
+ * `referenceTime - localPhysicalTime`. Once set, drift evaluation and remote
266
+ * timestamp validation are performed against reference-corrected time, so a
267
+ * device with a wrong local clock still validates remote timestamps correctly.
268
+ */
269
+ setReferenceOffset(offsetMs) {
270
+ this.referenceOffsetMs = offsetMs;
271
+ }
272
+ getReferenceOffset() {
273
+ return this.referenceOffsetMs;
274
+ }
275
+ /** Physical time corrected by the known reference offset, when available. */
276
+ effectiveTime(physicalTime) {
277
+ return physicalTime + (this.referenceOffsetMs ?? 0);
278
+ }
220
279
  /**
221
280
  * Generate a new timestamp for a local event.
222
281
  * Guarantees monotonicity: each call returns a timestamp strictly greater than the previous.
223
282
  *
224
- * @throws {ClockDriftError} If physical time is more than 5 minutes behind the HLC wallTime
283
+ * Never throws and never blocks a local write: if the physical clock has
284
+ * fallen behind the HLC (e.g. the user corrected a fast clock), the HLC
285
+ * freezes wallTime and advances the logical counter, and drift is reported
286
+ * through the onDriftWarning / onDriftError callbacks instead.
225
287
  */
226
288
  now() {
227
289
  const physicalTime = this.timeSource.now();
@@ -232,17 +294,40 @@ var HybridLogicalClock = class {
232
294
  } else {
233
295
  this.logical++;
234
296
  }
297
+ this.carryLogicalOverflow();
235
298
  return { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId };
236
299
  }
237
300
  /**
238
301
  * Update clock on receiving a remote timestamp.
239
302
  * Merges the remote clock state with the local state to maintain causal ordering.
240
303
  *
241
- * @throws {ClockDriftError} If physical time is more than 5 minutes behind the resulting wallTime
304
+ * @throws {InvalidTimestampError} If the remote timestamp has non-integer or
305
+ * negative wallTime/logical, or logical beyond {@link MAX_LOGICAL}. Checked
306
+ * BEFORE any state change, so malformed input cannot corrupt this clock.
307
+ * @throws {RemoteClockDriftError} If the remote timestamp is more than 5 minutes
308
+ * ahead of reference-corrected local time. Validation happens BEFORE any state
309
+ * is adopted, so a rejected timestamp cannot poison this clock. When no
310
+ * reference offset is known and this clock is uninitialized (cold start with
311
+ * a possibly-wrong local clock), validation is skipped, matching the previous
312
+ * cold-start behavior but now without a corrupting failure mode afterward.
242
313
  */
243
314
  receive(remote) {
244
315
  const physicalTime = this.timeSource.now();
245
316
  const wasColdStart = this.wallTime === 0;
317
+ if (!Number.isInteger(remote.wallTime) || remote.wallTime < 0 || !Number.isInteger(remote.logical) || remote.logical < 0 || remote.logical > MAX_LOGICAL) {
318
+ throw new InvalidTimestampError(
319
+ `Rejected remote HLC timestamp with invalid fields (wallTime=${remote.wallTime}, logical=${remote.logical}). wallTime and logical must be non-negative integers and logical must not exceed ${MAX_LOGICAL}.`,
320
+ remote.wallTime,
321
+ remote.logical
322
+ );
323
+ }
324
+ const canValidate = this.referenceOffsetMs !== null || !wasColdStart;
325
+ if (canValidate) {
326
+ const reference = this.effectiveTime(physicalTime);
327
+ if (remote.wallTime > reference + MAX_REMOTE_FUTURE_MS) {
328
+ throw new RemoteClockDriftError(remote.wallTime, reference);
329
+ }
330
+ }
246
331
  if (physicalTime > this.wallTime && physicalTime > remote.wallTime) {
247
332
  this.wallTime = physicalTime;
248
333
  this.logical = 0;
@@ -254,11 +339,54 @@ var HybridLogicalClock = class {
254
339
  } else {
255
340
  this.logical++;
256
341
  }
342
+ this.carryLogicalOverflow();
257
343
  if (!wasColdStart) {
258
344
  this.checkDrift(physicalTime);
259
345
  }
260
346
  return { wallTime: this.wallTime, logical: this.logical, nodeId: this.nodeId };
261
347
  }
348
+ /**
349
+ * Advance this clock to at least the given timestamp.
350
+ *
351
+ * Used after a timestamp rebase rewrites unsynced operations: future `now()`
352
+ * timestamps must sort after every rebased operation, otherwise a write issued
353
+ * immediately after the rebase could interleave with (or precede) rebased ops
354
+ * and break the log's total order. Never moves the clock backward — a
355
+ * timestamp at or before the current state is a no-op, preserving the
356
+ * monotonicity guarantee of `now()`.
357
+ *
358
+ * Inputs with logical > MAX_LOGICAL are normalized deterministically by
359
+ * carrying the excess into wallTime, so the adopted state always leaves room
360
+ * for the next increment inside the serializable range.
361
+ */
362
+ advanceTo(ts) {
363
+ const normalized = {
364
+ wallTime: ts.wallTime + Math.floor(ts.logical / LOGICAL_MODULUS),
365
+ logical: ts.logical % LOGICAL_MODULUS,
366
+ nodeId: ts.nodeId
367
+ };
368
+ const current = {
369
+ wallTime: this.wallTime,
370
+ logical: this.logical,
371
+ nodeId: this.nodeId
372
+ };
373
+ if (_HybridLogicalClock.compare(normalized, current) > 0) {
374
+ this.wallTime = normalized.wallTime;
375
+ this.logical = normalized.logical;
376
+ }
377
+ }
378
+ /**
379
+ * Carry the logical counter into wallTime when an increment pushed it past
380
+ * MAX_LOGICAL. Bumping wallTime by 1ms keeps the timestamp strictly greater
381
+ * than everything issued before (monotonicity) while keeping the logical
382
+ * counter inside the 5-digit slot the serialized form depends on.
383
+ */
384
+ carryLogicalOverflow() {
385
+ if (this.logical > MAX_LOGICAL) {
386
+ this.wallTime += 1;
387
+ this.logical = 0;
388
+ }
389
+ }
262
390
  /**
263
391
  * Compare two timestamps. Returns negative if a < b, positive if a > b, zero if equal.
264
392
  * Total order: wallTime first, then logical, then nodeId (lexicographic).
@@ -273,8 +401,23 @@ var HybridLogicalClock = class {
273
401
  /**
274
402
  * Serialize an HLC timestamp to a string that sorts lexicographically.
275
403
  * Format: zero-padded wallTime:logical:nodeId
404
+ *
405
+ * @throws {InvalidTimestampError} If wallTime does not fit 15 digits or
406
+ * logical does not fit 5 digits (or either is negative/non-integer). Such a
407
+ * value would overflow its zero-padded slot and the serialized string would
408
+ * no longer sort in the same order as {@link HybridLogicalClock.compare} —
409
+ * silently corrupting every LWW comparison on stored `_version` columns.
410
+ * Internal clocks can no longer produce such values (the logical counter
411
+ * carries into wallTime); this guards against hand-built timestamps.
276
412
  */
277
413
  static serialize(ts) {
414
+ if (!Number.isInteger(ts.wallTime) || ts.wallTime < 0 || ts.wallTime >= WALL_TIME_LIMIT || !Number.isInteger(ts.logical) || ts.logical < 0 || ts.logical > MAX_LOGICAL) {
415
+ throw new InvalidTimestampError(
416
+ `Cannot serialize HLC timestamp (wallTime=${ts.wallTime}, logical=${ts.logical}): wallTime must be an integer in [0, 10^15) and logical an integer in [0, ${MAX_LOGICAL}]. Values outside these ranges overflow the zero-padded serialized form, which must sort lexicographically identically to HybridLogicalClock.compare.`,
417
+ ts.wallTime,
418
+ ts.logical
419
+ );
420
+ }
278
421
  const wall = ts.wallTime.toString().padStart(15, "0");
279
422
  const log = ts.logical.toString().padStart(5, "0");
280
423
  return `${wall}:${log}:${ts.nodeId}`;
@@ -294,10 +437,16 @@ var HybridLogicalClock = class {
294
437
  nodeId: parts.slice(2).join(":")
295
438
  };
296
439
  }
440
+ /**
441
+ * Reports drift between the HLC and (reference-corrected) physical time.
442
+ * Reporting only: local timestamp generation is never blocked, because the
443
+ * user's data always outranks the quality of its timestamps.
444
+ */
297
445
  checkDrift(physicalTime) {
298
- const drift = this.wallTime - physicalTime;
446
+ const drift = this.wallTime - this.effectiveTime(physicalTime);
299
447
  if (drift > DRIFT_ERROR_MS) {
300
- throw new ClockDriftError(this.wallTime, physicalTime);
448
+ this.onDriftError?.(drift);
449
+ return;
301
450
  }
302
451
  if (drift > DRIFT_WARN_MS) {
303
452
  this.onDriftWarning?.(drift);
@@ -680,6 +829,106 @@ function toAtomicOp(sentinel) {
680
829
  return { type: sentinel.type, value: sentinel.value };
681
830
  }
682
831
 
832
+ // src/operations/op-data-binary.ts
833
+ function isKoraBytesValue(value) {
834
+ if (typeof value !== "object" || value === null) {
835
+ return false;
836
+ }
837
+ const record = value;
838
+ return Object.keys(record).length === 1 && typeof record.$koraBytes === "string";
839
+ }
840
+ function isLegacyNumericByteObject(value) {
841
+ if (typeof value !== "object" || value === null || ArrayBuffer.isView(value)) {
842
+ return false;
843
+ }
844
+ const record = value;
845
+ const keys = Object.keys(record);
846
+ if (keys.length === 0) {
847
+ return false;
848
+ }
849
+ for (let i = 0; i < keys.length; i++) {
850
+ const byte = record[String(i)];
851
+ if (typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255) {
852
+ return false;
853
+ }
854
+ }
855
+ return true;
856
+ }
857
+ var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
858
+ var BASE64_REVERSE = new Map(
859
+ Array.from(BASE64_ALPHABET, (char, index) => [char, index])
860
+ );
861
+ function bytesToBase64(bytes) {
862
+ let out = "";
863
+ for (let i = 0; i < bytes.length; i += 3) {
864
+ const b0 = bytes[i] ?? 0;
865
+ const b1 = bytes[i + 1] ?? 0;
866
+ const b2 = bytes[i + 2] ?? 0;
867
+ const triple = b0 << 16 | b1 << 8 | b2;
868
+ out += BASE64_ALPHABET[triple >> 18 & 63] ?? "";
869
+ out += BASE64_ALPHABET[triple >> 12 & 63] ?? "";
870
+ out += i + 1 < bytes.length ? BASE64_ALPHABET[triple >> 6 & 63] ?? "" : "=";
871
+ out += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] ?? "" : "=";
872
+ }
873
+ return out;
874
+ }
875
+ function base64ToBytes(base64) {
876
+ const cleaned = base64.replace(/=+$/, "");
877
+ const out = new Uint8Array(Math.floor(cleaned.length * 6 / 8));
878
+ let buffer = 0;
879
+ let bits = 0;
880
+ let index = 0;
881
+ for (const char of cleaned) {
882
+ const value = BASE64_REVERSE.get(char);
883
+ if (value === void 0) {
884
+ throw new OperationError(`Invalid base64 character "${char}" in tagged binary value.`, {
885
+ char
886
+ });
887
+ }
888
+ buffer = buffer << 6 | value;
889
+ bits += 6;
890
+ if (bits >= 8) {
891
+ bits -= 8;
892
+ out[index] = buffer >> bits & 255;
893
+ index += 1;
894
+ }
895
+ }
896
+ return out;
897
+ }
898
+ function encodeBytesForOpData(value) {
899
+ if (typeof value === "string") {
900
+ return value;
901
+ }
902
+ const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
903
+ return { $koraBytes: bytesToBase64(bytes) };
904
+ }
905
+ function decodeBytesFromOpData(value) {
906
+ if (typeof value === "string") {
907
+ return value;
908
+ }
909
+ if (value instanceof Uint8Array) {
910
+ return value;
911
+ }
912
+ if (value instanceof ArrayBuffer) {
913
+ return new Uint8Array(value);
914
+ }
915
+ if (isKoraBytesValue(value)) {
916
+ return base64ToBytes(value.$koraBytes);
917
+ }
918
+ if (isLegacyNumericByteObject(value)) {
919
+ const keys = Object.keys(value);
920
+ const bytes = new Uint8Array(keys.length);
921
+ for (let i = 0; i < keys.length; i++) {
922
+ bytes[i] = value[String(i)] ?? 0;
923
+ }
924
+ return bytes;
925
+ }
926
+ throw new OperationError(
927
+ "Binary op-data value must be a string, Uint8Array, ArrayBuffer, tagged { $koraBytes } object, or legacy numeric-key byte object.",
928
+ { receivedType: typeof value }
929
+ );
930
+ }
931
+
683
932
  // src/scopes/sync-scope-bindings.ts
684
933
  function hasSchemaSyncRules(schema) {
685
934
  return schema.sync !== void 0 && Object.keys(schema.sync).length > 0;
@@ -1109,6 +1358,7 @@ function generateSQL(collectionName, collection, relations) {
1109
1358
  columns.push("_created_at INTEGER NOT NULL");
1110
1359
  columns.push("_updated_at INTEGER NOT NULL");
1111
1360
  columns.push("_version TEXT NOT NULL DEFAULT ''");
1361
+ columns.push("_field_versions TEXT NOT NULL DEFAULT '{}'");
1112
1362
  columns.push("_deleted INTEGER NOT NULL DEFAULT 0");
1113
1363
  statements.push(`CREATE TABLE IF NOT EXISTS ${collectionName} (
1114
1364
  ${columns.join(",\n ")}
@@ -1121,6 +1371,10 @@ ALTER TABLE ${collectionName} ADD COLUMN ${colDef}`);
1121
1371
  statements.push(
1122
1372
  `--kora:safe-alter
1123
1373
  ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
1374
+ );
1375
+ statements.push(
1376
+ `--kora:safe-alter
1377
+ ALTER TABLE ${collectionName} ADD COLUMN _field_versions TEXT NOT NULL DEFAULT '{}'`
1124
1378
  );
1125
1379
  for (const indexField of collection.indexes) {
1126
1380
  statements.push(
@@ -1148,6 +1402,9 @@ ALTER TABLE ${collectionName} ADD COLUMN _version TEXT NOT NULL DEFAULT ''`
1148
1402
  schema_version INTEGER NOT NULL
1149
1403
  )`
1150
1404
  );
1405
+ statements.push(
1406
+ `CREATE INDEX IF NOT EXISTS idx_kora_ops_${collectionName}_record_id ON _kora_ops_${collectionName} (record_id)`
1407
+ );
1151
1408
  return statements;
1152
1409
  }
1153
1410
  function generateFullDDL(schema) {
@@ -1613,9 +1870,9 @@ function validateFieldValue(collection, fieldName, descriptor, value) {
1613
1870
  break;
1614
1871
  }
1615
1872
  case "richtext": {
1616
- if (!(value instanceof Uint8Array) && typeof value !== "string") {
1873
+ if (!(value instanceof Uint8Array) && !(value instanceof ArrayBuffer) && typeof value !== "string") {
1617
1874
  throw new SchemaValidationError(
1618
- `Field "${fieldName}" in collection "${collection}" must be a Uint8Array or string for richtext, got ${typeof value}`,
1875
+ `Field "${fieldName}" in collection "${collection}" must be a Uint8Array, ArrayBuffer, or string for richtext, got ${typeof value}`,
1619
1876
  {
1620
1877
  collection,
1621
1878
  field: fieldName,
@@ -2479,21 +2736,26 @@ function generateProtoDefinitions(schema) {
2479
2736
  EnumFieldBuilder,
2480
2737
  FieldBuilder,
2481
2738
  HybridLogicalClock,
2739
+ InvalidTimestampError,
2482
2740
  KORA_ERROR_FIX_SUGGESTIONS,
2483
2741
  KoraError,
2742
+ MAX_LOGICAL,
2484
2743
  MERGE_STRATEGIES,
2485
2744
  MergeConflictError,
2486
2745
  MigrationBuilder,
2487
2746
  MigrationRollbackError,
2488
2747
  OperationError,
2748
+ RemoteClockDriftError,
2489
2749
  RollbackBuilder,
2490
2750
  SchemaValidationError,
2491
2751
  StorageError,
2492
2752
  SyncError,
2493
2753
  advanceVector,
2494
2754
  applyOperationTransforms,
2755
+ base64ToBytes,
2495
2756
  buildScopeMap,
2496
2757
  buildStateMachineConstraints,
2758
+ bytesToBase64,
2497
2759
  canAutoRollback,
2498
2760
  collectSchemaScopeFields,
2499
2761
  collectSchemaScopeValueKeys,
@@ -2501,11 +2763,13 @@ function generateProtoDefinitions(schema) {
2501
2763
  createOperation,
2502
2764
  createReversibleMigration,
2503
2765
  createVersionVector,
2766
+ decodeBytesFromOpData,
2504
2767
  defaultApplyFailureReason,
2505
2768
  defaultSequenceFormat,
2506
2769
  defineSchema,
2507
2770
  deserializeVector,
2508
2771
  dominates,
2772
+ encodeBytesForOpData,
2509
2773
  extractScopeValuesFromClaims,
2510
2774
  extractTimestamp,
2511
2775
  formatSequenceValue,
@@ -2521,6 +2785,8 @@ function generateProtoDefinitions(schema) {
2521
2785
  isApplyFailure,
2522
2786
  isAtomicOp,
2523
2787
  isCollectionSyncScoped,
2788
+ isKoraBytesValue,
2789
+ isLegacyNumericByteObject,
2524
2790
  isValidOperation,
2525
2791
  isValidUUIDv7,
2526
2792
  mergeVectors,