@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.js CHANGED
@@ -1,11 +1,21 @@
1
+ import {
2
+ exportBackup,
3
+ readBackupManifest,
4
+ restoreBackup,
5
+ verifyBackupChecksum
6
+ } from "./chunk-OCOGQRHW.js";
1
7
  import {
2
8
  decodeAuditExport,
3
9
  readAuditExportManifest,
4
10
  verifyAuditExportChecksum
5
- } from "./chunk-EIVXWF4B.js";
11
+ } from "./chunk-RCTG3IJG.js";
6
12
  import {
7
13
  persistedAuditTraceFromEvent
8
14
  } from "./chunk-MYSJN3DF.js";
15
+ import {
16
+ BlobIntegrityError,
17
+ MemoryBlobStore
18
+ } from "./chunk-W4MTTNEZ.js";
9
19
  import {
10
20
  InvalidStateTransitionError,
11
21
  allocateNextSequenceInTransaction,
@@ -29,23 +39,7 @@ import {
29
39
  serializeRowVersion,
30
40
  validateStateTransition,
31
41
  validateUpdateStateMachine
32
- } from "./chunk-OUQDC5Z6.js";
33
- import {
34
- AdapterError,
35
- OptimisticLockError,
36
- PersistenceError,
37
- QueryError,
38
- RecordNotFoundError,
39
- StoreNotOpenError,
40
- WorkerInitError,
41
- WorkerTimeoutError
42
- } from "./chunk-QL2XVZRN.js";
43
- import {
44
- exportBackup,
45
- readBackupManifest,
46
- restoreBackup,
47
- verifyBackupChecksum
48
- } from "./chunk-5R4QMY3Z.js";
42
+ } from "./chunk-2VRPF25R.js";
49
43
  import {
50
44
  decodeRichtext,
51
45
  decodeRichtextFieldsFromOpData,
@@ -57,13 +51,23 @@ import {
57
51
  richtextToPlainText,
58
52
  serializeOperation,
59
53
  serializeRecord
60
- } from "./chunk-ETDJEH2I.js";
54
+ } from "./chunk-6NKLDBTY.js";
55
+ import {
56
+ AdapterError,
57
+ OptimisticLockError,
58
+ PersistenceError,
59
+ QueryError,
60
+ RecordNotFoundError,
61
+ StoreNotOpenError,
62
+ WorkerInitError,
63
+ WorkerTimeoutError
64
+ } from "./chunk-QL2XVZRN.js";
61
65
 
62
66
  // src/compaction/types.ts
63
67
  var COMPACTION_BASELINE_META_KEY = "compaction_baseline_at";
64
68
 
65
69
  // src/compaction/compact-operation-log.ts
