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

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 (45) 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.d.cts +1 -1
  4. package/dist/adapters/indexeddb.d.ts +1 -1
  5. package/dist/adapters/sqlite-wasm.d.cts +1 -1
  6. package/dist/adapters/sqlite-wasm.d.ts +1 -1
  7. package/dist/{backup-5XZ7THNJ.js → backup-ZV4Y424B.js} +3 -3
  8. package/dist/blob-fs.cjs +166 -0
  9. package/dist/blob-fs.cjs.map +1 -0
  10. package/dist/blob-fs.d.cts +29 -0
  11. package/dist/blob-fs.d.ts +29 -0
  12. package/dist/blob-fs.js +128 -0
  13. package/dist/blob-fs.js.map +1 -0
  14. package/dist/{chunk-ETDJEH2I.js → chunk-6NKLDBTY.js} +11 -1
  15. package/dist/{chunk-ETDJEH2I.js.map → chunk-6NKLDBTY.js.map} +1 -1
  16. package/dist/{chunk-EIVXWF4B.js → chunk-FFYA7J5H.js} +2 -2
  17. package/dist/chunk-W4MTTNEZ.js +55 -0
  18. package/dist/chunk-W4MTTNEZ.js.map +1 -0
  19. package/dist/{chunk-5R4QMY3Z.js → chunk-X57MWW35.js} +2 -2
  20. package/dist/{chunk-OUQDC5Z6.js → chunk-ZLWAYQEQ.js} +28 -12
  21. package/dist/chunk-ZLWAYQEQ.js.map +1 -0
  22. package/dist/content-addressed-blob-store-Dcj3SB9r.d.cts +60 -0
  23. package/dist/content-addressed-blob-store-Dcj3SB9r.d.ts +60 -0
  24. package/dist/{export-audit-VPGQWNR6.js → export-audit-AJ7RORNK.js} +3 -3
  25. package/dist/index.cjs +632 -9
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +397 -6
  28. package/dist/index.d.ts +397 -6
  29. package/dist/index.js +561 -26
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal.cjs +25 -5
  32. package/dist/internal.cjs.map +1 -1
  33. package/dist/internal.d.cts +3 -3
  34. package/dist/internal.d.ts +3 -3
  35. package/dist/internal.js +3 -3
  36. package/dist/{types-CyjXwrjW.d.cts → types-5h4Hqcwc.d.ts} +10 -3
  37. package/dist/{types-DAxCladl.d.ts → types-DfPiPwKr.d.cts} +10 -3
  38. package/dist/{types-CJOVKxLw.d.ts → types-k6AZgiH4.d.cts} +6 -1
  39. package/dist/{types-CJOVKxLw.d.cts → types-k6AZgiH4.d.ts} +6 -1
  40. package/package.json +13 -2
  41. package/dist/chunk-OUQDC5Z6.js.map +0 -1
  42. /package/dist/{backup-5XZ7THNJ.js.map → backup-ZV4Y424B.js.map} +0 -0
  43. /package/dist/{chunk-EIVXWF4B.js.map → chunk-FFYA7J5H.js.map} +0 -0
  44. /package/dist/{chunk-5R4QMY3Z.js.map → chunk-X57MWW35.js.map} +0 -0
  45. /package/dist/{export-audit-VPGQWNR6.js.map → export-audit-AJ7RORNK.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-X57MWW35.js";
1
7
  import {
2
8
  decodeAuditExport,
3
9
  readAuditExportManifest,
4
10
  verifyAuditExportChecksum
5
- } from "./chunk-EIVXWF4B.js";
11
+ } from "./chunk-FFYA7J5H.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-ZLWAYQEQ.js";
49
43
  import {
50
44
  decodeRichtext,
51
45
  decodeRichtextFieldsFromOpData,
@@ -57,7 +51,17 @@ 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";
@@ -210,7 +214,7 @@ import {
210
214
 
211
215
  // src/collection/collection.ts
212
216
  var Collection = class {
213
- constructor(name, definition, schema, adapter, clock, nodeId, allocateSequenceNumber, onMutation, relationEnforcer, mutationHandler, causalTracker) {
217
+ constructor(name, definition, schema, adapter, clock, nodeId, allocateSequenceNumber, onMutation, relationEnforcer, mutationHandler, causalTracker, secretKeyProvider) {
214
218
  this.name = name;
215
219
  this.definition = definition;
216
220
  this.schema = schema;
@@ -222,6 +226,7 @@ var Collection = class {
222
226
  this.relationEnforcer = relationEnforcer;
223
227
  this.mutationHandler = mutationHandler;
224
228
  this.causalTracker = causalTracker;
229
+ this.secretKeyProvider = secretKeyProvider;
225
230
  }
226
231
  name;
227
232
  definition;
@@ -234,6 +239,7 @@ var Collection = class {
234
239
  relationEnforcer;
235
240
  mutationHandler;
236
241
  causalTracker;
242
+ secretKeyProvider;
237
243
  mutationContext() {
238
244
  return {
239
245
  collection: this.name,
@@ -246,7 +252,8 @@ var Collection = class {
246
252
  onMutation: this.onMutation,
247
253
  relationEnforcer: this.relationEnforcer,
248
254
  causalTracker: this.causalTracker,
249
- inTransaction: false
255
+ inTransaction: false,
256
+ secretKeyProvider: this.secretKeyProvider
250
257
  };
251
258
  }
252
259
  async insert(data) {
@@ -2020,6 +2027,7 @@ var Store = class {
2020
2027
  localMutationHandler;
2021
2028
  relationEnforcer = null;
2022
2029
  causalTracker = null;
2030
+ secretKeyProvider;
2023
2031
  constructor(config) {
2024
2032
  this.schema = config.schema;
2025
2033
  this.adapter = config.adapter;
@@ -2028,6 +2036,7 @@ var Store = class {
2028
2036
  this.isolation = config.isolation ?? "shared";
2029
2037
  this.emitter = config.emitter ?? null;
2030
2038
  this.localMutationHandler = config.localMutationHandler ?? null;
2039
+ this.secretKeyProvider = config.secretKeyProvider;
2031
2040
  this.subscriptionManager = new SubscriptionManager({
2032
2041
  onQuerySubscribed: config.onQuerySubscribed
2033
2042
  });
@@ -2070,7 +2079,8 @@ var Store = class {
2070
2079
  },
2071
2080
  this.relationEnforcer,
2072
2081
  this.localMutationHandler,
2073
- this.causalTracker
2082
+ this.causalTracker,
2083
+ this.secretKeyProvider
2074
2084
  );
2075
2085
  this.collections.set(name, col);
2076
2086
  }
@@ -2486,7 +2496,7 @@ var Store = class {
2486
2496
  */
2487
2497
  async exportAudit(options) {
2488
2498
  this.ensureOpen();
2489
- const { exportAudit: doExport } = await import("./export-audit-VPGQWNR6.js");
2499
+ const { exportAudit: doExport } = await import("./export-audit-AJ7RORNK.js");
2490
2500
  return doExport(this.adapter, this.schema, this.nodeId, this.schema.version, options);
2491
2501
  }
2492
2502
  /**
@@ -2531,7 +2541,8 @@ var Store = class {
2531
2541
  relationEnforcer: this.relationEnforcer,
2532
2542
  causalTracker: this.causalTracker,
2533
2543
  inTransaction: options?.inTransaction ?? false,
2534
- extraCausalDeps: options?.extraCausalDeps
2544
+ extraCausalDeps: options?.extraCausalDeps,
2545
+ secretKeyProvider: this.secretKeyProvider
2535
2546
  };
2536
2547
  }
2537
2548
  /**
@@ -2780,7 +2791,7 @@ var Store = class {
2780
2791
  */
2781
2792
  async exportBackup(options) {
2782
2793
  this.ensureOpen();
2783
- const { exportBackup: doExport } = await import("./backup-5XZ7THNJ.js");
2794
+ const { exportBackup: doExport } = await import("./backup-ZV4Y424B.js");
2784
2795
  return doExport(this.adapter, this.schema, this.nodeId, this.schema.version, options);
2785
2796
  }
2786
2797
  /**
@@ -2792,7 +2803,7 @@ var Store = class {
2792
2803
  */
2793
2804
  async importBackup(data, options) {
2794
2805
  this.ensureOpen();
2795
- const { restoreBackup: doRestore } = await import("./backup-5XZ7THNJ.js");
2806
+ const { restoreBackup: doRestore } = await import("./backup-ZV4Y424B.js");
2796
2807
  return doRestore(this.adapter, this.schema, data, options);
2797
2808
  }
2798
2809
  /**
@@ -3422,12 +3433,518 @@ function asRichTextSyncEngine(engine) {
3422
3433
  }
3423
3434
  return engine;
3424
3435
  }
3436
+
3437
+ // src/blob/opfs-blob-store.ts
3438
+ import { createBlobRef, hashBlob } from "@korajs/core";
3439
+ var OpfsBlobStore = class {
3440
+ constructor(dir) {
3441
+ this.dir = dir;
3442
+ }
3443
+ dir;
3444
+ shardFor(hash) {
3445
+ return hash.slice(0, 2);
3446
+ }
3447
+ async put(bytes, metadata = {}) {
3448
+ const ref = await createBlobRef(bytes, metadata);
3449
+ const shard = this.shardFor(ref.hash);
3450
+ if (await this.dir.has(shard, ref.hash)) {
3451
+ return ref;
3452
+ }
3453
+ const out = new Uint8Array(bytes.byteLength);
3454
+ out.set(bytes);
3455
+ await this.dir.write(shard, ref.hash, out);
3456
+ return ref;
3457
+ }
3458
+ async get(hash) {
3459
+ const bytes = await this.dir.read(this.shardFor(hash), hash);
3460
+ if (bytes === null) {
3461
+ return null;
3462
+ }
3463
+ const actual = await hashBlob(bytes);
3464
+ if (actual !== hash) {
3465
+ throw new BlobIntegrityError(hash, actual);
3466
+ }
3467
+ return bytes;
3468
+ }
3469
+ async has(hash) {
3470
+ return this.dir.has(this.shardFor(hash), hash);
3471
+ }
3472
+ async delete(hash) {
3473
+ return this.dir.remove(this.shardFor(hash), hash);
3474
+ }
3475
+ async size() {
3476
+ return this.dir.count();
3477
+ }
3478
+ async list() {
3479
+ return this.dir.keys();
3480
+ }
3481
+ };
3482
+ function getStorageManager() {
3483
+ const nav = globalThis.navigator;
3484
+ if (!nav?.storage || typeof nav.storage.getDirectory !== "function") {
3485
+ throw new Error(
3486
+ "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."
3487
+ );
3488
+ }
3489
+ return nav.storage;
3490
+ }
3491
+ function isNotFoundError(error) {
3492
+ return typeof error === "object" && error !== null && error.name === "NotFoundError";
3493
+ }
3494
+ async function createOpfsBlobDirectory(rootDirName = "kora-blobs") {
3495
+ const storage = getStorageManager();
3496
+ if (typeof storage.persist === "function") {
3497
+ try {
3498
+ await storage.persist();
3499
+ } catch {
3500
+ }
3501
+ }
3502
+ const opfsRoot = await storage.getDirectory();
3503
+ const root = await opfsRoot.getDirectoryHandle(rootDirName, { create: true });
3504
+ async function shardHandle(shard, create) {
3505
+ try {
3506
+ return await root.getDirectoryHandle(shard, { create });
3507
+ } catch (error) {
3508
+ if (isNotFoundError(error)) {
3509
+ return null;
3510
+ }
3511
+ throw error;
3512
+ }
3513
+ }
3514
+ return {
3515
+ async read(shard, name) {
3516
+ const dir = await shardHandle(shard, false);
3517
+ if (!dir) {
3518
+ return null;
3519
+ }
3520
+ let handle;
3521
+ try {
3522
+ handle = await dir.getFileHandle(name, { create: false });
3523
+ } catch (error) {
3524
+ if (isNotFoundError(error)) {
3525
+ return null;
3526
+ }
3527
+ throw error;
3528
+ }
3529
+ const file = await handle.getFile();
3530
+ return new Uint8Array(await file.arrayBuffer());
3531
+ },
3532
+ async write(shard, name, bytes) {
3533
+ const dir = await root.getDirectoryHandle(shard, { create: true });
3534
+ const handle = await dir.getFileHandle(name, { create: true });
3535
+ const writable = await handle.createWritable({ keepExistingData: false });
3536
+ try {
3537
+ await writable.write(bytes);
3538
+ } finally {
3539
+ await writable.close();
3540
+ }
3541
+ },
3542
+ async remove(shard, name) {
3543
+ const dir = await shardHandle(shard, false);
3544
+ if (!dir) {
3545
+ return false;
3546
+ }
3547
+ try {
3548
+ await dir.removeEntry(name);
3549
+ return true;
3550
+ } catch (error) {
3551
+ if (isNotFoundError(error)) {
3552
+ return false;
3553
+ }
3554
+ throw error;
3555
+ }
3556
+ },
3557
+ async has(shard, name) {
3558
+ const dir = await shardHandle(shard, false);
3559
+ if (!dir) {
3560
+ return false;
3561
+ }
3562
+ try {
3563
+ await dir.getFileHandle(name, { create: false });
3564
+ return true;
3565
+ } catch (error) {
3566
+ if (isNotFoundError(error)) {
3567
+ return false;
3568
+ }
3569
+ throw error;
3570
+ }
3571
+ },
3572
+ async count() {
3573
+ let total = 0;
3574
+ for await (const shard of root.keys()) {
3575
+ const dir = await shardHandle(shard, false);
3576
+ if (!dir) {
3577
+ continue;
3578
+ }
3579
+ for await (const name of dir.keys()) {
3580
+ if (!name.endsWith(".tmp")) {
3581
+ total++;
3582
+ }
3583
+ }
3584
+ }
3585
+ return total;
3586
+ },
3587
+ async keys() {
3588
+ const names = [];
3589
+ for await (const shard of root.keys()) {
3590
+ const dir = await shardHandle(shard, false);
3591
+ if (!dir) {
3592
+ continue;
3593
+ }
3594
+ for await (const name of dir.keys()) {
3595
+ if (!name.endsWith(".tmp")) {
3596
+ names.push(name);
3597
+ }
3598
+ }
3599
+ }
3600
+ return names;
3601
+ }
3602
+ };
3603
+ }
3604
+ async function createOpfsBlobStore(rootDirName = "kora-blobs") {
3605
+ return new OpfsBlobStore(await createOpfsBlobDirectory(rootDirName));
3606
+ }
3607
+
3608
+ // src/blob/blob-chunking.ts
3609
+ import { hashBlob as hashBlob2 } from "@korajs/core";
3610
+ var DEFAULT_CHUNK_SIZE = 256 * 1024;
3611
+ async function chunkBlob(bytes, chunkSize = DEFAULT_CHUNK_SIZE, metadata = {}) {
3612
+ if (chunkSize <= 0) {
3613
+ throw new Error(`chunkBlob requires a positive chunkSize, got ${chunkSize}`);
3614
+ }
3615
+ const chunkHashes = [];
3616
+ const chunks = /* @__PURE__ */ new Map();
3617
+ for (let offset = 0; offset < bytes.byteLength; offset += chunkSize) {
3618
+ const end = Math.min(offset + chunkSize, bytes.byteLength);
3619
+ const chunk = new Uint8Array(end - offset);
3620
+ chunk.set(bytes.subarray(offset, end));
3621
+ const hash = await hashBlob2(chunk);
3622
+ chunkHashes.push(hash);
3623
+ if (!chunks.has(hash)) {
3624
+ chunks.set(hash, chunk);
3625
+ }
3626
+ }
3627
+ const blobHash = await hashBlob2(bytes);
3628
+ const manifest = {
3629
+ blobHash,
3630
+ size: bytes.byteLength,
3631
+ chunkSize,
3632
+ chunkHashes
3633
+ };
3634
+ if (metadata.mimeType !== void 0) {
3635
+ manifest.mimeType = metadata.mimeType;
3636
+ }
3637
+ if (metadata.filename !== void 0) {
3638
+ manifest.filename = metadata.filename;
3639
+ }
3640
+ return { manifest, chunks };
3641
+ }
3642
+ async function reassembleBlob(manifest, chunkStore) {
3643
+ const parts = [];
3644
+ let total = 0;
3645
+ for (const hash of manifest.chunkHashes) {
3646
+ const chunk = await chunkStore.get(hash);
3647
+ if (chunk === null) {
3648
+ throw new Error(`Cannot reassemble blob ${manifest.blobHash}: missing chunk ${hash}`);
3649
+ }
3650
+ parts.push(chunk);
3651
+ total += chunk.byteLength;
3652
+ }
3653
+ const out = new Uint8Array(total);
3654
+ let offset = 0;
3655
+ for (const part of parts) {
3656
+ out.set(part, offset);
3657
+ offset += part.byteLength;
3658
+ }
3659
+ const actual = await hashBlob2(out);
3660
+ if (actual !== manifest.blobHash) {
3661
+ throw new BlobIntegrityError(manifest.blobHash, actual);
3662
+ }
3663
+ return out;
3664
+ }
3665
+
3666
+ // src/blob/blob-transfer.ts
3667
+ import { hashBlob as hashBlob3 } from "@korajs/core";
3668
+ async function receiveBlob(manifest, provider, stores) {
3669
+ let chunksFetched = 0;
3670
+ let chunksSkipped = 0;
3671
+ const seen = /* @__PURE__ */ new Set();
3672
+ for (const hash of manifest.chunkHashes) {
3673
+ if (seen.has(hash)) {
3674
+ continue;
3675
+ }
3676
+ seen.add(hash);
3677
+ if (await stores.chunkStore.has(hash)) {
3678
+ chunksSkipped++;
3679
+ continue;
3680
+ }
3681
+ const bytes = await provider.getChunk(hash);
3682
+ if (bytes === null) {
3683
+ throw new Error(`Blob transfer failed: provider could not supply chunk ${hash}`);
3684
+ }
3685
+ const actual = await hashBlob3(bytes);
3686
+ if (actual !== hash) {
3687
+ throw new BlobIntegrityError(hash, actual);
3688
+ }
3689
+ await stores.chunkStore.put(bytes);
3690
+ chunksFetched++;
3691
+ }
3692
+ const full = await reassembleBlob(manifest, stores.chunkStore);
3693
+ const ref = await stores.blobStore.put(full, {
3694
+ mimeType: manifest.mimeType,
3695
+ filename: manifest.filename
3696
+ });
3697
+ return { ref, chunksFetched, chunksSkipped };
3698
+ }
3699
+ async function prepareBlobForSend(bytes, chunkStore, options = {}) {
3700
+ const { manifest, chunks } = await chunkBlob(bytes, options.chunkSize, {
3701
+ mimeType: options.mimeType,
3702
+ filename: options.filename
3703
+ });
3704
+ for (const chunk of chunks.values()) {
3705
+ await chunkStore.put(chunk);
3706
+ }
3707
+ const provider = {
3708
+ getChunk: (hash) => chunkStore.get(hash)
3709
+ };
3710
+ return { manifest, provider };
3711
+ }
3712
+
3713
+ // src/blob/blob-manifest-transfer.ts
3714
+ import { hashBlob as hashBlob4 } from "@korajs/core";
3715
+ function serializeBlobManifest(manifest) {
3716
+ const canonical = {
3717
+ blobHash: manifest.blobHash,
3718
+ size: manifest.size,
3719
+ chunkSize: manifest.chunkSize,
3720
+ chunkHashes: manifest.chunkHashes
3721
+ };
3722
+ if (manifest.mimeType !== void 0) {
3723
+ canonical.mimeType = manifest.mimeType;
3724
+ }
3725
+ if (manifest.filename !== void 0) {
3726
+ canonical.filename = manifest.filename;
3727
+ }
3728
+ return new TextEncoder().encode(JSON.stringify(canonical));
3729
+ }
3730
+ function parseBlobManifest(bytes) {
3731
+ const parsed = JSON.parse(new TextDecoder().decode(bytes));
3732
+ if (typeof parsed !== "object" || parsed === null) {
3733
+ throw new Error("Invalid blob manifest: not an object");
3734
+ }
3735
+ const obj = parsed;
3736
+ if (typeof obj.blobHash !== "string") {
3737
+ throw new Error("Invalid blob manifest: missing blobHash");
3738
+ }
3739
+ if (typeof obj.size !== "number" || typeof obj.chunkSize !== "number") {
3740
+ throw new Error("Invalid blob manifest: missing size or chunkSize");
3741
+ }
3742
+ if (!Array.isArray(obj.chunkHashes) || obj.chunkHashes.some((h) => typeof h !== "string")) {
3743
+ throw new Error("Invalid blob manifest: chunkHashes must be an array of strings");
3744
+ }
3745
+ const manifest = {
3746
+ blobHash: obj.blobHash,
3747
+ size: obj.size,
3748
+ chunkSize: obj.chunkSize,
3749
+ chunkHashes: obj.chunkHashes
3750
+ };
3751
+ if (typeof obj.mimeType === "string") {
3752
+ manifest.mimeType = obj.mimeType;
3753
+ }
3754
+ if (typeof obj.filename === "string") {
3755
+ manifest.filename = obj.filename;
3756
+ }
3757
+ return manifest;
3758
+ }
3759
+ async function putBlobForTransfer(store, bytes, options = {}) {
3760
+ const { manifest, chunks } = await chunkBlob(bytes, options.chunkSize, {
3761
+ ...options.mimeType !== void 0 ? { mimeType: options.mimeType } : {},
3762
+ ...options.filename !== void 0 ? { filename: options.filename } : {}
3763
+ });
3764
+ for (const chunk of chunks.values()) {
3765
+ await store.put(chunk);
3766
+ }
3767
+ const baseRef = await store.put(bytes, {
3768
+ ...options.mimeType !== void 0 ? { mimeType: options.mimeType } : {},
3769
+ ...options.filename !== void 0 ? { filename: options.filename } : {}
3770
+ });
3771
+ const manifestRef = await store.put(serializeBlobManifest(manifest));
3772
+ return { ref: { ...baseRef, manifestHash: manifestRef.hash }, manifest };
3773
+ }
3774
+ async function fetchBlobManifest(provider, manifestHash) {
3775
+ const bytes = await provider.getChunk(manifestHash);
3776
+ if (bytes === null) {
3777
+ throw new Error(`Blob manifest ${manifestHash} could not be fetched from the provider`);
3778
+ }
3779
+ const actual = await hashBlob4(bytes);
3780
+ if (actual !== manifestHash) {
3781
+ throw new BlobIntegrityError(manifestHash, actual);
3782
+ }
3783
+ return parseBlobManifest(bytes);
3784
+ }
3785
+ async function resolveBlobManifest(provider, ref) {
3786
+ if (ref.manifestHash === void 0) {
3787
+ throw new Error(
3788
+ `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.`
3789
+ );
3790
+ }
3791
+ return fetchBlobManifest(provider, ref.manifestHash);
3792
+ }
3793
+
3794
+ // src/blob/server-blob-callbacks.ts
3795
+ import { hashBlob as hashBlob5 } from "@korajs/core";
3796
+ function toServerBlobCallbacks(store) {
3797
+ return {
3798
+ resolveBlobChunk: (hash) => store.get(hash),
3799
+ async persistBlobChunk(hash, bytes) {
3800
+ const actual = await hashBlob5(bytes);
3801
+ if (actual !== hash) {
3802
+ return;
3803
+ }
3804
+ await store.put(bytes);
3805
+ }
3806
+ };
3807
+ }
3808
+ function createMemoryServerBlobStore() {
3809
+ const store = new MemoryBlobStore();
3810
+ return { store, callbacks: toServerBlobCallbacks(store) };
3811
+ }
3812
+
3813
+ // src/blob/blob-gc.ts
3814
+ import { isBlobRef } from "@korajs/core";
3815
+ function extractBlobRefs(record) {
3816
+ const refs = [];
3817
+ for (const value of Object.values(record)) {
3818
+ if (isBlobRef(value)) {
3819
+ refs.push(value);
3820
+ }
3821
+ }
3822
+ return refs;
3823
+ }
3824
+ async function collectBlobGarbage(store, liveRefs, options = {}) {
3825
+ const live = /* @__PURE__ */ new Set();
3826
+ for (const ref of liveRefs) {
3827
+ live.add(ref.hash);
3828
+ if (ref.manifestHash === void 0) {
3829
+ continue;
3830
+ }
3831
+ live.add(ref.manifestHash);
3832
+ try {
3833
+ const manifestBytes = await store.get(ref.manifestHash);
3834
+ if (manifestBytes !== null) {
3835
+ const manifest = parseBlobManifest(manifestBytes);
3836
+ for (const chunkHash of manifest.chunkHashes) {
3837
+ live.add(chunkHash);
3838
+ }
3839
+ }
3840
+ } catch {
3841
+ }
3842
+ }
3843
+ const all = await store.list();
3844
+ const collectedHashes = [];
3845
+ for (const hash of all) {
3846
+ if (live.has(hash)) {
3847
+ continue;
3848
+ }
3849
+ collectedHashes.push(hash);
3850
+ if (!options.dryRun) {
3851
+ await store.delete(hash);
3852
+ }
3853
+ }
3854
+ return {
3855
+ scanned: all.length,
3856
+ live: live.size,
3857
+ collected: collectedHashes.length,
3858
+ collectedHashes
3859
+ };
3860
+ }
3861
+
3862
+ // src/blob/blob-chunk-transport.ts
3863
+ import { generateUUIDv7 as generateUUIDv74 } from "@korajs/core";
3864
+ function createRemoteChunkProvider(port, options = {}) {
3865
+ const timeoutMs = options.timeoutMs ?? 3e4;
3866
+ const pending = /* @__PURE__ */ new Map();
3867
+ port.onMessage((message) => {
3868
+ if (message.type !== "blob-chunk-response") {
3869
+ return;
3870
+ }
3871
+ const entry = pending.get(message.requestId);
3872
+ if (!entry) {
3873
+ return;
3874
+ }
3875
+ pending.delete(message.requestId);
3876
+ clearTimeout(entry.timer);
3877
+ entry.resolve(message.bytes);
3878
+ });
3879
+ return {
3880
+ pendingCount: () => pending.size,
3881
+ getChunk(hash) {
3882
+ const requestId = generateUUIDv74();
3883
+ return new Promise((resolve, reject) => {
3884
+ const timer = setTimeout(() => {
3885
+ pending.delete(requestId);
3886
+ reject(new Error(`Blob chunk request for ${hash} timed out after ${timeoutMs}ms`));
3887
+ }, timeoutMs);
3888
+ pending.set(requestId, { resolve, reject, timer });
3889
+ port.send({ type: "blob-chunk-request", requestId, hash });
3890
+ });
3891
+ }
3892
+ };
3893
+ }
3894
+ function serveBlobChunks(port, blobStore) {
3895
+ port.onMessage((message) => {
3896
+ if (message.type !== "blob-chunk-request") {
3897
+ return;
3898
+ }
3899
+ void blobStore.get(message.hash).then(
3900
+ (bytes) => {
3901
+ port.send({ type: "blob-chunk-response", requestId: message.requestId, bytes });
3902
+ },
3903
+ () => {
3904
+ port.send({ type: "blob-chunk-response", requestId: message.requestId, bytes: null });
3905
+ }
3906
+ );
3907
+ });
3908
+ }
3909
+ function createChunkPortPair() {
3910
+ const handlersA = [];
3911
+ const handlersB = [];
3912
+ const a = {
3913
+ send(message) {
3914
+ queueMicrotask(() => {
3915
+ for (const handler of handlersB) {
3916
+ handler(message);
3917
+ }
3918
+ });
3919
+ },
3920
+ onMessage(handler) {
3921
+ handlersA.push(handler);
3922
+ }
3923
+ };
3924
+ const b = {
3925
+ send(message) {
3926
+ queueMicrotask(() => {
3927
+ for (const handler of handlersA) {
3928
+ handler(message);
3929
+ }
3930
+ });
3931
+ },
3932
+ onMessage(handler) {
3933
+ handlersB.push(handler);
3934
+ }
3935
+ };
3936
+ return { a, b };
3937
+ }
3425
3938
  export {
3426
3939
  AdapterError,
3940
+ BlobIntegrityError,
3427
3941
  COMPACTION_BASELINE_META_KEY,
3428
3942
  Collection,
3943
+ DEFAULT_CHUNK_SIZE,
3429
3944
  InvalidStateTransitionError,
3430
3945
  LAST_ACKED_SERVER_VECTOR_META_KEY,
3946
+ MemoryBlobStore,
3947
+ OpfsBlobStore,
3431
3948
  OptimisticLockError,
3432
3949
  PersistenceError,
3433
3950
  QueryBuilder,
@@ -3445,26 +3962,44 @@ export {
3445
3962
  WorkerTimeoutError,
3446
3963
  asRichTextSyncEngine,
3447
3964
  assertQueryReady,
3965
+ chunkBlob,
3966
+ collectBlobGarbage,
3448
3967
  collectOperationsAheadOfServer,
3449
3968
  compactOperationLog,
3450
3969
  computeAckCompactionWatermark,
3970
+ createChunkPortPair,
3971
+ createMemoryServerBlobStore,
3972
+ createOpfsBlobDirectory,
3973
+ createOpfsBlobStore,
3974
+ createRemoteChunkProvider,
3451
3975
  createRichTextController,
3452
3976
  decodeAuditExport,
3453
3977
  decodeRichtext,
3454
3978
  deserializeVersionVectorFromMeta,
3455
3979
  encodeRichtext,
3456
3980
  exportBackup,
3981
+ extractBlobRefs,
3982
+ fetchBlobManifest,
3457
3983
  getSharedQueryStoreCache,
3458
3984
  mergeVersionVectors,
3985
+ parseBlobManifest,
3459
3986
  persistedAuditTraceFromEvent,
3460
3987
  pluralize,
3988
+ prepareBlobForSend,
3989
+ putBlobForTransfer,
3461
3990
  readAuditExportManifest,
3462
3991
  readBackupManifest,
3992
+ reassembleBlob,
3993
+ receiveBlob,
3994
+ resolveBlobManifest,
3463
3995
  restoreBackup,
3464
3996
  richtextStatesEqual,
3465
3997
  richtextToPlainText,
3998
+ serializeBlobManifest,
3466
3999
  serializeVersionVectorToMeta,
4000
+ serveBlobChunks,
3467
4001
  singularize,
4002
+ toServerBlobCallbacks,
3468
4003
  validateStateTransition,
3469
4004
  validateUpdateStateMachine,
3470
4005
  verifyAuditExportChecksum,