@korajs/store 1.0.0-beta.0 → 1.0.0-beta.2

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 (61) hide show
  1. package/dist/adapters/better-sqlite3.d.cts +1 -1
  2. package/dist/adapters/better-sqlite3.d.ts +1 -1
  3. package/dist/adapters/indexeddb.cjs +39 -5
  4. package/dist/adapters/indexeddb.cjs.map +1 -1
  5. package/dist/adapters/indexeddb.d.cts +1 -1
  6. package/dist/adapters/indexeddb.d.ts +1 -1
  7. package/dist/adapters/indexeddb.js +13 -6
  8. package/dist/adapters/indexeddb.js.map +1 -1
  9. package/dist/adapters/sqlite-wasm-worker.cjs +25 -3
  10. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  11. package/dist/adapters/sqlite-wasm-worker.js +25 -3
  12. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  13. package/dist/adapters/sqlite-wasm.cjs +29 -0
  14. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  15. package/dist/adapters/sqlite-wasm.d.cts +17 -2
  16. package/dist/adapters/sqlite-wasm.d.ts +17 -2
  17. package/dist/adapters/sqlite-wasm.js +1 -1
  18. package/dist/{backup-5XZ7THNJ.js → backup-IQE4QC7Q.js} +3 -3
  19. package/dist/blob-fs.cjs +166 -0
  20. package/dist/blob-fs.cjs.map +1 -0
  21. package/dist/blob-fs.d.cts +29 -0
  22. package/dist/blob-fs.d.ts +29 -0
  23. package/dist/blob-fs.js +128 -0
  24. package/dist/blob-fs.js.map +1 -0
  25. package/dist/{chunk-OUQDC5Z6.js → chunk-2VRPF25R.js} +58 -38
  26. package/dist/chunk-2VRPF25R.js.map +1 -0
  27. package/dist/{chunk-ETDJEH2I.js → chunk-6NKLDBTY.js} +11 -1
  28. package/dist/{chunk-ETDJEH2I.js.map → chunk-6NKLDBTY.js.map} +1 -1
  29. package/dist/{chunk-5R4QMY3Z.js → chunk-OCOGQRHW.js} +13 -10
  30. package/dist/chunk-OCOGQRHW.js.map +1 -0
  31. package/dist/{chunk-RBPVEAVM.js → chunk-QUJGXSZ6.js} +30 -1
  32. package/dist/chunk-QUJGXSZ6.js.map +1 -0
  33. package/dist/{chunk-EIVXWF4B.js → chunk-RCTG3IJG.js} +4 -3
  34. package/dist/chunk-RCTG3IJG.js.map +1 -0
  35. package/dist/chunk-W4MTTNEZ.js +55 -0
  36. package/dist/chunk-W4MTTNEZ.js.map +1 -0
  37. package/dist/content-addressed-blob-store-Dcj3SB9r.d.cts +60 -0
  38. package/dist/content-addressed-blob-store-Dcj3SB9r.d.ts +60 -0
  39. package/dist/{export-audit-VPGQWNR6.js → export-audit-MT2LTMIN.js} +3 -3
  40. package/dist/index.cjs +765 -128
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +397 -6
  43. package/dist/index.d.ts +397 -6
  44. package/dist/index.js +603 -60
  45. package/dist/index.js.map +1 -1
  46. package/dist/internal.cjs +68 -43
  47. package/dist/internal.cjs.map +1 -1
  48. package/dist/internal.d.cts +3 -3
  49. package/dist/internal.d.ts +3 -3
  50. package/dist/internal.js +3 -3
  51. package/dist/{types-CyjXwrjW.d.cts → types-5h4Hqcwc.d.ts} +10 -3
  52. package/dist/{types-DAxCladl.d.ts → types-DfPiPwKr.d.cts} +10 -3
  53. package/dist/{types-CJOVKxLw.d.cts → types-k6AZgiH4.d.cts} +6 -1
  54. package/dist/{types-CJOVKxLw.d.ts → types-k6AZgiH4.d.ts} +6 -1
  55. package/package.json +13 -2
  56. package/dist/chunk-5R4QMY3Z.js.map +0 -1
  57. package/dist/chunk-EIVXWF4B.js.map +0 -1
  58. package/dist/chunk-OUQDC5Z6.js.map +0 -1
  59. package/dist/chunk-RBPVEAVM.js.map +0 -1
  60. /package/dist/{backup-5XZ7THNJ.js.map → backup-IQE4QC7Q.js.map} +0 -0
  61. /package/dist/{export-audit-VPGQWNR6.js.map → export-audit-MT2LTMIN.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -239,6 +239,9 @@ function serializeValue(value, descriptor) {
239
239
  case "boolean":
240
240
  return value ? 1 : 0;
241
241
  case "array":
242
+ case "object":
243
+ case "json":
244
+ case "blob":
242
245
  return JSON.stringify(value);
243
246
  case "richtext":
244
247
  return encodeRichtext(value);
@@ -255,6 +258,13 @@ function deserializeValue(value, descriptor) {
255
258
  return JSON.parse(value);
256
259
  }
257
260
  return value;
261
+ case "object":
262
+ case "json":
263
+ case "blob":
264
+ if (typeof value === "string") {
265
+ return JSON.parse(value);
266
+ }
267
+ return value;
258
268
  case "richtext":
259
269
  return decodeRichtext(value);
260
270
  default:
@@ -518,7 +528,7 @@ async function restoreBackup(adapter, schema, data, options) {
518
528
  schema_version: op.schemaVersion
519
529
  };
520
530
  await tx.execute(
521
- `INSERT OR IGNORE INTO _kora_ops_${collection} (id, node_id, type, record_id, data, previous_data, timestamp, sequence_number, causal_deps, schema_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
531
+ `INSERT OR IGNORE INTO ${(0, import_core6.quoteIdent)(`_kora_ops_${collection}`)} (id, node_id, type, record_id, data, previous_data, timestamp, sequence_number, causal_deps, schema_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
522
532
  [
523
533
  opRow.id,
524
534
  opRow.node_id,
@@ -537,8 +547,8 @@ async function restoreBackup(adapter, schema, data, options) {
537
547
  } else {
538
548
  await adapter.transaction(async (tx) => {
539
549
  for (const collection of Object.keys(schema.collections)) {
540
- await tx.execute(`DELETE FROM _kora_ops_${collection}`);
541
- await tx.execute(`DELETE FROM ${collection}`);
550
+ await tx.execute(`DELETE FROM ${(0, import_core6.quoteIdent)(`_kora_ops_${collection}`)}`);
551
+ await tx.execute(`DELETE FROM ${(0, import_core6.quoteIdent)(collection)}`);
542
552
  }
543
553
  await tx.execute("DELETE FROM _kora_version_vector");
544
554
  await tx.execute("DELETE FROM _kora_meta");
@@ -558,7 +568,7 @@ async function restoreBackup(adapter, schema, data, options) {
558
568
  for (const op of operations) {
559
569
  const collection = op.collection;
560
570
  await tx.execute(
561
- `INSERT INTO _kora_ops_${collection} (id, node_id, type, record_id, data, previous_data, timestamp, sequence_number, causal_deps, schema_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
571
+ `INSERT INTO ${(0, import_core6.quoteIdent)(`_kora_ops_${collection}`)} (id, node_id, type, record_id, data, previous_data, timestamp, sequence_number, causal_deps, schema_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
562
572
  [
563
573
  op.id,
564
574
  op.nodeId,
@@ -590,6 +600,7 @@ async function restoreBackup(adapter, schema, data, options) {
590
600
  for (const record of records) {
591
601
  const keys = Object.keys(record);
592
602
  const placeholders = keys.map(() => "?").join(", ");
603
+ const quotedKeys = keys.map((k) => (0, import_core6.quoteIdent)(k)).join(", ");
593
604
  const values = keys.map((k) => {
594
605
  const v = record[k];
595
606
  if (typeof v === "boolean") return v ? 1 : 0;
@@ -597,7 +608,7 @@ async function restoreBackup(adapter, schema, data, options) {
597
608
  return v;
598
609
  });
599
610
  await tx.execute(
600
- `INSERT OR REPLACE INTO ${col} (${keys.join(", ")}) VALUES (${placeholders})`,
611
+ `INSERT OR REPLACE INTO ${(0, import_core6.quoteIdent)(col)} (${quotedKeys}) VALUES (${placeholders})`,
601
612
  values
602
613
  );
603
614
  }
@@ -630,7 +641,7 @@ async function readAllOperations(adapter, schema) {
630
641
  const allOps = [];
631
642
  for (const collectionName of Object.keys(schema.collections)) {
632
643
  const rows = await adapter.query(
633
- `SELECT * FROM _kora_ops_${collectionName} ORDER BY sequence_number ASC`
644
+ `SELECT * FROM ${(0, import_core6.quoteIdent)(`_kora_ops_${collectionName}`)} ORDER BY sequence_number ASC`
634
645
  );
635
646
  for (const row of rows) {
636
647
  allOps.push(deserializeOperationWithCollection(row, collectionName));
@@ -639,7 +650,9 @@ async function readAllOperations(adapter, schema) {
639
650
  return allOps;
640
651
  }
641
652
  async function readCollectionRecords(adapter, collection) {
642
- return adapter.query(`SELECT * FROM ${collection} WHERE _deleted = 0`);
653
+ return adapter.query(
654
+ `SELECT * FROM ${(0, import_core6.quoteIdent)(collection)} WHERE _deleted = 0`
655
+ );
643
656
  }
644
657
  async function computeSha256(data) {
645
658
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
@@ -707,7 +720,7 @@ __export(audit_trace_store_exports, {
707
720
  });
708
721
  function persistedAuditTraceFromEvent(event) {
709
722
  return {
710
- id: (0, import_core18.generateUUIDv7)(),
723
+ id: (0, import_core21.generateUUIDv7)(),
711
724
  recordedAt: Date.now(),
712
725
  eventType: event.type,
713
726
  constraint: event.type === "constraint:violated" ? event.constraint : void 0,
@@ -773,11 +786,11 @@ function auditTraceIndexFields(trace) {
773
786
  recordId: trace.operationA.recordId
774
787
  };
775
788
  }
776
- var import_core18;
789
+ var import_core21;
777
790
  var init_audit_trace_store = __esm({
778
791
  "src/audit/audit-trace-store.ts"() {
779
792
  "use strict";
780
- import_core18 = require("@korajs/core");
793
+ import_core21 = require("@korajs/core");
781
794
  init_audit_json();
782
795
  }
783
796
  });
@@ -849,7 +862,7 @@ async function readAllOperations2(adapter, schema, collectionFilter) {
849
862
  const allOps = [];
850
863
  for (const collectionName of collections) {
851
864
  const rows = await adapter.query(
852
- `SELECT * FROM _kora_ops_${collectionName} ORDER BY sequence_number ASC`
865
+ `SELECT * FROM ${(0, import_core22.quoteIdent)(`_kora_ops_${collectionName}`)} ORDER BY sequence_number ASC`
853
866
  );
854
867
  for (const row of rows) {
855
868
  allOps.push(deserializeOperationWithCollection(row, collectionName));
@@ -960,9 +973,11 @@ function parseNdjsonSection(content) {
960
973
  }
961
974
  return text.split("\n").map((line) => deserializeAuditJson(line));
962
975
  }
976
+ var import_core22;
963
977
  var init_export_audit = __esm({
964
978
  "src/audit/export-audit.ts"() {
965
979
  "use strict";
980
+ import_core22 = require("@korajs/core");
966
981
  init_serializer();
967
982
  init_audit_json();
968
983
  init_audit_trace_store();
@@ -973,10 +988,14 @@ var init_export_audit = __esm({
973
988
  var index_exports = {};
974
989
  __export(index_exports, {
975
990
  AdapterError: () => AdapterError,
991
+ BlobIntegrityError: () => BlobIntegrityError,
976
992
  COMPACTION_BASELINE_META_KEY: () => COMPACTION_BASELINE_META_KEY,
977
993
  Collection: () => Collection,
994
+ DEFAULT_CHUNK_SIZE: () => DEFAULT_CHUNK_SIZE,
978
995
  InvalidStateTransitionError: () => InvalidStateTransitionError,
979
996
  LAST_ACKED_SERVER_VECTOR_META_KEY: () => LAST_ACKED_SERVER_VECTOR_META_KEY,
997
+ MemoryBlobStore: () => MemoryBlobStore,
998
+ OpfsBlobStore: () => OpfsBlobStore,
980
999
  OptimisticLockError: () => OptimisticLockError,
981
1000
  PersistenceError: () => PersistenceError,
982
1001
  QueryBuilder: () => QueryBuilder,
@@ -994,26 +1013,44 @@ __export(index_exports, {
994
1013
  WorkerTimeoutError: () => WorkerTimeoutError,
995
1014
  asRichTextSyncEngine: () => asRichTextSyncEngine,
996
1015
  assertQueryReady: () => assertQueryReady,
1016
+ chunkBlob: () => chunkBlob,
1017
+ collectBlobGarbage: () => collectBlobGarbage,
997
1018
  collectOperationsAheadOfServer: () => collectOperationsAheadOfServer,
998
1019
  compactOperationLog: () => compactOperationLog,
999
1020
  computeAckCompactionWatermark: () => computeAckCompactionWatermark,
1021
+ createChunkPortPair: () => createChunkPortPair,
1022
+ createMemoryServerBlobStore: () => createMemoryServerBlobStore,
1023
+ createOpfsBlobDirectory: () => createOpfsBlobDirectory,
1024
+ createOpfsBlobStore: () => createOpfsBlobStore,
1025
+ createRemoteChunkProvider: () => createRemoteChunkProvider,
1000
1026
  createRichTextController: () => createRichTextController,
1001
1027
  decodeAuditExport: () => decodeAuditExport,
1002
1028
  decodeRichtext: () => decodeRichtext,
1003
1029
  deserializeVersionVectorFromMeta: () => deserializeVersionVectorFromMeta,
1004
1030
  encodeRichtext: () => encodeRichtext,
1005
1031
  exportBackup: () => exportBackup,
1032
+ extractBlobRefs: () => extractBlobRefs,
1033
+ fetchBlobManifest: () => fetchBlobManifest,
1006
1034
  getSharedQueryStoreCache: () => getSharedQueryStoreCache,
1007
1035
  mergeVersionVectors: () => mergeVersionVectors,
1036
+ parseBlobManifest: () => parseBlobManifest,
1008
1037
  persistedAuditTraceFromEvent: () => persistedAuditTraceFromEvent,
1009
1038
  pluralize: () => pluralize,
1039
+ prepareBlobForSend: () => prepareBlobForSend,
1040
+ putBlobForTransfer: () => putBlobForTransfer,
1010
1041
  readAuditExportManifest: () => readAuditExportManifest,
1011
1042
  readBackupManifest: () => readBackupManifest,
1043
+ reassembleBlob: () => reassembleBlob,
1044
+ receiveBlob: () => receiveBlob,
1045
+ resolveBlobManifest: () => resolveBlobManifest,
1012
1046
  restoreBackup: () => restoreBackup,
1013
1047
  richtextStatesEqual: () => richtextStatesEqual,
1014
1048
  richtextToPlainText: () => richtextToPlainText,
1049
+ serializeBlobManifest: () => serializeBlobManifest,
1015
1050
  serializeVersionVectorToMeta: () => serializeVersionVectorToMeta,
1051
+ serveBlobChunks: () => serveBlobChunks,
1016
1052
  singularize: () => singularize,
1053
+ toServerBlobCallbacks: () => toServerBlobCallbacks,
1017
1054
  validateStateTransition: () => validateStateTransition,
1018
1055
  validateUpdateStateMachine: () => validateUpdateStateMachine,
1019
1056
  verifyAuditExportChecksum: () => verifyAuditExportChecksum,
@@ -1115,7 +1152,7 @@ async function compactOperationLog(adapter, schema, strategy, serverVector) {
1115
1152
  const collectionNames = Object.keys(schema.collections);
1116
1153
  await adapter.transaction(async (tx) => {
1117
1154
  for (const collectionName of collectionNames) {
1118
- const table = `_kora_ops_${collectionName}`;
1155
+ const table = (0, import_core2.quoteIdent)(`_kora_ops_${collectionName}`);
1119
1156
  for (const [nodeId, maxSeq] of watermark) {
1120
1157
  if (maxSeq <= 0) continue;
1121
1158
  const countSql = buildCountSql(table, ageCutoffSerialized !== null);
@@ -1224,11 +1261,14 @@ async function saveDeltaCursor(adapter, cursor) {
1224
1261
  }
1225
1262
 
1226
1263
  // src/store/store.ts
1227
- var import_core19 = require("@korajs/core");
1264
+ var import_core23 = require("@korajs/core");
1228
1265
  init_backup();
1229
1266
 
1267
+ // src/collection/collection.ts
1268
+ var import_core13 = require("@korajs/core");
1269
+
1230
1270
  // src/mutations/execute-delete.ts
1231
- var import_core8 = require("@korajs/core");
1271
+ var import_core9 = require("@korajs/core");
1232
1272
 
1233
1273
  // src/lww/row-version.ts
1234
1274
  var import_core7 = require("@korajs/core");
@@ -1254,9 +1294,10 @@ function lwwVersionWhereClause(remoteVersion) {
1254
1294
  }
1255
1295
 
1256
1296
  // src/query/sql-builder.ts
1297
+ var import_core8 = require("@korajs/core");
1257
1298
  function buildSelectQuery(descriptor, fields) {
1258
1299
  const params = [];
1259
- const parts = [`SELECT * FROM ${descriptor.collection}`];
1300
+ const parts = [`SELECT * FROM ${(0, import_core8.quoteIdent)(descriptor.collection)}`];
1260
1301
  const whereClause = buildWhereClauseParts(descriptor.where, fields, params);
1261
1302
  const deletedFilter = "_deleted = 0";
1262
1303
  if (whereClause) {
@@ -1267,7 +1308,7 @@ function buildSelectQuery(descriptor, fields) {
1267
1308
  if (descriptor.orderBy.length > 0) {
1268
1309
  const orderParts = descriptor.orderBy.map((o) => {
1269
1310
  validateFieldName(o.field, fields);
1270
- return `${o.field} ${o.direction.toUpperCase()}`;
1311
+ return `${(0, import_core8.quoteIdent)(o.field)} ${o.direction.toUpperCase()}`;
1271
1312
  });
1272
1313
  parts.push(`ORDER BY ${orderParts.join(", ")}`);
1273
1314
  }
@@ -1281,7 +1322,7 @@ function buildSelectQuery(descriptor, fields) {
1281
1322
  }
1282
1323
  function buildCountQuery(descriptor, fields) {
1283
1324
  const params = [];
1284
- const parts = [`SELECT COUNT(*) as count FROM ${descriptor.collection}`];
1325
+ const parts = [`SELECT COUNT(*) as count FROM ${(0, import_core8.quoteIdent)(descriptor.collection)}`];
1285
1326
  const whereClause = buildWhereClauseParts(descriptor.where, fields, params);
1286
1327
  const deletedFilter = "_deleted = 0";
1287
1328
  if (whereClause) {
@@ -1295,17 +1336,18 @@ function buildInsertQuery(collection, record) {
1295
1336
  const columns = Object.keys(record);
1296
1337
  const placeholders = columns.map(() => "?");
1297
1338
  const params = Object.values(record);
1298
- const sql = `INSERT INTO ${collection} (${columns.join(", ")}) VALUES (${placeholders.join(", ")})`;
1339
+ const quotedColumns = columns.map((col) => (0, import_core8.quoteIdent)(col));
1340
+ const sql = `INSERT INTO ${(0, import_core8.quoteIdent)(collection)} (${quotedColumns.join(", ")}) VALUES (${placeholders.join(", ")})`;
1299
1341
  return { sql, params };
1300
1342
  }
1301
1343
  function buildUpdateQuery(collection, id, changes) {
1302
- const setClauses = Object.keys(changes).map((col) => `${col} = ?`);
1344
+ const setClauses = Object.keys(changes).map((col) => `${(0, import_core8.quoteIdent)(col)} = ?`);
1303
1345
  const params = [...Object.values(changes), id];
1304
- const sql = `UPDATE ${collection} SET ${setClauses.join(", ")} WHERE id = ?`;
1346
+ const sql = `UPDATE ${(0, import_core8.quoteIdent)(collection)} SET ${setClauses.join(", ")} WHERE id = ?`;
1305
1347
  return { sql, params };
1306
1348
  }
1307
1349
  function buildFieldFastForwardUpdateQuery(collection, id, fieldChanges, remoteVersion, wallTime, options) {
1308
- const fieldClauses = Object.keys(fieldChanges).map((col) => `${col} = ?`);
1350
+ const fieldClauses = Object.keys(fieldChanges).map((col) => `${(0, import_core8.quoteIdent)(col)} = ?`);
1309
1351
  const setClauses = [
1310
1352
  ...fieldClauses,
1311
1353
  "_version = CASE WHEN _version >= ? THEN _version ELSE ? END",
@@ -1323,25 +1365,25 @@ function buildFieldFastForwardUpdateQuery(collection, id, fieldChanges, remoteVe
1323
1365
  params.push(options.maxCreatedAt, options.maxCreatedAt);
1324
1366
  }
1325
1367
  params.push(id);
1326
- const sql = `UPDATE ${collection} SET ${setClauses.join(", ")} WHERE id = ?`;
1368
+ const sql = `UPDATE ${(0, import_core8.quoteIdent)(collection)} SET ${setClauses.join(", ")} WHERE id = ?`;
1327
1369
  return { sql, params };
1328
1370
  }
1329
1371
  function buildSoftDeleteQuery(collection, id, updatedAt, version) {
1330
1372
  if (version !== void 0) {
1331
1373
  return {
1332
- sql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ?`,
1374
+ sql: `UPDATE ${(0, import_core8.quoteIdent)(collection)} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ?`,
1333
1375
  params: [updatedAt, version, id]
1334
1376
  };
1335
1377
  }
1336
1378
  return {
1337
- sql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ? WHERE id = ?`,
1379
+ sql: `UPDATE ${(0, import_core8.quoteIdent)(collection)} SET _deleted = 1, _updated_at = ? WHERE id = ?`,
1338
1380
  params: [updatedAt, id]
1339
1381
  };
1340
1382
  }
1341
1383
  function buildLwwSoftDeleteQuery(collection, id, updatedAt, version) {
1342
1384
  const lww = lwwVersionWhereClause(version);
1343
1385
  return {
1344
- sql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ? AND ${lww.sql}`,
1386
+ sql: `UPDATE ${(0, import_core8.quoteIdent)(collection)} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ? AND ${lww.sql}`,
1345
1387
  params: [updatedAt, version, id, ...lww.params]
1346
1388
  };
1347
1389
  }
@@ -1372,31 +1414,32 @@ function buildWhereClauseParts(where, fields, params) {
1372
1414
  }
1373
1415
  function buildOperatorCondition(fieldName, operator, value, descriptor, params) {
1374
1416
  const sqlValue = descriptor?.kind === "boolean" && typeof value === "boolean" ? value ? 1 : 0 : value;
1417
+ const column = (0, import_core8.quoteIdent)(fieldName);
1375
1418
  switch (operator) {
1376
1419
  case "$eq":
1377
1420
  if (sqlValue === null) {
1378
- return `${fieldName} IS NULL`;
1421
+ return `${column} IS NULL`;
1379
1422
  }
1380
1423
  params.push(sqlValue);
1381
- return `${fieldName} = ?`;
1424
+ return `${column} = ?`;
1382
1425
  case "$ne":
1383
1426
  if (sqlValue === null) {
1384
- return `${fieldName} IS NOT NULL`;
1427
+ return `${column} IS NOT NULL`;
1385
1428
  }
1386
1429
  params.push(sqlValue);
1387
- return `${fieldName} != ?`;
1430
+ return `${column} != ?`;
1388
1431
  case "$gt":
1389
1432
  params.push(sqlValue);
1390
- return `${fieldName} > ?`;
1433
+ return `${column} > ?`;
1391
1434
  case "$gte":
1392
1435
  params.push(sqlValue);
1393
- return `${fieldName} >= ?`;
1436
+ return `${column} >= ?`;
1394
1437
  case "$lt":
1395
1438
  params.push(sqlValue);
1396
- return `${fieldName} < ?`;
1439
+ return `${column} < ?`;
1397
1440
  case "$lte":
1398
1441
  params.push(sqlValue);
1399
- return `${fieldName} <= ?`;
1442
+ return `${column} <= ?`;
1400
1443
  case "$in": {
1401
1444
  if (!Array.isArray(sqlValue)) {
1402
1445
  throw new QueryError(`$in operator requires an array value for field "${fieldName}"`, {
@@ -1410,7 +1453,7 @@ function buildOperatorCondition(fieldName, operator, value, descriptor, params)
1410
1453
  descriptor?.kind === "boolean" && typeof item === "boolean" ? item ? 1 : 0 : item
1411
1454
  );
1412
1455
  }
1413
- return `${fieldName} IN (${placeholders.join(", ")})`;
1456
+ return `${column} IN (${placeholders.join(", ")})`;
1414
1457
  }
1415
1458
  default:
1416
1459
  throw new QueryError(`Unknown operator "${operator}"`, { operator });
@@ -1490,7 +1533,7 @@ function resolveCausalDeps(ctx) {
1490
1533
  // src/mutations/execute-delete.ts
1491
1534
  async function executeDelete(ctx, id, options) {
1492
1535
  const currentRows = await ctx.adapter.query(
1493
- `SELECT * FROM ${ctx.collection} WHERE id = ? AND _deleted = 0`,
1536
+ `SELECT * FROM ${(0, import_core9.quoteIdent)(ctx.collection)} WHERE id = ? AND _deleted = 0`,
1494
1537
  [id]
1495
1538
  );
1496
1539
  if (!currentRows[0]) {
@@ -1502,7 +1545,7 @@ async function executeDelete(ctx, id, options) {
1502
1545
  await ctx.adapter.transaction(async (tx) => {
1503
1546
  if (!operation) {
1504
1547
  const sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId);
1505
- operation = await (0, import_core8.createOperation)(
1548
+ operation = await (0, import_core9.createOperation)(
1506
1549
  {
1507
1550
  nodeId: ctx.nodeId,
1508
1551
  type: "delete",
@@ -1549,7 +1592,7 @@ async function executeDelete(ctx, id, options) {
1549
1592
  }
1550
1593
 
1551
1594
  // src/mutations/execute-insert.ts
1552
- var import_core9 = require("@korajs/core");
1595
+ var import_core10 = require("@korajs/core");
1553
1596
 
1554
1597
  // src/lww/field-versions.ts
1555
1598
  function parseFieldVersions(raw) {
@@ -1606,19 +1649,24 @@ function resolvePerFieldLww(current, incomingFields, incomingVersion, rowVersion
1606
1649
  init_op_data_encoding();
1607
1650
  init_serializer();
1608
1651
  async function executeInsert(ctx, data) {
1609
- const validated = (0, import_core9.validateRecord)(ctx.collection, ctx.definition, data, "insert");
1610
- const recordId = (0, import_core9.generateUUIDv7)();
1652
+ const validated = (0, import_core10.validateRecord)(ctx.collection, ctx.definition, data, "insert");
1653
+ const recordId = (0, import_core10.generateUUIDv7)();
1611
1654
  for (const [fieldName, descriptor] of Object.entries(ctx.definition.fields)) {
1612
1655
  if (descriptor.auto && descriptor.kind === "timestamp") {
1613
1656
  validated[fieldName] = Date.now();
1614
1657
  }
1615
1658
  }
1659
+ const writeData = await (0, import_core10.transformSecretFieldsForWrite)(
1660
+ validated,
1661
+ ctx.definition,
1662
+ ctx.secretKeyProvider
1663
+ );
1616
1664
  const causalDeps = resolveCausalDeps(ctx);
1617
1665
  let operation;
1618
1666
  let record;
1619
1667
  await ctx.adapter.transaction(async (tx) => {
1620
1668
  const sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId);
1621
- operation = await (0, import_core9.createOperation)(
1669
+ operation = await (0, import_core10.createOperation)(
1622
1670
  {
1623
1671
  nodeId: ctx.nodeId,
1624
1672
  type: "insert",
@@ -1627,7 +1675,7 @@ async function executeInsert(ctx, data) {
1627
1675
  // Binary richtext values are tagged as canonical JSON BEFORE the
1628
1676
  // operation is content-hashed, so the hash input, persisted JSON,
1629
1677
  // and wire payload are the identical value.
1630
- data: encodeRichtextFieldsForOpData(validated, ctx.definition.fields),
1678
+ data: encodeRichtextFieldsForOpData(writeData, ctx.definition.fields),
1631
1679
  previousData: null,
1632
1680
  sequenceNumber,
1633
1681
  causalDeps,
@@ -1636,7 +1684,7 @@ async function executeInsert(ctx, data) {
1636
1684
  ctx.clock
1637
1685
  );
1638
1686
  ctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction);
1639
- const serializedData = serializeRecord(validated, ctx.definition.fields);
1687
+ const serializedData = serializeRecord(writeData, ctx.definition.fields);
1640
1688
  const version = serializeRowVersion(operation.timestamp);
1641
1689
  record = {
1642
1690
  id: recordId,
@@ -1661,20 +1709,20 @@ async function executeInsert(ctx, data) {
1661
1709
  ctx.onMutation(ctx.collection, operation);
1662
1710
  return {
1663
1711
  id: recordId,
1664
- ...validated,
1712
+ ...writeData,
1665
1713
  createdAt: operation.timestamp.wallTime,
1666
1714
  updatedAt: operation.timestamp.wallTime
1667
1715
  };
1668
1716
  }
1669
1717
 
1670
1718
  // src/mutations/execute-update.ts
1671
- var import_core11 = require("@korajs/core");
1719
+ var import_core12 = require("@korajs/core");
1672
1720
  init_op_data_encoding();
1673
1721
  init_serializer();
1674
1722
 
1675
1723
  // src/state-machine/state-validator.ts
1676
- var import_core10 = require("@korajs/core");
1677
- var InvalidStateTransitionError = class extends import_core10.KoraError {
1724
+ var import_core11 = require("@korajs/core");
1725
+ var InvalidStateTransitionError = class extends import_core11.KoraError {
1678
1726
  constructor(collection, recordId, field, fromState, toState, allowedStates) {
1679
1727
  super(
1680
1728
  `Invalid state transition in collection "${collection}": cannot transition field "${field}" from "${fromState}" to "${toState}". Allowed transitions from "${fromState}": ${allowedStates.length > 0 ? allowedStates.join(", ") : "(none -- terminal state)"}`,
@@ -1708,7 +1756,7 @@ function validateStateTransition(collectionName, recordId, stateMachine, current
1708
1756
  collection: collectionName,
1709
1757
  transitions: stateMachine.transitions
1710
1758
  };
1711
- const transitionResult = (0, import_core10.validateTransition)(constraint, currentState, newState);
1759
+ const transitionResult = (0, import_core11.validateTransition)(constraint, currentState, newState);
1712
1760
  if (transitionResult.valid) {
1713
1761
  return { valid: true, allowedStates: transitionResult.allowedTargets };
1714
1762
  }
@@ -1757,14 +1805,14 @@ function validateUpdateStateMachine(collectionName, recordId, collectionDef, cur
1757
1805
  // src/mutations/execute-update.ts
1758
1806
  async function executeUpdate(ctx, id, data) {
1759
1807
  const currentRows = await ctx.adapter.query(
1760
- `SELECT * FROM ${ctx.collection} WHERE id = ? AND _deleted = 0`,
1808
+ `SELECT * FROM ${(0, import_core12.quoteIdent)(ctx.collection)} WHERE id = ? AND _deleted = 0`,
1761
1809
  [id]
1762
1810
  );
1763
1811
  const currentRow = currentRows[0];
1764
1812
  if (!currentRow) {
1765
1813
  throw new RecordNotFoundError(ctx.collection, id);
1766
1814
  }
1767
- let validated = (0, import_core11.validateRecord)(ctx.collection, ctx.definition, data, "update");
1815
+ let validated = (0, import_core12.validateRecord)(ctx.collection, ctx.definition, data, "update");
1768
1816
  const currentRecord = deserializeRecord(currentRow, ctx.definition.fields);
1769
1817
  validated = validateUpdateStateMachine(
1770
1818
  ctx.collection,
@@ -1782,19 +1830,24 @@ async function executeUpdate(ctx, id, data) {
1782
1830
  for (const key of Object.keys(validated)) {
1783
1831
  const value = validated[key];
1784
1832
  previousData[key] = currentRecord[key];
1785
- if ((0, import_core11.isAtomicOp)(value)) {
1786
- resolvedData[key] = (0, import_core11.resolveAtomicOp)(currentRecord[key], value);
1787
- atomicOps[key] = (0, import_core11.toAtomicOp)(value);
1833
+ if ((0, import_core12.isAtomicOp)(value)) {
1834
+ resolvedData[key] = (0, import_core12.resolveAtomicOp)(currentRecord[key], value);
1835
+ atomicOps[key] = (0, import_core12.toAtomicOp)(value);
1788
1836
  } else {
1789
1837
  resolvedData[key] = value;
1790
1838
  }
1791
1839
  }
1840
+ const writeData = await (0, import_core12.transformSecretFieldsForWrite)(
1841
+ resolvedData,
1842
+ ctx.definition,
1843
+ ctx.secretKeyProvider
1844
+ );
1792
1845
  const hasAtomicOps = Object.keys(atomicOps).length > 0;
1793
1846
  const causalDeps = resolveCausalDeps(ctx);
1794
1847
  let operation;
1795
1848
  await ctx.adapter.transaction(async (tx) => {
1796
1849
  const sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId);
1797
- operation = await (0, import_core11.createOperation)(
1850
+ operation = await (0, import_core12.createOperation)(
1798
1851
  {
1799
1852
  nodeId: ctx.nodeId,
1800
1853
  type: "update",
@@ -1803,7 +1856,7 @@ async function executeUpdate(ctx, id, data) {
1803
1856
  // Binary richtext values (new and previous) are tagged as canonical
1804
1857
  // JSON BEFORE the operation is content-hashed, so the hash input,
1805
1858
  // persisted JSON, and wire payload are the identical value.
1806
- data: encodeRichtextFieldsForOpData(resolvedData, ctx.definition.fields),
1859
+ data: encodeRichtextFieldsForOpData(writeData, ctx.definition.fields),
1807
1860
  previousData: encodeRichtextFieldsForOpData(previousData, ctx.definition.fields),
1808
1861
  sequenceNumber,
1809
1862
  causalDeps,
@@ -1813,7 +1866,7 @@ async function executeUpdate(ctx, id, data) {
1813
1866
  ctx.clock
1814
1867
  );
1815
1868
  ctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction);
1816
- const serializedChanges = serializeRecord(resolvedData, ctx.definition.fields);
1869
+ const serializedChanges = serializeRecord(writeData, ctx.definition.fields);
1817
1870
  const version = serializeRowVersion(operation.timestamp);
1818
1871
  const mergedFieldVersions = parseFieldVersions(currentRow._field_versions);
1819
1872
  for (const changedField of Object.keys(serializedChanges)) {
@@ -1834,7 +1887,7 @@ async function executeUpdate(ctx, id, data) {
1834
1887
  });
1835
1888
  ctx.onMutation(ctx.collection, operation);
1836
1889
  const rows = await ctx.adapter.query(
1837
- `SELECT * FROM ${ctx.collection} WHERE id = ? AND _deleted = 0`,
1890
+ `SELECT * FROM ${(0, import_core12.quoteIdent)(ctx.collection)} WHERE id = ? AND _deleted = 0`,
1838
1891
  [id]
1839
1892
  );
1840
1893
  const row = rows[0];
@@ -1847,7 +1900,7 @@ async function executeUpdate(ctx, id, data) {
1847
1900
  // src/collection/collection.ts
1848
1901
  init_serializer();
1849
1902
  var Collection = class {
1850
- constructor(name, definition, schema, adapter, clock, nodeId, allocateSequenceNumber, onMutation, relationEnforcer, mutationHandler, causalTracker) {
1903
+ constructor(name, definition, schema, adapter, clock, nodeId, allocateSequenceNumber, onMutation, relationEnforcer, mutationHandler, causalTracker, secretKeyProvider) {
1851
1904
  this.name = name;
1852
1905
  this.definition = definition;
1853
1906
  this.schema = schema;
@@ -1859,6 +1912,7 @@ var Collection = class {
1859
1912
  this.relationEnforcer = relationEnforcer;
1860
1913
  this.mutationHandler = mutationHandler;
1861
1914
  this.causalTracker = causalTracker;
1915
+ this.secretKeyProvider = secretKeyProvider;
1862
1916
  }
1863
1917
  name;
1864
1918
  definition;
@@ -1871,6 +1925,7 @@ var Collection = class {
1871
1925
  relationEnforcer;
1872
1926
  mutationHandler;
1873
1927
  causalTracker;
1928
+ secretKeyProvider;
1874
1929
  mutationContext() {
1875
1930
  return {
1876
1931
  collection: this.name,
@@ -1883,7 +1938,8 @@ var Collection = class {
1883
1938
  onMutation: this.onMutation,
1884
1939
  relationEnforcer: this.relationEnforcer,
1885
1940
  causalTracker: this.causalTracker,
1886
- inTransaction: false
1941
+ inTransaction: false,
1942
+ secretKeyProvider: this.secretKeyProvider
1887
1943
  };
1888
1944
  }
1889
1945
  async insert(data) {
@@ -1894,7 +1950,7 @@ var Collection = class {
1894
1950
  }
1895
1951
  async findById(id) {
1896
1952
  const rows = await this.adapter.query(
1897
- `SELECT * FROM ${this.name} WHERE id = ? AND _deleted = 0`,
1953
+ `SELECT * FROM ${(0, import_core13.quoteIdent)(this.name)} WHERE id = ? AND _deleted = 0`,
1898
1954
  [id]
1899
1955
  );
1900
1956
  const row = rows[0];
@@ -1927,6 +1983,7 @@ var Collection = class {
1927
1983
  };
1928
1984
 
1929
1985
  // src/query/query-builder.ts
1986
+ var import_core14 = require("@korajs/core");
1930
1987
  init_serializer();
1931
1988
 
1932
1989
  // src/query/pluralize.ts
@@ -2158,7 +2215,7 @@ var QueryBuilder = class _QueryBuilder {
2158
2215
  const relatedDef = this.schema?.collections[relatedCollection];
2159
2216
  if (!relatedDef) return;
2160
2217
  const placeholders = uniqueFks.map(() => "?").join(", ");
2161
- const sql = `SELECT * FROM ${relatedCollection} WHERE id IN (${placeholders}) AND _deleted = 0`;
2218
+ const sql = `SELECT * FROM ${(0, import_core14.quoteIdent)(relatedCollection)} WHERE id IN (${placeholders}) AND _deleted = 0`;
2162
2219
  const rows = await this.adapter.query(sql, uniqueFks);
2163
2220
  const relatedRecords = rows.map((row) => deserializeRecord(row, relatedDef.fields));
2164
2221
  const lookup = /* @__PURE__ */ new Map();
@@ -2181,7 +2238,7 @@ var QueryBuilder = class _QueryBuilder {
2181
2238
  if (!relatedDef) return;
2182
2239
  const fkField = relation.field;
2183
2240
  const placeholders = primaryIds.map(() => "?").join(", ");
2184
- const sql = `SELECT * FROM ${relatedCollection} WHERE ${fkField} IN (${placeholders}) AND _deleted = 0`;
2241
+ const sql = `SELECT * FROM ${(0, import_core14.quoteIdent)(relatedCollection)} WHERE ${(0, import_core14.quoteIdent)(fkField)} IN (${placeholders}) AND _deleted = 0`;
2185
2242
  const rows = await this.adapter.query(sql, primaryIds);
2186
2243
  const relatedRecords = rows.map((row) => deserializeRecord(row, relatedDef.fields));
2187
2244
  const grouped = /* @__PURE__ */ new Map();
@@ -2223,7 +2280,7 @@ var QueryError2 = class extends Error {
2223
2280
  };
2224
2281
 
2225
2282
  // src/relations/relation-enforcer.ts
2226
- var import_core12 = require("@korajs/core");
2283
+ var import_core15 = require("@korajs/core");
2227
2284
  init_serializer();
2228
2285
 
2229
2286
  // src/relations/relation-lookup.ts
@@ -2248,7 +2305,7 @@ function getIncomingRelations(lookup, collection) {
2248
2305
  }
2249
2306
 
2250
2307
  // src/relations/relation-enforcer.ts
2251
- var ReferentialIntegrityError = class extends import_core12.KoraError {
2308
+ var ReferentialIntegrityError = class extends import_core15.KoraError {
2252
2309
  constructor(collection, recordId, referencingCollection, relationName, referencingCount) {
2253
2310
  super(
2254
2311
  `Cannot delete record "${recordId}" from "${collection}": ${referencingCount} record(s) in "${referencingCollection}" reference it via relation "${relationName}" with onDelete: 'restrict'. Delete or reassign the referencing records first.`,
@@ -2327,7 +2384,7 @@ var RelationEnforcer = class {
2327
2384
  async enforceCascade(incoming, deletedRecordId, tx, causalDeps) {
2328
2385
  const { sourceCollection, foreignKeyField } = incoming;
2329
2386
  const referencingRows = await tx.query(
2330
- `SELECT id FROM ${sourceCollection} WHERE ${foreignKeyField} = ? AND _deleted = 0`,
2387
+ `SELECT id FROM ${(0, import_core15.quoteIdent)(sourceCollection)} WHERE ${(0, import_core15.quoteIdent)(foreignKeyField)} = ? AND _deleted = 0`,
2331
2388
  [deletedRecordId]
2332
2389
  );
2333
2390
  if (referencingRows.length === 0) {
@@ -2337,7 +2394,7 @@ var RelationEnforcer = class {
2337
2394
  for (const row of referencingRows) {
2338
2395
  const now = Date.now();
2339
2396
  const sequenceNumber = await allocateNextSequenceInTransaction(tx, this.nodeId);
2340
- const operation = await (0, import_core12.createOperation)(
2397
+ const operation = await (0, import_core15.createOperation)(
2341
2398
  {
2342
2399
  nodeId: this.nodeId,
2343
2400
  type: "delete",
@@ -2374,7 +2431,7 @@ var RelationEnforcer = class {
2374
2431
  return [];
2375
2432
  }
2376
2433
  const referencingRows = await tx.query(
2377
- `SELECT id FROM ${sourceCollection} WHERE ${foreignKeyField} = ? AND _deleted = 0`,
2434
+ `SELECT id FROM ${(0, import_core15.quoteIdent)(sourceCollection)} WHERE ${(0, import_core15.quoteIdent)(foreignKeyField)} = ? AND _deleted = 0`,
2378
2435
  [deletedRecordId]
2379
2436
  );
2380
2437
  if (referencingRows.length === 0) {
@@ -2386,7 +2443,7 @@ var RelationEnforcer = class {
2386
2443
  const sequenceNumber = await allocateNextSequenceInTransaction(tx, this.nodeId);
2387
2444
  const updateData = { [foreignKeyField]: null };
2388
2445
  const previousData = { [foreignKeyField]: deletedRecordId };
2389
- const operation = await (0, import_core12.createOperation)(
2446
+ const operation = await (0, import_core15.createOperation)(
2390
2447
  {
2391
2448
  nodeId: this.nodeId,
2392
2449
  type: "update",
@@ -2422,7 +2479,7 @@ var RelationEnforcer = class {
2422
2479
  async enforceRestrict(incoming, deletedRecordId, tx) {
2423
2480
  const { sourceCollection, foreignKeyField, relationName } = incoming;
2424
2481
  const countRows = await tx.query(
2425
- `SELECT COUNT(*) as cnt FROM ${sourceCollection} WHERE ${foreignKeyField} = ? AND _deleted = 0`,
2482
+ `SELECT COUNT(*) as cnt FROM ${(0, import_core15.quoteIdent)(sourceCollection)} WHERE ${(0, import_core15.quoteIdent)(foreignKeyField)} = ? AND _deleted = 0`,
2426
2483
  [deletedRecordId]
2427
2484
  );
2428
2485
  const count = countRows[0]?.cnt ?? 0;
@@ -2447,7 +2504,7 @@ var RelationEnforcer = class {
2447
2504
  };
2448
2505
 
2449
2506
  // src/replay/replay-to.ts
2450
- var import_core13 = require("@korajs/core");
2507
+ var import_core16 = require("@korajs/core");
2451
2508
  var import_internal = require("@korajs/core/internal");
2452
2509
  init_op_data_encoding();
2453
2510
  function collectCausalClosure(allOps, targetOperationId) {
@@ -2457,7 +2514,7 @@ function collectCausalClosure(allOps, targetOperationId) {
2457
2514
  }
2458
2515
  const target = opMap.get(targetOperationId);
2459
2516
  if (!target) {
2460
- throw new import_core13.OperationError(
2517
+ throw new import_core16.OperationError(
2461
2518
  `Operation "${targetOperationId}" not found in the local operation log`,
2462
2519
  {
2463
2520
  operationId: targetOperationId
@@ -2489,7 +2546,7 @@ function buildReplaySnapshot(schema, allOps, targetOperationId) {
2489
2546
  const operationsApplied = collectCausalClosure(allOps, targetOperationId);
2490
2547
  const targetOperation = operationsApplied.find((op) => op.id === targetOperationId);
2491
2548
  if (!targetOperation) {
2492
- throw new import_core13.OperationError(`Operation "${targetOperationId}" not found after causal sort`, {
2549
+ throw new import_core16.OperationError(`Operation "${targetOperationId}" not found after causal sort`, {
2493
2550
  operationId: targetOperationId
2494
2551
  });
2495
2552
  }
@@ -2615,7 +2672,7 @@ function toCollectionRecord(record, definition) {
2615
2672
  }
2616
2673
 
2617
2674
  // src/sequences/sequence-manager.ts
2618
- var import_core14 = require("@korajs/core");
2675
+ var import_core17 = require("@korajs/core");
2619
2676
  var SequenceManager = class {
2620
2677
  adapter;
2621
2678
  nodeId;
@@ -2633,7 +2690,7 @@ var SequenceManager = class {
2633
2690
  async next(name, config) {
2634
2691
  const scope = config?.scope ?? "";
2635
2692
  const startAt = config?.startAt ?? 1;
2636
- const format = config?.format ?? (0, import_core14.defaultSequenceFormat)(name);
2693
+ const format = config?.format ?? (0, import_core17.defaultSequenceFormat)(name);
2637
2694
  let counter = 0;
2638
2695
  await this.adapter.transaction(async (tx) => {
2639
2696
  const rows = await tx.query(
@@ -2655,7 +2712,7 @@ var SequenceManager = class {
2655
2712
  );
2656
2713
  }
2657
2714
  });
2658
- return (0, import_core14.formatSequenceValue)(format, counter, this.nodeId);
2715
+ return (0, import_core17.formatSequenceValue)(format, counter, this.nodeId);
2659
2716
  }
2660
2717
  /**
2661
2718
  * Get the current counter value without incrementing.
@@ -3114,10 +3171,10 @@ var SubscriptionManager = class {
3114
3171
  };
3115
3172
 
3116
3173
  // src/sync/rebase-unsynced-operations.ts
3117
- var import_core15 = require("@korajs/core");
3174
+ var import_core18 = require("@korajs/core");
3118
3175
  var import_internal2 = require("@korajs/core/internal");
3119
3176
  init_serializer();
3120
- var LOGICAL_COUNTER_LIMIT = import_core15.MAX_LOGICAL + 1;
3177
+ var LOGICAL_COUNTER_LIMIT = import_core18.MAX_LOGICAL + 1;
3121
3178
  function emptyResult() {
3122
3179
  return { operations: [], idMapping: {}, rebasedCount: 0, newMaxTimestamp: null };
3123
3180
  }
@@ -3129,7 +3186,9 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3129
3186
  const rebased = [];
3130
3187
  let maxOtherSerializedTs = null;
3131
3188
  for (const collectionName of Object.keys(schema.collections)) {
3132
- const rows = await adapter.query(`SELECT * FROM _kora_ops_${collectionName}`);
3189
+ const rows = await adapter.query(
3190
+ `SELECT * FROM ${(0, import_core18.quoteIdent)(`_kora_ops_${collectionName}`)}`
3191
+ );
3133
3192
  for (const row of rows) {
3134
3193
  if (rebaseIds.has(row.id)) {
3135
3194
  rebased.push(deserializeOperationWithCollection(row, collectionName));
@@ -3141,9 +3200,9 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3141
3200
  if (rebased.length === 0) {
3142
3201
  return emptyResult();
3143
3202
  }
3144
- const maxOtherWallTime = maxOtherSerializedTs === null ? Number.NEGATIVE_INFINITY : import_core15.HybridLogicalClock.deserialize(maxOtherSerializedTs).wallTime;
3203
+ const maxOtherWallTime = maxOtherSerializedTs === null ? Number.NEGATIVE_INFINITY : import_core18.HybridLogicalClock.deserialize(maxOtherSerializedTs).wallTime;
3145
3204
  const baseWallTime = Math.max(correctedNowMs, maxOtherWallTime + 1);
3146
- rebased.sort((a, b) => import_core15.HybridLogicalClock.compare(a.timestamp, b.timestamp));
3205
+ rebased.sort((a, b) => import_core18.HybridLogicalClock.compare(a.timestamp, b.timestamp));
3147
3206
  const newTimestamps = rebased.map((op, i) => ({
3148
3207
  // Spill into wallTime past the 5-digit logical serialization limit so the
3149
3208
  // serialized order stays correct even for enormous offline queues.
@@ -3171,7 +3230,7 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3171
3230
  schemaVersion: op.schemaVersion,
3172
3231
  ...op.atomicOps !== void 0 ? { atomicOps: op.atomicOps } : {}
3173
3232
  };
3174
- const newId = await (0, import_internal2.computeOperationId)(input, import_core15.HybridLogicalClock.serialize(ts));
3233
+ const newId = await (0, import_internal2.computeOperationId)(input, import_core18.HybridLogicalClock.serialize(ts));
3175
3234
  idMapping[op.id] = newId;
3176
3235
  newIds.push(newId);
3177
3236
  }
@@ -3198,7 +3257,7 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3198
3257
  continue;
3199
3258
  }
3200
3259
  const opsTable = `_kora_ops_${oldOp.collection}`;
3201
- await tx.execute(`DELETE FROM ${opsTable} WHERE id = ?`, [oldOp.id]);
3260
+ await tx.execute(`DELETE FROM ${(0, import_core18.quoteIdent)(opsTable)} WHERE id = ?`, [oldOp.id]);
3202
3261
  const opInsert = buildInsertQuery(
3203
3262
  opsTable,
3204
3263
  serializeOperation(newOp)
@@ -3207,11 +3266,11 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3207
3266
  const oldVersion = serializeRowVersion(oldOp.timestamp);
3208
3267
  const newVersion = serializeRowVersion(newOp.timestamp);
3209
3268
  await tx.execute(
3210
- `UPDATE ${oldOp.collection} SET _version = ?, _updated_at = ? WHERE id = ? AND _version = ?`,
3269
+ `UPDATE ${(0, import_core18.quoteIdent)(oldOp.collection)} SET _version = ?, _updated_at = ? WHERE id = ? AND _version = ?`,
3211
3270
  [newVersion, newOp.timestamp.wallTime, oldOp.recordId, oldVersion]
3212
3271
  );
3213
3272
  const rows = await tx.query(
3214
- `SELECT _field_versions FROM ${oldOp.collection} WHERE id = ?`,
3273
+ `SELECT _field_versions FROM ${(0, import_core18.quoteIdent)(oldOp.collection)} WHERE id = ?`,
3215
3274
  [oldOp.recordId]
3216
3275
  );
3217
3276
  const fieldVersions = parseFieldVersions(rows[0]?._field_versions);
@@ -3223,14 +3282,14 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3223
3282
  }
3224
3283
  }
3225
3284
  if (changed) {
3226
- await tx.execute(`UPDATE ${oldOp.collection} SET _field_versions = ? WHERE id = ?`, [
3227
- serializeFieldVersions(fieldVersions),
3228
- oldOp.recordId
3229
- ]);
3285
+ await tx.execute(
3286
+ `UPDATE ${(0, import_core18.quoteIdent)(oldOp.collection)} SET _field_versions = ? WHERE id = ?`,
3287
+ [serializeFieldVersions(fieldVersions), oldOp.recordId]
3288
+ );
3230
3289
  }
3231
3290
  if (oldOp.type === "insert") {
3232
3291
  await tx.execute(
3233
- `UPDATE ${oldOp.collection} SET _created_at = ? WHERE id = ? AND _created_at = ?`,
3292
+ `UPDATE ${(0, import_core18.quoteIdent)(oldOp.collection)} SET _created_at = ? WHERE id = ? AND _created_at = ?`,
3234
3293
  [newOp.timestamp.wallTime, oldOp.recordId, oldOp.timestamp.wallTime]
3235
3294
  );
3236
3295
  }
@@ -3246,7 +3305,7 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
3246
3305
  }
3247
3306
 
3248
3307
  // src/transaction/transaction-context.ts
3249
- var import_core16 = require("@korajs/core");
3308
+ var import_core19 = require("@korajs/core");
3250
3309
  init_op_data_encoding();
3251
3310
  init_serializer();
3252
3311
  var TransactionContext = class {
@@ -3258,7 +3317,7 @@ var TransactionContext = class {
3258
3317
  config;
3259
3318
  constructor(config) {
3260
3319
  this.config = config;
3261
- this.transactionId = (0, import_core16.generateUUIDv7)();
3320
+ this.transactionId = (0, import_core19.generateUUIDv7)();
3262
3321
  }
3263
3322
  /**
3264
3323
  * Set a human-readable mutation name for this transaction.
@@ -3373,8 +3432,8 @@ var TransactionContext = class {
3373
3432
  }
3374
3433
  async insert(collectionName, definition, data) {
3375
3434
  this.ensureActive();
3376
- const validated = (0, import_core16.validateRecord)(collectionName, definition, data, "insert");
3377
- const recordId = (0, import_core16.generateUUIDv7)();
3435
+ const validated = (0, import_core19.validateRecord)(collectionName, definition, data, "insert");
3436
+ const recordId = (0, import_core19.generateUUIDv7)();
3378
3437
  const now = Date.now();
3379
3438
  for (const [fieldName, descriptor] of Object.entries(definition.fields)) {
3380
3439
  if (descriptor.auto && descriptor.kind === "timestamp") {
@@ -3383,7 +3442,7 @@ var TransactionContext = class {
3383
3442
  }
3384
3443
  const sequenceNumber = await this.config.sequenceAllocator.allocate();
3385
3444
  const causalDeps = this.config.causalTracker?.nextCausalDeps(collectionName, true) ?? [];
3386
- const operation = await (0, import_core16.createOperation)(
3445
+ const operation = await (0, import_core19.createOperation)(
3387
3446
  {
3388
3447
  nodeId: this.config.nodeId,
3389
3448
  type: "insert",
@@ -3438,7 +3497,7 @@ var TransactionContext = class {
3438
3497
  if (!currentRecord) {
3439
3498
  throw new RecordNotFoundError(collectionName, id);
3440
3499
  }
3441
- const validated = (0, import_core16.validateRecord)(collectionName, definition, data, "update");
3500
+ const validated = (0, import_core19.validateRecord)(collectionName, definition, data, "update");
3442
3501
  const now = Date.now();
3443
3502
  const previousData = {};
3444
3503
  const resolvedData = {};
@@ -3446,9 +3505,9 @@ var TransactionContext = class {
3446
3505
  for (const key of Object.keys(validated)) {
3447
3506
  const value = validated[key];
3448
3507
  previousData[key] = currentRecord[key];
3449
- if ((0, import_core16.isAtomicOp)(value)) {
3450
- resolvedData[key] = (0, import_core16.resolveAtomicOp)(currentRecord[key], value);
3451
- atomicOps[key] = (0, import_core16.toAtomicOp)(value);
3508
+ if ((0, import_core19.isAtomicOp)(value)) {
3509
+ resolvedData[key] = (0, import_core19.resolveAtomicOp)(currentRecord[key], value);
3510
+ atomicOps[key] = (0, import_core19.toAtomicOp)(value);
3452
3511
  } else {
3453
3512
  resolvedData[key] = value;
3454
3513
  }
@@ -3456,7 +3515,7 @@ var TransactionContext = class {
3456
3515
  const hasAtomicOps = Object.keys(atomicOps).length > 0;
3457
3516
  const sequenceNumber = await this.config.sequenceAllocator.allocate();
3458
3517
  const causalDeps = this.config.causalTracker?.nextCausalDeps(collectionName, true) ?? [];
3459
- const operation = await (0, import_core16.createOperation)(
3518
+ const operation = await (0, import_core19.createOperation)(
3460
3519
  {
3461
3520
  nodeId: this.config.nodeId,
3462
3521
  type: "update",
@@ -3511,7 +3570,7 @@ var TransactionContext = class {
3511
3570
  const now = Date.now();
3512
3571
  const sequenceNumber = await this.config.sequenceAllocator.allocate();
3513
3572
  const causalDeps = this.config.causalTracker?.nextCausalDeps(collectionName, true) ?? [];
3514
- const operation = await (0, import_core16.createOperation)(
3573
+ const operation = await (0, import_core19.createOperation)(
3515
3574
  {
3516
3575
  nodeId: this.config.nodeId,
3517
3576
  type: "delete",
@@ -3561,7 +3620,7 @@ var TransactionContext = class {
3561
3620
  }
3562
3621
  }
3563
3622
  const rows = await this.config.adapter.query(
3564
- `SELECT * FROM ${collectionName} WHERE id = ? AND _deleted = 0`,
3623
+ `SELECT * FROM ${(0, import_core19.quoteIdent)(collectionName)} WHERE id = ? AND _deleted = 0`,
3565
3624
  [id]
3566
3625
  );
3567
3626
  let record = rows[0] ? deserializeRecord(rows[0], definition.fields) : null;
@@ -3616,7 +3675,7 @@ var TransactionSequenceAllocator = class {
3616
3675
  };
3617
3676
 
3618
3677
  // src/store/tab-node-id.ts
3619
- var import_core17 = require("@korajs/core");
3678
+ var import_core20 = require("@korajs/core");
3620
3679
  function perTabStorageKey(dbName) {
3621
3680
  return `kora-tab-node-${dbName}`;
3622
3681
  }
@@ -3637,7 +3696,7 @@ function resolvePerTabNodeId(dbName) {
3637
3696
  if (existing) {
3638
3697
  return existing;
3639
3698
  }
3640
- const nodeId = (0, import_core17.generateUUIDv7)();
3699
+ const nodeId = (0, import_core20.generateUUIDv7)();
3641
3700
  savePerTabNodeId(dbName, nodeId);
3642
3701
  return nodeId;
3643
3702
  }
@@ -3647,7 +3706,7 @@ var Store = class {
3647
3706
  opened = false;
3648
3707
  nodeId = "";
3649
3708
  sequenceNumber = 0;
3650
- versionVector = (0, import_core19.createVersionVector)();
3709
+ versionVector = (0, import_core23.createVersionVector)();
3651
3710
  clock = null;
3652
3711
  collections = /* @__PURE__ */ new Map();
3653
3712
  subscriptionManager;
@@ -3661,6 +3720,7 @@ var Store = class {
3661
3720
  localMutationHandler;
3662
3721
  relationEnforcer = null;
3663
3722
  causalTracker = null;
3723
+ secretKeyProvider;
3664
3724
  constructor(config) {
3665
3725
  this.schema = config.schema;
3666
3726
  this.adapter = config.adapter;
@@ -3669,6 +3729,7 @@ var Store = class {
3669
3729
  this.isolation = config.isolation ?? "shared";
3670
3730
  this.emitter = config.emitter ?? null;
3671
3731
  this.localMutationHandler = config.localMutationHandler ?? null;
3732
+ this.secretKeyProvider = config.secretKeyProvider;
3672
3733
  this.subscriptionManager = new SubscriptionManager({
3673
3734
  onQuerySubscribed: config.onQuerySubscribed
3674
3735
  });
@@ -3681,8 +3742,8 @@ var Store = class {
3681
3742
  await this.adapter.open(this.schema);
3682
3743
  await this.runMigrationsIfNeeded();
3683
3744
  this.nodeId = await this.loadOrGenerateNodeId();
3684
- this.clock = new import_core19.HybridLogicalClock(this.nodeId);
3685
- this.causalTracker = new import_core19.CausalTracker();
3745
+ this.clock = new import_core23.HybridLogicalClock(this.nodeId);
3746
+ this.causalTracker = new import_core23.CausalTracker();
3686
3747
  this.sequenceManager = new SequenceManager(this.adapter, this.nodeId);
3687
3748
  this.sequenceNumber = await this.loadSequenceNumber();
3688
3749
  this.versionVector = await this.loadVersionVector();
@@ -3711,7 +3772,8 @@ var Store = class {
3711
3772
  },
3712
3773
  this.relationEnforcer,
3713
3774
  this.localMutationHandler,
3714
- this.causalTracker
3775
+ this.causalTracker,
3776
+ this.secretKeyProvider
3715
3777
  );
3716
3778
  this.collections.set(name, col);
3717
3779
  }
@@ -3793,7 +3855,7 @@ var Store = class {
3793
3855
  return "skipped";
3794
3856
  }
3795
3857
  const existing = await this.adapter.query(
3796
- `SELECT id FROM _kora_ops_${collection} WHERE id = ?`,
3858
+ `SELECT id FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collection}`)} WHERE id = ?`,
3797
3859
  [op.id]
3798
3860
  );
3799
3861
  if (existing.length > 0) {
@@ -3898,7 +3960,7 @@ var Store = class {
3898
3960
  const serializedChanges = serializeRecord(materializeSource, definition.fields);
3899
3961
  const changedFields = Object.keys(serializedChanges);
3900
3962
  const currentRows = await tx.query(
3901
- `SELECT _version, _field_versions FROM ${collection} WHERE id = ?`,
3963
+ `SELECT _version, _field_versions FROM ${(0, import_core23.quoteIdent)(collection)} WHERE id = ?`,
3902
3964
  [op.recordId]
3903
3965
  );
3904
3966
  const currentRow = currentRows[0];
@@ -3994,20 +4056,20 @@ var Store = class {
3994
4056
  */
3995
4057
  async foldOrphanedOperations(tx, collection, definition, recordId) {
3996
4058
  const orphanRows = await tx.query(
3997
- `SELECT * FROM _kora_ops_${collection} WHERE record_id = ? AND type IN ('update', 'delete')`,
4059
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collection}`)} WHERE record_id = ? AND type IN ('update', 'delete')`,
3998
4060
  [recordId]
3999
4061
  );
4000
4062
  if (orphanRows.length === 0) {
4001
4063
  return;
4002
4064
  }
4003
- const orphans = orphanRows.map((r) => deserializeOperationWithCollection(r, collection)).sort((a, b) => import_core19.HybridLogicalClock.compare(a.timestamp, b.timestamp));
4065
+ const orphans = orphanRows.map((r) => deserializeOperationWithCollection(r, collection)).sort((a, b) => import_core23.HybridLogicalClock.compare(a.timestamp, b.timestamp));
4004
4066
  for (const orphan of orphans) {
4005
4067
  const orphanVersion = serializeRowVersion(orphan.timestamp);
4006
4068
  const orphanWall = orphan.timestamp.wallTime;
4007
4069
  if (orphan.type === "update" && orphan.data) {
4008
4070
  const orphanChanges = serializeRecord(orphan.data, definition.fields);
4009
4071
  const rows = await tx.query(
4010
- `SELECT _version, _field_versions FROM ${collection} WHERE id = ?`,
4072
+ `SELECT _version, _field_versions FROM ${(0, import_core23.quoteIdent)(collection)} WHERE id = ?`,
4011
4073
  [recordId]
4012
4074
  );
4013
4075
  const current = rows[0];
@@ -4057,7 +4119,7 @@ var Store = class {
4057
4119
  const allOps = [];
4058
4120
  for (const collectionName of Object.keys(this.schema.collections)) {
4059
4121
  const rows = await this.adapter.query(
4060
- `SELECT * FROM _kora_ops_${collectionName} WHERE node_id = ? AND sequence_number >= ? AND sequence_number <= ? ORDER BY sequence_number ASC`,
4122
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collectionName}`)} WHERE node_id = ? AND sequence_number >= ? AND sequence_number <= ? ORDER BY sequence_number ASC`,
4061
4123
  [nodeId, fromSeq, toSeq]
4062
4124
  );
4063
4125
  for (const row of rows) {
@@ -4076,7 +4138,7 @@ var Store = class {
4076
4138
  const allOps = [];
4077
4139
  for (const collectionName of Object.keys(this.schema.collections)) {
4078
4140
  const rows = await this.adapter.query(
4079
- `SELECT * FROM _kora_ops_${collectionName} ORDER BY sequence_number ASC`
4141
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collectionName}`)} ORDER BY sequence_number ASC`
4080
4142
  );
4081
4143
  for (const row of rows) {
4082
4144
  allOps.push(deserializeOperationWithCollection(row, collectionName));
@@ -4172,7 +4234,8 @@ var Store = class {
4172
4234
  relationEnforcer: this.relationEnforcer,
4173
4235
  causalTracker: this.causalTracker,
4174
4236
  inTransaction: options?.inTransaction ?? false,
4175
- extraCausalDeps: options?.extraCausalDeps
4237
+ extraCausalDeps: options?.extraCausalDeps,
4238
+ secretKeyProvider: this.secretKeyProvider
4176
4239
  };
4177
4240
  }
4178
4241
  /**
@@ -4185,7 +4248,7 @@ var Store = class {
4185
4248
  return null;
4186
4249
  }
4187
4250
  const rows = await this.adapter.query(
4188
- `SELECT * FROM ${collection} WHERE id = ?`,
4251
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(collection)} WHERE id = ?`,
4189
4252
  [recordId]
4190
4253
  );
4191
4254
  const row = rows[0];
@@ -4210,7 +4273,7 @@ var Store = class {
4210
4273
  return null;
4211
4274
  }
4212
4275
  const rows = await this.adapter.query(
4213
- `SELECT _version, _field_versions FROM ${collection} WHERE id = ?`,
4276
+ `SELECT _version, _field_versions FROM ${(0, import_core23.quoteIdent)(collection)} WHERE id = ?`,
4214
4277
  [recordId]
4215
4278
  );
4216
4279
  const row = rows[0];
@@ -4302,7 +4365,7 @@ var Store = class {
4302
4365
  async compact(strategy) {
4303
4366
  this.ensureOpen();
4304
4367
  if (strategy.mode === "never") {
4305
- return compactOperationLog(this.adapter, this.schema, strategy, (0, import_core19.createVersionVector)());
4368
+ return compactOperationLog(this.adapter, this.schema, strategy, (0, import_core23.createVersionVector)());
4306
4369
  }
4307
4370
  const serverVector = strategy.serverVector ?? await loadLastAckedServerVector(this.adapter);
4308
4371
  return compactOperationLog(this.adapter, this.schema, strategy, serverVector);
@@ -4316,7 +4379,7 @@ var Store = class {
4316
4379
  async getLatestLocalOperationForRecord(collection, recordId) {
4317
4380
  this.ensureOpen();
4318
4381
  const rows = await this.adapter.query(
4319
- `SELECT * FROM _kora_ops_${collection} WHERE node_id = ? AND record_id = ? ORDER BY sequence_number DESC LIMIT 1`,
4382
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collection}`)} WHERE node_id = ? AND record_id = ? ORDER BY sequence_number DESC LIMIT 1`,
4320
4383
  [this.nodeId, recordId]
4321
4384
  );
4322
4385
  const row = rows[0];
@@ -4331,13 +4394,13 @@ var Store = class {
4331
4394
  async getLatestOperationForRecord(collection, recordId) {
4332
4395
  this.ensureOpen();
4333
4396
  const rows = await this.adapter.query(
4334
- `SELECT * FROM _kora_ops_${collection} WHERE record_id = ?`,
4397
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collection}`)} WHERE record_id = ?`,
4335
4398
  [recordId]
4336
4399
  );
4337
4400
  let latest = null;
4338
4401
  for (const row of rows) {
4339
4402
  const op = deserializeOperationWithCollection(row, collection);
4340
- if (!latest || import_core19.HybridLogicalClock.compare(op.timestamp, latest.timestamp) > 0) {
4403
+ if (!latest || import_core23.HybridLogicalClock.compare(op.timestamp, latest.timestamp) > 0) {
4341
4404
  latest = op;
4342
4405
  }
4343
4406
  }
@@ -4480,7 +4543,7 @@ var Store = class {
4480
4543
  for (let v = storedVersion + 1; v <= targetVersion; v++) {
4481
4544
  const migration = migrations[v];
4482
4545
  if (!migration) continue;
4483
- const sqlStatements = (0, import_core19.migrationStepsToSQL)(migration.steps);
4546
+ const sqlStatements = (0, import_core23.migrationStepsToSQL)(migration.steps);
4484
4547
  for (const sql of sqlStatements) {
4485
4548
  try {
4486
4549
  await this.adapter.execute(sql);
@@ -4518,14 +4581,14 @@ var Store = class {
4518
4581
  */
4519
4582
  async runBackfill(collection, transform) {
4520
4583
  const rows = await this.adapter.query(
4521
- `SELECT * FROM ${collection} WHERE _deleted = 0`
4584
+ `SELECT * FROM ${(0, import_core23.quoteIdent)(collection)} WHERE _deleted = 0`
4522
4585
  );
4523
4586
  await this.adapter.transaction(async (tx) => {
4524
4587
  for (const row of rows) {
4525
4588
  const updates = transform(row);
4526
4589
  const fields = Object.keys(updates);
4527
4590
  if (fields.length === 0) continue;
4528
- const setClauses = fields.map((f) => `${f} = ?`).join(", ");
4591
+ const setClauses = fields.map((f) => `${(0, import_core23.quoteIdent)(f)} = ?`).join(", ");
4529
4592
  const values = fields.map((f) => {
4530
4593
  const val = updates[f];
4531
4594
  if (typeof val === "boolean") return val ? 1 : 0;
@@ -4535,7 +4598,7 @@ var Store = class {
4535
4598
  return val;
4536
4599
  });
4537
4600
  values.push(row.id);
4538
- await tx.execute(`UPDATE ${collection} SET ${setClauses} WHERE id = ?`, values);
4601
+ await tx.execute(`UPDATE ${(0, import_core23.quoteIdent)(collection)} SET ${setClauses} WHERE id = ?`, values);
4539
4602
  }
4540
4603
  });
4541
4604
  }
@@ -4558,7 +4621,7 @@ var Store = class {
4558
4621
  if (rows[0]) {
4559
4622
  return rows[0].value;
4560
4623
  }
4561
- const newNodeId = (0, import_core19.generateUUIDv7)();
4624
+ const newNodeId = (0, import_core23.generateUUIDv7)();
4562
4625
  await this.adapter.execute("INSERT INTO _kora_meta (key, value) VALUES ('node_id', ?)", [
4563
4626
  newNodeId
4564
4627
  ]);
@@ -4575,7 +4638,7 @@ var Store = class {
4575
4638
  const rows = await this.adapter.query(
4576
4639
  "SELECT node_id, sequence_number FROM _kora_version_vector"
4577
4640
  );
4578
- const vector = (0, import_core19.createVersionVector)();
4641
+ const vector = (0, import_core23.createVersionVector)();
4579
4642
  for (const row of rows) {
4580
4643
  vector.set(row.node_id, row.sequence_number);
4581
4644
  }
@@ -4649,12 +4712,12 @@ var QueryStore = class {
4649
4712
  };
4650
4713
 
4651
4714
  // src/reactivity/assert-query-ready.ts
4652
- var import_core20 = require("@korajs/core");
4715
+ var import_core24 = require("@korajs/core");
4653
4716
  var LEGACY_PENDING_COLLECTION = "__pending__";
4654
4717
  function assertQueryReady(query) {
4655
4718
  const descriptor = query.getDescriptor();
4656
4719
  if (descriptor.collection === LEGACY_PENDING_COLLECTION) {
4657
- throw new import_core20.AppNotReadyError(
4720
+ throw new import_core24.AppNotReadyError(
4658
4721
  "Cannot use useQuery() before app.ready. Await app.ready or wrap your UI in <KoraProvider app={app}>."
4659
4722
  );
4660
4723
  }
@@ -5075,13 +5138,569 @@ init_backup();
5075
5138
  init_audit_trace_store();
5076
5139
  init_audit_json();
5077
5140
  init_export_audit();
5141
+
5142
+ // src/blob/content-addressed-blob-store.ts
5143
+ var import_core25 = require("@korajs/core");
5144
+ var BlobIntegrityError = class extends Error {
5145
+ constructor(expectedHash, actualHash) {
5146
+ super(`Blob integrity check failed: content under hash ${expectedHash} hashes to ${actualHash}`);
5147
+ this.expectedHash = expectedHash;
5148
+ this.actualHash = actualHash;
5149
+ this.name = "BlobIntegrityError";
5150
+ }
5151
+ expectedHash;
5152
+ actualHash;
5153
+ };
5154
+ var MemoryBlobStore = class {
5155
+ blobs = /* @__PURE__ */ new Map();
5156
+ async put(bytes, metadata = {}) {
5157
+ const ref = await (0, import_core25.createBlobRef)(bytes, metadata);
5158
+ if (!this.blobs.has(ref.hash)) {
5159
+ const stored = new Uint8Array(bytes.byteLength);
5160
+ stored.set(bytes);
5161
+ this.blobs.set(ref.hash, stored);
5162
+ }
5163
+ return ref;
5164
+ }
5165
+ async get(hash) {
5166
+ const stored = this.blobs.get(hash);
5167
+ if (stored === void 0) {
5168
+ return null;
5169
+ }
5170
+ const actualHash = await (0, import_core25.hashBlob)(stored);
5171
+ if (actualHash !== hash) {
5172
+ throw new BlobIntegrityError(hash, actualHash);
5173
+ }
5174
+ const out = new Uint8Array(stored.byteLength);
5175
+ out.set(stored);
5176
+ return out;
5177
+ }
5178
+ async has(hash) {
5179
+ return this.blobs.has(hash);
5180
+ }
5181
+ async delete(hash) {
5182
+ return this.blobs.delete(hash);
5183
+ }
5184
+ async size() {
5185
+ return this.blobs.size;
5186
+ }
5187
+ async list() {
5188
+ return [...this.blobs.keys()];
5189
+ }
5190
+ };
5191
+
5192
+ // src/blob/opfs-blob-store.ts
5193
+ var import_core26 = require("@korajs/core");
5194
+ var OpfsBlobStore = class {
5195
+ constructor(dir) {
5196
+ this.dir = dir;
5197
+ }
5198
+ dir;
5199
+ shardFor(hash) {
5200
+ return hash.slice(0, 2);
5201
+ }
5202
+ async put(bytes, metadata = {}) {
5203
+ const ref = await (0, import_core26.createBlobRef)(bytes, metadata);
5204
+ const shard = this.shardFor(ref.hash);
5205
+ if (await this.dir.has(shard, ref.hash)) {
5206
+ return ref;
5207
+ }
5208
+ const out = new Uint8Array(bytes.byteLength);
5209
+ out.set(bytes);
5210
+ await this.dir.write(shard, ref.hash, out);
5211
+ return ref;
5212
+ }
5213
+ async get(hash) {
5214
+ const bytes = await this.dir.read(this.shardFor(hash), hash);
5215
+ if (bytes === null) {
5216
+ return null;
5217
+ }
5218
+ const actual = await (0, import_core26.hashBlob)(bytes);
5219
+ if (actual !== hash) {
5220
+ throw new BlobIntegrityError(hash, actual);
5221
+ }
5222
+ return bytes;
5223
+ }
5224
+ async has(hash) {
5225
+ return this.dir.has(this.shardFor(hash), hash);
5226
+ }
5227
+ async delete(hash) {
5228
+ return this.dir.remove(this.shardFor(hash), hash);
5229
+ }
5230
+ async size() {
5231
+ return this.dir.count();
5232
+ }
5233
+ async list() {
5234
+ return this.dir.keys();
5235
+ }
5236
+ };
5237
+ function getStorageManager() {
5238
+ const nav = globalThis.navigator;
5239
+ if (!nav?.storage || typeof nav.storage.getDirectory !== "function") {
5240
+ throw new Error(
5241
+ "OPFS is unavailable in this environment. The OPFS blob store requires a browser with Origin Private File System support; use MemoryBlobStore or a server-side store elsewhere."
5242
+ );
5243
+ }
5244
+ return nav.storage;
5245
+ }
5246
+ function isNotFoundError(error) {
5247
+ return typeof error === "object" && error !== null && error.name === "NotFoundError";
5248
+ }
5249
+ async function createOpfsBlobDirectory(rootDirName = "kora-blobs") {
5250
+ const storage = getStorageManager();
5251
+ if (typeof storage.persist === "function") {
5252
+ try {
5253
+ await storage.persist();
5254
+ } catch {
5255
+ }
5256
+ }
5257
+ const opfsRoot = await storage.getDirectory();
5258
+ const root = await opfsRoot.getDirectoryHandle(rootDirName, { create: true });
5259
+ async function shardHandle(shard, create) {
5260
+ try {
5261
+ return await root.getDirectoryHandle(shard, { create });
5262
+ } catch (error) {
5263
+ if (isNotFoundError(error)) {
5264
+ return null;
5265
+ }
5266
+ throw error;
5267
+ }
5268
+ }
5269
+ return {
5270
+ async read(shard, name) {
5271
+ const dir = await shardHandle(shard, false);
5272
+ if (!dir) {
5273
+ return null;
5274
+ }
5275
+ let handle;
5276
+ try {
5277
+ handle = await dir.getFileHandle(name, { create: false });
5278
+ } catch (error) {
5279
+ if (isNotFoundError(error)) {
5280
+ return null;
5281
+ }
5282
+ throw error;
5283
+ }
5284
+ const file = await handle.getFile();
5285
+ return new Uint8Array(await file.arrayBuffer());
5286
+ },
5287
+ async write(shard, name, bytes) {
5288
+ const dir = await root.getDirectoryHandle(shard, { create: true });
5289
+ const handle = await dir.getFileHandle(name, { create: true });
5290
+ const writable = await handle.createWritable({ keepExistingData: false });
5291
+ try {
5292
+ await writable.write(bytes);
5293
+ } finally {
5294
+ await writable.close();
5295
+ }
5296
+ },
5297
+ async remove(shard, name) {
5298
+ const dir = await shardHandle(shard, false);
5299
+ if (!dir) {
5300
+ return false;
5301
+ }
5302
+ try {
5303
+ await dir.removeEntry(name);
5304
+ return true;
5305
+ } catch (error) {
5306
+ if (isNotFoundError(error)) {
5307
+ return false;
5308
+ }
5309
+ throw error;
5310
+ }
5311
+ },
5312
+ async has(shard, name) {
5313
+ const dir = await shardHandle(shard, false);
5314
+ if (!dir) {
5315
+ return false;
5316
+ }
5317
+ try {
5318
+ await dir.getFileHandle(name, { create: false });
5319
+ return true;
5320
+ } catch (error) {
5321
+ if (isNotFoundError(error)) {
5322
+ return false;
5323
+ }
5324
+ throw error;
5325
+ }
5326
+ },
5327
+ async count() {
5328
+ let total = 0;
5329
+ for await (const shard of root.keys()) {
5330
+ const dir = await shardHandle(shard, false);
5331
+ if (!dir) {
5332
+ continue;
5333
+ }
5334
+ for await (const name of dir.keys()) {
5335
+ if (!name.endsWith(".tmp")) {
5336
+ total++;
5337
+ }
5338
+ }
5339
+ }
5340
+ return total;
5341
+ },
5342
+ async keys() {
5343
+ const names = [];
5344
+ for await (const shard of root.keys()) {
5345
+ const dir = await shardHandle(shard, false);
5346
+ if (!dir) {
5347
+ continue;
5348
+ }
5349
+ for await (const name of dir.keys()) {
5350
+ if (!name.endsWith(".tmp")) {
5351
+ names.push(name);
5352
+ }
5353
+ }
5354
+ }
5355
+ return names;
5356
+ }
5357
+ };
5358
+ }
5359
+ async function createOpfsBlobStore(rootDirName = "kora-blobs") {
5360
+ return new OpfsBlobStore(await createOpfsBlobDirectory(rootDirName));
5361
+ }
5362
+
5363
+ // src/blob/blob-chunking.ts
5364
+ var import_core27 = require("@korajs/core");
5365
+ var DEFAULT_CHUNK_SIZE = 256 * 1024;
5366
+ async function chunkBlob(bytes, chunkSize = DEFAULT_CHUNK_SIZE, metadata = {}) {
5367
+ if (chunkSize <= 0) {
5368
+ throw new Error(`chunkBlob requires a positive chunkSize, got ${chunkSize}`);
5369
+ }
5370
+ const chunkHashes = [];
5371
+ const chunks = /* @__PURE__ */ new Map();
5372
+ for (let offset = 0; offset < bytes.byteLength; offset += chunkSize) {
5373
+ const end = Math.min(offset + chunkSize, bytes.byteLength);
5374
+ const chunk = new Uint8Array(end - offset);
5375
+ chunk.set(bytes.subarray(offset, end));
5376
+ const hash = await (0, import_core27.hashBlob)(chunk);
5377
+ chunkHashes.push(hash);
5378
+ if (!chunks.has(hash)) {
5379
+ chunks.set(hash, chunk);
5380
+ }
5381
+ }
5382
+ const blobHash = await (0, import_core27.hashBlob)(bytes);
5383
+ const manifest = {
5384
+ blobHash,
5385
+ size: bytes.byteLength,
5386
+ chunkSize,
5387
+ chunkHashes
5388
+ };
5389
+ if (metadata.mimeType !== void 0) {
5390
+ manifest.mimeType = metadata.mimeType;
5391
+ }
5392
+ if (metadata.filename !== void 0) {
5393
+ manifest.filename = metadata.filename;
5394
+ }
5395
+ return { manifest, chunks };
5396
+ }
5397
+ async function reassembleBlob(manifest, chunkStore) {
5398
+ const parts = [];
5399
+ let total = 0;
5400
+ for (const hash of manifest.chunkHashes) {
5401
+ const chunk = await chunkStore.get(hash);
5402
+ if (chunk === null) {
5403
+ throw new Error(`Cannot reassemble blob ${manifest.blobHash}: missing chunk ${hash}`);
5404
+ }
5405
+ parts.push(chunk);
5406
+ total += chunk.byteLength;
5407
+ }
5408
+ const out = new Uint8Array(total);
5409
+ let offset = 0;
5410
+ for (const part of parts) {
5411
+ out.set(part, offset);
5412
+ offset += part.byteLength;
5413
+ }
5414
+ const actual = await (0, import_core27.hashBlob)(out);
5415
+ if (actual !== manifest.blobHash) {
5416
+ throw new BlobIntegrityError(manifest.blobHash, actual);
5417
+ }
5418
+ return out;
5419
+ }
5420
+
5421
+ // src/blob/blob-transfer.ts
5422
+ var import_core28 = require("@korajs/core");
5423
+ async function receiveBlob(manifest, provider, stores) {
5424
+ let chunksFetched = 0;
5425
+ let chunksSkipped = 0;
5426
+ const seen = /* @__PURE__ */ new Set();
5427
+ for (const hash of manifest.chunkHashes) {
5428
+ if (seen.has(hash)) {
5429
+ continue;
5430
+ }
5431
+ seen.add(hash);
5432
+ if (await stores.chunkStore.has(hash)) {
5433
+ chunksSkipped++;
5434
+ continue;
5435
+ }
5436
+ const bytes = await provider.getChunk(hash);
5437
+ if (bytes === null) {
5438
+ throw new Error(`Blob transfer failed: provider could not supply chunk ${hash}`);
5439
+ }
5440
+ const actual = await (0, import_core28.hashBlob)(bytes);
5441
+ if (actual !== hash) {
5442
+ throw new BlobIntegrityError(hash, actual);
5443
+ }
5444
+ await stores.chunkStore.put(bytes);
5445
+ chunksFetched++;
5446
+ }
5447
+ const full = await reassembleBlob(manifest, stores.chunkStore);
5448
+ const ref = await stores.blobStore.put(full, {
5449
+ mimeType: manifest.mimeType,
5450
+ filename: manifest.filename
5451
+ });
5452
+ return { ref, chunksFetched, chunksSkipped };
5453
+ }
5454
+ async function prepareBlobForSend(bytes, chunkStore, options = {}) {
5455
+ const { manifest, chunks } = await chunkBlob(bytes, options.chunkSize, {
5456
+ mimeType: options.mimeType,
5457
+ filename: options.filename
5458
+ });
5459
+ for (const chunk of chunks.values()) {
5460
+ await chunkStore.put(chunk);
5461
+ }
5462
+ const provider = {
5463
+ getChunk: (hash) => chunkStore.get(hash)
5464
+ };
5465
+ return { manifest, provider };
5466
+ }
5467
+
5468
+ // src/blob/blob-manifest-transfer.ts
5469
+ var import_core29 = require("@korajs/core");
5470
+ function serializeBlobManifest(manifest) {
5471
+ const canonical = {
5472
+ blobHash: manifest.blobHash,
5473
+ size: manifest.size,
5474
+ chunkSize: manifest.chunkSize,
5475
+ chunkHashes: manifest.chunkHashes
5476
+ };
5477
+ if (manifest.mimeType !== void 0) {
5478
+ canonical.mimeType = manifest.mimeType;
5479
+ }
5480
+ if (manifest.filename !== void 0) {
5481
+ canonical.filename = manifest.filename;
5482
+ }
5483
+ return new TextEncoder().encode(JSON.stringify(canonical));
5484
+ }
5485
+ function parseBlobManifest(bytes) {
5486
+ const parsed = JSON.parse(new TextDecoder().decode(bytes));
5487
+ if (typeof parsed !== "object" || parsed === null) {
5488
+ throw new Error("Invalid blob manifest: not an object");
5489
+ }
5490
+ const obj = parsed;
5491
+ if (typeof obj.blobHash !== "string") {
5492
+ throw new Error("Invalid blob manifest: missing blobHash");
5493
+ }
5494
+ if (typeof obj.size !== "number" || typeof obj.chunkSize !== "number") {
5495
+ throw new Error("Invalid blob manifest: missing size or chunkSize");
5496
+ }
5497
+ if (!Array.isArray(obj.chunkHashes) || obj.chunkHashes.some((h) => typeof h !== "string")) {
5498
+ throw new Error("Invalid blob manifest: chunkHashes must be an array of strings");
5499
+ }
5500
+ const manifest = {
5501
+ blobHash: obj.blobHash,
5502
+ size: obj.size,
5503
+ chunkSize: obj.chunkSize,
5504
+ chunkHashes: obj.chunkHashes
5505
+ };
5506
+ if (typeof obj.mimeType === "string") {
5507
+ manifest.mimeType = obj.mimeType;
5508
+ }
5509
+ if (typeof obj.filename === "string") {
5510
+ manifest.filename = obj.filename;
5511
+ }
5512
+ return manifest;
5513
+ }
5514
+ async function putBlobForTransfer(store, bytes, options = {}) {
5515
+ const { manifest, chunks } = await chunkBlob(bytes, options.chunkSize, {
5516
+ ...options.mimeType !== void 0 ? { mimeType: options.mimeType } : {},
5517
+ ...options.filename !== void 0 ? { filename: options.filename } : {}
5518
+ });
5519
+ for (const chunk of chunks.values()) {
5520
+ await store.put(chunk);
5521
+ }
5522
+ const baseRef = await store.put(bytes, {
5523
+ ...options.mimeType !== void 0 ? { mimeType: options.mimeType } : {},
5524
+ ...options.filename !== void 0 ? { filename: options.filename } : {}
5525
+ });
5526
+ const manifestRef = await store.put(serializeBlobManifest(manifest));
5527
+ return { ref: { ...baseRef, manifestHash: manifestRef.hash }, manifest };
5528
+ }
5529
+ async function fetchBlobManifest(provider, manifestHash) {
5530
+ const bytes = await provider.getChunk(manifestHash);
5531
+ if (bytes === null) {
5532
+ throw new Error(`Blob manifest ${manifestHash} could not be fetched from the provider`);
5533
+ }
5534
+ const actual = await (0, import_core29.hashBlob)(bytes);
5535
+ if (actual !== manifestHash) {
5536
+ throw new BlobIntegrityError(manifestHash, actual);
5537
+ }
5538
+ return parseBlobManifest(bytes);
5539
+ }
5540
+ async function resolveBlobManifest(provider, ref) {
5541
+ if (ref.manifestHash === void 0) {
5542
+ throw new Error(
5543
+ `Blob ${ref.hash} has no manifest to resolve. Store it with putBlobForTransfer (or app.blobs.put) so its reference carries a manifestHash, or pull with an explicit manifest.`
5544
+ );
5545
+ }
5546
+ return fetchBlobManifest(provider, ref.manifestHash);
5547
+ }
5548
+
5549
+ // src/blob/server-blob-callbacks.ts
5550
+ var import_core30 = require("@korajs/core");
5551
+ function toServerBlobCallbacks(store) {
5552
+ return {
5553
+ resolveBlobChunk: (hash) => store.get(hash),
5554
+ async persistBlobChunk(hash, bytes) {
5555
+ const actual = await (0, import_core30.hashBlob)(bytes);
5556
+ if (actual !== hash) {
5557
+ return;
5558
+ }
5559
+ await store.put(bytes);
5560
+ }
5561
+ };
5562
+ }
5563
+ function createMemoryServerBlobStore() {
5564
+ const store = new MemoryBlobStore();
5565
+ return { store, callbacks: toServerBlobCallbacks(store) };
5566
+ }
5567
+
5568
+ // src/blob/blob-gc.ts
5569
+ var import_core31 = require("@korajs/core");
5570
+ function extractBlobRefs(record) {
5571
+ const refs = [];
5572
+ for (const value of Object.values(record)) {
5573
+ if ((0, import_core31.isBlobRef)(value)) {
5574
+ refs.push(value);
5575
+ }
5576
+ }
5577
+ return refs;
5578
+ }
5579
+ async function collectBlobGarbage(store, liveRefs, options = {}) {
5580
+ const live = /* @__PURE__ */ new Set();
5581
+ for (const ref of liveRefs) {
5582
+ live.add(ref.hash);
5583
+ if (ref.manifestHash === void 0) {
5584
+ continue;
5585
+ }
5586
+ live.add(ref.manifestHash);
5587
+ try {
5588
+ const manifestBytes = await store.get(ref.manifestHash);
5589
+ if (manifestBytes !== null) {
5590
+ const manifest = parseBlobManifest(manifestBytes);
5591
+ for (const chunkHash of manifest.chunkHashes) {
5592
+ live.add(chunkHash);
5593
+ }
5594
+ }
5595
+ } catch {
5596
+ }
5597
+ }
5598
+ const all = await store.list();
5599
+ const collectedHashes = [];
5600
+ for (const hash of all) {
5601
+ if (live.has(hash)) {
5602
+ continue;
5603
+ }
5604
+ collectedHashes.push(hash);
5605
+ if (!options.dryRun) {
5606
+ await store.delete(hash);
5607
+ }
5608
+ }
5609
+ return {
5610
+ scanned: all.length,
5611
+ live: live.size,
5612
+ collected: collectedHashes.length,
5613
+ collectedHashes
5614
+ };
5615
+ }
5616
+
5617
+ // src/blob/blob-chunk-transport.ts
5618
+ var import_core32 = require("@korajs/core");
5619
+ function createRemoteChunkProvider(port, options = {}) {
5620
+ const timeoutMs = options.timeoutMs ?? 3e4;
5621
+ const pending = /* @__PURE__ */ new Map();
5622
+ port.onMessage((message) => {
5623
+ if (message.type !== "blob-chunk-response") {
5624
+ return;
5625
+ }
5626
+ const entry = pending.get(message.requestId);
5627
+ if (!entry) {
5628
+ return;
5629
+ }
5630
+ pending.delete(message.requestId);
5631
+ clearTimeout(entry.timer);
5632
+ entry.resolve(message.bytes);
5633
+ });
5634
+ return {
5635
+ pendingCount: () => pending.size,
5636
+ getChunk(hash) {
5637
+ const requestId = (0, import_core32.generateUUIDv7)();
5638
+ return new Promise((resolve, reject) => {
5639
+ const timer = setTimeout(() => {
5640
+ pending.delete(requestId);
5641
+ reject(new Error(`Blob chunk request for ${hash} timed out after ${timeoutMs}ms`));
5642
+ }, timeoutMs);
5643
+ pending.set(requestId, { resolve, reject, timer });
5644
+ port.send({ type: "blob-chunk-request", requestId, hash });
5645
+ });
5646
+ }
5647
+ };
5648
+ }
5649
+ function serveBlobChunks(port, blobStore) {
5650
+ port.onMessage((message) => {
5651
+ if (message.type !== "blob-chunk-request") {
5652
+ return;
5653
+ }
5654
+ void blobStore.get(message.hash).then(
5655
+ (bytes) => {
5656
+ port.send({ type: "blob-chunk-response", requestId: message.requestId, bytes });
5657
+ },
5658
+ () => {
5659
+ port.send({ type: "blob-chunk-response", requestId: message.requestId, bytes: null });
5660
+ }
5661
+ );
5662
+ });
5663
+ }
5664
+ function createChunkPortPair() {
5665
+ const handlersA = [];
5666
+ const handlersB = [];
5667
+ const a = {
5668
+ send(message) {
5669
+ queueMicrotask(() => {
5670
+ for (const handler of handlersB) {
5671
+ handler(message);
5672
+ }
5673
+ });
5674
+ },
5675
+ onMessage(handler) {
5676
+ handlersA.push(handler);
5677
+ }
5678
+ };
5679
+ const b = {
5680
+ send(message) {
5681
+ queueMicrotask(() => {
5682
+ for (const handler of handlersA) {
5683
+ handler(message);
5684
+ }
5685
+ });
5686
+ },
5687
+ onMessage(handler) {
5688
+ handlersB.push(handler);
5689
+ }
5690
+ };
5691
+ return { a, b };
5692
+ }
5078
5693
  // Annotate the CommonJS export names for ESM import in node:
5079
5694
  0 && (module.exports = {
5080
5695
  AdapterError,
5696
+ BlobIntegrityError,
5081
5697
  COMPACTION_BASELINE_META_KEY,
5082
5698
  Collection,
5699
+ DEFAULT_CHUNK_SIZE,
5083
5700
  InvalidStateTransitionError,
5084
5701
  LAST_ACKED_SERVER_VECTOR_META_KEY,
5702
+ MemoryBlobStore,
5703
+ OpfsBlobStore,
5085
5704
  OptimisticLockError,
5086
5705
  PersistenceError,
5087
5706
  QueryBuilder,
@@ -5099,26 +5718,44 @@ init_export_audit();
5099
5718
  WorkerTimeoutError,
5100
5719
  asRichTextSyncEngine,
5101
5720
  assertQueryReady,
5721
+ chunkBlob,
5722
+ collectBlobGarbage,
5102
5723
  collectOperationsAheadOfServer,
5103
5724
  compactOperationLog,
5104
5725
  computeAckCompactionWatermark,
5726
+ createChunkPortPair,
5727
+ createMemoryServerBlobStore,
5728
+ createOpfsBlobDirectory,
5729
+ createOpfsBlobStore,
5730
+ createRemoteChunkProvider,
5105
5731
  createRichTextController,
5106
5732
  decodeAuditExport,
5107
5733
  decodeRichtext,
5108
5734
  deserializeVersionVectorFromMeta,
5109
5735
  encodeRichtext,
5110
5736
  exportBackup,
5737
+ extractBlobRefs,
5738
+ fetchBlobManifest,
5111
5739
  getSharedQueryStoreCache,
5112
5740
  mergeVersionVectors,
5741
+ parseBlobManifest,
5113
5742
  persistedAuditTraceFromEvent,
5114
5743
  pluralize,
5744
+ prepareBlobForSend,
5745
+ putBlobForTransfer,
5115
5746
  readAuditExportManifest,
5116
5747
  readBackupManifest,
5748
+ reassembleBlob,
5749
+ receiveBlob,
5750
+ resolveBlobManifest,
5117
5751
  restoreBackup,
5118
5752
  richtextStatesEqual,
5119
5753
  richtextToPlainText,
5754
+ serializeBlobManifest,
5120
5755
  serializeVersionVectorToMeta,
5756
+ serveBlobChunks,
5121
5757
  singularize,
5758
+ toServerBlobCallbacks,
5122
5759
  validateStateTransition,
5123
5760
  validateUpdateStateMachine,
5124
5761
  verifyAuditExportChecksum,