66
- import { HybridLogicalClock, createVersionVector } from "@korajs/core";
70
+ import { HybridLogicalClock, createVersionVector, quoteIdent } from "@korajs/core";
67
71
  function computeAckCompactionWatermark(serverVector) {
68
72
  const watermark = createVersionVector();
69
73
  for (const [nodeId, seq] of serverVector) {
@@ -91,7 +95,7 @@ async function compactOperationLog(adapter, schema, strategy, serverVector) {
91
95
  const collectionNames = Object.keys(schema.collections);
92
96
  await adapter.transaction(async (tx) => {
93
97
  for (const collectionName of collectionNames) {
94
- const table = `_kora_ops_${collectionName}`;
98
+ const table = quoteIdent(`_kora_ops_${collectionName}`);
95
99
  for (const [nodeId, maxSeq] of watermark) {
96
100
  if (maxSeq <= 0) continue;
97
101
  const countSql = buildCountSql(table, ageCutoffSerialized !== null);
@@ -205,12 +209,14 @@ import {
205
209
  HybridLogicalClock as HybridLogicalClock3,
206
210
  createVersionVector as createVersionVector3,
207
211
  generateUUIDv7 as generateUUIDv73,
208
- migrationStepsToSQL
212
+ migrationStepsToSQL,
213
+ quoteIdent as quoteIdent7
209
214
  } from "@korajs/core";
210
215
 
211
216
  // src/collection/collection.ts
217
+ import { quoteIdent as quoteIdent2 } from "@korajs/core";
212
218
  var Collection = class {
213
- constructor(name, definition, schema, adapter, clock, nodeId, allocateSequenceNumber, onMutation, relationEnforcer, mutationHandler, causalTracker) {
219
+ constructor(name, definition, schema, adapter, clock, nodeId, allocateSequenceNumber, onMutation, relationEnforcer, mutationHandler, causalTracker, secretKeyProvider) {
214
220
  this.name = name;
215
221
  this.definition = definition;
216
222
  this.schema = schema;
@@ -222,6 +228,7 @@ var Collection = class {
222
228
  this.relationEnforcer = relationEnforcer;
223
229
  this.mutationHandler = mutationHandler;
224
230
  this.causalTracker = causalTracker;
231
+ this.secretKeyProvider = secretKeyProvider;
225
232
  }
226
233
  name;
227
234
  definition;
@@ -234,6 +241,7 @@ var Collection = class {
234
241
  relationEnforcer;
235
242
  mutationHandler;
236
243
  causalTracker;
244
+ secretKeyProvider;
237
245
  mutationContext() {
238
246
  return {
239
247
  collection: this.name,
@@ -246,7 +254,8 @@ var Collection = class {
246
254
  onMutation: this.onMutation,
247
255
  relationEnforcer: this.relationEnforcer,
248
256
  causalTracker: this.causalTracker,
249
- inTransaction: false
257
+ inTransaction: false,
258
+ secretKeyProvider: this.secretKeyProvider
250
259
  };
251
260
  }
252
261
  async insert(data) {
@@ -257,7 +266,7 @@ var Collection = class {
257
266
  }
258
267
  async findById(id) {
259
268
  const rows = await this.adapter.query(
260
- `SELECT * FROM ${this.name} WHERE id = ? AND _deleted = 0`,
269
+ `SELECT * FROM ${quoteIdent2(this.name)} WHERE id = ? AND _deleted = 0`,
261
270
  [id]
262
271
  );
263
272
  const row = rows[0];
@@ -289,6 +298,9 @@ var Collection = class {
289
298
  }
290
299
  };
291
300
 
301
+ // src/query/query-builder.ts
302
+ import { quoteIdent as quoteIdent3 } from "@korajs/core";
303
+
292
304
  // src/query/pluralize.ts
293
305
  function isVowel(char) {
294
306
  if (!char) return false;
@@ -518,7 +530,7 @@ var QueryBuilder = class _QueryBuilder {
518
530
  const relatedDef = this.schema?.collections[relatedCollection];
519
531
  if (!relatedDef) return;
520
532
  const placeholders = uniqueFks.map(() => "?").join(", ");
521
- const sql = `SELECT * FROM ${relatedCollection} WHERE id IN (${placeholders}) AND _deleted = 0`;
533
+ const sql = `SELECT * FROM ${quoteIdent3(relatedCollection)} WHERE id IN (${placeholders}) AND _deleted = 0`;
522
534
  const rows = await this.adapter.query(sql, uniqueFks);
523
535
  const relatedRecords = rows.map((row) => deserializeRecord(row, relatedDef.fields));
524
536
  const lookup = /* @__PURE__ */ new Map();
@@ -541,7 +553,7 @@ var QueryBuilder = class _QueryBuilder {
541
553
  if (!relatedDef) return;
542
554
  const fkField = relation.field;
543
555
  const placeholders = primaryIds.map(() => "?").join(", ");
544
- const sql = `SELECT * FROM ${relatedCollection} WHERE ${fkField} IN (${placeholders}) AND _deleted = 0`;
556
+ const sql = `SELECT * FROM ${quoteIdent3(relatedCollection)} WHERE ${quoteIdent3(fkField)} IN (${placeholders}) AND _deleted = 0`;
545
557
  const rows = await this.adapter.query(sql, primaryIds);
546
558
  const relatedRecords = rows.map((row) => deserializeRecord(row, relatedDef.fields));
547
559
  const grouped = /* @__PURE__ */ new Map();
@@ -583,7 +595,7 @@ var QueryError2 = class extends Error {
583
595
  };
584
596
 
585
597
  // src/relations/relation-enforcer.ts
586
- import { KoraError, createOperation } from "@korajs/core";
598
+ import { KoraError, createOperation, quoteIdent as quoteIdent4 } from "@korajs/core";
587
599
 
588
600
  // src/relations/relation-lookup.ts
589
601
  function buildRelationLookup(schema) {
@@ -686,7 +698,7 @@ var RelationEnforcer = class {
686
698
  async enforceCascade(incoming, deletedRecordId, tx, causalDeps) {
687
699
  const { sourceCollection, foreignKeyField } = incoming;
688
700
  const referencingRows = await tx.query(
689
- `SELECT id FROM ${sourceCollection} WHERE ${foreignKeyField} = ? AND _deleted = 0`,
701
+ `SELECT id FROM ${quoteIdent4(sourceCollection)} WHERE ${quoteIdent4(foreignKeyField)} = ? AND _deleted = 0`,
690
702
  [deletedRecordId]
691
703
  );
692
704
  if (referencingRows.length === 0) {
@@ -733,7 +745,7 @@ var RelationEnforcer = class {
733
745
  return [];
734
746
  }
735
747
  const referencingRows = await tx.query(
736
- `SELECT id FROM ${sourceCollection} WHERE ${foreignKeyField} = ? AND _deleted = 0`,
748
+ `SELECT id FROM ${quoteIdent4(sourceCollection)} WHERE ${quoteIdent4(foreignKeyField)} = ? AND _deleted = 0`,
737
749
  [deletedRecordId]
738
750
  );
739
751
  if (referencingRows.length === 0) {
@@ -781,7 +793,7 @@ var RelationEnforcer = class {
781
793
  async enforceRestrict(incoming, deletedRecordId, tx) {
782
794
  const { sourceCollection, foreignKeyField, relationName } = incoming;
783
795
  const countRows = await tx.query(
784
- `SELECT COUNT(*) as cnt FROM ${sourceCollection} WHERE ${foreignKeyField} = ? AND _deleted = 0`,
796
+ `SELECT COUNT(*) as cnt FROM ${quoteIdent4(sourceCollection)} WHERE ${quoteIdent4(foreignKeyField)} = ? AND _deleted = 0`,
785
797
  [deletedRecordId]
786
798
  );
787
799
  const count = countRows[0]?.cnt ?? 0;
@@ -1469,7 +1481,7 @@ var SubscriptionManager = class {
1469
1481
  };
1470
1482
 
1471
1483
  // src/sync/rebase-unsynced-operations.ts
1472
- import { HybridLogicalClock as HybridLogicalClock2, MAX_LOGICAL } from "@korajs/core";
1484
+ import { HybridLogicalClock as HybridLogicalClock2, MAX_LOGICAL, quoteIdent as quoteIdent5 } from "@korajs/core";
1473
1485
  import { computeOperationId } from "@korajs/core/internal";
1474
1486
  var LOGICAL_COUNTER_LIMIT = MAX_LOGICAL + 1;
1475
1487
  function emptyResult() {
@@ -1483,7 +1495,9 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
1483
1495
  const rebased = [];
1484
1496
  let maxOtherSerializedTs = null;
1485
1497
  for (const collectionName of Object.keys(schema.collections)) {
1486
- const rows = await adapter.query(`SELECT * FROM _kora_ops_${collectionName}`);
1498
+ const rows = await adapter.query(
1499
+ `SELECT * FROM ${quoteIdent5(`_kora_ops_${collectionName}`)}`
1500
+ );
1487
1501
  for (const row of rows) {
1488
1502
  if (rebaseIds.has(row.id)) {
1489
1503
  rebased.push(deserializeOperationWithCollection(row, collectionName));
@@ -1552,7 +1566,7 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
1552
1566
  continue;
1553
1567
  }
1554
1568
  const opsTable = `_kora_ops_${oldOp.collection}`;
1555
- await tx.execute(`DELETE FROM ${opsTable} WHERE id = ?`, [oldOp.id]);
1569
+ await tx.execute(`DELETE FROM ${quoteIdent5(opsTable)} WHERE id = ?`, [oldOp.id]);
1556
1570
  const opInsert = buildInsertQuery(
1557
1571
  opsTable,
1558
1572
  serializeOperation(newOp)
@@ -1561,11 +1575,11 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
1561
1575
  const oldVersion = serializeRowVersion(oldOp.timestamp);
1562
1576
  const newVersion = serializeRowVersion(newOp.timestamp);
1563
1577
  await tx.execute(
1564
- `UPDATE ${oldOp.collection} SET _version = ?, _updated_at = ? WHERE id = ? AND _version = ?`,
1578
+ `UPDATE ${quoteIdent5(oldOp.collection)} SET _version = ?, _updated_at = ? WHERE id = ? AND _version = ?`,
1565
1579
  [newVersion, newOp.timestamp.wallTime, oldOp.recordId, oldVersion]
1566
1580
  );
1567
1581
  const rows = await tx.query(
1568
- `SELECT _field_versions FROM ${oldOp.collection} WHERE id = ?`,
1582
+ `SELECT _field_versions FROM ${quoteIdent5(oldOp.collection)} WHERE id = ?`,
1569
1583
  [oldOp.recordId]
1570
1584
  );
1571
1585
  const fieldVersions = parseFieldVersions(rows[0]?._field_versions);
@@ -1577,14 +1591,14 @@ async function rebaseUnsyncedOperationsInLog(adapter, schema, unsyncedOpIds, cor
1577
1591
  }
1578
1592
  }
1579
1593
  if (changed) {
1580
- await tx.execute(`UPDATE ${oldOp.collection} SET _field_versions = ? WHERE id = ?`, [
1581
- serializeFieldVersions(fieldVersions),
1582
- oldOp.recordId
1583
- ]);
1594
+ await tx.execute(
1595
+ `UPDATE ${quoteIdent5(oldOp.collection)} SET _field_versions = ? WHERE id = ?`,
1596
+ [serializeFieldVersions(fieldVersions), oldOp.recordId]
1597
+ );
1584
1598
  }
1585
1599
  if (oldOp.type === "insert") {
1586
1600
  await tx.execute(
1587
- `UPDATE ${oldOp.collection} SET _created_at = ? WHERE id = ? AND _created_at = ?`,
1601
+ `UPDATE ${quoteIdent5(oldOp.collection)} SET _created_at = ? WHERE id = ? AND _created_at = ?`,
1588
1602
  [newOp.timestamp.wallTime, oldOp.recordId, oldOp.timestamp.wallTime]
1589
1603
  );
1590
1604
  }
@@ -1604,6 +1618,7 @@ import {
1604
1618
  createOperation as createOperation2,
1605
1619
  generateUUIDv7,
1606
1620
  isAtomicOp,
1621
+ quoteIdent as quoteIdent6,
1607
1622
  resolveAtomicOp,
1608
1623
  toAtomicOp,
1609
1624
  validateRecord
@@ -1920,7 +1935,7 @@ var TransactionContext = class {
1920
1935
  }
1921
1936
  }
1922
1937
  const rows = await this.config.adapter.query(
1923
- `SELECT * FROM ${collectionName} WHERE id = ? AND _deleted = 0`,
1938
+ `SELECT * FROM ${quoteIdent6(collectionName)} WHERE id = ? AND _deleted = 0`,
1924
1939
  [id]
1925
1940
  );
1926
1941
  let record = rows[0] ? deserializeRecord(rows[0], definition.fields) : null;
@@ -2020,6 +2035,7 @@ var Store = class {
2020
2035
  localMutationHandler;
2021
2036
  relationEnforcer = null;
2022
2037
  causalTracker = null;
2038
+ secretKeyProvider;
2023
2039
  constructor(config) {
2024
2040
  this.schema = config.schema;
2025
2041
  this.adapter = config.adapter;
@@ -2028,6 +2044,7 @@ var Store = class {
2028
2044
  this.isolation = config.isolation ?? "shared";
2029
2045
  this.emitter = config.emitter ?? null;
2030
2046
  this.localMutationHandler = config.localMutationHandler ?? null;
2047
+ this.secretKeyProvider = config.secretKeyProvider;
2031
2048
  this.subscriptionManager = new SubscriptionManager({
2032
2049
  onQuerySubscribed: config.onQuerySubscribed
2033
2050
  });
@@ -2070,7 +2087,8 @@ var Store = class {
2070
2087
  },
2071
2088
  this.relationEnforcer,
2072
2089
  this.localMutationHandler,
2073
- this.causalTracker
2090
+ this.causalTracker,
2091
+ this.secretKeyProvider
2074
2092
  );
2075
2093
  this.collections.set(name, col);
2076
2094
  }
@@ -2152,7 +2170,7 @@ var Store = class {
2152
2170
  return "skipped";
2153
2171
  }
2154
2172
  const existing = await this.adapter.query(
2155
- `SELECT id FROM _kora_ops_${collection} WHERE id = ?`,
2173
+ `SELECT id FROM ${quoteIdent7(`_kora_ops_${collection}`)} WHERE id = ?`,
2156
2174
  [op.id]
2157
2175
  );
2158
2176
  if (existing.length > 0) {
@@ -2257,7 +2275,7 @@ var Store = class {
2257
2275
  const serializedChanges = serializeRecord(materializeSource, definition.fields);
2258
2276
  const changedFields = Object.keys(serializedChanges);
2259
2277
  const currentRows = await tx.query(
2260
- `SELECT _version, _field_versions FROM ${collection} WHERE id = ?`,
2278
+ `SELECT _version, _field_versions FROM ${quoteIdent7(collection)} WHERE id = ?`,
2261
2279
  [op.recordId]
2262
2280
  );
2263
2281
  const currentRow = currentRows[0];
@@ -2353,7 +2371,7 @@ var Store = class {
2353
2371
  */
2354
2372
  async foldOrphanedOperations(tx, collection, definition, recordId) {
2355
2373
  const orphanRows = await tx.query(
2356
- `SELECT * FROM _kora_ops_${collection} WHERE record_id = ? AND type IN ('update', 'delete')`,
2374
+ `SELECT * FROM ${quoteIdent7(`_kora_ops_${collection}`)} WHERE record_id = ? AND type IN ('update', 'delete')`,
2357
2375
  [recordId]
2358
2376
  );
2359
2377
  if (orphanRows.length === 0) {
@@ -2366,7 +2384,7 @@ var Store = class {
2366
2384
  if (orphan.type === "update" && orphan.data) {
2367
2385
  const orphanChanges = serializeRecord(orphan.data, definition.fields);
2368
2386
  const rows = await tx.query(
2369
- `SELECT _version, _field_versions FROM ${collection} WHERE id = ?`,
2387
+ `SELECT _version, _field_versions FROM ${quoteIdent7(collection)} WHERE id = ?`,
2370
2388
  [recordId]
2371
2389
  );
2372
2390
  const current = rows[0];
@@ -2416,7 +2434,7 @@ var Store = class {
2416
2434
  const allOps = [];
2417
2435
  for (const collectionName of Object.keys(this.schema.collections)) {
2418
2436
  const rows = await this.adapter.query(
2419
- `SELECT * FROM _kora_ops_${collectionName} WHERE node_id = ? AND sequence_number >= ? AND sequence_number <= ? ORDER BY sequence_number ASC`,
2437
+ `SELECT * FROM ${quoteIdent7(`_kora_ops_${collectionName}`)} WHERE node_id = ? AND sequence_number >= ? AND sequence_number <= ? ORDER BY sequence_number ASC`,
2420
2438
  [nodeId, fromSeq, toSeq]
2421
2439
  );
2422
2440
  for (const row of rows) {
@@ -2435,7 +2453,7 @@ var Store = class {
2435
2453
  const allOps = [];
2436
2454
  for (const collectionName of Object.keys(this.schema.collections)) {
2437
2455
  const rows = await this.adapter.query(
2438
- `SELECT * FROM _kora_ops_${collectionName} ORDER BY sequence_number ASC`
2456
+ `SELECT * FROM ${quoteIdent7(`_kora_ops_${collectionName}`)} ORDER BY sequence_number ASC`
2439
2457
  );
2440
2458
  for (const row of rows) {
2441
2459
  allOps.push(deserializeOperationWithCollection(row, collectionName));
@@ -2486,7 +2504,7 @@ var Store = class {
2486
2504
  */
2487
2505
  async exportAudit(options) {
2488
2506
  this.ensureOpen();
2489
- const { exportAudit: doExport } = await import("./export-audit-VPGQWNR6.js");
2507
+ const { exportAudit: doExport } = await import("./export-audit-MT2LTMIN.js");
2490
2508
  return doExport(this.adapter, this.schema, this.nodeId, this.schema.version, options);
2491
2509
  }
2492
2510
  /**
@@ -2531,7 +2549,8 @@ var Store = class {
2531
2549
  relationEnforcer: this.relationEnforcer,
2532
2550
  causalTracker: this.causalTracker,
2533
2551
  inTransaction: options?.inTransaction ?? false,
2534
- extraCausalDeps: options?.extraCausalDeps
2552
+ extraCausalDeps: options?.extraCausalDeps,
2553
+ secretKeyProvider: this.secretKeyProvider
2535
2554
  };
2536
2555
  }
2537
2556
  /**
@@ -2544,7 +2563,7 @@ var Store = class {
2544
2563
  return null;
2545
2564
  }
2546
2565
  const rows = await this.adapter.query(
2547
- `SELECT * FROM ${collection} WHERE id = ?`,
2566
+ `SELECT * FROM ${quoteIdent7(collection)} WHERE id = ?`,
2548
2567
  [recordId]
2549
2568
  );
2550
2569
  const row = rows[0];
@@ -2569,7 +2588,7 @@ var Store = class {
2569
2588
  return null;
2570
2589
  }
2571
2590
  const rows = await this.adapter.query(
2572
- `SELECT _version, _field_versions FROM ${collection} WHERE id = ?`,
2591
+ `SELECT _version, _field_versions FROM ${quoteIdent7(collection)} WHERE id = ?`,
2573
2592
  [recordId]
2574
2593
  );
2575
2594
  const row = rows[0];
@@ -2675,7 +2694,7 @@ var Store = class {
2675
2694
  async getLatestLocalOperationForRecord(collection, recordId) {
2676
2695
  this.ensureOpen();
2677
2696
  const rows = await this.adapter.query(
2678
- `SELECT * FROM _kora_ops_${collection} WHERE node_id = ? AND record_id = ? ORDER BY sequence_number DESC LIMIT 1`,
2697
+ `SELECT * FROM ${quoteIdent7(`_kora_ops_${collection}`)} WHERE node_id = ? AND record_id = ? ORDER BY sequence_number DESC LIMIT 1`,
2679
2698
  [this.nodeId, recordId]
2680
2699
  );
2681
2700
  const row = rows[0];
@@ -2690,7 +2709,7 @@ var Store = class {
2690
2709
  async getLatestOperationForRecord(collection, recordId) {
2691
2710
  this.ensureOpen();
2692
2711
  const rows = await this.adapter.query(
2693
- `SELECT * FROM _kora_ops_${collection} WHERE record_id = ?`,
2712
+ `SELECT * FROM ${quoteIdent7(`_kora_ops_${collection}`)} WHERE record_id = ?`,
2694
2713
  [recordId]
2695
2714
  );
2696
2715
  let latest = null;
@@ -2780,7 +2799,7 @@ var Store = class {
2780
2799
  */
2781
2800
  async exportBackup(options) {
2782
2801
  this.ensureOpen();
2783
- const { exportBackup: doExport } = await import("./backup-5XZ7THNJ.js");
2802
+ const { exportBackup: doExport } = await import("./backup-IQE4QC7Q.js");
2784
2803
  return doExport(this.adapter, this.schema, this.nodeId, this.schema.version, options);
2785
2804
  }
2786
2805
  /**
@@ -2792,7 +2811,7 @@ var Store = class {
2792
2811
  */
2793
2812
  async importBackup(data, options) {
2794
2813
  this.ensureOpen();
2795
- const { restoreBackup: doRestore } = await import("./backup-5XZ7THNJ.js");
2814
+ const { restoreBackup: doRestore } = await import("./backup-IQE4QC7Q.js");
2796
2815
  return doRestore(this.adapter, this.schema, data, options);
2797
2816
  }
2798
2817
  /**
@@ -2877,14 +2896,14 @@ var Store = class {
2877
2896
  */
2878
2897
  async runBackfill(collection, transform) {
2879
2898
  const rows = await this.adapter.query(
2880
- `SELECT * FROM ${collection} WHERE _deleted = 0`
2899
+ `SELECT * FROM ${quoteIdent7(collection)} WHERE _deleted = 0`
2881
2900
  );
2882
2901
  await this.adapter.transaction(async (tx) => {
2883
2902
  for (const row of rows) {
2884
2903
  const updates = transform(row);
2885
2904
  const fields = Object.keys(updates);
2886
2905
  if (fields.length === 0) continue;
2887
- const setClauses = fields.map((f) => `${f} = ?`).join(", ");
2906
+ const setClauses = fields.map((f) => `${quoteIdent7(f)} = ?`).join(", ");
2888
2907
  const values = fields.map((f) => {
2889
2908
  const val = updates[f];
2890
2909
  if (typeof val === "boolean") return val ? 1 : 0;
@@ -2894,7 +2913,7 @@ var Store = class {
2894
2913
  return val;
2895
2914
  });
2896
2915
  values.push(row.id);
2897
- await tx.execute(`UPDATE ${collection} SET ${setClauses} WHERE id = ?`, values);
2916
+ await tx.execute(`UPDATE ${quoteIdent7(collection)} SET ${setClauses} WHERE id = ?`, values);
2898
2917
  }
2899
2918
  });
2900
2919
  }
@@ -3422,12 +3441,518 @@ function asRichTextSyncEngine(engine) {
3422
3441
  }
3423
3442
  return engine;
3424
3443
  }
3444
+
3445
+ // src/blob/opfs-blob-store.ts
3446
+ import { createBlobRef, hashBlob } from "@korajs/core";
3447
+ var OpfsBlobStore = class {
3448
+ constructor(dir) {
3449
+ this.dir = dir;
3450
+ }
3451
+ dir;
3452
+ shardFor(hash) {
3453
+ return hash.slice(0, 2);
3454
+ }
3455
+ async put(bytes, metadata = {}) {
3456
+ const ref = await createBlobRef(bytes, metadata);
3457
+ const shard = this.shardFor(ref.hash);
3458
+ if (await this.dir.has(shard, ref.hash)) {
3459
+ return ref;
3460
+ }
3461
+ const out = new Uint8Array(bytes.byteLength);
3462
+ out.set(bytes);
3463
+ await this.dir.write(shard, ref.hash, out);
3464
+ return ref;
3465
+ }
3466
+ async get(hash) {
3467
+ const bytes = await this.dir.read(this.shardFor(hash), hash);
3468
+ if (bytes === null) {
3469
+ return null;
3470
+ }
3471
+ const actual = await hashBlob(bytes);
3472
+ if (actual !== hash) {
3473
+ throw new BlobIntegrityError(hash, actual);
3474
+ }
3475
+ return bytes;
3476
+ }
3477
+ async has(hash) {
3478
+ return this.dir.has(this.shardFor(hash), hash);
3479
+ }
3480
+ async delete(hash) {
3481
+ return this.dir.remove(this.shardFor(hash), hash);
3482
+ }
3483
+ async size() {
3484
+ return this.dir.count();
3485
+ }
3486
+ async list() {
3487
+ return this.dir.keys();
3488
+ }
3489
+ };
3490
+ function getStorageManager() {
3491
+ const nav = globalThis.navigator;
3492
+ if (!nav?.storage || typeof nav.storage.getDirectory !== "function") {
3493
+ throw new Error(
3494
+ "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."
3495
+ );
3496
+ }
3497
+ return nav.storage;
3498
+ }
3499
+ function isNotFoundError(error) {
3500
+ return typeof error === "object" && error !== null && error.name === "NotFoundError";
3501
+ }
3502
+ async function createOpfsBlobDirectory(rootDirName = "kora-blobs") {
3503
+ const storage = getStorageManager();
3504
+ if (typeof storage.persist === "function") {
3505
+ try {
3506
+ await storage.persist();
3507
+ } catch {
3508
+ }
3509
+ }
3510
+ const opfsRoot = await storage.getDirectory();
3511
+ const root = await opfsRoot.getDirectoryHandle(rootDirName, { create: true });
3512
+ async function shardHandle(shard, create) {
3513
+ try {
3514
+ return await root.getDirectoryHandle(shard, { create });
3515
+ } catch (error) {
3516
+ if (isNotFoundError(error)) {
3517
+ return null;
3518
+ }
3519
+ throw error;
3520
+ }
3521
+ }
3522
+ return {
3523
+ async read(shard, name) {
3524
+ const dir = await shardHandle(shard, false);
3525
+ if (!dir) {
3526
+ return null;
3527
+ }
3528
+ let handle;
3529
+ try {
3530
+ handle = await dir.getFileHandle(name, { create: false });
3531
+ } catch (error) {
3532
+ if (isNotFoundError(error)) {
3533
+ return null;
3534
+ }
3535
+ throw error;
3536
+ }
3537
+ const file = await handle.getFile();
3538
+ return new Uint8Array(await file.arrayBuffer());
3539
+ },
3540
+ async write(shard, name, bytes) {
3541
+ const dir = await root.getDirectoryHandle(shard, { create: true });
3542
+ const handle = await dir.getFileHandle(name, { create: true });
3543
+ const writable = await handle.createWritable({ keepExistingData: false });
3544
+ try {
3545
+ await writable.write(bytes);
3546
+ } finally {
3547
+ await writable.close();
3548
+ }
3549
+ },
3550
+ async remove(shard, name) {
3551
+ const dir = await shardHandle(shard, false);
3552
+ if (!dir) {
3553
+ return false;
3554
+ }
3555
+ try {
3556
+ await dir.removeEntry(name);
3557
+ return true;
3558
+ } catch (error) {
3559
+ if (isNotFoundError(error)) {
3560
+ return false;
3561
+ }
3562
+ throw error;
3563
+ }
3564
+ },
3565
+ async has(shard, name) {
3566
+ const dir = await shardHandle(shard, false);
3567
+ if (!dir) {
3568
+ return false;
3569
+ }
3570
+ try {
3571
+ await dir.getFileHandle(name, { create: false });
3572
+ return true;
3573
+ } catch (error) {
3574
+ if (isNotFoundError(error)) {
3575
+ return false;
3576
+ }
3577
+ throw error;
3578
+ }
3579
+ },
3580
+ async count() {
3581
+ let total = 0;
3582
+ for await (const shard of root.keys()) {
3583
+ const dir = await shardHandle(shard, false);
3584
+ if (!dir) {
3585
+ continue;
3586
+ }
3587
+ for await (const name of dir.keys()) {
3588
+ if (!name.endsWith(".tmp")) {
3589
+ total++;
3590
+ }
3591
+ }
3592
+ }
3593
+ return total;
3594
+ },
3595
+ async keys() {
3596
+ const names = [];
3597
+ for await (const shard of root.keys()) {
3598
+ const dir = await shardHandle(shard, false);
3599
+ if (!dir) {
3600
+ continue;
3601
+ }
3602
+ for await (const name of dir.keys()) {
3603
+ if (!name.endsWith(".tmp")) {
3604
+ names.push(name);
3605
+ }
3606
+ }
3607
+ }
3608
+ return names;
3609
+ }
3610
+ };
3611
+ }
3612
+ async function createOpfsBlobStore(rootDirName = "kora-blobs") {
3613
+ return new OpfsBlobStore(await createOpfsBlobDirectory(rootDirName));
3614
+ }
3615
+
3616
+ // src/blob/blob-chunking.ts
3617
+ import { hashBlob as hashBlob2 } from "@korajs/core";
3618
+ var DEFAULT_CHUNK_SIZE = 256 * 1024;
3619
+ async function chunkBlob(bytes, chunkSize = DEFAULT_CHUNK_SIZE, metadata = {}) {
3620
+ if (chunkSize <= 0) {
3621
+ throw new Error(`chunkBlob requires a positive chunkSize, got ${chunkSize}`);
3622
+ }
3623
+ const chunkHashes = [];
3624
+ const chunks = /* @__PURE__ */ new Map();
3625
+ for (let offset = 0; offset < bytes.byteLength; offset += chunkSize) {
3626
+ const end = Math.min(offset + chunkSize, bytes.byteLength);
3627
+ const chunk = new Uint8Array(end - offset);
3628
+ chunk.set(bytes.subarray(offset, end));
3629
+ const hash = await hashBlob2(chunk);
3630
+ chunkHashes.push(hash);
3631
+ if (!chunks.has(hash)) {
3632
+ chunks.set(hash, chunk);
3633
+ }
3634
+ }
3635
+ const blobHash = await hashBlob2(bytes);
3636
+ const manifest = {
3637
+ blobHash,
3638
+ size: bytes.byteLength,
3639
+ chunkSize,
3640
+ chunkHashes
3641
+ };
3642
+ if (metadata.mimeType !== void 0) {
3643
+ manifest.mimeType = metadata.mimeType;
3644
+ }
3645
+ if (metadata.filename !== void 0) {
3646
+ manifest.filename = metadata.filename;
3647
+ }
3648
+ return { manifest, chunks };
3649
+ }
3650
+ async function reassembleBlob(manifest, chunkStore) {
3651
+ const parts = [];
3652
+ let total = 0;
3653
+ for (const hash of manifest.chunkHashes) {
3654
+ const chunk = await chunkStore.get(hash);
3655
+ if (chunk === null) {
3656
+ throw new Error(`Cannot reassemble blob ${manifest.blobHash}: missing chunk ${hash}`);
3657
+ }
3658
+ parts.push(chunk);
3659
+ total += chunk.byteLength;
3660
+ }
3661
+ const out = new Uint8Array(total);
3662
+ let offset = 0;
3663
+ for (const part of parts) {
3664
+ out.set(part, offset);
3665
+ offset += part.byteLength;
3666
+ }
3667
+ const actual = await hashBlob2(out);
3668
+ if (actual !== manifest.blobHash) {
3669
+ throw new BlobIntegrityError(manifest.blobHash, actual);
3670
+ }
3671
+ return out;
3672
+ }
3673
+
3674
+ // src/blob/blob-transfer.ts
3675
+ import { hashBlob as hashBlob3 } from "@korajs/core";
3676
+ async function receiveBlob(manifest, provider, stores) {
3677
+ let chunksFetched = 0;
3678
+ let chunksSkipped = 0;
3679
+ const seen = /* @__PURE__ */ new Set();
3680
+ for (const hash of manifest.chunkHashes) {
3681
+ if (seen.has(hash)) {
3682
+ continue;
3683
+ }
3684
+ seen.add(hash);
3685
+ if (await stores.chunkStore.has(hash)) {
3686
+ chunksSkipped++;
3687
+ continue;
3688
+ }
3689
+ const bytes = await provider.getChunk(hash);
3690
+ if (bytes === null) {
3691
+ throw new Error(`Blob transfer failed: provider could not supply chunk ${hash}`);
3692
+ }
3693
+ const actual = await hashBlob3(bytes);
3694
+ if (actual !== hash) {
3695
+ throw new BlobIntegrityError(hash, actual);
3696
+ }
3697
+ await stores.chunkStore.put(bytes);
3698
+ chunksFetched++;
3699
+ }
3700
+ const full = await reassembleBlob(manifest, stores.chunkStore);
3701
+ const ref = await stores.blobStore.put(full, {
3702
+ mimeType: manifest.mimeType,
3703
+ filename: manifest.filename
3704
+ });
3705
+ return { ref, chunksFetched, chunksSkipped };
3706
+ }
3707
+ async function prepareBlobForSend(bytes, chunkStore, options = {}) {
3708
+ const { manifest, chunks } = await chunkBlob(bytes, options.chunkSize, {
3709
+ mimeType: options.mimeType,
3710
+ filename: options.filename
3711
+ });
3712
+ for (const chunk of chunks.values()) {
3713
+ await chunkStore.put(chunk);
3714
+ }
3715
+ const provider = {
3716
+ getChunk: (hash) => chunkStore.get(hash)
3717
+ };
3718
+ return { manifest, provider };
3719
+ }
3720
+
3721
+ // src/blob/blob-manifest-transfer.ts
3722
+ import { hashBlob as hashBlob4 } from "@korajs/core";
3723
+ function serializeBlobManifest(manifest) {
3724
+ const canonical = {
3725
+ blobHash: manifest.blobHash,
3726
+ size: manifest.size,
3727
+ chunkSize: manifest.chunkSize,
3728
+ chunkHashes: manifest.chunkHashes
3729
+ };
3730
+ if (manifest.mimeType !== void 0) {
3731
+ canonical.mimeType = manifest.mimeType;
3732
+ }
3733
+ if (manifest.filename !== void 0) {
3734
+ canonical.filename = manifest.filename;
3735
+ }
3736
+ return new TextEncoder().encode(JSON.stringify(canonical));
3737
+ }
3738
+ function parseBlobManifest(bytes) {
3739
+ const parsed = JSON.parse(new TextDecoder().decode(bytes));
3740
+ if (typeof parsed !== "object" || parsed === null) {
3741
+ throw new Error("Invalid blob manifest: not an object");
3742
+ }
3743
+ const obj = parsed;
3744
+ if (typeof obj.blobHash !== "string") {
3745
+ throw new Error("Invalid blob manifest: missing blobHash");
3746
+ }
3747
+ if (typeof obj.size !== "number" || typeof obj.chunkSize !== "number") {
3748
+ throw new Error("Invalid blob manifest: missing size or chunkSize");
3749
+ }
3750
+ if (!Array.isArray(obj.chunkHashes) || obj.chunkHashes.some((h) => typeof h !== "string")) {
3751
+ throw new Error("Invalid blob manifest: chunkHashes must be an array of strings");
3752
+ }
3753
+ const manifest = {
3754
+ blobHash: obj.blobHash,
3755
+ size: obj.size,
3756
+ chunkSize: obj.chunkSize,
3757
+ chunkHashes: obj.chunkHashes
3758
+ };
3759
+ if (typeof obj.mimeType === "string") {
3760
+ manifest.mimeType = obj.mimeType;
3761
+ }
3762
+ if (typeof obj.filename === "string") {
3763
+ manifest.filename = obj.filename;
3764
+ }
3765
+ return manifest;
3766
+ }
3767
+ async function putBlobForTransfer(store, bytes, options = {}) {
3768
+ const { manifest, chunks } = await chunkBlob(bytes, options.chunkSize, {
3769
+ ...options.mimeType !== void 0 ? { mimeType: options.mimeType } : {},
3770
+ ...options.filename !== void 0 ? { filename: options.filename } : {}
3771
+ });
3772
+ for (const chunk of chunks.values()) {
3773
+ await store.put(chunk);
3774
+ }
3775
+ const baseRef = await store.put(bytes, {
3776
+ ...options.mimeType !== void 0 ? { mimeType: options.mimeType } : {},
3777
+ ...options.filename !== void 0 ? { filename: options.filename } : {}
3778
+ });
3779
+ const manifestRef = await store.put(serializeBlobManifest(manifest));
3780
+ return { ref: { ...baseRef, manifestHash: manifestRef.hash }, manifest };
3781
+ }
3782
+ async function fetchBlobManifest(provider, manifestHash) {
3783
+ const bytes = await provider.getChunk(manifestHash);
3784
+ if (bytes === null) {
3785
+ throw new Error(`Blob manifest ${manifestHash} could not be fetched from the provider`);
3786
+ }
3787
+ const actual = await hashBlob4(bytes);
3788
+ if (actual !== manifestHash) {
3789
+ throw new BlobIntegrityError(manifestHash, actual);
3790
+ }
3791
+ return parseBlobManifest(bytes);
3792
+ }
3793
+ async function resolveBlobManifest(provider, ref) {
3794
+ if (ref.manifestHash === void 0) {
3795
+ throw new Error(
3796
+ `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.`
3797
+ );
3798
+ }
3799
+ return fetchBlobManifest(provider, ref.manifestHash);
3800
+ }
3801
+
3802
+ // src/blob/server-blob-callbacks.ts
3803
+ import { hashBlob as hashBlob5 } from "@korajs/core";
3804
+ function toServerBlobCallbacks(store) {
3805
+ return {
3806
+ resolveBlobChunk: (hash) => store.get(hash),
3807
+ async persistBlobChunk(hash, bytes) {
3808
+ const actual = await hashBlob5(bytes);
3809
+ if (actual !== hash) {
3810
+ return;
3811
+ }
3812
+ await store.put(bytes);
3813
+ }
3814
+ };
3815
+ }
3816
+ function createMemoryServerBlobStore() {
3817
+ const store = new MemoryBlobStore();
3818
+ return { store, callbacks: toServerBlobCallbacks(store) };
3819
+ }
3820
+
3821
+ // src/blob/blob-gc.ts
3822
+ import { isBlobRef } from "@korajs/core";
3823
+ function extractBlobRefs(record) {
3824
+ const refs = [];
3825
+ for (const value of Object.values(record)) {
3826
+ if (isBlobRef(value)) {
3827
+ refs.push(value);
3828
+ }
3829
+ }
3830
+ return refs;
3831
+ }
3832
+ async function collectBlobGarbage(store, liveRefs, options = {}) {
3833
+ const live = /* @__PURE__ */ new Set();
3834
+ for (const ref of liveRefs) {
3835
+ live.add(ref.hash);
3836
+ if (ref.manifestHash === void 0) {
3837
+ continue;
3838
+ }
3839
+ live.add(ref.manifestHash);
3840
+ try {
3841
+ const manifestBytes = await store.get(ref.manifestHash);
3842
+ if (manifestBytes !== null) {
3843
+ const manifest = parseBlobManifest(manifestBytes);
3844
+ for (const chunkHash of manifest.chunkHashes) {
3845
+ live.add(chunkHash);
3846
+ }
3847
+ }
3848
+ } catch {
3849
+ }
3850
+ }
3851
+ const all = await store.list();
3852
+ const collectedHashes = [];
3853
+ for (const hash of all) {
3854
+ if (live.has(hash)) {
3855
+ continue;
3856
+ }
3857
+ collectedHashes.push(hash);
3858
+ if (!options.dryRun) {
3859
+ await store.delete(hash);
3860
+ }
3861
+ }
3862
+ return {
3863
+ scanned: all.length,
3864
+ live: live.size,
3865
+ collected: collectedHashes.length,
3866
+ collectedHashes
3867
+ };
3868
+ }
3869
+
3870
+ // src/blob/blob-chunk-transport.ts
3871
+ import { generateUUIDv7 as generateUUIDv74 } from "@korajs/core";
3872
+ function createRemoteChunkProvider(port, options = {}) {
3873
+ const timeoutMs = options.timeoutMs ?? 3e4;
3874
+ const pending = /* @__PURE__ */ new Map();
3875
+ port.onMessage((message) => {
3876
+ if (message.type !== "blob-chunk-response") {
3877
+ return;
3878
+ }
3879
+ const entry = pending.get(message.requestId);
3880
+ if (!entry) {
3881
+ return;
3882
+ }
3883
+ pending.delete(message.requestId);
3884
+ clearTimeout(entry.timer);
3885
+ entry.resolve(message.bytes);
3886
+ });
3887
+ return {
3888
+ pendingCount: () => pending.size,
3889
+ getChunk(hash) {
3890
+ const requestId = generateUUIDv74();
3891
+ return new Promise((resolve, reject) => {
3892
+ const timer = setTimeout(() => {
3893
+ pending.delete(requestId);
3894
+ reject(new Error(`Blob chunk request for ${hash} timed out after ${timeoutMs}ms`));
3895
+ }, timeoutMs);
3896
+ pending.set(requestId, { resolve, reject, timer });
3897
+ port.send({ type: "blob-chunk-request", requestId, hash });
3898
+ });
3899
+ }
3900
+ };
3901
+ }
3902
+ function serveBlobChunks(port, blobStore) {
3903
+ port.onMessage((message) => {
3904
+ if (message.type !== "blob-chunk-request") {
3905
+ return;
3906
+ }
3907
+ void blobStore.get(message.hash).then(
3908
+ (bytes) => {
3909
+ port.send({ type: "blob-chunk-response", requestId: message.requestId, bytes });
3910
+ },
3911
+ () => {
3912
+ port.send({ type: "blob-chunk-response", requestId: message.requestId, bytes: null });
3913
+ }
3914
+ );
3915
+ });
3916
+ }
3917
+ function createChunkPortPair() {
3918
+ const handlersA = [];
3919
+ const handlersB = [];
3920
+ const a = {
3921
+ send(message) {
3922
+ queueMicrotask(() => {
3923
+ for (const handler of handlersB) {
3924
+ handler(message);
3925
+ }
3926
+ });
3927
+ },
3928
+ onMessage(handler) {
3929
+ handlersA.push(handler);
3930
+ }
3931
+ };
3932
+ const b = {
3933
+ send(message) {
3934
+ queueMicrotask(() => {
3935
+ for (const handler of handlersA) {
3936
+ handler(message);
3937
+ }
3938
+ });
3939
+ },
3940
+ onMessage(handler) {
3941
+ handlersB.push(handler);
3942
+ }
3943
+ };
3944
+ return { a, b };
3945
+ }
3425
3946
  export {
3426
3947
  AdapterError,
3948
+ BlobIntegrityError,
3427
3949
  COMPACTION_BASELINE_META_KEY,
3428
3950
  Collection,
3951
+ DEFAULT_CHUNK_SIZE,
3429
3952
  InvalidStateTransitionError,
3430
3953
  LAST_ACKED_SERVER_VECTOR_META_KEY,
3954
+ MemoryBlobStore,
3955
+ OpfsBlobStore,
3431
3956
  OptimisticLockError,
3432
3957
  PersistenceError,
3433
3958
  QueryBuilder,
@@ -3445,26 +3970,44 @@ export {
3445
3970
  WorkerTimeoutError,
3446
3971
  asRichTextSyncEngine,
3447
3972
  assertQueryReady,
3973
+ chunkBlob,
3974
+ collectBlobGarbage,
3448
3975
  collectOperationsAheadOfServer,
3449
3976
  compactOperationLog,
3450
3977
  computeAckCompactionWatermark,
3978
+ createChunkPortPair,
3979
+ createMemoryServerBlobStore,
3980
+ createOpfsBlobDirectory,
3981
+ createOpfsBlobStore,
3982
+ createRemoteChunkProvider,
3451
3983
  createRichTextController,
3452
3984
  decodeAuditExport,
3453
3985
  decodeRichtext,
3454
3986
  deserializeVersionVectorFromMeta,
3455
3987
  encodeRichtext,
3456
3988
  exportBackup,
3989
+ extractBlobRefs,
3990
+ fetchBlobManifest,
3457
3991
  getSharedQueryStoreCache,
3458
3992
  mergeVersionVectors,
3993
+ parseBlobManifest,
3459
3994
  persistedAuditTraceFromEvent,
3460
3995
  pluralize,
3996
+ prepareBlobForSend,
3997
+ putBlobForTransfer,
3461
3998
  readAuditExportManifest,
3462
3999
  readBackupManifest,
4000
+ reassembleBlob,
4001
+ receiveBlob,
4002
+ resolveBlobManifest,
3463
4003
  restoreBackup,
3464
4004
  richtextStatesEqual,
3465
4005
  richtextToPlainText,
4006
+ serializeBlobManifest,
3466
4007
  serializeVersionVectorToMeta,
4008
+ serveBlobChunks,
3467
4009
  singularize,
4010
+ toServerBlobCallbacks,
3468
4011
  validateStateTransition,
3469
4012
  validateUpdateStateMachine,
3470
4013
  verifyAuditExportChecksum,