@fireproof/core 0.19.99 → 0.19.101

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 (62) hide show
  1. package/{chunk-OFGPKRCM.js → chunk-3EB3ENHT.js} +54 -25
  2. package/chunk-3EB3ENHT.js.map +1 -0
  3. package/chunk-HQ7D3PEU.js +61 -0
  4. package/chunk-HQ7D3PEU.js.map +1 -0
  5. package/chunk-PZ5AY32C.js +10 -0
  6. package/deno-filesystem-Q2IJ7YDR.js +57 -0
  7. package/deno-filesystem-Q2IJ7YDR.js.map +1 -0
  8. package/deno.json +3 -1
  9. package/{gateway-5FCWPX5W.js → gateway-GK5QZ6KP.js} +13 -12
  10. package/gateway-GK5QZ6KP.js.map +1 -0
  11. package/{gateway-H7UD6TNB.js → gateway-TQTGDRCN.js} +9 -8
  12. package/gateway-TQTGDRCN.js.map +1 -0
  13. package/index.cjs +2232 -1781
  14. package/index.cjs.map +1 -1
  15. package/index.d.cts +261 -117
  16. package/index.d.ts +261 -117
  17. package/index.global.js +12776 -11829
  18. package/index.global.js.map +1 -1
  19. package/index.js +1936 -1579
  20. package/index.js.map +1 -1
  21. package/{key-bag-file-WADZBHYG.js → key-bag-file-VOSSK46F.js} +4 -3
  22. package/{key-bag-file-WADZBHYG.js.map → key-bag-file-VOSSK46F.js.map} +1 -1
  23. package/{key-bag-indexdb-PGVAI3FJ.js → key-bag-indexdb-AXTQOSMC.js} +4 -3
  24. package/{key-bag-indexdb-PGVAI3FJ.js.map → key-bag-indexdb-AXTQOSMC.js.map} +1 -1
  25. package/key-bag-memory-LWE6ARPX.js +29 -0
  26. package/key-bag-memory-LWE6ARPX.js.map +1 -0
  27. package/metafile-cjs.json +1 -1
  28. package/metafile-esm.json +1 -1
  29. package/metafile-iife.json +1 -1
  30. package/{node-filesystem-INX4ZTHE.js → node-filesystem-CFRXFSO7.js} +6 -9
  31. package/node-filesystem-CFRXFSO7.js.map +1 -0
  32. package/package.json +4 -2
  33. package/tests/blockstore/keyed-crypto-indexdb-file.test.ts +129 -0
  34. package/tests/blockstore/keyed-crypto.test.ts +63 -227
  35. package/tests/blockstore/loader.test.ts +19 -11
  36. package/tests/blockstore/store.test.ts +23 -19
  37. package/tests/blockstore/transaction.test.ts +12 -12
  38. package/tests/fireproof/all-gateway.test.ts +201 -193
  39. package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.ts +324 -316
  40. package/tests/fireproof/crdt.test.ts +67 -16
  41. package/tests/fireproof/database.test.ts +183 -21
  42. package/tests/fireproof/fireproof.test.ts +83 -74
  43. package/tests/fireproof/hello.test.ts +18 -14
  44. package/tests/fireproof/indexer.test.ts +53 -43
  45. package/tests/fireproof/utils.test.ts +18 -6
  46. package/tests/gateway/file/loader-config.test.ts +303 -0
  47. package/tests/gateway/indexdb/loader-config.test.ts +75 -0
  48. package/tests/helpers.ts +27 -9
  49. package/tests/react/useFireproof.test.tsx +1 -1
  50. package/{utils-QO2HIWGI.js → utils-STA2C35G.js} +4 -3
  51. package/utils-STA2C35G.js.map +1 -0
  52. package/chunk-OFGPKRCM.js.map +0 -1
  53. package/chunk-WS3YRPIA.js +0 -75
  54. package/chunk-WS3YRPIA.js.map +0 -1
  55. package/gateway-5FCWPX5W.js.map +0 -1
  56. package/gateway-H7UD6TNB.js.map +0 -1
  57. package/mem-filesystem-YPPJV7Q2.js +0 -41
  58. package/mem-filesystem-YPPJV7Q2.js.map +0 -1
  59. package/node-filesystem-INX4ZTHE.js.map +0 -1
  60. package/tests/fireproof/config.test.ts +0 -172
  61. /package/{utils-QO2HIWGI.js.map → chunk-PZ5AY32C.js.map} +0 -0
  62. /package/tests/fireproof/{fireproof.test.fixture.ts → fireproof.fixture.ts} +0 -0
package/index.cjs CHANGED
@@ -30,6 +30,47 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
+ // src/types.ts
34
+ function isFalsy(value) {
35
+ return value === false && value === null && value === void 0;
36
+ }
37
+ function throwFalsy(value) {
38
+ if (isFalsy(value)) {
39
+ throw new Error("value is Falsy");
40
+ }
41
+ return value;
42
+ }
43
+ function falsyToUndef(value) {
44
+ if (isFalsy(value)) {
45
+ return void 0;
46
+ }
47
+ return value;
48
+ }
49
+ var PARAM;
50
+ var init_types = __esm({
51
+ "src/types.ts"() {
52
+ "use strict";
53
+ PARAM = /* @__PURE__ */ ((PARAM2) => {
54
+ PARAM2["SUFFIX"] = "suffix";
55
+ PARAM2["URL_GEN"] = "urlGen";
56
+ PARAM2["STORE_KEY"] = "storekey";
57
+ PARAM2["STORE"] = "store";
58
+ PARAM2["KEY"] = "key";
59
+ PARAM2["INDEX"] = "index";
60
+ PARAM2["NAME"] = "name";
61
+ PARAM2["VERSION"] = "version";
62
+ PARAM2["FRAG_SIZE"] = "fragSize";
63
+ PARAM2["IV_VERIFY"] = "ivVerify";
64
+ PARAM2["IV_HASH"] = "ivHash";
65
+ PARAM2["FRAG_FID"] = "fid";
66
+ PARAM2["FRAG_OFS"] = "ofs";
67
+ PARAM2["FRAG_LEN"] = "len";
68
+ PARAM2["FRAG_HEAD"] = "headerSize";
69
+ return PARAM2;
70
+ })(PARAM || {});
71
+ }
72
+ });
73
+
33
74
  // src/utils.ts
34
75
  function presetEnv() {
35
76
  const penv = new Map([
@@ -87,6 +128,10 @@ function ensureLogger(sthis, componentName, ctx) {
87
128
  exposeStack = true;
88
129
  delete ctx.exposeStack;
89
130
  }
131
+ if ("exposeStack" in ctx) {
132
+ exposeStack = true;
133
+ delete ctx.exposeStack;
134
+ }
90
135
  if ("this" in ctx) {
91
136
  cLogger.Str("this", sthis.nextId(4).str);
92
137
  delete ctx.this;
@@ -153,10 +198,13 @@ function ensureLogger(sthis, componentName, ctx) {
153
198
  logger.SetExposeStack(true);
154
199
  }
155
200
  const out = cLogger.Logger();
201
+ if (sthis.env.get("FP_CONSTRUCTOR_DEBUG")) {
202
+ out.Debug().Msg("constructor");
203
+ }
156
204
  return out;
157
205
  }
158
206
  function getStore(url, sthis, joiner) {
159
- const store = url.getParam("store");
207
+ const store = url.getParam("store" /* STORE */);
160
208
  switch (store) {
161
209
  case "data":
162
210
  case "wal":
@@ -164,20 +212,21 @@ function getStore(url, sthis, joiner) {
164
212
  break;
165
213
  default:
166
214
  throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
215
+ throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
167
216
  }
168
217
  let name = store;
169
218
  if (url.hasParam("index")) {
170
- name = joiner(url.getParam("index") || "idx", name);
219
+ name = joiner(url.getParam("index" /* INDEX */) || "idx", name);
171
220
  }
172
221
  return { store, name };
173
222
  }
174
223
  function getKey(url, logger) {
175
- const result = url.getParam("key");
224
+ const result = url.getParam("key" /* KEY */);
176
225
  if (!result) throw logger.Error().Str("url", url.toString()).Msg(`key not found`).AsError();
177
226
  return result;
178
227
  }
179
228
  function getName(sthis, url) {
180
- let result = url.getParam("name");
229
+ let result = url.getParam("name" /* NAME */);
181
230
  if (!result) {
182
231
  result = sthis.pathOps.dirname(url.pathname);
183
232
  if (result.length === 0) {
@@ -197,17 +246,6 @@ function isNotFoundError(e) {
197
246
  if (e.code === "ENOENT") return true;
198
247
  return false;
199
248
  }
200
- function dataDir(sthis, name, base) {
201
- if (!base) {
202
- if (!(0, import_cement.runtimeFn)().isBrowser) {
203
- const home = sthis.env.get("HOME") || "./";
204
- base = sthis.env.get("FP_STORAGE_URL") || `file://${sthis.pathOps.join(home, ".fireproof")}`;
205
- } else {
206
- base = sthis.env.get("FP_STORAGE_URL") || `indexdb://fp`;
207
- }
208
- }
209
- return import_cement.URI.from(base.toString()).build().setParam("name", name || "").URI();
210
- }
211
249
  function UInt8ArrayEqual(a, b) {
212
250
  if (a.length !== b.length) {
213
251
  return false;
@@ -224,6 +262,7 @@ var init_utils = __esm({
224
262
  "src/utils.ts"() {
225
263
  "use strict";
226
264
  import_cement = require("@adviser/cement");
265
+ init_types();
227
266
  import_base58 = require("multiformats/bases/base58");
228
267
  globalLogger = new import_cement.LoggerImpl();
229
268
  registerFP_DEBUG = new import_cement.ResolveOnce();
@@ -274,6 +313,9 @@ var init_utils = __esm({
274
313
  dirname(path) {
275
314
  return path.split("/").slice(0, -1).join("/");
276
315
  }
316
+ basename(path) {
317
+ return path.split("/").pop() || "";
318
+ }
277
319
  // homedir() {
278
320
  // throw new Error("SysContainer:homedir is not available in seeded state");
279
321
  // }
@@ -292,45 +334,63 @@ var init_utils = __esm({
292
334
  }
293
335
  });
294
336
 
295
- // src/runtime/gateways/file/mem-filesystem.ts
296
- var mem_filesystem_exports = {};
297
- __export(mem_filesystem_exports, {
298
- MemFileSystem: () => MemFileSystem
337
+ // src/runtime/gateways/file/deno-filesystem.ts
338
+ var deno_filesystem_exports = {};
339
+ __export(deno_filesystem_exports, {
340
+ DenoFileSystem: () => DenoFileSystem
299
341
  });
300
- var import_memfs, MemFileSystem;
301
- var init_mem_filesystem = __esm({
302
- "src/runtime/gateways/file/mem-filesystem.ts"() {
342
+ var DenoFileSystem;
343
+ var init_deno_filesystem = __esm({
344
+ "src/runtime/gateways/file/deno-filesystem.ts"() {
303
345
  "use strict";
304
- import_memfs = require("memfs");
305
- init_utils2();
306
- MemFileSystem = class {
346
+ DenoFileSystem = class {
307
347
  async start() {
348
+ this.fs = Deno;
308
349
  return this;
309
350
  }
310
- mkdir(path, options) {
311
- return import_memfs.fs.promises.mkdir(path, options);
351
+ async mkdir(path, options) {
352
+ return this.fs?.mkdir(path, options).then(() => path);
312
353
  }
313
- readdir(path, options) {
314
- return import_memfs.fs.promises.readdir(path, options);
354
+ async readdir(path) {
355
+ const ret = [];
356
+ for await (const dirEntry of this.fs.readdir(path)) {
357
+ ret.push(dirEntry.name);
358
+ }
359
+ return ret;
315
360
  }
316
- rm(path, options) {
317
- return import_memfs.fs.promises.rm(path, options);
361
+ async rm(path, options) {
362
+ return this.fs?.rm(path, options);
318
363
  }
319
- copyFile(source, destination) {
320
- return import_memfs.fs.promises.copyFile(source, destination);
364
+ async copyFile(source, destination) {
365
+ return this.fs?.copyFile(source, destination);
321
366
  }
322
- async readfile(path, options) {
323
- const ret = await import_memfs.fs.promises.readFile(path, options);
324
- return toArrayBuffer(ret);
367
+ async readfile(path) {
368
+ return this.fs.readFile(path);
325
369
  }
326
- stat(path) {
327
- return import_memfs.fs.promises.stat(path);
370
+ async stat(path) {
371
+ const x = await this.fs.stat(path);
372
+ return {
373
+ isFile: () => x.isFile,
374
+ isDirectory: () => x.isDirectory,
375
+ isBlockDevice: () => !!x.isBlockDevice,
376
+ isCharacterDevice: () => !!x.isCharDevice,
377
+ isSymbolicLink: () => !!x.isSymlink,
378
+ isFIFO: () => !!x.isFifo,
379
+ isSocket: () => !!x.isSocket,
380
+ uid: x.uid,
381
+ gid: x.gid,
382
+ size: x.size,
383
+ atime: x.atime,
384
+ mtime: x.mtime,
385
+ ctime: x.birthtime,
386
+ birthtime: x.birthtime
387
+ };
328
388
  }
329
- unlink(path) {
330
- return import_memfs.fs.promises.unlink(path);
389
+ async unlink(path) {
390
+ return this.fs?.unlink(path);
331
391
  }
332
- writefile(path, data) {
333
- return import_memfs.fs.promises.writeFile(path, Buffer.from(data));
392
+ async writefile(path, data) {
393
+ return this.fs?.writeFile(path, Buffer.from(data));
334
394
  }
335
395
  };
336
396
  }
@@ -341,12 +401,11 @@ var node_filesystem_exports = {};
341
401
  __export(node_filesystem_exports, {
342
402
  NodeFileSystem: () => NodeFileSystem
343
403
  });
344
- var import_cement4, NodeFileSystem;
404
+ var NodeFileSystem;
345
405
  var init_node_filesystem = __esm({
346
406
  "src/runtime/gateways/file/node-filesystem.ts"() {
347
407
  "use strict";
348
408
  init_utils2();
349
- import_cement4 = require("@adviser/cement");
350
409
  NodeFileSystem = class {
351
410
  async start() {
352
411
  this.fs = await import("fs/promises");
@@ -369,15 +428,12 @@ var init_node_filesystem = __esm({
369
428
  return toArrayBuffer(ret);
370
429
  }
371
430
  stat(path) {
372
- return this.fs?.stat(path);
431
+ return this.fs.stat(path);
373
432
  }
374
433
  async unlink(path) {
375
434
  return this.fs?.unlink(path);
376
435
  }
377
436
  async writefile(path, data) {
378
- if ((0, import_cement4.runtimeFn)().isDeno) {
379
- return this.fs?.writeFile(path, data);
380
- }
381
437
  return this.fs?.writeFile(path, Buffer.from(data));
382
438
  }
383
439
  };
@@ -393,49 +449,34 @@ __export(utils_exports, {
393
449
  toArrayBuffer: () => toArrayBuffer
394
450
  });
395
451
  async function getFileSystem(url) {
396
- const name = url.getParam("fs");
397
- let fs2;
398
- switch (name) {
399
- case "mem":
400
- {
401
- const { MemFileSystem: MemFileSystem2 } = await Promise.resolve().then(() => (init_mem_filesystem(), mem_filesystem_exports));
402
- fs2 = new MemFileSystem2();
403
- }
404
- break;
405
- // case 'deno': {
406
- // const { DenoFileSystem } = await import("./deno-filesystem.js");
407
- // fs = new DenoFileSystem();
408
- // break;
409
- // }
410
- case "node": {
411
- const { NodeFileSystem: NodeFileSystem2 } = await Promise.resolve().then(() => (init_node_filesystem(), node_filesystem_exports));
412
- fs2 = new NodeFileSystem2();
413
- break;
414
- }
415
- case "sys":
416
- default: {
417
- return getFileSystem(url.build().setParam("fs", "node").URI());
418
- }
452
+ let fs;
453
+ if ((0, import_cement4.runtimeFn)().isDeno) {
454
+ const { DenoFileSystem: DenoFileSystem2 } = await Promise.resolve().then(() => (init_deno_filesystem(), deno_filesystem_exports));
455
+ fs = new DenoFileSystem2();
456
+ } else if ((0, import_cement4.runtimeFn)().isNodeIsh) {
457
+ const { NodeFileSystem: NodeFileSystem2 } = await Promise.resolve().then(() => (init_node_filesystem(), node_filesystem_exports));
458
+ fs = new NodeFileSystem2();
459
+ } else {
460
+ throw new Error("unsupported runtime");
419
461
  }
420
- return fs2.start();
462
+ return fs.start();
421
463
  }
422
464
  function getPath(url, sthis) {
423
465
  const basePath = url.pathname;
424
- const name = url.getParam("name");
466
+ const name = url.getParam("name" /* NAME */);
425
467
  if (name) {
426
- const version = url.getParam("version");
427
- if (!version) throw sthis.logger.Error().Url(url).Msg(`version not found`).AsError();
428
- return sthis.pathOps.join(basePath, version, name);
468
+ return sthis.pathOps.join(basePath, name);
429
469
  }
430
470
  return sthis.pathOps.join(basePath);
431
471
  }
432
472
  function getFileName(url, sthis) {
433
- const key = url.getParam("key");
473
+ const key = url.getParam("key" /* KEY */);
434
474
  if (!key) throw sthis.logger.Error().Url(url).Msg(`key not found`).AsError();
435
475
  const res = getStore(url, sthis, (...a) => a.join("-"));
436
476
  switch (res.store) {
437
- case "data":
438
- return sthis.pathOps.join(res.name, key + ".car");
477
+ case "data": {
478
+ return sthis.pathOps.join(res.name, key + (url.getParam("suffix" /* SUFFIX */) || ""));
479
+ }
439
480
  case "wal":
440
481
  case "meta":
441
482
  return sthis.pathOps.join(res.name, key + ".json");
@@ -454,10 +495,13 @@ function toArrayBuffer(buffer) {
454
495
  }
455
496
  return view;
456
497
  }
498
+ var import_cement4;
457
499
  var init_utils2 = __esm({
458
500
  "src/runtime/gateways/file/utils.ts"() {
459
501
  "use strict";
502
+ import_cement4 = require("@adviser/cement");
460
503
  init_utils();
504
+ init_types();
461
505
  }
462
506
  });
463
507
 
@@ -572,6 +616,40 @@ var init_key_bag_indexdb = __esm({
572
616
  }
573
617
  });
574
618
 
619
+ // src/runtime/key-bag-memory.ts
620
+ var key_bag_memory_exports = {};
621
+ __export(key_bag_memory_exports, {
622
+ KeyBagProviderMemory: () => KeyBagProviderMemory
623
+ });
624
+ var memoryKeyBag, KeyBagProviderMemory;
625
+ var init_key_bag_memory = __esm({
626
+ "src/runtime/key-bag-memory.ts"() {
627
+ "use strict";
628
+ memoryKeyBag = /* @__PURE__ */ new Map();
629
+ KeyBagProviderMemory = class {
630
+ constructor(url, sthis) {
631
+ this.url = url;
632
+ this.sthis = sthis;
633
+ }
634
+ key(id) {
635
+ return `${this.url.pathname}/${id}`;
636
+ }
637
+ async get(id) {
638
+ const binKeyItem = memoryKeyBag.get(this.key(id));
639
+ if (binKeyItem) {
640
+ const ki = JSON.parse(this.sthis.txt.decode(binKeyItem));
641
+ return ki;
642
+ }
643
+ return void 0;
644
+ }
645
+ async set(id, item) {
646
+ const p = this.sthis.txt.encode(JSON.stringify(item, null, 2));
647
+ memoryKeyBag.set(this.key(id), p);
648
+ }
649
+ };
650
+ }
651
+ });
652
+
575
653
  // src/runtime/gateways/file/version.ts
576
654
  var FILESTORE_VERSION;
577
655
  var init_version = __esm({
@@ -587,23 +665,24 @@ __export(gateway_exports, {
587
665
  FileGateway: () => FileGateway,
588
666
  FileTestStore: () => FileTestStore
589
667
  });
590
- var import_cement11, versionFiles, FileGateway, FileTestStore;
668
+ var import_cement12, versionFiles, FileGateway, FileTestStore;
591
669
  var init_gateway = __esm({
592
670
  "src/runtime/gateways/file/gateway.ts"() {
593
671
  "use strict";
594
672
  init_version();
595
- import_cement11 = require("@adviser/cement");
673
+ import_cement12 = require("@adviser/cement");
596
674
  init_utils();
597
675
  init_utils2();
598
- versionFiles = new import_cement11.KeyedResolvOnce();
676
+ init_types();
677
+ versionFiles = new import_cement12.KeyedResolvOnce();
599
678
  FileGateway = class {
600
679
  get fs() {
601
680
  if (!this._fs) throw this.logger.Error().Msg("fs not initialized").AsError();
602
681
  return this._fs;
603
682
  }
604
683
  constructor(sthis) {
605
- this.sthis = sthis;
606
- this.logger = sthis.logger;
684
+ this.sthis = ensureSuperLog(sthis, "FileGateway", { this: 1 });
685
+ this.logger = this.sthis.logger;
607
686
  }
608
687
  async getVersionFromFile(path, logger) {
609
688
  return versionFiles.get(path).once(async () => {
@@ -625,34 +704,33 @@ var init_gateway = __esm({
625
704
  });
626
705
  }
627
706
  start(baseURL) {
628
- return (0, import_cement11.exception2Result)(async () => {
707
+ return (0, import_cement12.exception2Result)(async () => {
629
708
  this._fs = await getFileSystem(baseURL);
630
- await this.fs.start();
631
709
  const url = baseURL.build();
632
- url.defParam("version", FILESTORE_VERSION);
710
+ url.defParam("version" /* VERSION */, FILESTORE_VERSION);
633
711
  const dbUrl = await this.buildUrl(url.URI(), "dummy");
634
712
  const dbdirFile = this.getFilePath(dbUrl.Ok());
635
713
  await this.fs.mkdir(this.sthis.pathOps.dirname(dbdirFile), { recursive: true });
636
714
  const dbroot = this.sthis.pathOps.dirname(dbdirFile);
637
715
  this.logger.Debug().Url(url.URI()).Str("dbroot", dbroot).Msg("start");
638
- url.setParam("version", await this.getVersionFromFile(dbroot, this.logger));
716
+ url.setParam("version" /* VERSION */, await this.getVersionFromFile(dbroot, this.logger));
639
717
  return url.URI();
640
718
  });
641
719
  }
642
720
  async buildUrl(baseUrl, key) {
643
- return import_cement11.Result.Ok(baseUrl.build().setParam("key", key).URI());
721
+ return import_cement12.Result.Ok(baseUrl.build().setParam("key" /* KEY */, key).URI());
644
722
  }
645
723
  async close() {
646
- return import_cement11.Result.Ok(void 0);
724
+ return import_cement12.Result.Ok(void 0);
647
725
  }
648
726
  // abstract buildUrl(baseUrl: URL, key: string): Promise<Result<URL>>;
649
727
  getFilePath(url) {
650
- const key = url.getParam("key");
728
+ const key = url.getParam("key" /* KEY */);
651
729
  if (!key) throw this.logger.Error().Url(url).Msg(`key not found`).AsError();
652
730
  return this.sthis.pathOps.join(getPath(url, this.sthis), getFileName(url, this.sthis));
653
731
  }
654
732
  async put(url, body) {
655
- return (0, import_cement11.exception2Result)(async () => {
733
+ return (0, import_cement12.exception2Result)(async () => {
656
734
  const file = await this.getFilePath(url);
657
735
  this.logger.Debug().Str("url", url.toString()).Str("file", file).Msg("put");
658
736
  await this.fs.writefile(file, body);
@@ -662,19 +740,19 @@ var init_gateway = __esm({
662
740
  return exceptionWrapper(async () => {
663
741
  const file = this.getFilePath(url);
664
742
  try {
743
+ this.logger.Debug().Url(url).Str("file", file).Msg("get");
665
744
  const res = await this.fs.readfile(file);
666
- this.logger.Debug().Url(url.asURL()).Str("file", file).Msg("get");
667
- return import_cement11.Result.Ok(new Uint8Array(res));
745
+ return import_cement12.Result.Ok(new Uint8Array(res));
668
746
  } catch (e) {
669
747
  if (isNotFoundError(e)) {
670
- return import_cement11.Result.Err(new NotFoundError(`file not found: ${file}`));
748
+ return import_cement12.Result.Err(new NotFoundError(`file not found: ${file}`));
671
749
  }
672
- return import_cement11.Result.Err(e);
750
+ return import_cement12.Result.Err(e);
673
751
  }
674
752
  });
675
753
  }
676
754
  async delete(url) {
677
- return (0, import_cement11.exception2Result)(async () => {
755
+ return (0, import_cement12.exception2Result)(async () => {
678
756
  await this.fs.unlink(this.getFilePath(url));
679
757
  });
680
758
  }
@@ -700,7 +778,7 @@ var init_gateway = __esm({
700
778
  }
701
779
  }
702
780
  }
703
- return import_cement11.Result.Ok(void 0);
781
+ return import_cement12.Result.Ok(void 0);
704
782
  }
705
783
  };
706
784
  FileTestStore = class {
@@ -709,7 +787,7 @@ var init_gateway = __esm({
709
787
  this.sthis = sthis;
710
788
  }
711
789
  async get(iurl, key) {
712
- const url = iurl.build().setParam("key", key).URI();
790
+ const url = iurl.build().setParam("key" /* KEY */, key).URI();
713
791
  const dbFile = this.sthis.pathOps.join(getPath(url, this.sthis), getFileName(url, this.sthis));
714
792
  this.logger.Debug().Url(url).Str("dbFile", dbFile).Msg("get");
715
793
  const buffer = await (await getFileSystem(url)).readfile(dbFile);
@@ -737,7 +815,7 @@ __export(gateway_exports2, {
737
815
  getIndexDBName: () => getIndexDBName
738
816
  });
739
817
  function ensureVersion(url) {
740
- return url.build().defParam("version", INDEXDB_VERSION).URI();
818
+ return url.build().defParam("version" /* VERSION */, INDEXDB_VERSION).URI();
741
819
  }
742
820
  function sanitzeKey(key) {
743
821
  if (key.length === 1) {
@@ -758,17 +836,17 @@ async function connectIdb(url, sthis) {
758
836
  }
759
837
  });
760
838
  const found = await db.get("version", "version");
761
- const version = ensureVersion(url).getParam("version");
839
+ const version = ensureVersion(url).getParam("version" /* VERSION */);
762
840
  if (!found) {
763
841
  await db.put("version", { version }, "version");
764
842
  } else if (found.version !== version) {
765
- sthis.logger.Warn().Str("url", url.toString()).Str("version", version).Str("found", found.version).Msg("version mismatch");
843
+ sthis.logger.Warn().Url(url).Str("version", version).Str("found", found.version).Msg("version mismatch");
766
844
  }
767
845
  return { db, dbName, version, url };
768
846
  });
769
847
  return {
770
848
  ...once,
771
- url: url.build().setParam("version", once.version).URI()
849
+ url: url.build().setParam("version" /* VERSION */, once.version).URI()
772
850
  };
773
851
  }
774
852
  function joinDBName(...names) {
@@ -777,7 +855,7 @@ function joinDBName(...names) {
777
855
  function getIndexDBName(iurl, sthis) {
778
856
  const url = ensureVersion(iurl);
779
857
  const fullDb = url.pathname.replace(/^\/+/, "").replace(/\?.*$/, "");
780
- const dbName = url.getParam("name");
858
+ const dbName = url.getParam("name" /* NAME */);
781
859
  if (!dbName) throw sthis.logger.Error().Str("url", url.toString()).Msg(`name not found`).AsError();
782
860
  const result = joinDBName(fullDb, dbName);
783
861
  const objStore = getStore(url, sthis, joinDBName).name;
@@ -789,15 +867,16 @@ function getIndexDBName(iurl, sthis) {
789
867
  dbName
790
868
  };
791
869
  }
792
- var import_idb2, import_cement12, onceIndexDB, IndexDBGateway, IndexDBTestStore;
870
+ var import_idb2, import_cement13, onceIndexDB, IndexDBGateway, IndexDBTestStore;
793
871
  var init_gateway2 = __esm({
794
872
  "src/runtime/gateways/indexdb/gateway.ts"() {
795
873
  "use strict";
796
874
  import_idb2 = require("idb");
797
- import_cement12 = require("@adviser/cement");
875
+ import_cement13 = require("@adviser/cement");
798
876
  init_version2();
799
877
  init_utils();
800
- onceIndexDB = new import_cement12.KeyedResolvOnce();
878
+ init_types();
879
+ onceIndexDB = new import_cement13.KeyedResolvOnce();
801
880
  IndexDBGateway = class {
802
881
  constructor(sthis) {
803
882
  this._db = {};
@@ -805,7 +884,7 @@ var init_gateway2 = __esm({
805
884
  this.sthis = sthis;
806
885
  }
807
886
  async start(baseURL) {
808
- return (0, import_cement12.exception2Result)(async () => {
887
+ return (0, import_cement13.exception2Result)(async () => {
809
888
  this.logger.Debug().Url(baseURL).Msg("starting");
810
889
  await this.sthis.start();
811
890
  const ic = await connectIdb(baseURL, this.sthis);
@@ -815,10 +894,10 @@ var init_gateway2 = __esm({
815
894
  });
816
895
  }
817
896
  async close() {
818
- return import_cement12.Result.Ok(void 0);
897
+ return import_cement13.Result.Ok(void 0);
819
898
  }
820
899
  async destroy(baseUrl) {
821
- return (0, import_cement12.exception2Result)(async () => {
900
+ return (0, import_cement13.exception2Result)(async () => {
822
901
  const type = getStore(baseUrl, this.sthis, joinDBName).name;
823
902
  const idb = this._db;
824
903
  const trans = idb.transaction(type, "readwrite");
@@ -834,7 +913,7 @@ var init_gateway2 = __esm({
834
913
  });
835
914
  }
836
915
  buildUrl(baseUrl, key) {
837
- return Promise.resolve(import_cement12.Result.Ok(baseUrl.build().setParam("key", key).URI()));
916
+ return Promise.resolve(import_cement13.Result.Ok(baseUrl.build().setParam("key" /* KEY */, key).URI()));
838
917
  }
839
918
  async get(url) {
840
919
  return exceptionWrapper(async () => {
@@ -845,13 +924,13 @@ var init_gateway2 = __esm({
845
924
  const bytes = await tx.objectStore(store).get(sanitzeKey(key));
846
925
  await tx.done;
847
926
  if (!bytes) {
848
- return import_cement12.Result.Err(new NotFoundError(`missing ${key}`));
927
+ return import_cement13.Result.Err(new NotFoundError(`missing ${key}`));
849
928
  }
850
- return import_cement12.Result.Ok(bytes);
929
+ return import_cement13.Result.Ok(bytes);
851
930
  });
852
931
  }
853
932
  async put(url, value) {
854
- return (0, import_cement12.exception2Result)(async () => {
933
+ return (0, import_cement13.exception2Result)(async () => {
855
934
  const key = getKey(url, this.logger);
856
935
  const store = getStore(url, this.sthis, joinDBName).name;
857
936
  this.logger.Debug().Url(url).Str("key", key).Str("store", store).Msg("putting");
@@ -861,14 +940,14 @@ var init_gateway2 = __esm({
861
940
  });
862
941
  }
863
942
  async delete(url) {
864
- return (0, import_cement12.exception2Result)(async () => {
943
+ return (0, import_cement13.exception2Result)(async () => {
865
944
  const key = getKey(url, this.logger);
866
945
  const store = getStore(url, this.sthis, joinDBName).name;
867
946
  this.logger.Debug().Url(url).Str("key", key).Str("store", store).Msg("deleting");
868
947
  const tx = this._db.transaction([store], "readwrite");
869
948
  await tx.objectStore(store).delete(sanitzeKey(key));
870
949
  await tx.done;
871
- return import_cement12.Result.Ok(void 0);
950
+ return import_cement13.Result.Ok(void 0);
872
951
  });
873
952
  }
874
953
  };
@@ -896,15 +975,16 @@ var init_gateway2 = __esm({
896
975
  var src_exports = {};
897
976
  __export(src_exports, {
898
977
  CRDT: () => CRDT,
899
- Database: () => Database,
978
+ DatabaseFactory: () => DatabaseFactory,
979
+ DatabaseShell: () => DatabaseShell,
900
980
  Index: () => Index,
901
981
  NotFoundError: () => NotFoundError,
902
982
  PACKAGE_VERSION: () => PACKAGE_VERSION,
983
+ PARAM: () => PARAM,
903
984
  Result: () => import_cement.Result,
904
985
  UInt8ArrayEqual: () => UInt8ArrayEqual,
905
986
  blockstore: () => blockstore_exports,
906
987
  bs: () => blockstore_exports,
907
- dataDir: () => dataDir,
908
988
  ensureLogger: () => ensureLogger,
909
989
  ensureSuperLog: () => ensureSuperLog,
910
990
  ensureSuperThis: () => ensureSuperThis,
@@ -915,16 +995,19 @@ __export(src_exports, {
915
995
  getName: () => getName,
916
996
  getStore: () => getStore,
917
997
  index: () => index,
998
+ isDatabase: () => isDatabase,
918
999
  isFalsy: () => isFalsy,
919
1000
  isNotFoundError: () => isNotFoundError,
1001
+ keyConfigOpts: () => keyConfigOpts,
920
1002
  rt: () => runtime_exports,
921
1003
  runtime: () => runtime_exports,
922
- throwFalsy: () => throwFalsy
1004
+ throwFalsy: () => throwFalsy,
1005
+ toStoreURIRuntime: () => toStoreURIRuntime
923
1006
  });
924
1007
  module.exports = __toCommonJS(src_exports);
925
1008
 
926
1009
  // src/database.ts
927
- var import_cement16 = require("@adviser/cement");
1010
+ var import_cement18 = require("@adviser/cement");
928
1011
 
929
1012
  // src/write-queue.ts
930
1013
  function writeQueue(worker, payload = Infinity, unbounded = false) {
@@ -967,86 +1050,7 @@ function writeQueue(worker, payload = Infinity, unbounded = false) {
967
1050
  }
968
1051
 
969
1052
  // src/crdt.ts
970
- var import_cement15 = require("@adviser/cement");
971
-
972
- // src/runtime/wait-pr-multiformats/block.ts
973
- var block_exports = {};
974
- __export(block_exports, {
975
- Block: () => Block,
976
- create: () => create,
977
- createUnsafe: () => createUnsafe,
978
- decode: () => decode,
979
- encode: () => encode
980
- });
981
- var import_multiformats = require("multiformats");
982
- var import_block = require("multiformats/block");
983
- var Block = import_block.Block;
984
- async function decode({
985
- bytes,
986
- codec: codec3,
987
- hasher: hasher7
988
- }) {
989
- if (bytes == null) throw new Error('Missing required argument "bytes"');
990
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
991
- const value = await Promise.resolve(codec3.decode(bytes));
992
- const hash = await hasher7.digest(bytes);
993
- const cid = import_multiformats.CID.create(1, codec3.code, hash);
994
- return new import_block.Block({ value, bytes, cid });
995
- }
996
- async function encode({
997
- value,
998
- codec: codec3,
999
- hasher: hasher7
1000
- }) {
1001
- if (typeof value === "undefined") throw new Error('Missing required argument "value"');
1002
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1003
- const bytes = await Promise.resolve(codec3.encode(value));
1004
- const hash = await hasher7.digest(bytes);
1005
- const cid = import_multiformats.CID.create(1, codec3.code, hash);
1006
- return new import_block.Block({ value, bytes, cid });
1007
- }
1008
- async function create({
1009
- bytes,
1010
- cid,
1011
- hasher: hasher7,
1012
- codec: codec3
1013
- }) {
1014
- if (bytes == null) throw new Error('Missing required argument "bytes"');
1015
- if (hasher7 == null) throw new Error('Missing required argument "hasher"');
1016
- const value = await Promise.resolve(codec3.decode(bytes));
1017
- const hash = await hasher7.digest(bytes);
1018
- if (!import_multiformats.bytes.equals(cid.multihash.bytes, hash.bytes)) {
1019
- throw new Error("CID hash does not match bytes");
1020
- }
1021
- return createUnsafe({
1022
- bytes,
1023
- cid,
1024
- value,
1025
- codec: codec3
1026
- });
1027
- }
1028
- async function createUnsafe({
1029
- bytes,
1030
- cid,
1031
- value: maybeValue,
1032
- codec: codec3
1033
- }) {
1034
- const value = await Promise.resolve(maybeValue !== void 0 ? maybeValue : codec3?.decode(bytes));
1035
- if (value === void 0) throw new Error('Missing required argument, must either provide "value" or "codec"');
1036
- return new Block({
1037
- cid,
1038
- bytes,
1039
- value
1040
- });
1041
- }
1042
-
1043
- // src/crdt-helpers.ts
1044
- var import_link = require("multiformats/link");
1045
- var import_sha25 = require("multiformats/hashes/sha2");
1046
- var codec = __toESM(require("@ipld/dag-cbor"), 1);
1047
- var import_crdt = require("@web3-storage/pail/crdt");
1048
- var import_clock2 = require("@web3-storage/pail/clock");
1049
- var Batch = __toESM(require("@web3-storage/pail/crdt/batch"), 1);
1053
+ var import_cement17 = require("@adviser/cement");
1050
1054
 
1051
1055
  // src/blockstore/index.ts
1052
1056
  var blockstore_exports = {};
@@ -1059,8 +1063,11 @@ __export(blockstore_exports, {
1059
1063
  FragmentGateway: () => FragmentGateway,
1060
1064
  Loader: () => Loader,
1061
1065
  addCryptoKeyToGatewayMetaPayload: () => addCryptoKeyToGatewayMetaPayload,
1062
- ensureStart: () => ensureStart,
1063
- getGatewayFromURL: () => getGatewayFromURL,
1066
+ ensureStoreEnDeFile: () => ensureStoreEnDeFile,
1067
+ fileGatewayFactoryItem: () => fileGatewayFactoryItem,
1068
+ getDefaultURI: () => getDefaultURI,
1069
+ getGatewayFactoryItem: () => getGatewayFactoryItem,
1070
+ getStartedGateway: () => getStartedGateway,
1064
1071
  parseCarFile: () => parseCarFile,
1065
1072
  registerStoreProtocol: () => registerStoreProtocol,
1066
1073
  setCryptoKeyFromGatewayMetaPayload: () => setCryptoKeyFromGatewayMetaPayload,
@@ -1075,7 +1082,7 @@ function toCIDBlock(block) {
1075
1082
  }
1076
1083
 
1077
1084
  // src/blockstore/store-factory.ts
1078
- var import_cement13 = require("@adviser/cement");
1085
+ var import_cement15 = require("@adviser/cement");
1079
1086
 
1080
1087
  // src/runtime/files.ts
1081
1088
  var files_exports = {};
@@ -1149,261 +1156,26 @@ var UnixFSFileBuilder = class {
1149
1156
  };
1150
1157
 
1151
1158
  // src/blockstore/store.ts
1152
- var import_p_limit2 = __toESM(require("p-limit"), 1);
1153
1159
  var import_dag_json2 = require("@ipld/dag-json");
1154
1160
  var import_cement10 = require("@adviser/cement");
1155
-
1156
- // src/types.ts
1157
- function isFalsy(value) {
1158
- return value === false && value === null && value === void 0;
1159
- }
1160
- function throwFalsy(value) {
1161
- if (isFalsy(value)) {
1162
- throw new Error("value is Falsy");
1163
- }
1164
- return value;
1165
- }
1166
- function falsyToUndef(value) {
1167
- if (isFalsy(value)) {
1168
- return void 0;
1169
- }
1170
- return value;
1171
- }
1172
-
1173
- // src/blockstore/store.ts
1161
+ init_types();
1174
1162
  init_utils();
1175
1163
 
1176
- // src/blockstore/loader.ts
1177
- var import_p_limit = __toESM(require("p-limit"), 1);
1178
- var import_car = require("@ipld/car");
1179
- var import_cement7 = require("@adviser/cement");
1180
-
1181
- // src/blockstore/loader-helpers.ts
1182
- var import_sha2 = require("multiformats/hashes/sha2");
1183
- var dagCodec = __toESM(require("@ipld/dag-cbor"), 1);
1184
- async function parseCarFile(reader, logger) {
1185
- const roots = await reader.getRoots();
1186
- const header = await reader.get(roots[0]);
1187
- if (!header) throw logger.Error().Msg("missing header block").AsError();
1188
- const dec = await decode({ bytes: header.bytes, hasher: import_sha2.sha256, codec: dagCodec });
1189
- const fpvalue = dec.value;
1190
- if (fpvalue && !fpvalue.fp) {
1191
- throw logger.Error().Msg("missing fp").AsError();
1192
- }
1193
- return fpvalue.fp;
1194
- }
1195
-
1196
- // src/blockstore/transaction.ts
1197
- var import_block3 = require("@web3-storage/pail/block");
1164
+ // src/blockstore/commit-queue.ts
1198
1165
  var import_cement2 = require("@adviser/cement");
1199
- init_utils();
1200
- var CarTransaction = class extends import_block3.MemoryBlockstore {
1201
- constructor(parent, opts = { add: true, noLoader: false }) {
1202
- super();
1203
- if (opts.add) {
1204
- parent.transactions.add(this);
1205
- }
1206
- this.parent = parent;
1207
- }
1208
- async get(cid) {
1209
- return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
1210
- }
1211
- async superGet(cid) {
1212
- return super.get(cid);
1166
+ var CommitQueue = class {
1167
+ constructor() {
1168
+ this.queue = [];
1169
+ this.processing = false;
1170
+ this._waitIdleItems = /* @__PURE__ */ new Set();
1213
1171
  }
1214
- };
1215
- function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
1216
- const logger = ensureLogger(sthis, component, ctx);
1217
- const store = opts.store || {};
1218
- return {
1219
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1220
- applyMeta: (meta, snap) => {
1172
+ waitIdle() {
1173
+ if (this.queue.length === 0 && !this.processing) {
1221
1174
  return Promise.resolve();
1222
- },
1223
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1224
- compact: async (blocks) => {
1225
- return {};
1226
- },
1227
- autoCompact: 100,
1228
- public: false,
1229
- name: void 0,
1230
- threshold: 1e3 * 1e3,
1231
- ...opts,
1232
- logger,
1233
- keyBag: opts.keyBag || {},
1234
- crypto: (0, import_cement2.toCryptoRuntime)(opts.crypto),
1235
- store,
1236
- storeRuntime: toStoreRuntime(store, sthis)
1237
- };
1238
- }
1239
- function blockstoreFactory(sthis, opts) {
1240
- if (opts.name) {
1241
- return new EncryptedBlockstore(sthis, opts);
1242
- } else {
1243
- return new BaseBlockstore(opts);
1244
- }
1245
- }
1246
- var BaseBlockstore = class {
1247
- constructor(ebOpts = {}) {
1248
- this.transactions = /* @__PURE__ */ new Set();
1249
- this.sthis = ensureSuperThis(ebOpts);
1250
- this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
1251
- this.logger = this.ebOpts.logger;
1252
- }
1253
- // ready: Promise<void>;
1254
- ready() {
1255
- return Promise.resolve();
1256
- }
1257
- async close() {
1258
- }
1259
- async destroy() {
1260
- }
1261
- async get(cid) {
1262
- if (!cid) throw this.logger.Error().Msg("required cid").AsError();
1263
- for (const f of this.transactions) {
1264
- const v = await f.superGet(cid);
1265
- if (v) return v;
1266
- }
1267
- }
1268
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1269
- async put(cid, block) {
1270
- throw this.logger.Error().Msg("use a transaction to put").AsError();
1271
- }
1272
- // TransactionMeta
1273
- async transaction(fn, _opts) {
1274
- const t = new CarTransaction(this, _opts);
1275
- const done = await fn(t);
1276
- this.lastTxMeta = done;
1277
- return { t, meta: done };
1278
- }
1279
- async *entries() {
1280
- const seen = /* @__PURE__ */ new Set();
1281
- for (const t of this.transactions) {
1282
- for await (const blk of t.entries()) {
1283
- if (seen.has(blk.cid.toString())) continue;
1284
- seen.add(blk.cid.toString());
1285
- yield blk;
1286
- }
1287
- }
1288
- }
1289
- };
1290
- var EncryptedBlockstore = class extends BaseBlockstore {
1291
- constructor(sthis, ebOpts) {
1292
- super(ebOpts);
1293
- this.compacting = false;
1294
- this.logger = ensureLogger(this.sthis, "EncryptedBlockstore");
1295
- const { name } = ebOpts;
1296
- if (!name) {
1297
- throw this.logger.Error().Msg("name required").AsError();
1298
- }
1299
- this.name = name;
1300
- this.loader = new Loader(this.name, ebOpts, sthis);
1301
- }
1302
- ready() {
1303
- return this.loader.ready();
1304
- }
1305
- close() {
1306
- return this.loader.close();
1307
- }
1308
- destroy() {
1309
- return this.loader.destroy();
1310
- }
1311
- async get(cid) {
1312
- const got = await super.get(cid);
1313
- if (got) return got;
1314
- if (!this.loader) {
1315
- return;
1316
- }
1317
- return falsyToUndef(await this.loader.getBlock(cid));
1318
- }
1319
- async transaction(fn, opts = { noLoader: false }) {
1320
- const { t, meta: done } = await super.transaction(fn);
1321
- const cars = await this.loader.commit(t, done, opts);
1322
- if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
1323
- setTimeout(() => void this.compact(), 10);
1324
- }
1325
- if (cars) {
1326
- this.transactions.delete(t);
1327
- return { meta: done, cars, t };
1328
- }
1329
- throw this.logger.Error().Msg("failed to commit car files").AsError();
1330
- }
1331
- async getFile(car, cid) {
1332
- await this.ready();
1333
- if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
1334
- const reader = await this.loader.loadFileCar(
1335
- car
1336
- /*, isPublic */
1337
- );
1338
- const block = await reader.get(cid);
1339
- if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
1340
- return block.bytes;
1341
- }
1342
- async compact() {
1343
- await this.ready();
1344
- if (!this.loader) throw this.logger.Error().Msg("loader required to compact").AsError();
1345
- if (this.loader.carLog.length < 2) return;
1346
- const compactFn = this.ebOpts.compact || ((blocks) => this.defaultCompact(blocks, this.logger));
1347
- if (!compactFn || this.compacting) return;
1348
- const blockLog = new CompactionFetcher(this);
1349
- this.compacting = true;
1350
- const meta = await compactFn(blockLog);
1351
- await this.loader?.commit(blockLog.loggedBlocks, meta, {
1352
- compact: true,
1353
- noLoader: true
1354
- });
1355
- this.compacting = false;
1356
- }
1357
- async defaultCompact(blocks, logger) {
1358
- if (!this.loader) {
1359
- throw logger.Error().Msg("no loader").AsError();
1360
- }
1361
- if (!this.lastTxMeta) {
1362
- throw logger.Error().Msg("no lastTxMeta").AsError();
1363
- }
1364
- for await (const blk of this.loader.entries(false)) {
1365
- blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
1366
- }
1367
- for (const t of this.transactions) {
1368
- for await (const blk of t.entries()) {
1369
- blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
1370
- }
1371
- }
1372
- return this.lastTxMeta;
1373
- }
1374
- async *entries() {
1375
- for await (const blk of this.loader.entries()) {
1376
- yield blk;
1377
- }
1378
- }
1379
- };
1380
- var CompactionFetcher = class {
1381
- constructor(blocks) {
1382
- this.blockstore = blocks;
1383
- this.loggedBlocks = new CarTransaction(blocks);
1384
- }
1385
- async get(cid) {
1386
- const block = await this.blockstore.get(cid);
1387
- if (block) this.loggedBlocks.putSync(cid, block.bytes);
1388
- return falsyToUndef(block);
1389
- }
1390
- };
1391
-
1392
- // src/blockstore/commit-queue.ts
1393
- var import_cement3 = require("@adviser/cement");
1394
- var CommitQueue = class {
1395
- constructor() {
1396
- this.queue = [];
1397
- this.processing = false;
1398
- this._waitIdleItems = /* @__PURE__ */ new Set();
1399
- }
1400
- waitIdle() {
1401
- if (this.queue.length === 0 && !this.processing) {
1402
- return Promise.resolve();
1403
- }
1404
- const fn = new import_cement3.Future();
1405
- this._waitIdleItems.add(fn);
1406
- return fn.asPromise();
1175
+ }
1176
+ const fn = new import_cement2.Future();
1177
+ this._waitIdleItems.add(fn);
1178
+ return fn.asPromise();
1407
1179
  }
1408
1180
  async enqueue(fn) {
1409
1181
  return new Promise((resolve, reject) => {
@@ -1440,1128 +1212,1499 @@ var CommitQueue = class {
1440
1212
  }
1441
1213
  };
1442
1214
 
1443
- // src/runtime/key-bag.ts
1444
- var key_bag_exports = {};
1445
- __export(key_bag_exports, {
1446
- KeyBag: () => KeyBag,
1447
- getKeyBag: () => getKeyBag,
1448
- registerKeyBagProviderFactory: () => registerKeyBagProviderFactory
1215
+ // src/runtime/keyed-crypto.ts
1216
+ var keyed_crypto_exports = {};
1217
+ __export(keyed_crypto_exports, {
1218
+ BlockIvKeyIdCodec: () => BlockIvKeyIdCodec,
1219
+ keyedCryptoFactory: () => keyedCryptoFactory
1449
1220
  });
1450
- var import_cement6 = require("@adviser/cement");
1451
1221
  init_utils();
1452
1222
  var import_base582 = require("multiformats/bases/base58");
1453
- var KeyBag = class {
1454
- constructor(rt) {
1455
- this.rt = rt;
1456
- this._warnOnce = new import_cement6.ResolveOnce();
1457
- this._seq = new import_cement6.ResolveSeq();
1458
- this.logger = ensureLogger(rt.sthis, "KeyBag");
1459
- this.logger.Debug().Msg("KeyBag created");
1460
- }
1461
- async subtleKey(key) {
1462
- const extractable = this.rt.url.getParam("extractKey") === "_deprecated_internal_api";
1463
- if (extractable) {
1464
- this._warnOnce.once(
1465
- () => this.logger.Warn().Msg("extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!")
1466
- );
1467
- }
1468
- return await this.rt.crypto.importKey(
1469
- "raw",
1470
- // raw or jwk
1471
- import_base582.base58btc.decode(key),
1472
- // hexStringToUint8Array(key), // raw data
1473
- "AES-GCM",
1474
- extractable,
1475
- ["encrypt", "decrypt"]
1476
- );
1477
- }
1478
- async ensureKeyFromUrl(url, keyFactory) {
1479
- const storeKey = url.getParam("storekey");
1480
- if (storeKey === "insecure") {
1481
- return import_cement6.Result.Ok(url);
1482
- }
1483
- if (!storeKey) {
1484
- const keyName = `@${keyFactory()}@`;
1485
- const ret = await this.getNamedKey(keyName);
1486
- if (ret.isErr()) {
1487
- return ret;
1488
- }
1489
- const urb = url.build().setParam("storekey", keyName);
1490
- return import_cement6.Result.Ok(urb.URI());
1223
+ var import_sha2 = require("multiformats/hashes/sha2");
1224
+ var CBOR = __toESM(require("cborg"), 1);
1225
+ init_types();
1226
+ var generateIV = {
1227
+ random: {
1228
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1229
+ calc: async (ko, crypto, data) => {
1230
+ return crypto.randomBytes(ko.ivLength);
1231
+ },
1232
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1233
+ verify: async (ko, crypto, iv, data) => {
1234
+ return true;
1491
1235
  }
1492
- if (storeKey.startsWith("@") && storeKey.endsWith("@")) {
1493
- const ret = await this.getNamedKey(storeKey);
1494
- if (ret.isErr()) {
1495
- return ret;
1236
+ },
1237
+ hash: {
1238
+ calc: async (ko, crypto, data) => {
1239
+ const hash = await import_sha2.sha256.digest(data);
1240
+ const hashBytes = new Uint8Array(hash.bytes);
1241
+ const hashArray = new Uint8Array(ko.ivLength);
1242
+ for (let i = 0; i < hashBytes.length; i++) {
1243
+ hashArray[i % ko.ivLength] ^= hashBytes[i];
1496
1244
  }
1245
+ return hashArray;
1246
+ },
1247
+ verify: async function(ko, crypto, iv, data) {
1248
+ return ko.url.getParam("ivVerify" /* IV_VERIFY */) !== "disable" && UInt8ArrayEqual(iv, await this.calc(ko, crypto, data));
1497
1249
  }
1498
- return import_cement6.Result.Ok(url);
1499
1250
  }
1500
- async toKeyWithFingerPrint(keyStr) {
1501
- const material = import_base582.base58btc.decode(keyStr);
1502
- const key = await this.subtleKey(keyStr);
1503
- const fpr = await this.rt.crypto.digestSHA256(material);
1504
- return import_cement6.Result.Ok({
1505
- key,
1506
- fingerPrint: import_base582.base58btc.encode(new Uint8Array(fpr))
1251
+ };
1252
+ function getGenerateIVFn(url, opts) {
1253
+ const ivhash = opts.ivCalc || url.getParam("ivHash" /* IV_HASH */) || "hash";
1254
+ return generateIV[ivhash] || generateIV["hash"];
1255
+ }
1256
+ var BlockIvKeyIdCodec = class {
1257
+ constructor(ko, iv, opts) {
1258
+ this.code = 3147065;
1259
+ this.name = "Fireproof@encrypted-block:aes-gcm";
1260
+ this.ko = ko;
1261
+ this.iv = iv;
1262
+ this.opts = opts || {};
1263
+ }
1264
+ async encode(data) {
1265
+ const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
1266
+ const { iv } = this.ko.algo(calcIv);
1267
+ const fprt = await this.ko.fingerPrint();
1268
+ const keyId = import_base582.base58btc.decode(fprt);
1269
+ this.ko.logger.Debug().Str("fp", fprt).Msg("encode");
1270
+ return CBOR.encode({
1271
+ iv,
1272
+ keyId,
1273
+ data: await this.ko._encrypt({ iv, bytes: data })
1507
1274
  });
1508
1275
  }
1509
- async setNamedKey(name, key) {
1510
- return this._seq.add(() => this._setNamedKey(name, key));
1276
+ async decode(abytes) {
1277
+ let bytes;
1278
+ if (abytes instanceof Uint8Array) {
1279
+ bytes = abytes;
1280
+ } else {
1281
+ bytes = new Uint8Array(abytes);
1282
+ }
1283
+ const { iv, keyId, data } = CBOR.decode(bytes);
1284
+ const fprt = await this.ko.fingerPrint();
1285
+ this.ko.logger.Debug().Str("fp", import_base582.base58btc.encode(keyId)).Msg("decode");
1286
+ if (import_base582.base58btc.encode(keyId) !== fprt) {
1287
+ throw this.ko.logger.Error().Str("fp", fprt).Str("keyId", import_base582.base58btc.encode(keyId)).Msg("keyId mismatch").AsError();
1288
+ }
1289
+ const result = await this.ko._decrypt({ iv, bytes: data });
1290
+ if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
1291
+ throw this.ko.logger.Error().Msg("iv missmatch").AsError();
1292
+ }
1293
+ return result;
1511
1294
  }
1512
- // avoid deadlock
1513
- async _setNamedKey(name, key) {
1514
- const item = {
1515
- name,
1516
- key
1295
+ };
1296
+ var keyedCrypto = class {
1297
+ constructor(url, key, cyopt, sthis) {
1298
+ this.ivLength = 12;
1299
+ this.isEncrypting = true;
1300
+ this.logger = ensureLogger(sthis, "keyedCrypto");
1301
+ this.crypto = cyopt;
1302
+ this.key = key;
1303
+ this.url = url;
1304
+ }
1305
+ fingerPrint() {
1306
+ return Promise.resolve(this.key.fingerPrint);
1307
+ }
1308
+ codec(iv, opts) {
1309
+ return new BlockIvKeyIdCodec(this, iv, opts);
1310
+ }
1311
+ algo(iv) {
1312
+ return {
1313
+ name: "AES-GCM",
1314
+ iv: iv || this.crypto.randomBytes(this.ivLength),
1315
+ tagLength: 128
1517
1316
  };
1518
- const bag = await this.rt.getBag();
1519
- this.logger.Debug().Str("name", name).Msg("setNamedKey");
1520
- await bag.set(name, item);
1521
- return await this.toKeyWithFingerPrint(item.key);
1522
1317
  }
1523
- async getNamedExtractableKey(name, failIfNotFound = false) {
1524
- const ret = await this.getNamedKey(name, failIfNotFound);
1525
- if (ret.isErr()) {
1526
- return ret;
1527
- }
1528
- const named = ret.Ok();
1529
- return import_cement6.Result.Ok({
1530
- ...named,
1531
- extract: async () => {
1532
- const ext = new Uint8Array(await this.rt.crypto.exportKey("raw", named.key));
1533
- return {
1534
- key: ext,
1535
- keyStr: import_base582.base58btc.encode(ext)
1536
- };
1537
- }
1538
- });
1318
+ async _decrypt(data) {
1319
+ this.logger.Debug().Len(data.bytes, "bytes").Len(data.iv, "iv").Str("fp", this.key.fingerPrint).Msg("decrypting");
1320
+ return new Uint8Array(await this.crypto.decrypt(this.algo(data.iv), this.key.key, data.bytes));
1539
1321
  }
1540
- async getNamedKey(name, failIfNotFound = false) {
1541
- const id = this.rt.sthis.nextId(4).str;
1542
- return this._seq.add(async () => {
1543
- const bag = await this.rt.getBag();
1544
- const named = await bag.get(name);
1545
- if (named) {
1546
- const fpr = await this.toKeyWithFingerPrint(named.key);
1547
- this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", fpr).Msg("fingerPrint getNamedKey");
1548
- return fpr;
1549
- }
1550
- if (failIfNotFound) {
1551
- this.logger.Debug().Str("id", id).Str("name", name).Msg("failIfNotFound getNamedKey");
1552
- return import_cement6.Result.Err(new Error(`Key not found: ${name}`));
1553
- }
1554
- const ret = await this._setNamedKey(name, import_base582.base58btc.encode(this.rt.crypto.randomBytes(this.rt.keyLength)));
1555
- this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", ret).Msg("createKey getNamedKey-post");
1556
- return ret;
1557
- });
1322
+ async _encrypt(data) {
1323
+ this.logger.Debug().Len(data.bytes).Str("fp", this.key.fingerPrint).Msg("encrypting");
1324
+ const a = this.algo(data.iv);
1325
+ return new Uint8Array(await this.crypto.encrypt(a, this.key.key, data.bytes));
1558
1326
  }
1559
1327
  };
1560
- var keyBagProviderFactories = new Map(
1561
- [
1562
- {
1563
- protocol: "file:",
1564
- factory: async (url, sthis) => {
1565
- const { KeyBagProviderFile: KeyBagProviderFile2 } = await Promise.resolve().then(() => (init_key_bag_file(), key_bag_file_exports));
1566
- return new KeyBagProviderFile2(url, sthis);
1567
- }
1568
- },
1569
- {
1570
- protocol: "indexdb:",
1571
- factory: async (url, sthis) => {
1572
- const { KeyBagProviderIndexDB: KeyBagProviderIndexDB2 } = await Promise.resolve().then(() => (init_key_bag_indexdb(), key_bag_indexdb_exports));
1573
- return new KeyBagProviderIndexDB2(url, sthis);
1574
- }
1575
- }
1576
- ].map((i) => [i.protocol, i])
1577
- );
1578
- function registerKeyBagProviderFactory(item) {
1579
- const protocol = item.protocol.endsWith(":") ? item.protocol : item.protocol + ":";
1580
- keyBagProviderFactories.set(protocol, {
1581
- ...item,
1582
- protocol
1583
- });
1584
- }
1585
- function defaultKeyBagOpts(sthis, kbo) {
1586
- if (kbo.keyRuntime) {
1587
- return kbo.keyRuntime;
1328
+ var nullCodec = class {
1329
+ constructor() {
1330
+ this.code = 0;
1331
+ this.name = "Fireproof@unencrypted-block";
1588
1332
  }
1589
- const logger = ensureLogger(sthis, "KeyBag");
1590
- let url;
1591
- if (kbo.url) {
1592
- url = import_cement6.URI.from(kbo.url);
1593
- logger.Debug().Url(url).Msg("from opts");
1594
- } else {
1595
- let bagFnameOrUrl = sthis.env.get("FP_KEYBAG_URL");
1596
- if ((0, import_cement6.runtimeFn)().isBrowser) {
1597
- url = import_cement6.URI.from(bagFnameOrUrl || "indexdb://fp-keybag");
1598
- } else {
1599
- if (!bagFnameOrUrl) {
1600
- const home = sthis.env.get("HOME");
1601
- bagFnameOrUrl = `${home}/.fireproof/keybag`;
1602
- url = import_cement6.URI.from(`file://${bagFnameOrUrl}`);
1603
- } else {
1604
- url = import_cement6.URI.from(bagFnameOrUrl);
1605
- }
1606
- }
1607
- logger.Debug().Url(url).Msg("from env");
1333
+ encode(data) {
1334
+ return data;
1608
1335
  }
1609
- const kitem = keyBagProviderFactories.get(url.protocol);
1610
- if (!kitem) {
1611
- throw logger.Error().Url(url).Msg("unsupported protocol").AsError();
1336
+ decode(data) {
1337
+ return data;
1612
1338
  }
1613
- const getBag = async () => kitem.factory(url, sthis);
1614
- if (url.hasParam("masterkey")) {
1615
- throw logger.Error().Url(url).Msg("masterkey is not supported").AsError();
1339
+ };
1340
+ var noCrypto = class {
1341
+ constructor(url, cyrt, sthis) {
1342
+ this.ivLength = 0;
1343
+ this.code = 0;
1344
+ this.name = "Fireproof@unencrypted-block";
1345
+ this.isEncrypting = false;
1346
+ this._fingerPrint = "noCrypto:" + Math.random();
1347
+ this.logger = ensureLogger(sthis, "noCrypto");
1348
+ this.crypto = cyrt;
1349
+ this.url = url;
1616
1350
  }
1617
- return {
1618
- url,
1619
- crypto: kbo.crypto || (0, import_cement6.toCryptoRuntime)({}),
1620
- sthis,
1621
- logger,
1622
- keyLength: kbo.keyLength || 16,
1623
- getBag,
1624
- id: () => {
1625
- return url.toString();
1626
- }
1627
- };
1628
- }
1629
- var _keyBags = new import_cement6.KeyedResolvOnce();
1630
- async function getKeyBag(sthis, kbo = {}) {
1631
- await sthis.start();
1632
- const rt = defaultKeyBagOpts(sthis, kbo);
1633
- return _keyBags.get(rt.id()).once(async () => new KeyBag(rt));
1634
- }
1635
-
1636
- // src/blockstore/commitor.ts
1637
- var CBW = __toESM(require("@ipld/car/buffer-writer"), 1);
1638
- var import_sha22 = require("multiformats/hashes/sha2");
1639
- var dagCodec2 = __toESM(require("@ipld/dag-cbor"), 1);
1640
- async function encodeCarFile(roots, t, codec3) {
1641
- let size = 0;
1642
- const headerSize = CBW.headerLength({ roots });
1643
- size += headerSize;
1644
- for (const { cid, bytes } of t.entries()) {
1645
- size += CBW.blockLength({ cid, bytes });
1351
+ fingerPrint() {
1352
+ return Promise.resolve(this._fingerPrint);
1646
1353
  }
1647
- const buffer = new Uint8Array(size);
1648
- const writer = CBW.createWriter(buffer, { headerSize });
1649
- for (const r of roots) {
1650
- writer.addRoot(r);
1354
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1355
+ codec(iv) {
1356
+ return new nullCodec();
1651
1357
  }
1652
- for (const { cid, bytes } of t.entries()) {
1653
- writer.write({ cid, bytes });
1358
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1359
+ algo(iv) {
1360
+ return {
1361
+ name: "noCrypto",
1362
+ iv: new Uint8Array(),
1363
+ tagLength: 0
1364
+ };
1654
1365
  }
1655
- writer.close();
1656
- return await encode({ value: writer.bytes, hasher: import_sha22.sha256, codec: codec3 });
1657
- }
1658
- async function createCarFile(encoder, cid, t) {
1659
- return encodeCarFile([cid], t, encoder);
1660
- }
1661
- async function commitFiles(fileStore, walStore, t, done) {
1662
- const { files: roots } = makeFileCarHeader(done);
1663
- const cids = [];
1664
- const codec3 = (await fileStore.keyedCrypto()).codec();
1665
- const cars = await prepareCarFilesFiles(codec3, roots, t);
1666
- for (const car of cars) {
1667
- const { cid, bytes } = car;
1668
- await fileStore.save({ cid, bytes });
1669
- await walStore.enqueueFile(
1670
- cid
1671
- /*, !!opts.public*/
1672
- );
1673
- cids.push(cid);
1366
+ _decrypt() {
1367
+ throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
1674
1368
  }
1675
- return cids;
1676
- }
1677
- function makeFileCarHeader(result) {
1678
- const files = [];
1679
- for (const [, meta] of Object.entries(result.files || {})) {
1680
- if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
1681
- files.push(meta.cid);
1369
+ _encrypt() {
1370
+ throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
1371
+ }
1372
+ };
1373
+ async function keyedCryptoFactory(url, kb, sthis) {
1374
+ const storekey = url.getParam("storekey" /* STORE_KEY */);
1375
+ if (storekey && storekey !== "insecure") {
1376
+ let rkey = await kb.getNamedKey(storekey, true);
1377
+ if (rkey.isErr()) {
1378
+ try {
1379
+ rkey = await kb.toKeyWithFingerPrint(storekey);
1380
+ } catch (e) {
1381
+ throw sthis.logger.Error().Err(e).Str("keybag", kb.rt.id()).Str("name", storekey).Msg("getNamedKey failed").AsError();
1382
+ }
1682
1383
  }
1384
+ return new keyedCrypto(url, rkey.Ok(), kb.rt.crypto, sthis);
1683
1385
  }
1684
- return { ...result, files };
1685
- }
1686
- async function prepareCarFilesFiles(encoder, roots, t) {
1687
- return [await encodeCarFile(roots, t, encoder)];
1688
- }
1689
- function makeCarHeader(meta, cars, compact = false) {
1690
- const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
1691
- return { ...coreHeader, meta };
1692
- }
1693
- async function encodeCarHeader(fp) {
1694
- return await encode({
1695
- value: { fp },
1696
- hasher: import_sha22.sha256,
1697
- codec: dagCodec2
1698
- });
1386
+ return new noCrypto(url, kb.rt.crypto, sthis);
1699
1387
  }
1700
- async function commit(params, t, done, opts = { noLoader: false, compact: false }) {
1701
- const fp = makeCarHeader(done, params.carLog, !!opts.compact);
1702
- const rootBlock = await encodeCarHeader(fp);
1703
- const cars = await prepareCarFiles(params.encoder, params.threshold, rootBlock, t);
1704
- const cids = [];
1705
- for (const car of cars) {
1706
- const { cid, bytes } = car;
1707
- await params.carStore.save({ cid, bytes });
1708
- cids.push(cid);
1388
+
1389
+ // src/blockstore/fragment-gateway.ts
1390
+ var import_cement3 = require("@adviser/cement");
1391
+ var import_base583 = require("multiformats/bases/base58");
1392
+ var import_cborg = require("cborg");
1393
+ init_types();
1394
+ init_utils();
1395
+ function getFragSize(url) {
1396
+ const fragSize = url.getParam("fragSize" /* FRAG_SIZE */);
1397
+ let ret = 0;
1398
+ if (fragSize) {
1399
+ ret = parseInt(fragSize);
1709
1400
  }
1710
- const newDbMeta = { cars: cids };
1711
- await params.WALStore.enqueue(newDbMeta, opts);
1712
- await params.metaStore.save(newDbMeta);
1713
- return { cgrp: cids, header: fp };
1401
+ if (isNaN(ret) || ret <= 0) {
1402
+ ret = 0;
1403
+ }
1404
+ return ret;
1714
1405
  }
1715
- async function prepareCarFiles(encoder, threshold, rootBlock, t) {
1716
- const carFiles = [];
1717
- threshold = threshold || 128e3 * 8;
1718
- let clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
1719
- clonedt.putSync(rootBlock.cid, rootBlock.bytes);
1720
- let newsize = CBW.blockLength(toCIDBlock(rootBlock));
1721
- let cidRootBlock = rootBlock;
1722
- for (const { cid, bytes } of t.entries()) {
1723
- newsize += CBW.blockLength(toCIDBlock({ cid, bytes }));
1724
- if (newsize >= threshold) {
1725
- carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
1726
- clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
1727
- clonedt.putSync(cid, bytes);
1728
- cidRootBlock = { cid, bytes };
1729
- newsize = CBW.blockLength(toCIDBlock({ cid, bytes }));
1730
- } else {
1731
- clonedt.putSync(cid, bytes);
1406
+ async function getFrags(url, innerGW, headerSize, logger) {
1407
+ const fragSize = getFragSize(url);
1408
+ if (!fragSize) {
1409
+ const res = await innerGW.get(url);
1410
+ if (res.isErr()) {
1411
+ return [res];
1732
1412
  }
1413
+ const data = res.unwrap();
1414
+ return [
1415
+ import_cement3.Result.Ok({
1416
+ fid: new Uint8Array(0),
1417
+ ofs: 0,
1418
+ len: data.length,
1419
+ data
1420
+ })
1421
+ ];
1733
1422
  }
1734
- carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
1735
- return carFiles;
1423
+ const firstRaw = await innerGW.get(url.build().setParam("ofs", "0").URI());
1424
+ if (firstRaw.isErr()) {
1425
+ return [firstRaw];
1426
+ }
1427
+ const firstFragment = (0, import_cborg.decode)(firstRaw.unwrap());
1428
+ const blockSize = firstFragment.data.length;
1429
+ const ops = [Promise.resolve(import_cement3.Result.Ok(firstFragment))];
1430
+ const fidStr = import_base583.base58btc.encode(firstFragment.fid);
1431
+ const fragUrl = url.build().setParam("fid" /* FRAG_FID */, fidStr).setParam("len" /* FRAG_LEN */, firstFragment.len.toString()).setParam("headerSize" /* FRAG_HEAD */, headerSize.toString());
1432
+ for (let ofs = blockSize; ofs < firstFragment.len; ofs += blockSize) {
1433
+ ops.push(
1434
+ (async (furl, ofs2) => {
1435
+ const raw2 = await innerGW.get(furl);
1436
+ if (raw2.isErr()) {
1437
+ return raw2;
1438
+ }
1439
+ const fragment = (0, import_cborg.decode)(raw2.unwrap());
1440
+ if (import_base583.base58btc.encode(fragment.fid) !== fidStr) {
1441
+ return import_cement3.Result.Err(logger.Error().Msg("Fragment fid mismatch").AsError());
1442
+ }
1443
+ if (fragment.ofs !== ofs2) {
1444
+ return import_cement3.Result.Err(logger.Error().Uint64("ofs", ofs2).Msg("Fragment ofs mismatch").AsError());
1445
+ }
1446
+ return import_cement3.Result.Ok(fragment);
1447
+ })(fragUrl.setParam("ofs", ofs.toString()).URI(), ofs)
1448
+ );
1449
+ }
1450
+ return Promise.all(ops);
1736
1451
  }
1737
-
1738
- // src/blockstore/loader.ts
1739
- var import_sha23 = require("multiformats/hashes/sha2");
1740
-
1741
- // src/blockstore/task-manager.ts
1742
- init_utils();
1743
- var TaskManager = class {
1744
- constructor(sthis, callback) {
1745
- this.eventsWeHandled = /* @__PURE__ */ new Set();
1746
- this.queue = [];
1747
- this.isProcessing = false;
1748
- this.logger = ensureLogger(sthis, "TaskManager");
1749
- this.callback = callback;
1452
+ var FragmentGateway = class {
1453
+ constructor(sthis, innerGW) {
1454
+ this.fidLength = 4;
1455
+ this.headerSize = 32;
1456
+ this.sthis = ensureSuperLog(sthis, "FragmentGateway");
1457
+ this.logger = this.sthis.logger;
1458
+ this.innerGW = innerGW;
1750
1459
  }
1751
- async handleEvent(cid, parents, dbMeta) {
1752
- for (const parent of parents) {
1753
- this.eventsWeHandled.add(parent.toString());
1460
+ slicer(url, body) {
1461
+ const fragSize = getFragSize(url);
1462
+ if (!fragSize) {
1463
+ return [this.innerGW.put(url, body)];
1754
1464
  }
1755
- this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
1756
- this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
1757
- void this.processQueue();
1758
- }
1759
- async processQueue() {
1760
- if (this.isProcessing) return;
1761
- this.isProcessing = true;
1762
- const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
1763
- const first = filteredQueue[0];
1764
- if (!first) {
1765
- return;
1465
+ const blocksize = fragSize - this.headerSize;
1466
+ if (blocksize <= 0) {
1467
+ throw this.logger.Error().Uint64("fragSize" /* FRAG_SIZE */, fragSize).Uint64("headerSize" /* FRAG_HEAD */, this.headerSize).Msg("Fragment size is too small").AsError();
1766
1468
  }
1767
- try {
1768
- await this.callback(first.dbMeta);
1769
- this.eventsWeHandled.add(first.cid);
1770
- this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
1771
- } catch (err) {
1772
- if (first.retries++ > 3) {
1773
- this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
1774
- this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
1775
- }
1776
- await new Promise((resolve) => setTimeout(resolve, 50));
1777
- throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
1778
- } finally {
1779
- this.isProcessing = false;
1780
- if (this.queue.length > 0) {
1781
- void this.processQueue();
1469
+ const ops = [];
1470
+ const fid = this.sthis.nextId(this.fidLength);
1471
+ const fragUrl = url.build().setParam("fid" /* FRAG_FID */, fid.str).setParam("len" /* FRAG_LEN */, body.length.toString()).setParam("headerSize" /* FRAG_HEAD */, this.headerSize.toString());
1472
+ for (let ofs = 0; ofs < body.length; ofs += blocksize) {
1473
+ const block = (0, import_cborg.encode)({
1474
+ fid: fid.bin,
1475
+ ofs,
1476
+ len: body.length,
1477
+ data: body.slice(ofs, ofs + blocksize)
1478
+ });
1479
+ if (block.length > fragSize) {
1480
+ throw this.logger.Error().Uint64("block", block.length).Uint64("fragSize", fragSize).Msg("Block size to big").AsError();
1782
1481
  }
1482
+ ops.push(this.innerGW.put(fragUrl.setParam("ofs", ofs.toString()).URI(), block));
1783
1483
  }
1484
+ return ops;
1784
1485
  }
1785
- };
1786
-
1787
- // src/blockstore/loader.ts
1788
- function carLogIncludesGroup(list, cids) {
1789
- return list.some((arr) => {
1790
- return arr.toString() === cids.toString();
1791
- });
1792
- }
1793
- function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
1794
- const byString = /* @__PURE__ */ new Map();
1795
- for (const cid of list) {
1796
- if (remove.has(cid.toString())) continue;
1797
- byString.set(cid.toString(), cid);
1798
- }
1799
- return [...byString.values()];
1800
- }
1801
- var Loader = class {
1802
- constructor(name, ebOpts, sthis) {
1803
- this.commitQueue = new CommitQueue();
1804
- this.isCompacting = false;
1805
- this.carReaders = /* @__PURE__ */ new Map();
1806
- this.seenCompacted = /* @__PURE__ */ new Set();
1807
- this.processedCars = /* @__PURE__ */ new Set();
1808
- this.carLog = [];
1809
- this.getBlockCache = /* @__PURE__ */ new Map();
1810
- this.seenMeta = /* @__PURE__ */ new Set();
1811
- this.writeLimit = (0, import_p_limit.default)(1);
1812
- this.onceReady = new import_cement7.ResolveOnce();
1813
- this.name = name;
1814
- this.sthis = sthis;
1815
- this.ebOpts = defaultedBlockstoreRuntime(
1816
- sthis,
1817
- {
1818
- ...ebOpts,
1819
- name
1820
- },
1821
- "Loader"
1822
- );
1823
- this.logger = this.ebOpts.logger;
1824
- this.taskManager = new TaskManager(sthis, async (dbMeta) => {
1825
- await this.handleDbMetasFromStore([dbMeta]);
1826
- });
1827
- }
1828
- // readonly id = uuidv4();
1829
- async keyBag() {
1830
- return getKeyBag(this.sthis, this.ebOpts.keyBag);
1486
+ buildUrl(baseUrl, key) {
1487
+ return this.innerGW.buildUrl(baseUrl, key);
1831
1488
  }
1832
- async carStore() {
1833
- return this.ebOpts.storeRuntime.makeDataStore(this);
1489
+ async destroy(iurl) {
1490
+ return this.innerGW.destroy(iurl);
1834
1491
  }
1835
- async fileStore() {
1836
- return this.ebOpts.storeRuntime.makeDataStore(this);
1492
+ async start(url) {
1493
+ this.headerSize = (0, import_cborg.encode)({
1494
+ fid: this.sthis.nextId(this.fidLength).bin,
1495
+ ofs: 1024 * 1024,
1496
+ // 32bit
1497
+ len: 16 * 1024 * 1024,
1498
+ // 32bit
1499
+ data: new Uint8Array(1024)
1500
+ }).length - 1024;
1501
+ return this.innerGW.start(url);
1837
1502
  }
1838
- async WALStore() {
1839
- return this.ebOpts.storeRuntime.makeWALStore(this);
1503
+ async close(url) {
1504
+ return this.innerGW.close(url);
1840
1505
  }
1841
- async metaStore() {
1842
- return this.ebOpts.storeRuntime.makeMetaStore(this);
1506
+ async put(url, body) {
1507
+ await Promise.all(this.slicer(url, body));
1508
+ return import_cement3.Result.Ok(void 0);
1843
1509
  }
1844
- async ready() {
1845
- return this.onceReady.once(async () => {
1846
- const metas = await (await this.metaStore()).load();
1847
- if (this.ebOpts.meta) {
1848
- await this.handleDbMetasFromStore([this.ebOpts.meta]);
1849
- } else if (metas) {
1850
- await this.handleDbMetasFromStore(metas);
1510
+ async get(url) {
1511
+ const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
1512
+ let buffer = void 0;
1513
+ for (const rfrag of rfrags) {
1514
+ if (rfrag.isErr()) {
1515
+ return import_cement3.Result.Err(rfrag.Err());
1851
1516
  }
1852
- });
1853
- }
1854
- async close() {
1855
- const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
1856
- await Promise.all(toClose.map((store) => store.close()));
1857
- }
1858
- async destroy() {
1859
- const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
1860
- await Promise.all(toDestroy.map((store) => store.destroy()));
1861
- }
1862
- // async snapToCar(carCid: AnyLink | string) {
1863
- // await this.ready
1864
- // if (typeof carCid === 'string') {
1865
- // carCid = CID.parse(carCid)
1866
- // }
1867
- // const carHeader = await this.loadCarHeaderFromMeta({ car: carCid, key: this.key || null })
1868
- // this.carLog = [carCid, ...carHeader.cars]
1869
- // await this.getMoreReaders(carHeader.cars)
1870
- // await this._applyCarHeader(carHeader, true)
1871
- // }
1872
- async handleDbMetasFromStore(metas) {
1873
- this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
1874
- for (const meta of metas) {
1875
- await this.writeLimit(async () => {
1876
- await this.mergeDbMetaIntoClock(meta);
1877
- });
1517
+ const frag = rfrag.Ok();
1518
+ buffer = buffer || new Uint8Array(frag.len);
1519
+ buffer.set(frag.data, frag.ofs);
1878
1520
  }
1521
+ return import_cement3.Result.Ok(buffer || new Uint8Array(0));
1879
1522
  }
1880
- async mergeDbMetaIntoClock(meta) {
1881
- if (this.isCompacting) {
1882
- throw this.logger.Error().Msg("cannot merge while compacting").AsError();
1523
+ async subscribe(url, callback) {
1524
+ if (this.innerGW.subscribe) {
1525
+ return this.innerGW.subscribe(url, callback);
1526
+ } else {
1527
+ return import_cement3.Result.Err(this.logger.Error().Url(url).Msg("subscribe not supported").AsError());
1883
1528
  }
1884
- if (this.seenMeta.has(meta.cars.toString())) return;
1885
- this.seenMeta.add(meta.cars.toString());
1886
- if (carLogIncludesGroup(this.carLog, meta.cars)) {
1887
- return;
1888
- }
1889
- const carHeader = await this.loadCarHeaderFromMeta(meta);
1890
- carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1891
- await this.getMoreReaders(carHeader.cars.flat());
1892
- this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
1893
- await this.ebOpts.applyMeta?.(carHeader.meta);
1894
- }
1895
- // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
1896
- // const { key } = meta;
1897
- // if (key) {
1898
- // await this.setKey(key);
1899
- // }
1900
- // }
1901
- async loadCarHeaderFromMeta({ cars: cids }) {
1902
- const reader = await this.loadCar(cids[0]);
1903
- return await parseCarFile(reader, this.logger);
1904
- }
1905
- // async _getKey(): Promise<string | undefined> {
1906
- // if (this.key) return this.key;
1907
- // // generate a random key
1908
- // if (!this.ebOpts.public) {
1909
- // await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
1910
- // }
1911
- // return this.key || undefined;
1912
- // }
1913
- async commitFiles(t, done) {
1914
- await this.ready();
1915
- const fstore = await this.fileStore();
1916
- const wstore = await this.WALStore();
1917
- return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
1918
1529
  }
1919
- async loadFileCar(cid) {
1920
- return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
1530
+ async delete(url) {
1531
+ const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
1532
+ for (const rfrag of rfrags) {
1533
+ if (rfrag.isErr()) {
1534
+ return import_cement3.Result.Err(rfrag.Err());
1535
+ }
1536
+ const frag = rfrag.Ok();
1537
+ let fragUrl;
1538
+ if (rfrags.length > 1) {
1539
+ const fidStr = import_base583.base58btc.encode(frag.fid);
1540
+ fragUrl = url.build().setParam("fid" /* FRAG_FID */, fidStr).setParam("len" /* FRAG_LEN */, frag.len.toString()).setParam("headerSize" /* FRAG_HEAD */, this.headerSize.toString()).URI();
1541
+ } else {
1542
+ fragUrl = url;
1543
+ }
1544
+ await this.innerGW.delete(fragUrl);
1545
+ }
1546
+ return import_cement3.Result.Ok(void 0);
1921
1547
  }
1922
- async commit(t, done, opts = { noLoader: false, compact: false }) {
1923
- await this.ready();
1924
- const fstore = await this.fileStore();
1925
- const params = {
1926
- encoder: (await fstore.keyedCrypto()).codec(),
1927
- carLog: this.carLog,
1928
- carStore: fstore,
1929
- WALStore: await this.WALStore(),
1930
- metaStore: await this.metaStore(),
1931
- threshold: this.ebOpts.threshold
1932
- };
1933
- return this.commitQueue.enqueue(async () => {
1934
- await this.cacheTransaction(t);
1935
- const ret = await commit(params, t, done, opts);
1936
- await this.updateCarLog(ret.cgrp, ret.header, !!opts.compact);
1937
- return ret.cgrp;
1548
+ };
1549
+
1550
+ // src/blockstore/meta-key-helper.ts
1551
+ var import_dag_json = require("@ipld/dag-json");
1552
+ var import_clock = require("@web3-storage/pail/clock");
1553
+ var import_multiformats = require("multiformats");
1554
+ var import_base64 = require("multiformats/bases/base64");
1555
+ var import_cement7 = require("@adviser/cement");
1556
+
1557
+ // src/runtime/key-bag.ts
1558
+ var key_bag_exports = {};
1559
+ __export(key_bag_exports, {
1560
+ KeyBag: () => KeyBag,
1561
+ defaultKeyBagOpts: () => defaultKeyBagOpts,
1562
+ getKeyBag: () => getKeyBag,
1563
+ registerKeyBagProviderFactory: () => registerKeyBagProviderFactory
1564
+ });
1565
+ var import_cement6 = require("@adviser/cement");
1566
+ init_utils();
1567
+ var import_base584 = require("multiformats/bases/base58");
1568
+ init_types();
1569
+ var KeyBag = class {
1570
+ constructor(rt) {
1571
+ this.rt = rt;
1572
+ this._warnOnce = new import_cement6.ResolveOnce();
1573
+ this._seq = new import_cement6.ResolveSeq();
1574
+ this.logger = ensureLogger(rt.sthis, "KeyBag", {
1575
+ id: rt.id()
1938
1576
  });
1577
+ this.logger.Debug().Msg("KeyBag created");
1939
1578
  }
1940
- async updateCarLog(cids, fp, compact) {
1941
- if (compact) {
1942
- const previousCompactCid = fp.compact[fp.compact.length - 1];
1943
- fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1944
- this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
1945
- await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
1946
- } else {
1947
- this.carLog.unshift(cids);
1579
+ async subtleKey(key) {
1580
+ const extractable = this.rt.url.getParam("extractKey") === "_deprecated_internal_api";
1581
+ if (extractable) {
1582
+ this._warnOnce.once(
1583
+ () => this.logger.Warn().Msg("extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!")
1584
+ );
1948
1585
  }
1586
+ return await this.rt.crypto.importKey(
1587
+ "raw",
1588
+ // raw or jwk
1589
+ import_base584.base58btc.decode(key),
1590
+ // hexStringToUint8Array(key), // raw data
1591
+ "AES-GCM",
1592
+ extractable,
1593
+ ["encrypt", "decrypt"]
1594
+ );
1949
1595
  }
1950
- async cacheTransaction(t) {
1951
- for await (const block of t.entries()) {
1952
- const sBlock = block.cid.toString();
1953
- if (!this.getBlockCache.has(sBlock)) {
1954
- this.getBlockCache.set(sBlock, block);
1596
+ async ensureKeyFromUrl(url, keyFactory) {
1597
+ const storeKey = url.getParam("storekey" /* STORE_KEY */);
1598
+ if (storeKey === "insecure") {
1599
+ return import_cement6.Result.Ok(url);
1600
+ }
1601
+ if (!storeKey) {
1602
+ const keyName = `@${keyFactory()}@`;
1603
+ const ret = await this.getNamedKey(keyName);
1604
+ if (ret.isErr()) {
1605
+ return ret;
1955
1606
  }
1607
+ const urb = url.build().setParam("storekey" /* STORE_KEY */, keyName);
1608
+ return import_cement6.Result.Ok(urb.URI());
1956
1609
  }
1957
- }
1958
- async cacheCarReader(carCidStr, reader) {
1959
- if (this.processedCars.has(carCidStr)) return;
1960
- this.processedCars.add(carCidStr);
1961
- for await (const block of reader.blocks()) {
1962
- const sBlock = block.cid.toString();
1963
- if (!this.getBlockCache.has(sBlock)) {
1964
- this.getBlockCache.set(sBlock, block);
1610
+ if (storeKey.startsWith("@") && storeKey.endsWith("@")) {
1611
+ const ret = await this.getNamedKey(storeKey);
1612
+ if (ret.isErr()) {
1613
+ return ret;
1965
1614
  }
1966
1615
  }
1616
+ return import_cement6.Result.Ok(url);
1967
1617
  }
1968
- async removeCidsForCompact(cid) {
1969
- const carHeader = await this.loadCarHeaderFromMeta({
1970
- cars: [cid]
1618
+ async toKeyWithFingerPrint(keyStr) {
1619
+ const material = import_base584.base58btc.decode(keyStr);
1620
+ const key = await this.subtleKey(keyStr);
1621
+ const fpr = await this.rt.crypto.digestSHA256(material);
1622
+ return import_cement6.Result.Ok({
1623
+ key,
1624
+ fingerPrint: import_base584.base58btc.encode(new Uint8Array(fpr))
1971
1625
  });
1972
- for (const cids of carHeader.compact) {
1973
- for (const cid2 of cids) {
1974
- await (await this.carStore()).remove(cid2);
1975
- }
1976
- }
1977
1626
  }
1978
- // async flushCars() {
1979
- // await this.ready
1980
- // // for each cid in car log, make a dbMeta
1981
- // for (const cid of this.carLog) {
1982
- // const dbMeta = { car: cid, key: this.key || null } as DbMeta
1983
- // await this.remoteWAL!.enqueue(dbMeta, { public: false })
1984
- // }
1985
- // }
1986
- async *entries(cache2 = true) {
1987
- await this.ready();
1988
- if (cache2) {
1989
- for (const [, block] of this.getBlockCache) {
1990
- yield block;
1627
+ async setNamedKey(name, key) {
1628
+ return this._seq.add(() => this._setNamedKey(name, key));
1629
+ }
1630
+ // avoid deadlock
1631
+ async _setNamedKey(name, key) {
1632
+ const item = {
1633
+ name,
1634
+ key
1635
+ };
1636
+ const bag = await this.rt.getBag();
1637
+ this.logger.Debug().Str("name", name).Msg("setNamedKey");
1638
+ await bag.set(name, item);
1639
+ return await this.toKeyWithFingerPrint(item.key);
1640
+ }
1641
+ async getNamedExtractableKey(name, failIfNotFound = false) {
1642
+ const ret = await this.getNamedKey(name, failIfNotFound);
1643
+ if (ret.isErr()) {
1644
+ return ret;
1645
+ }
1646
+ const named = ret.Ok();
1647
+ return import_cement6.Result.Ok({
1648
+ ...named,
1649
+ extract: async () => {
1650
+ const ext = new Uint8Array(await this.rt.crypto.exportKey("raw", named.key));
1651
+ return {
1652
+ key: ext,
1653
+ keyStr: import_base584.base58btc.encode(ext)
1654
+ };
1991
1655
  }
1992
- } else {
1993
- for (const [, block] of this.getBlockCache) {
1994
- yield block;
1656
+ });
1657
+ }
1658
+ async getNamedKey(name, failIfNotFound = false) {
1659
+ const id = this.rt.sthis.nextId(4).str;
1660
+ return this._seq.add(async () => {
1661
+ const bag = await this.rt.getBag();
1662
+ const named = await bag.get(name);
1663
+ if (named) {
1664
+ const fpr = await this.toKeyWithFingerPrint(named.key);
1665
+ this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", fpr).Msg("fingerPrint getNamedKey");
1666
+ return fpr;
1995
1667
  }
1996
- for (const cids of this.carLog) {
1997
- for (const cid of cids) {
1998
- const reader = await this.loadCar(cid);
1999
- if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
2000
- for await (const block of reader.blocks()) {
2001
- const sCid = block.cid.toString();
2002
- if (!this.getBlockCache.has(sCid)) {
2003
- yield block;
2004
- }
2005
- }
2006
- }
1668
+ if (failIfNotFound) {
1669
+ this.logger.Debug().Str("id", id).Str("name", name).Msg("failIfNotFound getNamedKey");
1670
+ return import_cement6.Result.Err(new Error(`Key not found: ${name}`));
2007
1671
  }
2008
- }
1672
+ const ret = await this._setNamedKey(name, import_base584.base58btc.encode(this.rt.crypto.randomBytes(this.rt.keyLength)));
1673
+ this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", ret).Msg("createKey getNamedKey-post");
1674
+ return ret;
1675
+ });
2009
1676
  }
2010
- async getBlock(cid) {
2011
- await this.ready();
2012
- const sCid = cid.toString();
2013
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2014
- const getCarCid = async (carCid) => {
2015
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2016
- const reader = await this.loadCar(carCid);
2017
- if (!reader) {
2018
- throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
1677
+ };
1678
+ var keyBagProviderFactories = new Map(
1679
+ [
1680
+ {
1681
+ protocol: "file:",
1682
+ factory: async (url, sthis) => {
1683
+ const { KeyBagProviderFile: KeyBagProviderFile2 } = await Promise.resolve().then(() => (init_key_bag_file(), key_bag_file_exports));
1684
+ return new KeyBagProviderFile2(url, sthis);
2019
1685
  }
2020
- await this.cacheCarReader(carCid.toString(), reader).catch(() => {
2021
- return;
2022
- });
2023
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2024
- throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
2025
- };
2026
- const getCompactCarCids = async (carCid) => {
2027
- const reader = await this.loadCar(carCid);
2028
- if (!reader) {
2029
- throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
1686
+ },
1687
+ {
1688
+ protocol: "indexdb:",
1689
+ factory: async (url, sthis) => {
1690
+ const { KeyBagProviderIndexDB: KeyBagProviderIndexDB2 } = await Promise.resolve().then(() => (init_key_bag_indexdb(), key_bag_indexdb_exports));
1691
+ return new KeyBagProviderIndexDB2(url, sthis);
2030
1692
  }
2031
- const header = await parseCarFile(reader, this.logger);
2032
- const compacts = header.compact;
2033
- let got2;
2034
- const batchSize2 = 5;
2035
- for (let i = 0; i < compacts.length; i += batchSize2) {
2036
- const promises = [];
2037
- for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
2038
- for (const cid2 of compacts[j]) {
2039
- promises.push(getCarCid(cid2));
2040
- }
2041
- }
2042
- try {
2043
- got2 = await Promise.any(promises);
2044
- } catch {
2045
- }
2046
- if (got2) break;
2047
- }
2048
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2049
- throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
2050
- };
2051
- let got;
2052
- const batchSize = 5;
2053
- for (let i = 0; i < this.carLog.length; i += batchSize) {
2054
- const batch = this.carLog.slice(i, i + batchSize);
2055
- const promises = batch.flatMap((slice) => slice.map(getCarCid));
2056
- try {
2057
- got = await Promise.any(promises);
2058
- } catch {
2059
- }
2060
- if (got) break;
2061
1693
  }
2062
- if (!got) {
2063
- try {
2064
- got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
2065
- } catch {
1694
+ ].map((i) => [i.protocol, i])
1695
+ );
1696
+ function registerKeyBagProviderFactory(item) {
1697
+ const protocol = item.protocol.endsWith(":") ? item.protocol : item.protocol + ":";
1698
+ keyBagProviderFactories.set(protocol, {
1699
+ ...item,
1700
+ protocol
1701
+ });
1702
+ }
1703
+ function defaultKeyBagOpts(sthis, kbo) {
1704
+ kbo = kbo || {};
1705
+ if (kbo.keyRuntime) {
1706
+ return kbo.keyRuntime;
1707
+ }
1708
+ const logger = ensureLogger(sthis, "KeyBag");
1709
+ let url;
1710
+ if (kbo.url) {
1711
+ url = import_cement6.URI.from(kbo.url);
1712
+ logger.Debug().Url(url).Msg("from opts");
1713
+ } else {
1714
+ let bagFnameOrUrl = sthis.env.get("FP_KEYBAG_URL");
1715
+ if ((0, import_cement6.runtimeFn)().isBrowser) {
1716
+ url = import_cement6.URI.from(bagFnameOrUrl || "indexdb://fp-keybag");
1717
+ } else {
1718
+ if (!bagFnameOrUrl) {
1719
+ const home = sthis.env.get("HOME");
1720
+ bagFnameOrUrl = `${home}/.fireproof/keybag`;
1721
+ url = import_cement6.URI.from(`file://${bagFnameOrUrl}`);
1722
+ } else {
1723
+ url = import_cement6.URI.from(bagFnameOrUrl);
2066
1724
  }
2067
1725
  }
2068
- return got;
1726
+ logger.Debug().Url(url).Msg("from env");
2069
1727
  }
2070
- async loadCar(cid) {
2071
- if (!this.carStore) {
2072
- throw this.logger.Error().Msg("car store not initialized").AsError();
1728
+ let keyProviderFactory;
1729
+ switch (url.protocol) {
1730
+ case "file:":
1731
+ keyProviderFactory = async () => {
1732
+ const { KeyBagProviderFile: KeyBagProviderFile2 } = await Promise.resolve().then(() => (init_key_bag_file(), key_bag_file_exports));
1733
+ return new KeyBagProviderFile2(url, sthis);
1734
+ };
1735
+ break;
1736
+ case "indexdb:":
1737
+ keyProviderFactory = async () => {
1738
+ const { KeyBagProviderIndexDB: KeyBagProviderIndexDB2 } = await Promise.resolve().then(() => (init_key_bag_indexdb(), key_bag_indexdb_exports));
1739
+ return new KeyBagProviderIndexDB2(url, sthis);
1740
+ };
1741
+ break;
1742
+ case "memory:":
1743
+ keyProviderFactory = async () => {
1744
+ const { KeyBagProviderMemory: KeyBagProviderMemory2 } = await Promise.resolve().then(() => (init_key_bag_memory(), key_bag_memory_exports));
1745
+ return new KeyBagProviderMemory2(url, sthis);
1746
+ };
1747
+ break;
1748
+ default:
1749
+ throw logger.Error().Url(url).Msg("unsupported protocol").AsError();
1750
+ }
1751
+ if (url.hasParam("masterkey")) {
1752
+ throw logger.Error().Url(url).Msg("masterkey is not supported").AsError();
1753
+ }
1754
+ return {
1755
+ url,
1756
+ crypto: kbo.crypto || (0, import_cement6.toCryptoRuntime)({}),
1757
+ sthis,
1758
+ logger,
1759
+ keyLength: kbo.keyLength || 16,
1760
+ getBag: keyProviderFactory,
1761
+ id: () => {
1762
+ return url.toString();
2073
1763
  }
2074
- const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
2075
- return loaded;
1764
+ };
1765
+ }
1766
+ var _keyBags = new import_cement6.KeyedResolvOnce();
1767
+ async function getKeyBag(sthis, kbo = {}) {
1768
+ await sthis.start();
1769
+ const rt = defaultKeyBagOpts(sthis, kbo);
1770
+ return _keyBags.get(rt.id()).once(async () => new KeyBag(rt));
1771
+ }
1772
+
1773
+ // src/blockstore/meta-key-helper.ts
1774
+ async function decodeGatewayMetaBytesToDbMeta(sthis, byteHeads) {
1775
+ const crdtEntries = JSON.parse(sthis.txt.decode(byteHeads));
1776
+ if (!crdtEntries.length) {
1777
+ sthis.logger.Debug().Str("byteHeads", new TextDecoder().decode(byteHeads)).Msg("No CRDT entries found");
1778
+ return [];
2076
1779
  }
2077
- async makeDecoderAndCarReader(cid, local, remote) {
2078
- const cidsString = cid.toString();
2079
- let loadedCar = void 0;
2080
- let activeStore = local;
2081
- try {
2082
- this.logger.Debug().Str("cid", cidsString).Msg("loading car");
2083
- loadedCar = await local.load(cid);
2084
- this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
2085
- } catch (e) {
2086
- if (remote) {
2087
- const remoteCar = await remote.load(cid);
2088
- if (remoteCar) {
2089
- this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
2090
- await local.save(remoteCar);
2091
- loadedCar = remoteCar;
2092
- activeStore = remote;
1780
+ if (!crdtEntries.map) {
1781
+ sthis.logger.Debug().Str("crdtEntries", JSON.stringify(crdtEntries)).Msg("No data in CRDT entries");
1782
+ return [];
1783
+ }
1784
+ return Promise.all(
1785
+ crdtEntries.map(async (crdtEntry) => {
1786
+ const eventBlock = await (0, import_clock.decodeEventBlock)(import_base64.base64pad.decode(crdtEntry.data));
1787
+ const dbMeta = (0, import_dag_json.parse)(sthis.txt.decode(eventBlock.value.data.dbMeta));
1788
+ return {
1789
+ eventCid: eventBlock.cid,
1790
+ parents: crdtEntry.parents,
1791
+ dbMeta
1792
+ };
1793
+ })
1794
+ );
1795
+ }
1796
+ async function setCryptoKeyFromGatewayMetaPayload(uri, sthis, data) {
1797
+ try {
1798
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Setting crypto key from gateway meta payload");
1799
+ const keyInfo = await decodeGatewayMetaBytesToDbMeta(sthis, data);
1800
+ if (keyInfo.length) {
1801
+ const dbMeta = keyInfo[0].dbMeta;
1802
+ if (dbMeta.key) {
1803
+ const kb = await getKeyBag(sthis);
1804
+ const keyName = getStoreKeyName(uri);
1805
+ const res = await kb.setNamedKey(keyName, dbMeta.key);
1806
+ if (res.isErr()) {
1807
+ sthis.logger.Debug().Str("keyName", keyName).Str("dbMeta.key", dbMeta.key).Msg("Failed to set named key");
1808
+ throw res.Err();
2093
1809
  }
2094
- } else {
2095
- this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
2096
1810
  }
1811
+ sthis.logger.Debug().Str("dbMeta.key", dbMeta.key).Str("uri", uri.toString()).Msg("Set crypto key from gateway meta payload");
1812
+ return import_cement7.Result.Ok(dbMeta);
2097
1813
  }
2098
- if (!loadedCar) {
2099
- throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
2100
- }
2101
- const bytes = await decode({ bytes: loadedCar.bytes, hasher: import_sha23.sha256, codec: (await activeStore.keyedCrypto()).codec() });
2102
- const rawReader = await import_car.CarReader.fromBytes(bytes.value);
2103
- const readerP = Promise.resolve(rawReader);
2104
- const cachedReaderP = readerP.then(async (reader) => {
2105
- await this.cacheCarReader(cidsString, reader).catch((e) => {
2106
- this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
2107
- return;
2108
- });
2109
- return reader;
2110
- });
2111
- this.carReaders.set(cidsString, cachedReaderP);
2112
- return readerP;
1814
+ sthis.logger.Debug().Str("data", new TextDecoder().decode(data)).Msg("No crypto in gateway meta payload");
1815
+ return import_cement7.Result.Ok(void 0);
1816
+ } catch (error) {
1817
+ sthis.logger.Debug().Err(error).Msg("Failed to set crypto key from gateway meta payload");
1818
+ return import_cement7.Result.Err(error);
2113
1819
  }
2114
- //What if instead it returns an Array of CarHeader
2115
- async storesLoadCar(cid, local, remote) {
2116
- const cidsString = cid.toString();
2117
- let dacr = this.carReaders.get(cidsString);
2118
- if (!dacr) {
2119
- dacr = this.makeDecoderAndCarReader(cid, local, remote);
2120
- this.carReaders.set(cidsString, dacr);
1820
+ }
1821
+ async function addCryptoKeyToGatewayMetaPayload(uri, sthis, body) {
1822
+ try {
1823
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Adding crypto key to gateway meta payload");
1824
+ const keyName = getStoreKeyName(uri);
1825
+ const kb = await getKeyBag(sthis);
1826
+ const res = await kb.getNamedExtractableKey(keyName, true);
1827
+ if (res.isErr()) {
1828
+ sthis.logger.Error().Str("keyName", keyName).Msg("Failed to get named extractable key");
1829
+ throw res.Err();
2121
1830
  }
2122
- return dacr;
1831
+ const keyData = await res.Ok().extract();
1832
+ const dbMetas = await decodeGatewayMetaBytesToDbMeta(sthis, body);
1833
+ const { dbMeta, parents } = dbMetas[0];
1834
+ const parentLinks = parents.map((p) => import_multiformats.CID.parse(p));
1835
+ dbMeta.key = keyData.keyStr;
1836
+ const events = await Promise.all([dbMeta].map((dbMeta2) => createDbMetaEventBlock(sthis, dbMeta2, parentLinks)));
1837
+ const encoded = await encodeEventsWithParents(sthis, events, parentLinks);
1838
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Added crypto key to gateway meta payload");
1839
+ return import_cement7.Result.Ok(encoded);
1840
+ } catch (error) {
1841
+ sthis.logger.Error().Err(error).Msg("Failed to add crypto key to gateway meta payload");
1842
+ return import_cement7.Result.Err(error);
2123
1843
  }
2124
- async getMoreReaders(cids) {
2125
- const limit = (0, import_p_limit.default)(5);
2126
- const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
2127
- await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
1844
+ }
1845
+ function getStoreKeyName(url) {
1846
+ const storeKeyName = [url.getParam("localName") || url.getParam("name")];
1847
+ const idx = url.getParam("index");
1848
+ if (idx) {
1849
+ storeKeyName.push(idx);
2128
1850
  }
2129
- };
1851
+ storeKeyName.push("data");
1852
+ return `@${storeKeyName.join(":")}@`;
1853
+ }
1854
+ async function createDbMetaEventBlock(sthis, dbMeta, parents) {
1855
+ const event = await import_clock.EventBlock.create(
1856
+ {
1857
+ dbMeta: sthis.txt.encode((0, import_dag_json.format)(dbMeta))
1858
+ },
1859
+ parents
1860
+ );
1861
+ return event;
1862
+ }
1863
+ async function encodeEventsWithParents(sthis, events, parents) {
1864
+ const crdtEntries = events.map((event) => {
1865
+ const base64String = import_base64.base64pad.encode(event.bytes);
1866
+ return {
1867
+ cid: event.cid.toString(),
1868
+ data: base64String,
1869
+ parents: parents.map((p) => p.toString())
1870
+ };
1871
+ });
1872
+ return sthis.txt.encode(JSON.stringify(crdtEntries));
1873
+ }
2130
1874
 
2131
- // src/runtime/keyed-crypto.ts
2132
- var keyed_crypto_exports = {};
2133
- __export(keyed_crypto_exports, {
2134
- BlockIvKeyIdCodec: () => BlockIvKeyIdCodec,
2135
- keyedCryptoFactory: () => keyedCryptoFactory
1875
+ // src/blockstore/store.ts
1876
+ var import_p_retry = __toESM(require("p-retry"), 1);
1877
+ var import_p_map = __toESM(require("p-map"), 1);
1878
+
1879
+ // src/blockstore/loader.ts
1880
+ var import_p_limit = __toESM(require("p-limit"), 1);
1881
+ var import_car = require("@ipld/car");
1882
+ var import_cement9 = require("@adviser/cement");
1883
+
1884
+ // src/runtime/wait-pr-multiformats/block.ts
1885
+ var block_exports = {};
1886
+ __export(block_exports, {
1887
+ Block: () => Block,
1888
+ create: () => create,
1889
+ createUnsafe: () => createUnsafe,
1890
+ decode: () => decode3,
1891
+ encode: () => encode3
2136
1892
  });
1893
+ var import_multiformats2 = require("multiformats");
1894
+ var import_block = require("multiformats/block");
1895
+ var Block = import_block.Block;
1896
+ async function decode3({
1897
+ bytes,
1898
+ codec: codec3,
1899
+ hasher: hasher7
1900
+ }) {
1901
+ if (bytes == null) throw new Error('Missing required argument "bytes"');
1902
+ if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1903
+ const value = await Promise.resolve(codec3.decode(bytes));
1904
+ const hash = await hasher7.digest(bytes);
1905
+ const cid = import_multiformats2.CID.create(1, codec3.code, hash);
1906
+ return new import_block.Block({ value, bytes, cid });
1907
+ }
1908
+ async function encode3({
1909
+ value,
1910
+ codec: codec3,
1911
+ hasher: hasher7
1912
+ }) {
1913
+ if (typeof value === "undefined") throw new Error('Missing required argument "value"');
1914
+ if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1915
+ const bytes = await Promise.resolve(codec3.encode(value));
1916
+ const hash = await hasher7.digest(bytes);
1917
+ const cid = import_multiformats2.CID.create(1, codec3.code, hash);
1918
+ return new import_block.Block({ value, bytes, cid });
1919
+ }
1920
+ async function create({
1921
+ bytes,
1922
+ cid,
1923
+ hasher: hasher7,
1924
+ codec: codec3
1925
+ }) {
1926
+ if (bytes == null) throw new Error('Missing required argument "bytes"');
1927
+ if (hasher7 == null) throw new Error('Missing required argument "hasher"');
1928
+ const value = await Promise.resolve(codec3.decode(bytes));
1929
+ const hash = await hasher7.digest(bytes);
1930
+ if (!import_multiformats2.bytes.equals(cid.multihash.bytes, hash.bytes)) {
1931
+ throw new Error("CID hash does not match bytes");
1932
+ }
1933
+ return createUnsafe({
1934
+ bytes,
1935
+ cid,
1936
+ value,
1937
+ codec: codec3
1938
+ });
1939
+ }
1940
+ async function createUnsafe({
1941
+ bytes,
1942
+ cid,
1943
+ value: maybeValue,
1944
+ codec: codec3
1945
+ }) {
1946
+ const value = await Promise.resolve(maybeValue !== void 0 ? maybeValue : codec3?.decode(bytes));
1947
+ if (value === void 0) throw new Error('Missing required argument, must either provide "value" or "codec"');
1948
+ return new Block({
1949
+ cid,
1950
+ bytes,
1951
+ value
1952
+ });
1953
+ }
1954
+
1955
+ // src/blockstore/loader-helpers.ts
1956
+ var import_sha22 = require("multiformats/hashes/sha2");
1957
+ var dagCodec = __toESM(require("@ipld/dag-cbor"), 1);
1958
+ async function parseCarFile(reader, logger) {
1959
+ const roots = await reader.getRoots();
1960
+ const header = await reader.get(roots[0]);
1961
+ if (!header) throw logger.Error().Msg("missing header block").AsError();
1962
+ const dec = await decode3({ bytes: header.bytes, hasher: import_sha22.sha256, codec: dagCodec });
1963
+ const fpvalue = dec.value;
1964
+ if (fpvalue && !fpvalue.fp) {
1965
+ throw logger.Error().Msg("missing fp").AsError();
1966
+ }
1967
+ return fpvalue.fp;
1968
+ }
1969
+
1970
+ // src/blockstore/transaction.ts
1971
+ var import_block3 = require("@web3-storage/pail/block");
1972
+ init_types();
1973
+ var import_cement8 = require("@adviser/cement");
2137
1974
  init_utils();
2138
- var import_base583 = require("multiformats/bases/base58");
2139
- var import_sha24 = require("multiformats/hashes/sha2");
2140
- var CBOR = __toESM(require("cborg"), 1);
2141
- var generateIV = {
2142
- random: {
1975
+ var CarTransaction = class extends import_block3.MemoryBlockstore {
1976
+ constructor(parent, opts = { add: true, noLoader: false }) {
1977
+ super();
1978
+ if (opts.add) {
1979
+ parent.transactions.add(this);
1980
+ }
1981
+ this.parent = parent;
1982
+ }
1983
+ async get(cid) {
1984
+ return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
1985
+ }
1986
+ async superGet(cid) {
1987
+ return super.get(cid);
1988
+ }
1989
+ };
1990
+ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
1991
+ const logger = ensureLogger(sthis, component, ctx);
1992
+ return {
2143
1993
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2144
- calc: async (ko, crypto, data) => {
2145
- return crypto.randomBytes(ko.ivLength);
1994
+ applyMeta: (meta, snap) => {
1995
+ return Promise.resolve();
2146
1996
  },
2147
1997
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2148
- verify: async (ko, crypto, iv, data) => {
2149
- return true;
1998
+ compact: async (blocks) => {
1999
+ return {};
2000
+ },
2001
+ autoCompact: 100,
2002
+ public: false,
2003
+ // name: undefined,
2004
+ threshold: 1e3 * 1e3,
2005
+ ...opts,
2006
+ logger,
2007
+ keyBag: opts.keyBag || {},
2008
+ crypto: (0, import_cement8.toCryptoRuntime)(opts.crypto),
2009
+ storeUrls: opts.storeUrls,
2010
+ // storeEnDeFile: ensureStoreEnDeFile(opts.storeEnDeFile),
2011
+ // store,
2012
+ storeRuntime: toStoreRuntime(sthis, ensureStoreEnDeFile(opts.storeEnDeFile))
2013
+ };
2014
+ }
2015
+ function blockstoreFactory(sthis, opts) {
2016
+ return new EncryptedBlockstore(sthis, opts);
2017
+ }
2018
+ var BaseBlockstore = class {
2019
+ constructor(ebOpts) {
2020
+ this.transactions = /* @__PURE__ */ new Set();
2021
+ this.sthis = ensureSuperThis(ebOpts);
2022
+ this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
2023
+ this.sthis = ensureSuperThis(ebOpts);
2024
+ this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
2025
+ this.logger = this.ebOpts.logger;
2026
+ }
2027
+ // readonly name?: string;
2028
+ // ready: Promise<void>;
2029
+ ready() {
2030
+ return Promise.resolve();
2031
+ }
2032
+ async close() {
2033
+ }
2034
+ async destroy() {
2035
+ }
2036
+ async compact() {
2037
+ }
2038
+ async get(cid) {
2039
+ if (!cid) throw this.logger.Error().Msg("required cid").AsError();
2040
+ for (const f of this.transactions) {
2041
+ const v = await f.superGet(cid);
2042
+ if (v) return v;
2150
2043
  }
2151
- },
2152
- hash: {
2153
- calc: async (ko, crypto, data) => {
2154
- const hash = await import_sha24.sha256.digest(data);
2155
- const hashBytes = new Uint8Array(hash.bytes);
2156
- const hashArray = new Uint8Array(ko.ivLength);
2157
- for (let i = 0; i < hashBytes.length; i++) {
2158
- hashArray[i % ko.ivLength] ^= hashBytes[i];
2044
+ }
2045
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2046
+ async put(cid, block) {
2047
+ throw this.logger.Error().Msg("use a transaction to put").AsError();
2048
+ }
2049
+ // TransactionMeta
2050
+ async transaction(fn, _opts) {
2051
+ this.logger.Debug().Msg("enter transaction");
2052
+ const t = new CarTransaction(this, _opts);
2053
+ this.logger.Debug().Msg("post CarTransaction");
2054
+ const done = await fn(t);
2055
+ this.logger.Debug().Msg("post fn");
2056
+ this.lastTxMeta = done;
2057
+ return { t, meta: done };
2058
+ }
2059
+ openTransaction(opts = { add: true, noLoader: false }) {
2060
+ return new CarTransaction(this, opts);
2061
+ }
2062
+ async commitTransaction(t, done, opts) {
2063
+ if (!this.loader) throw this.logger.Error().Msg("loader required to commit").AsError();
2064
+ const cars = await this.loader?.commit(t, done, opts);
2065
+ if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
2066
+ setTimeout(() => void this.compact(), 10);
2067
+ }
2068
+ if (cars) {
2069
+ this.transactions.delete(t);
2070
+ return { meta: done, cars, t };
2071
+ }
2072
+ throw this.logger.Error().Msg("failed to commit car files").AsError();
2073
+ }
2074
+ async *entries() {
2075
+ const seen = /* @__PURE__ */ new Set();
2076
+ for (const t of this.transactions) {
2077
+ for await (const blk of t.entries()) {
2078
+ if (seen.has(blk.cid.toString())) continue;
2079
+ seen.add(blk.cid.toString());
2080
+ yield blk;
2159
2081
  }
2160
- return hashArray;
2161
- },
2162
- verify: async function(ko, crypto, iv, data) {
2163
- return ko.url.getParam("ivverify") !== "disable" && UInt8ArrayEqual(iv, await this.calc(ko, crypto, data));
2164
2082
  }
2165
2083
  }
2166
2084
  };
2167
- function getGenerateIVFn(url, opts) {
2168
- const ivhash = opts.ivCalc || url.getParam("ivhash") || "hash";
2169
- return generateIV[ivhash] || generateIV["hash"];
2170
- }
2171
- var BlockIvKeyIdCodec = class {
2172
- constructor(ko, iv, opts) {
2173
- this.code = 3147065;
2174
- this.name = "Fireproof@encrypted-block:aes-gcm";
2175
- this.ko = ko;
2176
- this.iv = iv;
2177
- this.opts = opts || {};
2085
+ var EncryptedBlockstore = class extends BaseBlockstore {
2086
+ constructor(sthis, ebOpts) {
2087
+ super(ebOpts);
2088
+ this.compacting = false;
2089
+ this.logger = ensureLogger(this.sthis, "EncryptedBlockstore", {
2090
+ this: 1
2091
+ });
2092
+ this.loader = new Loader(sthis, ebOpts);
2178
2093
  }
2179
- async encode(data) {
2180
- const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
2181
- const { iv } = this.ko.algo(calcIv);
2182
- const fprt = await this.ko.fingerPrint();
2183
- const keyId = import_base583.base58btc.decode(fprt);
2184
- this.ko.logger.Debug().Str("fp", fprt).Msg("encode");
2185
- return CBOR.encode({
2186
- iv,
2187
- keyId,
2188
- data: await this.ko._encrypt({ iv, bytes: data })
2094
+ ready() {
2095
+ return this.loader.ready();
2096
+ }
2097
+ close() {
2098
+ return this.loader.close();
2099
+ }
2100
+ destroy() {
2101
+ return this.loader.destroy();
2102
+ }
2103
+ async get(cid) {
2104
+ const got = await super.get(cid);
2105
+ if (got) return got;
2106
+ if (!this.loader) {
2107
+ return;
2108
+ }
2109
+ return falsyToUndef(await this.loader.getBlock(cid));
2110
+ }
2111
+ async transaction(fn, opts = { noLoader: false }) {
2112
+ this.logger.Debug().Msg("enter transaction");
2113
+ const { t, meta: done } = await super.transaction(fn);
2114
+ this.logger.Debug().Msg("post super.transaction");
2115
+ const cars = await this.loader.commit(t, done, opts);
2116
+ this.logger.Debug().Msg("post this.loader.commit");
2117
+ if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
2118
+ setTimeout(() => void this.compact(), 10);
2119
+ }
2120
+ if (cars) {
2121
+ this.transactions.delete(t);
2122
+ return { meta: done, cars, t };
2123
+ }
2124
+ throw this.logger.Error().Msg("failed to commit car files").AsError();
2125
+ }
2126
+ async getFile(car, cid) {
2127
+ await this.ready();
2128
+ if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
2129
+ const reader = await this.loader.loadFileCar(
2130
+ car
2131
+ /*, isPublic */
2132
+ );
2133
+ const block = await reader.get(cid);
2134
+ if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
2135
+ return block.bytes;
2136
+ }
2137
+ async compact() {
2138
+ await this.ready();
2139
+ if (!this.loader) throw this.logger.Error().Msg("loader required to compact").AsError();
2140
+ if (this.loader.carLog.length < 2) return;
2141
+ const compactFn = this.ebOpts.compact || ((blocks) => this.defaultCompact(blocks, this.logger));
2142
+ if (!compactFn || this.compacting) return;
2143
+ const blockLog = new CompactionFetcher(this);
2144
+ this.compacting = true;
2145
+ const meta = await compactFn(blockLog);
2146
+ await this.loader?.commit(blockLog.loggedBlocks, meta, {
2147
+ compact: true,
2148
+ noLoader: true
2189
2149
  });
2150
+ this.compacting = false;
2190
2151
  }
2191
- async decode(abytes) {
2192
- let bytes;
2193
- if (abytes instanceof Uint8Array) {
2194
- bytes = abytes;
2195
- } else {
2196
- bytes = new Uint8Array(abytes);
2152
+ async defaultCompact(blocks, logger) {
2153
+ if (!this.loader) {
2154
+ throw logger.Error().Msg("no loader").AsError();
2197
2155
  }
2198
- const { iv, keyId, data } = CBOR.decode(bytes);
2199
- const fprt = await this.ko.fingerPrint();
2200
- this.ko.logger.Debug().Str("fp", import_base583.base58btc.encode(keyId)).Msg("decode");
2201
- if (import_base583.base58btc.encode(keyId) !== fprt) {
2202
- throw this.ko.logger.Error().Str("fp", fprt).Str("keyId", import_base583.base58btc.encode(keyId)).Msg("keyId mismatch").AsError();
2156
+ if (!this.lastTxMeta) {
2157
+ throw logger.Error().Msg("no lastTxMeta").AsError();
2203
2158
  }
2204
- const result = await this.ko._decrypt({ iv, bytes: data });
2205
- if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
2206
- throw this.ko.logger.Error().Msg("iv missmatch").AsError();
2159
+ for await (const blk of this.loader.entries(false)) {
2160
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
2161
+ }
2162
+ for (const t of this.transactions) {
2163
+ for await (const blk of t.entries()) {
2164
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
2165
+ }
2166
+ }
2167
+ return this.lastTxMeta;
2168
+ }
2169
+ async *entries() {
2170
+ for await (const blk of this.loader.entries()) {
2171
+ yield blk;
2207
2172
  }
2208
- return result;
2209
2173
  }
2210
2174
  };
2211
- var keyedCrypto = class {
2212
- constructor(url, key, cyopt, sthis) {
2213
- this.ivLength = 12;
2214
- this.isEncrypting = true;
2215
- this.logger = ensureLogger(sthis, "keyedCrypto");
2216
- this.crypto = cyopt;
2217
- this.key = key;
2218
- this.url = url;
2175
+ var CompactionFetcher = class {
2176
+ constructor(blocks) {
2177
+ this.blockstore = blocks;
2178
+ this.loggedBlocks = new CarTransaction(blocks);
2219
2179
  }
2220
- fingerPrint() {
2221
- return Promise.resolve(this.key.fingerPrint);
2180
+ async get(cid) {
2181
+ const block = await this.blockstore.get(cid);
2182
+ if (block) this.loggedBlocks.putSync(cid, block.bytes);
2183
+ return falsyToUndef(block);
2222
2184
  }
2223
- codec(iv, opts) {
2224
- return new BlockIvKeyIdCodec(this, iv, opts);
2185
+ };
2186
+
2187
+ // src/blockstore/commitor.ts
2188
+ var CBW = __toESM(require("@ipld/car/buffer-writer"), 1);
2189
+ var import_sha23 = require("multiformats/hashes/sha2");
2190
+ var dagCodec2 = __toESM(require("@ipld/dag-cbor"), 1);
2191
+ async function encodeCarFile(roots, t, codec3) {
2192
+ let size = 0;
2193
+ const headerSize = CBW.headerLength({ roots });
2194
+ size += headerSize;
2195
+ for (const { cid, bytes } of t.entries()) {
2196
+ size += CBW.blockLength({ cid, bytes });
2225
2197
  }
2226
- algo(iv) {
2227
- return {
2228
- name: "AES-GCM",
2229
- iv: iv || this.crypto.randomBytes(this.ivLength),
2230
- tagLength: 128
2231
- };
2198
+ const buffer = new Uint8Array(size);
2199
+ const writer = CBW.createWriter(buffer, { headerSize });
2200
+ for (const r of roots) {
2201
+ writer.addRoot(r);
2232
2202
  }
2233
- async _decrypt(data) {
2234
- this.logger.Debug().Len(data.bytes, "bytes").Len(data.iv, "iv").Str("fp", this.key.fingerPrint).Msg("decrypting");
2235
- return new Uint8Array(await this.crypto.decrypt(this.algo(data.iv), this.key.key, data.bytes));
2203
+ for (const { cid, bytes } of t.entries()) {
2204
+ writer.write({ cid, bytes });
2236
2205
  }
2237
- async _encrypt(data) {
2238
- this.logger.Debug().Len(data.bytes).Str("fp", this.key.fingerPrint).Msg("encrypting");
2239
- const a = this.algo(data.iv);
2240
- return new Uint8Array(await this.crypto.encrypt(a, this.key.key, data.bytes));
2206
+ writer.close();
2207
+ return await encode3({ value: writer.bytes, hasher: import_sha23.sha256, codec: codec3 });
2208
+ }
2209
+ async function createCarFile(encoder, cid, t) {
2210
+ return encodeCarFile([cid], t, encoder);
2211
+ }
2212
+ async function commitFiles(fileStore, walStore, t, done) {
2213
+ const { files: roots } = makeFileCarHeader(done);
2214
+ const cids = [];
2215
+ const codec3 = (await fileStore.keyedCrypto()).codec();
2216
+ const cars = await prepareCarFilesFiles(codec3, roots, t);
2217
+ for (const car of cars) {
2218
+ const { cid, bytes } = car;
2219
+ await fileStore.save({ cid, bytes });
2220
+ await walStore.enqueueFile(
2221
+ cid
2222
+ /*, !!opts.public*/
2223
+ );
2224
+ cids.push(cid);
2241
2225
  }
2242
- };
2243
- var nullCodec = class {
2244
- constructor() {
2245
- this.code = 0;
2246
- this.name = "Fireproof@unencrypted-block";
2226
+ return cids;
2227
+ }
2228
+ function makeFileCarHeader(result) {
2229
+ const files = [];
2230
+ for (const [, meta] of Object.entries(result.files || {})) {
2231
+ if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
2232
+ files.push(meta.cid);
2233
+ }
2247
2234
  }
2248
- encode(data) {
2249
- return data;
2235
+ return { ...result, files };
2236
+ }
2237
+ async function prepareCarFilesFiles(encoder, roots, t) {
2238
+ return [await encodeCarFile(roots, t, encoder)];
2239
+ }
2240
+ function makeCarHeader(meta, cars, compact = false) {
2241
+ const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
2242
+ return { ...coreHeader, meta };
2243
+ }
2244
+ async function encodeCarHeader(fp) {
2245
+ return await encode3({
2246
+ value: { fp },
2247
+ hasher: import_sha23.sha256,
2248
+ codec: dagCodec2
2249
+ });
2250
+ }
2251
+ async function commit(params, t, done, opts = { noLoader: false, compact: false }) {
2252
+ const fp = makeCarHeader(done, params.carLog, !!opts.compact);
2253
+ const rootBlock = await encodeCarHeader(fp);
2254
+ const cars = await prepareCarFiles(params.encoder, params.threshold, rootBlock, t);
2255
+ const cids = [];
2256
+ for (const car of cars) {
2257
+ const { cid, bytes } = car;
2258
+ await params.carStore.save({ cid, bytes });
2259
+ cids.push(cid);
2250
2260
  }
2251
- decode(data) {
2252
- return data;
2261
+ const newDbMeta = { cars: cids };
2262
+ await params.WALStore.enqueue(newDbMeta, opts);
2263
+ await params.metaStore.save(newDbMeta);
2264
+ return { cgrp: cids, header: fp };
2265
+ }
2266
+ async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2267
+ const carFiles = [];
2268
+ threshold = threshold || 128e3 * 8;
2269
+ let clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
2270
+ clonedt.putSync(rootBlock.cid, rootBlock.bytes);
2271
+ let newsize = CBW.blockLength(toCIDBlock(rootBlock));
2272
+ let cidRootBlock = rootBlock;
2273
+ for (const { cid, bytes } of t.entries()) {
2274
+ newsize += CBW.blockLength(toCIDBlock({ cid, bytes }));
2275
+ if (newsize >= threshold) {
2276
+ carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
2277
+ clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
2278
+ clonedt.putSync(cid, bytes);
2279
+ cidRootBlock = { cid, bytes };
2280
+ newsize = CBW.blockLength(toCIDBlock({ cid, bytes }));
2281
+ } else {
2282
+ clonedt.putSync(cid, bytes);
2283
+ }
2284
+ }
2285
+ carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
2286
+ return carFiles;
2287
+ }
2288
+
2289
+ // src/blockstore/loader.ts
2290
+ var import_sha24 = require("multiformats/hashes/sha2");
2291
+
2292
+ // src/blockstore/task-manager.ts
2293
+ init_utils();
2294
+ var TaskManager = class {
2295
+ constructor(sthis, callback) {
2296
+ this.eventsWeHandled = /* @__PURE__ */ new Set();
2297
+ this.queue = [];
2298
+ this.isProcessing = false;
2299
+ this.logger = ensureLogger(sthis, "TaskManager");
2300
+ this.callback = callback;
2301
+ }
2302
+ async handleEvent(cid, parents, dbMeta) {
2303
+ for (const parent of parents) {
2304
+ this.eventsWeHandled.add(parent.toString());
2305
+ }
2306
+ this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
2307
+ this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
2308
+ void this.processQueue();
2309
+ }
2310
+ async processQueue() {
2311
+ if (this.isProcessing) return;
2312
+ this.isProcessing = true;
2313
+ const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
2314
+ const first = filteredQueue[0];
2315
+ if (!first) {
2316
+ return;
2317
+ }
2318
+ try {
2319
+ await this.callback(first.dbMeta);
2320
+ this.eventsWeHandled.add(first.cid);
2321
+ this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
2322
+ } catch (err) {
2323
+ if (first.retries++ > 3) {
2324
+ this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
2325
+ this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
2326
+ }
2327
+ await new Promise((resolve) => setTimeout(resolve, 50));
2328
+ throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
2329
+ } finally {
2330
+ this.isProcessing = false;
2331
+ if (this.queue.length > 0) {
2332
+ void this.processQueue();
2333
+ }
2334
+ }
2253
2335
  }
2254
2336
  };
2255
- var noCrypto = class {
2256
- constructor(url, cyrt, sthis) {
2257
- this.ivLength = 0;
2258
- this.code = 0;
2259
- this.name = "Fireproof@unencrypted-block";
2260
- this.isEncrypting = false;
2261
- this._fingerPrint = "noCrypto:" + Math.random();
2262
- this.logger = ensureLogger(sthis, "noCrypto");
2263
- this.crypto = cyrt;
2264
- this.url = url;
2337
+
2338
+ // src/blockstore/loader.ts
2339
+ function carLogIncludesGroup(list, cids) {
2340
+ return list.some((arr) => {
2341
+ return arr.toString() === cids.toString();
2342
+ });
2343
+ }
2344
+ function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
2345
+ const byString = /* @__PURE__ */ new Map();
2346
+ for (const cid of list) {
2347
+ if (remove.has(cid.toString())) continue;
2348
+ byString.set(cid.toString(), cid);
2349
+ }
2350
+ return [...byString.values()];
2351
+ }
2352
+ var Loader = class {
2353
+ constructor(sthis, ebOpts) {
2354
+ this.commitQueue = new CommitQueue();
2355
+ this.isCompacting = false;
2356
+ this.carReaders = /* @__PURE__ */ new Map();
2357
+ this.seenCompacted = /* @__PURE__ */ new Set();
2358
+ this.processedCars = /* @__PURE__ */ new Set();
2359
+ this.carLog = [];
2360
+ this.getBlockCache = /* @__PURE__ */ new Map();
2361
+ this.seenMeta = /* @__PURE__ */ new Set();
2362
+ this.writeLimit = (0, import_p_limit.default)(1);
2363
+ this._carStore = new import_cement9.ResolveOnce();
2364
+ this._fileStore = new import_cement9.ResolveOnce();
2365
+ this._WALStore = new import_cement9.ResolveOnce();
2366
+ this._metaStore = new import_cement9.ResolveOnce();
2367
+ this.onceReady = new import_cement9.ResolveOnce();
2368
+ this.sthis = sthis;
2369
+ this.ebOpts = defaultedBlockstoreRuntime(
2370
+ sthis,
2371
+ {
2372
+ ...ebOpts
2373
+ // name,
2374
+ },
2375
+ "Loader"
2376
+ );
2377
+ this.logger = this.ebOpts.logger;
2378
+ this.taskManager = new TaskManager(sthis, async (dbMeta) => {
2379
+ await this.handleDbMetasFromStore([dbMeta]);
2380
+ });
2265
2381
  }
2266
- fingerPrint() {
2267
- return Promise.resolve(this._fingerPrint);
2382
+ async carStore() {
2383
+ return this._carStore.once(
2384
+ async () => this.ebOpts.storeRuntime.makeDataStore({
2385
+ sthis: this.sthis,
2386
+ url: this.ebOpts.storeUrls.data,
2387
+ keybag: await this.keyBag()
2388
+ })
2389
+ );
2268
2390
  }
2269
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2270
- codec(iv) {
2271
- return new nullCodec();
2391
+ async fileStore() {
2392
+ return this._fileStore.once(
2393
+ async () => this.ebOpts.storeRuntime.makeDataStore({
2394
+ sthis: this.sthis,
2395
+ url: this.ebOpts.storeUrls.file,
2396
+ keybag: await this.keyBag()
2397
+ })
2398
+ );
2272
2399
  }
2273
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2274
- algo(iv) {
2275
- return {
2276
- name: "noCrypto",
2277
- iv: new Uint8Array(),
2278
- tagLength: 0
2279
- };
2400
+ async WALStore() {
2401
+ return this._WALStore.once(
2402
+ async () => this.ebOpts.storeRuntime.makeWALStore({
2403
+ sthis: this.sthis,
2404
+ url: this.ebOpts.storeUrls.wal,
2405
+ keybag: await this.keyBag()
2406
+ })
2407
+ );
2280
2408
  }
2281
- _decrypt() {
2282
- throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
2409
+ async metaStore() {
2410
+ return this._metaStore.once(
2411
+ async () => this.ebOpts.storeRuntime.makeMetaStore({
2412
+ sthis: this.sthis,
2413
+ url: this.ebOpts.storeUrls.meta,
2414
+ keybag: await this.keyBag()
2415
+ })
2416
+ );
2283
2417
  }
2284
- _encrypt() {
2285
- throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
2418
+ keyBag() {
2419
+ return getKeyBag(this.sthis, this.ebOpts.keyBag);
2286
2420
  }
2287
- };
2288
- async function keyedCryptoFactory(url, kb, sthis) {
2289
- const storekey = url.getParam("storekey");
2290
- if (storekey && storekey !== "insecure") {
2291
- let rkey = await kb.getNamedKey(storekey, true);
2292
- if (rkey.isErr()) {
2293
- try {
2294
- rkey = await kb.toKeyWithFingerPrint(storekey);
2295
- } catch (e) {
2296
- throw sthis.logger.Error().Err(e).Str("keybag", kb.rt.id()).Str("name", storekey).Msg("getNamedKey failed").AsError();
2421
+ async ready() {
2422
+ return this.onceReady.once(async () => {
2423
+ const metas = await (await this.metaStore()).load();
2424
+ if (this.ebOpts.meta) {
2425
+ await this.handleDbMetasFromStore([this.ebOpts.meta]);
2426
+ } else if (metas) {
2427
+ await this.handleDbMetasFromStore(metas);
2297
2428
  }
2298
- }
2299
- return new keyedCrypto(url, rkey.Ok(), kb.rt.crypto, sthis);
2429
+ });
2300
2430
  }
2301
- return new noCrypto(url, kb.rt.crypto, sthis);
2302
- }
2303
-
2304
- // src/blockstore/fragment-gateway.ts
2305
- var import_cement8 = require("@adviser/cement");
2306
- var import_base584 = require("multiformats/bases/base58");
2307
- var import_cborg = require("cborg");
2308
- init_utils();
2309
- function getFragSize(url) {
2310
- const fragSize = url.getParam("fragSize");
2311
- let ret = 0;
2312
- if (fragSize) {
2313
- ret = parseInt(fragSize);
2431
+ async close() {
2432
+ await this.commitQueue.waitIdle();
2433
+ const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
2434
+ await Promise.all(toClose.map((store) => store.close()));
2314
2435
  }
2315
- if (isNaN(ret) || ret <= 0) {
2316
- ret = 0;
2436
+ async destroy() {
2437
+ const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
2438
+ await Promise.all(toDestroy.map((store) => store.destroy()));
2317
2439
  }
2318
- return ret;
2319
- }
2320
- async function getFrags(url, innerGW, headerSize, logger) {
2321
- const fragSize = getFragSize(url);
2322
- if (!fragSize) {
2323
- const res = await innerGW.get(url);
2324
- if (res.isErr()) {
2325
- return [res];
2440
+ // async snapToCar(carCid: AnyLink | string) {
2441
+ // await this.ready
2442
+ // if (typeof carCid === 'string') {
2443
+ // carCid = CID.parse(carCid)
2444
+ // }
2445
+ // const carHeader = await this.loadCarHeaderFromMeta({ car: carCid, key: this.key || null })
2446
+ // this.carLog = [carCid, ...carHeader.cars]
2447
+ // await this.getMoreReaders(carHeader.cars)
2448
+ // await this._applyCarHeader(carHeader, true)
2449
+ // }
2450
+ async handleDbMetasFromStore(metas) {
2451
+ this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
2452
+ for (const meta of metas) {
2453
+ await this.writeLimit(async () => {
2454
+ await this.mergeDbMetaIntoClock(meta);
2455
+ });
2326
2456
  }
2327
- const data = res.unwrap();
2328
- return [
2329
- import_cement8.Result.Ok({
2330
- fid: new Uint8Array(0),
2331
- ofs: 0,
2332
- len: data.length,
2333
- data
2334
- })
2335
- ];
2336
- }
2337
- const firstRaw = await innerGW.get(url.build().setParam("ofs", "0").URI());
2338
- if (firstRaw.isErr()) {
2339
- return [firstRaw];
2340
- }
2341
- const firstFragment = (0, import_cborg.decode)(firstRaw.unwrap());
2342
- const blockSize = firstFragment.data.length;
2343
- const ops = [Promise.resolve(import_cement8.Result.Ok(firstFragment))];
2344
- const fidStr = import_base584.base58btc.encode(firstFragment.fid);
2345
- const fragUrl = url.build().setParam("fid", fidStr).setParam("len", firstFragment.len.toString()).setParam("headerSize", headerSize.toString());
2346
- for (let ofs = blockSize; ofs < firstFragment.len; ofs += blockSize) {
2347
- ops.push(
2348
- (async (furl, ofs2) => {
2349
- const raw2 = await innerGW.get(furl);
2350
- if (raw2.isErr()) {
2351
- return raw2;
2352
- }
2353
- const fragment = (0, import_cborg.decode)(raw2.unwrap());
2354
- if (import_base584.base58btc.encode(fragment.fid) !== fidStr) {
2355
- return import_cement8.Result.Err(logger.Error().Msg("Fragment fid mismatch").AsError());
2356
- }
2357
- if (fragment.ofs !== ofs2) {
2358
- return import_cement8.Result.Err(logger.Error().Uint64("ofs", ofs2).Msg("Fragment ofs mismatch").AsError());
2359
- }
2360
- return import_cement8.Result.Ok(fragment);
2361
- })(fragUrl.setParam("ofs", ofs.toString()).URI(), ofs)
2362
- );
2363
- }
2364
- return Promise.all(ops);
2365
- }
2366
- var FragmentGateway = class {
2367
- constructor(sthis, innerGW) {
2368
- this.fidLength = 4;
2369
- this.headerSize = 32;
2370
- this.sthis = ensureSuperLog(sthis, "FragmentGateway");
2371
- this.logger = this.sthis.logger;
2372
- this.innerGW = innerGW;
2373
2457
  }
2374
- slicer(url, body) {
2375
- const fragSize = getFragSize(url);
2376
- if (!fragSize) {
2377
- return [this.innerGW.put(url, body)];
2378
- }
2379
- const blocksize = fragSize - this.headerSize;
2380
- if (blocksize <= 0) {
2381
- throw this.logger.Error().Uint64("fragSize", fragSize).Uint64("headerSize", this.headerSize).Msg("Fragment size is too small").AsError();
2458
+ async mergeDbMetaIntoClock(meta) {
2459
+ if (this.isCompacting) {
2460
+ throw this.logger.Error().Msg("cannot merge while compacting").AsError();
2382
2461
  }
2383
- const ops = [];
2384
- const fid = this.sthis.nextId(this.fidLength);
2385
- const fragUrl = url.build().setParam("fid", fid.str).setParam("len", body.length.toString()).setParam("headerSize", this.headerSize.toString());
2386
- for (let ofs = 0; ofs < body.length; ofs += blocksize) {
2387
- const block = (0, import_cborg.encode)({
2388
- fid: fid.bin,
2389
- ofs,
2390
- len: body.length,
2391
- data: body.slice(ofs, ofs + blocksize)
2392
- });
2393
- if (block.length > fragSize) {
2394
- throw this.logger.Error().Uint64("block", block.length).Uint64("fragSize", fragSize).Msg("Block size to big").AsError();
2395
- }
2396
- ops.push(this.innerGW.put(fragUrl.setParam("ofs", ofs.toString()).URI(), block));
2462
+ if (this.seenMeta.has(meta.cars.toString())) return;
2463
+ this.seenMeta.add(meta.cars.toString());
2464
+ if (carLogIncludesGroup(this.carLog, meta.cars)) {
2465
+ return;
2397
2466
  }
2398
- return ops;
2467
+ const carHeader = await this.loadCarHeaderFromMeta(meta);
2468
+ carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2469
+ await this.getMoreReaders(carHeader.cars.flat());
2470
+ this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
2471
+ await this.ebOpts.applyMeta?.(carHeader.meta);
2399
2472
  }
2400
- buildUrl(baseUrl, key) {
2401
- return this.innerGW.buildUrl(baseUrl, key);
2473
+ // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
2474
+ // const { key } = meta;
2475
+ // if (key) {
2476
+ // await this.setKey(key);
2477
+ // }
2478
+ // }
2479
+ async loadCarHeaderFromMeta({ cars: cids }) {
2480
+ const reader = await this.loadCar(cids[0]);
2481
+ return await parseCarFile(reader, this.logger);
2402
2482
  }
2403
- async destroy(iurl) {
2404
- return this.innerGW.destroy(iurl);
2483
+ // async _getKey(): Promise<string | undefined> {
2484
+ // if (this.key) return this.key;
2485
+ // // generate a random key
2486
+ // if (!this.ebOpts.public) {
2487
+ // await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
2488
+ // }
2489
+ // return this.key || undefined;
2490
+ // }
2491
+ async commitFiles(t, done) {
2492
+ await this.ready();
2493
+ const fstore = await this.fileStore();
2494
+ const wstore = await this.WALStore();
2495
+ return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
2405
2496
  }
2406
- async start(url) {
2407
- this.headerSize = (0, import_cborg.encode)({
2408
- fid: this.sthis.nextId(this.fidLength).bin,
2409
- ofs: 1024 * 1024,
2410
- // 32bit
2411
- len: 16 * 1024 * 1024,
2412
- // 32bit
2413
- data: new Uint8Array(1024)
2414
- }).length - 1024;
2415
- return this.innerGW.start(url);
2497
+ async loadFileCar(cid) {
2498
+ return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
2416
2499
  }
2417
- async close(url) {
2418
- return this.innerGW.close(url);
2500
+ async commit(t, done, opts = { noLoader: false, compact: false }) {
2501
+ await this.ready();
2502
+ const carStore = await this.carStore();
2503
+ const params = {
2504
+ encoder: (await carStore.keyedCrypto()).codec(),
2505
+ carLog: this.carLog,
2506
+ carStore,
2507
+ WALStore: await this.WALStore(),
2508
+ metaStore: await this.metaStore(),
2509
+ threshold: this.ebOpts.threshold
2510
+ };
2511
+ return this.commitQueue.enqueue(async () => {
2512
+ await this.cacheTransaction(t);
2513
+ const ret = await commit(params, t, done, opts);
2514
+ await this.updateCarLog(ret.cgrp, ret.header, !!opts.compact);
2515
+ return ret.cgrp;
2516
+ });
2419
2517
  }
2420
- async put(url, body) {
2421
- await Promise.all(this.slicer(url, body));
2422
- return import_cement8.Result.Ok(void 0);
2518
+ async updateCarLog(cids, fp, compact) {
2519
+ if (compact) {
2520
+ const previousCompactCid = fp.compact[fp.compact.length - 1];
2521
+ fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2522
+ this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
2523
+ await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
2524
+ } else {
2525
+ this.carLog.unshift(cids);
2526
+ }
2423
2527
  }
2424
- async get(url) {
2425
- const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
2426
- let buffer = void 0;
2427
- for (const rfrag of rfrags) {
2428
- if (rfrag.isErr()) {
2429
- return import_cement8.Result.Err(rfrag.Err());
2528
+ async cacheTransaction(t) {
2529
+ for await (const block of t.entries()) {
2530
+ const sBlock = block.cid.toString();
2531
+ if (!this.getBlockCache.has(sBlock)) {
2532
+ this.getBlockCache.set(sBlock, block);
2533
+ }
2534
+ }
2535
+ }
2536
+ async cacheCarReader(carCidStr, reader) {
2537
+ if (this.processedCars.has(carCidStr)) return;
2538
+ this.processedCars.add(carCidStr);
2539
+ for await (const block of reader.blocks()) {
2540
+ const sBlock = block.cid.toString();
2541
+ if (!this.getBlockCache.has(sBlock)) {
2542
+ this.getBlockCache.set(sBlock, block);
2543
+ }
2544
+ }
2545
+ }
2546
+ async removeCidsForCompact(cid) {
2547
+ const carHeader = await this.loadCarHeaderFromMeta({
2548
+ cars: [cid]
2549
+ });
2550
+ for (const cids of carHeader.compact) {
2551
+ for (const cid2 of cids) {
2552
+ await (await this.carStore()).remove(cid2);
2430
2553
  }
2431
- const frag = rfrag.Ok();
2432
- buffer = buffer || new Uint8Array(frag.len);
2433
- buffer.set(frag.data, frag.ofs);
2434
2554
  }
2435
- return import_cement8.Result.Ok(buffer || new Uint8Array(0));
2436
2555
  }
2437
- async subscribe(url, callback) {
2438
- if (this.innerGW.subscribe) {
2439
- return this.innerGW.subscribe(url, callback);
2556
+ // async flushCars() {
2557
+ // await this.ready
2558
+ // // for each cid in car log, make a dbMeta
2559
+ // for (const cid of this.carLog) {
2560
+ // const dbMeta = { car: cid, key: this.key || null } as DbMeta
2561
+ // await this.remoteWAL!.enqueue(dbMeta, { public: false })
2562
+ // }
2563
+ // }
2564
+ async *entries(cache2 = true) {
2565
+ await this.ready();
2566
+ if (cache2) {
2567
+ for (const [, block] of this.getBlockCache) {
2568
+ yield block;
2569
+ }
2440
2570
  } else {
2441
- return import_cement8.Result.Err(this.logger.Error().Url(url).Msg("subscribe not supported").AsError());
2571
+ for (const [, block] of this.getBlockCache) {
2572
+ yield block;
2573
+ }
2574
+ for (const cids of this.carLog) {
2575
+ for (const cid of cids) {
2576
+ const reader = await this.loadCar(cid);
2577
+ if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
2578
+ for await (const block of reader.blocks()) {
2579
+ const sCid = block.cid.toString();
2580
+ if (!this.getBlockCache.has(sCid)) {
2581
+ yield block;
2582
+ }
2583
+ }
2584
+ }
2585
+ }
2442
2586
  }
2443
2587
  }
2444
- async delete(url) {
2445
- const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
2446
- for (const rfrag of rfrags) {
2447
- if (rfrag.isErr()) {
2448
- return import_cement8.Result.Err(rfrag.Err());
2588
+ async getBlock(cid) {
2589
+ await this.ready();
2590
+ const sCid = cid.toString();
2591
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2592
+ const getCarCid = async (carCid) => {
2593
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2594
+ const reader = await this.loadCar(carCid);
2595
+ if (!reader) {
2596
+ throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
2449
2597
  }
2450
- const frag = rfrag.Ok();
2451
- const fidStr = import_base584.base58btc.encode(frag.fid);
2452
- const fragUrl = url.build().setParam("fid", fidStr).setParam("len", frag.len.toString()).setParam("headerSize", this.headerSize.toString()).URI();
2453
- await this.innerGW.delete(fragUrl);
2598
+ await this.cacheCarReader(carCid.toString(), reader).catch(() => {
2599
+ return;
2600
+ });
2601
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2602
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
2603
+ };
2604
+ const getCompactCarCids = async (carCid) => {
2605
+ const reader = await this.loadCar(carCid);
2606
+ if (!reader) {
2607
+ throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
2608
+ }
2609
+ const header = await parseCarFile(reader, this.logger);
2610
+ const compacts = header.compact;
2611
+ let got2;
2612
+ const batchSize2 = 5;
2613
+ for (let i = 0; i < compacts.length; i += batchSize2) {
2614
+ const promises = [];
2615
+ for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
2616
+ for (const cid2 of compacts[j]) {
2617
+ promises.push(getCarCid(cid2));
2618
+ }
2619
+ }
2620
+ try {
2621
+ got2 = await Promise.any(promises);
2622
+ } catch {
2623
+ }
2624
+ if (got2) break;
2625
+ }
2626
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2627
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
2628
+ };
2629
+ let got;
2630
+ const batchSize = 5;
2631
+ for (let i = 0; i < this.carLog.length; i += batchSize) {
2632
+ const batch = this.carLog.slice(i, i + batchSize);
2633
+ const promises = batch.flatMap((slice) => slice.map(getCarCid));
2634
+ try {
2635
+ got = await Promise.any(promises);
2636
+ } catch {
2637
+ }
2638
+ if (got) break;
2454
2639
  }
2455
- return import_cement8.Result.Ok(void 0);
2456
- }
2457
- };
2458
-
2459
- // src/blockstore/meta-key-helper.ts
2460
- var import_dag_json = require("@ipld/dag-json");
2461
- var import_clock = require("@web3-storage/pail/clock");
2462
- var import_multiformats2 = require("multiformats");
2463
- var import_base64 = require("multiformats/bases/base64");
2464
- var import_cement9 = require("@adviser/cement");
2465
- async function decodeGatewayMetaBytesToDbMeta(sthis, byteHeads) {
2466
- const crdtEntries = JSON.parse(sthis.txt.decode(byteHeads));
2467
- if (!crdtEntries.length) {
2468
- sthis.logger.Debug().Str("byteHeads", new TextDecoder().decode(byteHeads)).Msg("No CRDT entries found");
2469
- return [];
2640
+ if (!got) {
2641
+ try {
2642
+ got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
2643
+ } catch {
2644
+ }
2645
+ }
2646
+ return got;
2470
2647
  }
2471
- if (!crdtEntries.map) {
2472
- sthis.logger.Debug().Str("crdtEntries", JSON.stringify(crdtEntries)).Msg("No data in CRDT entries");
2473
- return [];
2648
+ async loadCar(cid) {
2649
+ if (!this.carStore) {
2650
+ throw this.logger.Error().Msg("car store not initialized").AsError();
2651
+ }
2652
+ const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
2653
+ return loaded;
2474
2654
  }
2475
- return Promise.all(
2476
- crdtEntries.map(async (crdtEntry) => {
2477
- const eventBlock = await (0, import_clock.decodeEventBlock)(import_base64.base64pad.decode(crdtEntry.data));
2478
- const dbMeta = (0, import_dag_json.parse)(sthis.txt.decode(eventBlock.value.data.dbMeta));
2479
- return {
2480
- eventCid: eventBlock.cid,
2481
- parents: crdtEntry.parents,
2482
- dbMeta
2483
- };
2484
- })
2485
- );
2486
- }
2487
- async function setCryptoKeyFromGatewayMetaPayload(uri, sthis, data) {
2488
- try {
2489
- sthis.logger.Debug().Str("uri", uri.toString()).Msg("Setting crypto key from gateway meta payload");
2490
- const keyInfo = await decodeGatewayMetaBytesToDbMeta(sthis, data);
2491
- if (keyInfo.length) {
2492
- const dbMeta = keyInfo[0].dbMeta;
2493
- if (dbMeta.key) {
2494
- const kb = await getKeyBag(sthis);
2495
- const keyName = getStoreKeyName(uri);
2496
- const res = await kb.setNamedKey(keyName, dbMeta.key);
2497
- if (res.isErr()) {
2498
- sthis.logger.Debug().Str("keyName", keyName).Str("dbMeta.key", dbMeta.key).Msg("Failed to set named key");
2499
- throw res.Err();
2655
+ async makeDecoderAndCarReader(cid, local, remote) {
2656
+ const cidsString = cid.toString();
2657
+ let loadedCar = void 0;
2658
+ let activeStore = local;
2659
+ try {
2660
+ this.logger.Debug().Str("cid", cidsString).Msg("loading car");
2661
+ loadedCar = await local.load(cid);
2662
+ this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
2663
+ } catch (e) {
2664
+ if (remote) {
2665
+ const remoteCar = await remote.load(cid);
2666
+ if (remoteCar) {
2667
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
2668
+ await local.save(remoteCar);
2669
+ loadedCar = remoteCar;
2670
+ activeStore = remote;
2500
2671
  }
2672
+ } else {
2673
+ this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
2501
2674
  }
2502
- sthis.logger.Debug().Str("dbMeta.key", dbMeta.key).Str("uri", uri.toString()).Msg("Set crypto key from gateway meta payload");
2503
- return import_cement9.Result.Ok(dbMeta);
2504
2675
  }
2505
- sthis.logger.Debug().Str("data", new TextDecoder().decode(data)).Msg("No crypto in gateway meta payload");
2506
- return import_cement9.Result.Ok(void 0);
2507
- } catch (error) {
2508
- sthis.logger.Debug().Err(error).Msg("Failed to set crypto key from gateway meta payload");
2509
- return import_cement9.Result.Err(error);
2676
+ if (!loadedCar) {
2677
+ throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
2678
+ }
2679
+ const bytes = await decode3({ bytes: loadedCar.bytes, hasher: import_sha24.sha256, codec: (await activeStore.keyedCrypto()).codec() });
2680
+ const rawReader = await import_car.CarReader.fromBytes(bytes.value);
2681
+ const readerP = Promise.resolve(rawReader);
2682
+ const cachedReaderP = readerP.then(async (reader) => {
2683
+ await this.cacheCarReader(cidsString, reader).catch((e) => {
2684
+ this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
2685
+ return;
2686
+ });
2687
+ return reader;
2688
+ });
2689
+ this.carReaders.set(cidsString, cachedReaderP);
2690
+ return readerP;
2510
2691
  }
2511
- }
2512
- async function addCryptoKeyToGatewayMetaPayload(uri, sthis, body) {
2513
- try {
2514
- sthis.logger.Debug().Str("uri", uri.toString()).Msg("Adding crypto key to gateway meta payload");
2515
- const keyName = getStoreKeyName(uri);
2516
- const kb = await getKeyBag(sthis);
2517
- const res = await kb.getNamedExtractableKey(keyName, true);
2518
- if (res.isErr()) {
2519
- sthis.logger.Error().Str("keyName", keyName).Msg("Failed to get named extractable key");
2520
- throw res.Err();
2692
+ //What if instead it returns an Array of CarHeader
2693
+ async storesLoadCar(cid, local, remote) {
2694
+ const cidsString = cid.toString();
2695
+ let dacr = this.carReaders.get(cidsString);
2696
+ if (!dacr) {
2697
+ dacr = this.makeDecoderAndCarReader(cid, local, remote);
2698
+ this.carReaders.set(cidsString, dacr);
2521
2699
  }
2522
- const keyData = await res.Ok().extract();
2523
- const dbMetas = await decodeGatewayMetaBytesToDbMeta(sthis, body);
2524
- const { dbMeta, parents } = dbMetas[0];
2525
- const parentLinks = parents.map((p) => import_multiformats2.CID.parse(p));
2526
- dbMeta.key = keyData.keyStr;
2527
- const events = await Promise.all([dbMeta].map((dbMeta2) => createDbMetaEventBlock(sthis, dbMeta2, parentLinks)));
2528
- const encoded = await encodeEventsWithParents(sthis, events, parentLinks);
2529
- sthis.logger.Debug().Str("uri", uri.toString()).Msg("Added crypto key to gateway meta payload");
2530
- return import_cement9.Result.Ok(encoded);
2531
- } catch (error) {
2532
- sthis.logger.Error().Err(error).Msg("Failed to add crypto key to gateway meta payload");
2533
- return import_cement9.Result.Err(error);
2700
+ return dacr;
2534
2701
  }
2535
- }
2536
- function getStoreKeyName(url) {
2537
- const storeKeyName = [url.getParam("localName") || url.getParam("name")];
2538
- const idx = url.getParam("index");
2539
- if (idx) {
2540
- storeKeyName.push(idx);
2702
+ async getMoreReaders(cids) {
2703
+ const limit = (0, import_p_limit.default)(5);
2704
+ const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
2705
+ await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
2541
2706
  }
2542
- storeKeyName.push("data");
2543
- return `@${storeKeyName.join(":")}@`;
2544
- }
2545
- async function createDbMetaEventBlock(sthis, dbMeta, parents) {
2546
- const event = await import_clock.EventBlock.create(
2547
- {
2548
- dbMeta: sthis.txt.encode((0, import_dag_json.format)(dbMeta))
2549
- },
2550
- parents
2551
- );
2552
- return event;
2553
- }
2554
- async function encodeEventsWithParents(sthis, events, parents) {
2555
- const crdtEntries = events.map((event) => {
2556
- const base64String = import_base64.base64pad.encode(event.bytes);
2557
- return {
2558
- cid: event.cid.toString(),
2559
- data: base64String,
2560
- parents: parents.map((p) => p.toString())
2561
- };
2562
- });
2563
- return sthis.txt.encode(JSON.stringify(crdtEntries));
2564
- }
2707
+ };
2565
2708
 
2566
2709
  // src/blockstore/store.ts
2567
2710
  function guardVersion(url) {
@@ -2571,16 +2714,21 @@ function guardVersion(url) {
2571
2714
  return import_cement10.Result.Ok(url);
2572
2715
  }
2573
2716
  var BaseStoreImpl = class {
2574
- constructor(name, url, opts, sthis, logger) {
2717
+ constructor(sthis, url, opts, logger) {
2575
2718
  this._onStarted = [];
2576
2719
  this._onClosed = [];
2577
- this.name = name;
2578
2720
  this._url = url;
2579
2721
  this.keybag = opts.keybag;
2722
+ this.loader = opts.loader;
2580
2723
  this.sthis = sthis;
2581
- this.logger = logger.With().Ref("url", () => this._url.toString()).Str("name", name).Logger();
2724
+ const name = this._url.getParam("name" /* NAME */);
2725
+ if (!name) {
2726
+ throw logger.Error().Str("url", this._url.toString()).Msg("missing name").AsError();
2727
+ }
2728
+ this.name = name;
2729
+ this.logger = logger.With().Str("this", this.sthis.nextId().str).Ref("url", () => this._url.toString()).Logger();
2730
+ this.realGateway = opts.gateway;
2582
2731
  this.gateway = new FragmentGateway(this.sthis, opts.gateway);
2583
- this.loader = opts.loader;
2584
2732
  }
2585
2733
  url() {
2586
2734
  return this._url;
@@ -2595,20 +2743,20 @@ var BaseStoreImpl = class {
2595
2743
  return;
2596
2744
  }
2597
2745
  async keyedCrypto() {
2598
- return keyedCryptoFactory(this._url, await this.keybag(), this.sthis);
2746
+ return keyedCryptoFactory(this._url, this.keybag, this.sthis);
2599
2747
  }
2600
2748
  async start() {
2601
2749
  this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
2602
- this._url = this._url.build().setParam("store", this.storeType).URI();
2750
+ this._url = this._url.build().setParam("store" /* STORE */, this.storeType).URI();
2603
2751
  const res = await this.gateway.start(this._url);
2604
2752
  if (res.isErr()) {
2605
2753
  this.logger.Error().Result("gw-start", res).Msg("started-gateway");
2606
2754
  return res;
2607
2755
  }
2608
2756
  this._url = res.Ok();
2609
- const kb = await this.keybag();
2757
+ const kb = await this.keybag;
2610
2758
  const skRes = await kb.ensureKeyFromUrl(this._url, () => {
2611
- const idx = this._url.getParam("index");
2759
+ const idx = this._url.getParam("index" /* INDEX */);
2612
2760
  const storeKeyName = [this.name];
2613
2761
  if (idx) {
2614
2762
  storeKeyName.push(idx);
@@ -2641,8 +2789,8 @@ var BaseStoreImpl = class {
2641
2789
  };
2642
2790
  var MetaStoreImpl = class extends BaseStoreImpl {
2643
2791
  // remote: boolean;
2644
- constructor(sthis, name, url, opts) {
2645
- super(name, url, { ...opts }, sthis, ensureLogger(sthis, "MetaStoreImpl"));
2792
+ constructor(sthis, url, opts) {
2793
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "MetaStoreImpl"));
2646
2794
  this.storeType = "meta";
2647
2795
  this.subscribers = /* @__PURE__ */ new Map();
2648
2796
  this.parents = [];
@@ -2713,13 +2861,21 @@ var MetaStoreImpl = class extends BaseStoreImpl {
2713
2861
  return import_cement10.Result.Ok(void 0);
2714
2862
  }
2715
2863
  async destroy() {
2864
+ this.logger.Debug().Msg("destroy");
2716
2865
  return this.gateway.destroy(this.url());
2717
2866
  }
2718
2867
  };
2719
2868
  var DataStoreImpl = class extends BaseStoreImpl {
2720
2869
  // readonly tag: string = "car-base";
2721
- constructor(sthis, name, url, opts) {
2722
- super(name, url, { ...opts }, sthis, ensureLogger(sthis, "DataStoreImpl"));
2870
+ constructor(sthis, url, opts) {
2871
+ super(
2872
+ sthis,
2873
+ url,
2874
+ {
2875
+ ...opts
2876
+ },
2877
+ ensureLogger(sthis, "DataStoreImpl")
2878
+ );
2723
2879
  this.storeType = "data";
2724
2880
  }
2725
2881
  async load(cid) {
@@ -2760,23 +2916,32 @@ var DataStoreImpl = class extends BaseStoreImpl {
2760
2916
  return import_cement10.Result.Ok(void 0);
2761
2917
  }
2762
2918
  destroy() {
2919
+ this.logger.Debug().Msg("destroy");
2763
2920
  return this.gateway.destroy(this.url());
2764
2921
  }
2765
2922
  };
2766
2923
  var WALStoreImpl = class extends BaseStoreImpl {
2767
- constructor(loader, url, opts) {
2768
- super(loader.name, url, { ...opts }, loader.sthis, ensureLogger(loader.sthis, "WALStoreImpl"));
2924
+ constructor(sthis, url, opts) {
2925
+ super(
2926
+ sthis,
2927
+ url,
2928
+ {
2929
+ ...opts
2930
+ },
2931
+ ensureLogger(sthis, "WALStoreImpl")
2932
+ );
2769
2933
  this.storeType = "wal";
2934
+ // readonly tag: string = "rwal-base";
2935
+ // readonly loader: Loadable;
2770
2936
  this._ready = new import_cement10.ResolveOnce();
2771
2937
  this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
2772
2938
  this.processing = void 0;
2773
2939
  this.processQueue = new CommitQueue();
2774
- this.loader = loader;
2775
2940
  }
2776
2941
  async ready() {
2777
2942
  return this._ready.once(async () => {
2778
2943
  const walState = await this.load().catch((e) => {
2779
- this.logger.Error().Any("error", e).Msg("error loading wal");
2944
+ this.logger.Error().Err(e).Msg("error loading wal");
2780
2945
  return void 0;
2781
2946
  });
2782
2947
  if (!walState) {
@@ -2809,7 +2974,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
2809
2974
  }
2810
2975
  async process() {
2811
2976
  await this.ready();
2812
- if (!this.loader.remoteCarStore) return;
2977
+ if (!this.loader?.remoteCarStore) return;
2813
2978
  await this.processQueue.enqueue(async () => {
2814
2979
  try {
2815
2980
  await this._doProcess();
@@ -2822,73 +2987,88 @@ var WALStoreImpl = class extends BaseStoreImpl {
2822
2987
  });
2823
2988
  }
2824
2989
  async _doProcess() {
2990
+ if (!this.loader) return;
2825
2991
  if (!this.loader.remoteCarStore) return;
2826
- const rmlp = (async () => {
2827
- const operations = [...this.walState.operations];
2828
- const fileOperations = [...this.walState.fileOperations];
2829
- const uploads = [];
2830
- const noLoaderOps = [...this.walState.noLoaderOps];
2831
- const limit = (0, import_p_limit2.default)(3);
2832
- if (operations.length + fileOperations.length + noLoaderOps.length === 0) return;
2833
- for (const dbMeta of noLoaderOps) {
2834
- const uploadP = limit(async () => {
2835
- for (const cid of dbMeta.cars) {
2836
- const car = await (await this.loader.carStore()).load(cid);
2837
- if (!car) {
2838
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars))
2839
- throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
2840
- } else {
2841
- await throwFalsy(this.loader.remoteCarStore).save(car);
2992
+ const operations = [...this.walState.operations];
2993
+ const noLoaderOps = [...this.walState.noLoaderOps];
2994
+ const fileOperations = [...this.walState.fileOperations];
2995
+ if (operations.length + noLoaderOps.length + fileOperations.length === 0) return;
2996
+ const concurrencyLimit = 3;
2997
+ const retryableUpload = (fn, description) => (0, import_p_retry.default)(fn, {
2998
+ retries: 5,
2999
+ onFailedAttempt: (error) => {
3000
+ this.logger.Warn().Msg(`Attempt ${error.attemptNumber} failed for ${description}. There are ${error.retriesLeft} retries left.`);
3001
+ }
3002
+ });
3003
+ try {
3004
+ await (0, import_p_map.default)(
3005
+ noLoaderOps,
3006
+ async (dbMeta) => {
3007
+ await retryableUpload(async () => {
3008
+ if (!this.loader) return;
3009
+ for (const cid of dbMeta.cars) {
3010
+ const car = await (await this.loader.carStore()).load(cid);
3011
+ if (!car) {
3012
+ if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
3013
+ throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
3014
+ }
3015
+ } else {
3016
+ await throwFalsy(this.loader.remoteCarStore).save(car);
3017
+ }
2842
3018
  }
2843
3019
  this.walState.noLoaderOps = this.walState.noLoaderOps.filter((op) => op !== dbMeta);
2844
- }
2845
- });
2846
- uploads.push(uploadP);
2847
- }
2848
- for (const dbMeta of operations) {
2849
- const uploadP = limit(async () => {
2850
- for (const cid of dbMeta.cars) {
2851
- const car = await (await this.loader.carStore()).load(cid).catch(() => null);
2852
- if (!car) {
2853
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars))
2854
- throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
2855
- } else {
2856
- await throwFalsy(this.loader.remoteCarStore).save(car);
3020
+ }, `noLoaderOp with dbMeta.cars=${dbMeta.cars.toString()}`);
3021
+ },
3022
+ { concurrency: concurrencyLimit }
3023
+ );
3024
+ await (0, import_p_map.default)(
3025
+ operations,
3026
+ async (dbMeta) => {
3027
+ await retryableUpload(async () => {
3028
+ if (!this.loader) return;
3029
+ for (const cid of dbMeta.cars) {
3030
+ const car = await (await this.loader.carStore()).load(cid);
3031
+ if (!car) {
3032
+ if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
3033
+ throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
3034
+ }
3035
+ } else {
3036
+ await throwFalsy(this.loader.remoteCarStore).save(car);
3037
+ }
2857
3038
  }
2858
- }
2859
- this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
2860
- });
2861
- uploads.push(uploadP);
2862
- }
2863
- if (fileOperations.length) {
2864
- const dbLoader = this.loader;
2865
- for (const { cid: fileCid, public: publicFile } of fileOperations) {
2866
- const uploadP = limit(async () => {
2867
- const fileBlock = await (await dbLoader.fileStore()).load(fileCid);
2868
- await dbLoader.remoteFileStore?.save(fileBlock, { public: publicFile });
3039
+ this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
3040
+ }, `operation with dbMeta.cars=${dbMeta.cars.toString()}`);
3041
+ },
3042
+ { concurrency: concurrencyLimit }
3043
+ );
3044
+ await (0, import_p_map.default)(
3045
+ fileOperations,
3046
+ async ({ cid: fileCid, public: publicFile }) => {
3047
+ await retryableUpload(async () => {
3048
+ if (!this.loader) return;
3049
+ const fileBlock = await (await this.loader.fileStore()).load(fileCid);
3050
+ if (!fileBlock) {
3051
+ throw this.logger.Error().Ref("cid", fileCid).Msg("missing file block").AsError();
3052
+ }
3053
+ await this.loader.remoteFileStore?.save(fileBlock, { public: publicFile });
2869
3054
  this.walState.fileOperations = this.walState.fileOperations.filter((op) => op.cid !== fileCid);
2870
- });
2871
- uploads.push(uploadP);
2872
- }
2873
- }
2874
- try {
2875
- const res = await Promise.allSettled(uploads);
2876
- const errors = res.filter((r) => r.status === "rejected");
2877
- if (errors.length) {
2878
- throw this.logger.Error().Any("errors", errors).Msg("error uploading").AsError();
2879
- }
2880
- if (operations.length) {
2881
- const lastOp = operations[operations.length - 1];
2882
- await this.loader.remoteMetaStore?.save(lastOp).catch((e) => {
2883
- this.walState.operations.push(lastOp);
2884
- throw this.logger.Error().Any("error", e).Msg("error saving remote meta").AsError();
2885
- });
2886
- }
2887
- } finally {
2888
- await this.save(this.walState);
3055
+ }, `fileOperation with cid=${fileCid.toString()}`);
3056
+ },
3057
+ { concurrency: concurrencyLimit }
3058
+ );
3059
+ if (operations.length) {
3060
+ const lastOp = operations[operations.length - 1];
3061
+ await retryableUpload(async () => {
3062
+ if (!this.loader) return;
3063
+ await this.loader.remoteMetaStore?.save(lastOp);
3064
+ }, `remoteMetaStore save with dbMeta.cars=${lastOp.cars.toString()}`);
2889
3065
  }
2890
- })();
2891
- await rmlp;
3066
+ } catch (error) {
3067
+ this.logger.Error().Any("error", error).Msg("Processing failed");
3068
+ return;
3069
+ } finally {
3070
+ await this.save(this.walState);
3071
+ }
2892
3072
  }
2893
3073
  async load() {
2894
3074
  this.logger.Debug().Msg("loading");
@@ -2905,6 +3085,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
2905
3085
  }
2906
3086
  try {
2907
3087
  return bytes && (0, import_dag_json2.parse)(this.sthis.txt.decode(bytes.Ok()));
3088
+ return bytes && (0, import_dag_json2.parse)(this.sthis.txt.decode(bytes.Ok()));
2908
3089
  } catch (e) {
2909
3090
  throw this.logger.Error().Err(e).Msg("error parse").AsError();
2910
3091
  }
@@ -2931,73 +3112,98 @@ var WALStoreImpl = class extends BaseStoreImpl {
2931
3112
  return import_cement10.Result.Ok(void 0);
2932
3113
  }
2933
3114
  destroy() {
3115
+ this.logger.Debug().Msg("destroy");
2934
3116
  return this.gateway.destroy(this.url());
2935
3117
  }
2936
3118
  };
2937
3119
 
2938
3120
  // src/blockstore/store-factory.ts
3121
+ init_types();
3122
+
3123
+ // src/blockstore/register-store-protocol.ts
3124
+ var import_cement14 = require("@adviser/cement");
3125
+ init_version();
3126
+
3127
+ // src/runtime/gateways/memory/gateway.ts
3128
+ var import_cement11 = require("@adviser/cement");
3129
+ init_types();
3130
+
3131
+ // src/runtime/gateways/memory/version.ts
3132
+ var MEMORY_VERSION = "v0.19-memory";
3133
+
3134
+ // src/runtime/gateways/memory/gateway.ts
2939
3135
  init_utils();
2940
- function ensureIsIndex(url, isIndex) {
2941
- if (isIndex) {
2942
- return url.build().setParam("index", isIndex).URI();
3136
+ var MemoryGateway = class {
3137
+ constructor(memorys) {
3138
+ this.memorys = memorys;
2943
3139
  }
2944
- return url.build().delParam("index").URI();
2945
- }
2946
- function ensureName(name, url) {
2947
- if (!url.hasParam("name")) {
2948
- return url.build().setParam("name", name).URI();
3140
+ buildUrl(baseUrl, key) {
3141
+ return Promise.resolve(import_cement11.Result.Ok(baseUrl.build().setParam("key" /* KEY */, key).URI()));
2949
3142
  }
2950
- return url;
2951
- }
3143
+ start(baseUrl) {
3144
+ return Promise.resolve(import_cement11.Result.Ok(baseUrl.build().setParam("version" /* VERSION */, MEMORY_VERSION).URI()));
3145
+ }
3146
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3147
+ close(baseUrl) {
3148
+ return Promise.resolve(import_cement11.Result.Ok(void 0));
3149
+ }
3150
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3151
+ destroy(baseUrl) {
3152
+ this.memorys.clear();
3153
+ return Promise.resolve(import_cement11.Result.Ok(void 0));
3154
+ }
3155
+ put(url, body) {
3156
+ this.memorys.set(url.toString(), body);
3157
+ return Promise.resolve(import_cement11.Result.Ok(void 0));
3158
+ }
3159
+ // get could return a NotFoundError if the key is not found
3160
+ get(url) {
3161
+ const x = this.memorys.get(url.toString());
3162
+ if (x === void 0) {
3163
+ return Promise.resolve(import_cement11.Result.Err(new NotFoundError("not found")));
3164
+ }
3165
+ return Promise.resolve(import_cement11.Result.Ok(x));
3166
+ }
3167
+ delete(url) {
3168
+ this.memorys.delete(url.toString());
3169
+ return Promise.resolve(import_cement11.Result.Ok(void 0));
3170
+ }
3171
+ };
3172
+ var MemoryTestGateway = class {
3173
+ constructor(memorys) {
3174
+ this.memorys = memorys;
3175
+ }
3176
+ async get(url, key) {
3177
+ return this.memorys.get(url.build().setParam("key" /* KEY */, key).toString());
3178
+ }
3179
+ };
3180
+
3181
+ // src/blockstore/register-store-protocol.ts
2952
3182
  var storeFactory = /* @__PURE__ */ new Map();
2953
- function buildURL(optURL, loader) {
2954
- const storeOpts = loader.ebOpts.store;
2955
- const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
2956
- let obuUrl;
2957
- if (obuItem && obuItem.overrideBaseURL) {
2958
- obuUrl = import_cement13.URI.from(obuItem.overrideBaseURL);
2959
- }
2960
- const ret = ensureIsIndex(
2961
- import_cement13.URI.from(optURL || obuUrl || dataDir(loader.sthis, loader.name, storeOpts.stores?.base)),
2962
- storeOpts.isIndex
2963
- );
2964
- return ret;
2965
- }
2966
- var onceGateway = new import_cement13.KeyedResolvOnce();
2967
- async function getGatewayFromURL(url, sthis) {
2968
- return onceGateway.get(url.toString()).once(async () => {
2969
- const item = storeFactory.get(url.protocol);
2970
- if (item) {
2971
- const ret = {
2972
- gateway: await item.gateway(sthis),
2973
- test: await item.test(sthis)
2974
- };
2975
- const res = await ret.gateway.start(url);
2976
- if (res.isErr()) {
2977
- sthis.logger.Error().Result("start", res).Msg("start failed");
2978
- return void 0;
2979
- }
2980
- return ret;
3183
+ function getDefaultURI(sthis, protocol) {
3184
+ if (protocol) {
3185
+ if (!protocol.endsWith(":")) {
3186
+ protocol += ":";
2981
3187
  }
2982
- sthis.logger.Warn().Url(url).Msg("unsupported protocol");
2983
- return void 0;
2984
- });
3188
+ const gfi = storeFactory.get(protocol);
3189
+ if (gfi) {
3190
+ return gfi.defaultURI(sthis);
3191
+ }
3192
+ }
3193
+ const found = Array.from(storeFactory.values()).find((item) => item.isDefault);
3194
+ if (!found) {
3195
+ throw sthis.logger.Error().Msg("no default found").AsError();
3196
+ }
3197
+ return found.defaultURI(sthis);
2985
3198
  }
2986
3199
  function registerStoreProtocol(item) {
2987
3200
  let protocol = item.protocol;
2988
3201
  if (!protocol.endsWith(":")) {
2989
3202
  protocol += ":";
2990
3203
  }
2991
- if (storeFactory.has(protocol)) {
2992
- if (!item.overrideBaseURL && storeFactory.get(protocol) !== item) {
2993
- throw new Error(`we need a logger here`);
2994
- return () => {
2995
- };
2996
- }
2997
- }
2998
- if (item.overrideBaseURL) {
3204
+ if (item.isDefault) {
2999
3205
  Array.from(storeFactory.values()).forEach((items) => {
3000
- items.overrideBaseURL = void 0;
3206
+ items.isDefault = false;
3001
3207
  });
3002
3208
  }
3003
3209
  storeFactory.set(protocol, item);
@@ -3005,134 +3211,186 @@ function registerStoreProtocol(item) {
3005
3211
  storeFactory.delete(protocol);
3006
3212
  };
3007
3213
  }
3008
- var onceDataStoreFactory = new import_cement13.KeyedResolvOnce();
3009
- async function dataStoreFactory(loader) {
3010
- const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.data, loader)).build().setParam("store", "data").URI();
3011
- const sthis = ensureSuperLog(loader.sthis, "dataStoreFactory", { url: url.toString() });
3012
- return onceDataStoreFactory.get(url.toString()).once(async () => {
3013
- const gateway = await getGatewayFromURL(url, sthis);
3014
- if (!gateway) {
3015
- throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
3016
- }
3017
- const store = new DataStoreImpl(sthis, loader.name, url, {
3018
- gateway: gateway.gateway,
3019
- keybag: () => getKeyBag(loader.sthis, {
3020
- ...loader.ebOpts.keyBag
3021
- })
3022
- });
3023
- return store;
3214
+ function getGatewayFactoryItem(protocol) {
3215
+ return storeFactory.get(protocol);
3216
+ }
3217
+ function fileGatewayFactoryItem() {
3218
+ return {
3219
+ protocol: "file:",
3220
+ isDefault: true,
3221
+ defaultURI: (sthis) => {
3222
+ return import_cement14.BuildURI.from("file://").pathname(`${sthis.env.get("HOME")}/.fireproof/${FILESTORE_VERSION.replace(/-.*$/, "")}`).URI();
3223
+ },
3224
+ gateway: async (sthis) => {
3225
+ const { FileGateway: FileGateway2 } = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
3226
+ return new FileGateway2(sthis);
3227
+ },
3228
+ test: async (sthis) => {
3229
+ const { FileTestStore: FileTestStore2 } = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
3230
+ return new FileTestStore2(sthis);
3231
+ }
3232
+ };
3233
+ }
3234
+ if ((0, import_cement14.runtimeFn)().isBrowser) {
3235
+ registerStoreProtocol({
3236
+ protocol: "indexdb:",
3237
+ isDefault: true,
3238
+ defaultURI: () => {
3239
+ return import_cement14.BuildURI.from("indexdb://").pathname("fp").URI();
3240
+ },
3241
+ gateway: async (logger) => {
3242
+ const { IndexDBGateway: IndexDBGateway2 } = await Promise.resolve().then(() => (init_gateway2(), gateway_exports2));
3243
+ return new IndexDBGateway2(logger);
3244
+ },
3245
+ test: async (logger) => {
3246
+ const { IndexDBTestStore: IndexDBTestStore2 } = await Promise.resolve().then(() => (init_gateway2(), gateway_exports2));
3247
+ return new IndexDBTestStore2(logger);
3248
+ }
3024
3249
  });
3250
+ } else {
3251
+ registerStoreProtocol(fileGatewayFactoryItem());
3025
3252
  }
3026
- var onceMetaStoreFactory = new import_cement13.KeyedResolvOnce();
3027
- async function metaStoreFactory(loader) {
3028
- const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.meta, loader)).build().setParam("store", "meta").URI();
3029
- const sthis = ensureSuperLog(loader.sthis, "metaStoreFactory", { url: () => url.toString() });
3030
- return onceMetaStoreFactory.get(url.toString()).once(async () => {
3031
- sthis.logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
3032
- const gateway = await getGatewayFromURL(url, sthis);
3033
- if (!gateway) {
3034
- throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
3035
- }
3036
- const store = new MetaStoreImpl(loader.sthis, loader.name, url, {
3037
- gateway: gateway.gateway,
3038
- keybag: () => getKeyBag(loader.sthis, {
3039
- ...loader.ebOpts.keyBag
3040
- })
3041
- });
3042
- return store;
3253
+ var memory = /* @__PURE__ */ new Map();
3254
+ registerStoreProtocol({
3255
+ protocol: "memory:",
3256
+ isDefault: false,
3257
+ defaultURI: () => {
3258
+ return import_cement14.BuildURI.from("memory://").pathname("ram").URI();
3259
+ },
3260
+ gateway: async () => {
3261
+ return new MemoryGateway(memory);
3262
+ },
3263
+ test: async () => {
3264
+ return new MemoryTestGateway(memory);
3265
+ }
3266
+ });
3267
+
3268
+ // src/blockstore/store-factory.ts
3269
+ var onceGateway = new import_cement15.KeyedResolvOnce();
3270
+ var gatewayInstances = new import_cement15.KeyedResolvOnce();
3271
+ async function getStartedGateway(sthis, url) {
3272
+ return onceGateway.get(url.toString()).once(async () => {
3273
+ const item = getGatewayFactoryItem(url.protocol);
3274
+ if (item) {
3275
+ const ret = {
3276
+ url,
3277
+ ...await gatewayInstances.get(url.protocol).once(async () => ({
3278
+ gateway: await item.gateway(sthis),
3279
+ test: await item.test(sthis)
3280
+ }))
3281
+ };
3282
+ const res = await ret.gateway.start(url);
3283
+ if (res.isErr()) {
3284
+ return import_cement15.Result.Err(sthis.logger.Error().Result("start", res).Msg("start failed").AsError());
3285
+ }
3286
+ ret.url = res.Ok();
3287
+ return import_cement15.Result.Ok(ret);
3288
+ }
3289
+ return import_cement15.Result.Err(sthis.logger.Warn().Url(url).Msg("unsupported protocol").AsError());
3043
3290
  });
3044
3291
  }
3045
- var onceRemoteWalFactory = new import_cement13.KeyedResolvOnce();
3046
- async function remoteWalFactory(loader) {
3047
- const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.wal, loader)).build().setParam("store", "wal").URI();
3048
- const sthis = ensureSuperLog(loader.sthis, "remoteWalFactory", { url: url.toString() });
3049
- return onceRemoteWalFactory.get(url.toString()).once(async () => {
3050
- const gateway = await getGatewayFromURL(url, sthis);
3051
- if (!gateway) {
3052
- throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
3053
- }
3054
- sthis.logger.Debug().Str("prepared", url.toString()).Msg("produced");
3055
- const store = new WALStoreImpl(loader, url, {
3056
- gateway: gateway.gateway,
3057
- keybag: () => getKeyBag(loader.sthis, {
3058
- ...loader.ebOpts.keyBag
3059
- })
3060
- });
3061
- return store;
3292
+ async function dataStoreFactory(sfi) {
3293
+ const storeUrl = sfi.url.build().setParam("store" /* STORE */, "data").URI();
3294
+ const rgateway = await getStartedGateway(sfi.sthis, storeUrl);
3295
+ if (rgateway.isErr()) {
3296
+ throw sfi.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
3297
+ }
3298
+ const gateway = rgateway.Ok();
3299
+ const store = new DataStoreImpl(sfi.sthis, gateway.url, {
3300
+ gateway: gateway.gateway,
3301
+ keybag: sfi.keybag
3302
+ });
3303
+ return store;
3304
+ }
3305
+ async function metaStoreFactory(sfi) {
3306
+ const storeUrl = sfi.url.build().setParam("store" /* STORE */, "meta").URI();
3307
+ const rgateway = await getStartedGateway(sfi.sthis, storeUrl);
3308
+ if (rgateway.isErr()) {
3309
+ throw sfi.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
3310
+ }
3311
+ const gateway = rgateway.Ok();
3312
+ const store = new MetaStoreImpl(sfi.sthis, gateway.url, {
3313
+ gateway: gateway.gateway,
3314
+ keybag: sfi.keybag
3062
3315
  });
3316
+ return store;
3317
+ }
3318
+ async function WALStoreFactory(sfi) {
3319
+ const storeUrl = sfi.url.build().setParam("store" /* STORE */, "wal").URI();
3320
+ const rgateway = await getStartedGateway(sfi.sthis, storeUrl);
3321
+ if (rgateway.isErr()) {
3322
+ throw sfi.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
3323
+ }
3324
+ const gateway = rgateway.Ok();
3325
+ const store = new WALStoreImpl(sfi.sthis, gateway.url, {
3326
+ gateway: gateway.gateway,
3327
+ keybag: sfi.keybag
3328
+ });
3329
+ return store;
3063
3330
  }
3064
3331
  async function testStoreFactory(url, sthis) {
3065
- sthis = ensureSuperLog(sthis, "testStoreFactory");
3066
- const gateway = await getGatewayFromURL(url, sthis);
3067
- if (!gateway) {
3332
+ const rgateway = await getStartedGateway(sthis, url);
3333
+ if (!rgateway) {
3068
3334
  throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
3069
3335
  }
3070
- return gateway.test;
3336
+ return rgateway.Ok().test;
3071
3337
  }
3072
- async function ensureStart(store, logger) {
3338
+ async function ensureStart(store) {
3073
3339
  const ret = await store.start();
3074
3340
  if (ret.isErr()) {
3075
- throw logger.Error().Result("start", ret).Msg("start failed").AsError();
3341
+ throw store.logger.Error().Result("start", ret).Msg("start failed").AsError();
3076
3342
  }
3077
- logger.Debug().Url(ret.Ok(), "prepared").Msg("produced");
3343
+ store.logger.Debug().Url(ret.Ok(), "prepared").Msg("produced");
3078
3344
  return store;
3079
3345
  }
3080
- function toStoreRuntime(opts, sthis) {
3081
- const logger = ensureLogger(sthis, "toStoreRuntime", {});
3346
+ function ensureStoreEnDeFile(ende) {
3347
+ ende = ende || {};
3082
3348
  return {
3083
- makeMetaStore: async (loader) => {
3084
- logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
3085
- return ensureStart(await (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader), logger);
3086
- },
3087
- makeDataStore: async (loader) => {
3088
- logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
3089
- return ensureStart(await (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader), logger);
3090
- },
3091
- makeWALStore: async (loader) => {
3092
- logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeWALStore).Msg("makeRemoteWAL");
3093
- return ensureStart(await (loader.ebOpts.store.makeWALStore || remoteWalFactory)(loader), logger);
3094
- },
3095
- encodeFile: opts.encodeFile || encodeFile,
3096
- decodeFile: opts.decodeFile || decodeFile
3349
+ encodeFile: ende.encodeFile || encodeFile,
3350
+ decodeFile: ende.decodeFile || decodeFile
3097
3351
  };
3098
3352
  }
3099
- registerStoreProtocol({
3100
- protocol: "file:",
3101
- gateway: async (sthis) => {
3102
- const { FileGateway: FileGateway2 } = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
3103
- return new FileGateway2(sthis);
3104
- },
3105
- test: async (sthis) => {
3106
- const { FileTestStore: FileTestStore2 } = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
3107
- return new FileTestStore2(sthis);
3108
- }
3109
- });
3110
- registerStoreProtocol({
3111
- protocol: "indexdb:",
3112
- gateway: async (sthis) => {
3113
- const { IndexDBGateway: IndexDBGateway2 } = await Promise.resolve().then(() => (init_gateway2(), gateway_exports2));
3114
- return new IndexDBGateway2(sthis);
3115
- },
3116
- test: async (sthis) => {
3117
- const { IndexDBTestStore: IndexDBTestStore2 } = await Promise.resolve().then(() => (init_gateway2(), gateway_exports2));
3118
- return new IndexDBTestStore2(sthis);
3119
- }
3120
- });
3353
+ function toStoreRuntime(sthis, endeOpts = {}) {
3354
+ return {
3355
+ makeMetaStore: async (sfi) => ensureStart(await metaStoreFactory(sfi)),
3356
+ // async (loader: Loadable) => {
3357
+ // logger
3358
+ // .Debug()
3359
+ // .Str("fromOpts", "" + !!endeOpts.func?.makeMetaStore)
3360
+ // .Msg("makeMetaStore");
3361
+ // return ensureStart(await (endeOpts.func?.makeMetaStore || metaStoreFactory)(loader), logger);
3362
+ // },
3363
+ makeDataStore: async (sfi) => ensureStart(await dataStoreFactory(sfi)),
3364
+ // async (loader: Loadable) => {
3365
+ // logger
3366
+ // .Debug()
3367
+ // .Str("fromOpts", "" + !!endeOpts.func?.makeDataStore)
3368
+ // .Msg("makeDataStore");
3369
+ // return ensureStart(await (endeOpts.func?.makeDataStore || dataStoreFactory)(loader), logger);
3370
+ // },
3371
+ makeWALStore: async (sfi) => ensureStart(await WALStoreFactory(sfi)),
3372
+ // async (loader: Loadable) => {
3373
+ // logger
3374
+ // .Debug()
3375
+ // .Str("fromOpts", "" + !!endeOpts.func?.makeWALStore)
3376
+ // .Msg("makeRemoteWAL");
3377
+ // return ensureStart(await (endeOpts.func?.makeWALStore || remoteWalFactory)(loader), logger);
3378
+ // },
3379
+ ...ensureStoreEnDeFile(endeOpts)
3380
+ };
3381
+ }
3382
+
3383
+ // src/blockstore/connection-base.ts
3384
+ init_types();
3121
3385
 
3122
3386
  // src/blockstore/store-remote.ts
3123
3387
  async function RemoteDataStore(sthis, name, url, opts) {
3124
- const ds = new DataStoreImpl(sthis, name, url, opts);
3388
+ const ds = new DataStoreImpl(sthis, url, opts);
3125
3389
  await ds.start();
3126
3390
  return ds;
3127
3391
  }
3128
3392
  async function RemoteMetaStore(sthis, name, url, opts) {
3129
- const ms = new MetaStoreImpl(
3130
- sthis,
3131
- name,
3132
- url,
3133
- opts
3134
- /* , true*/
3135
- );
3393
+ const ms = new MetaStoreImpl(sthis, url, opts);
3136
3394
  await ms.start();
3137
3395
  return ms;
3138
3396
  }
@@ -3157,14 +3415,17 @@ var ConnectionBase = class {
3157
3415
  if (!loader) throw this.logger.Error().Msg("connectMeta_X: loader is required").AsError();
3158
3416
  this.loader = loader;
3159
3417
  await this.onConnect();
3160
- const metaUrl = this.url.build().defParam("store", "meta").URI();
3161
- const gateway = await getGatewayFromURL(metaUrl, this.loader.sthis);
3162
- if (!gateway) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: gateway is required").AsError();
3418
+ const metaUrl = this.url.build().defParam("store" /* STORE */, "meta").URI();
3419
+ const rgateway = await getStartedGateway(loader.sthis, metaUrl);
3420
+ if (rgateway.isErr())
3421
+ throw this.logger.Error().Result("err", rgateway).Url(metaUrl).Msg("connectMeta_X: gateway is required").AsError();
3422
+ const name = metaUrl.toString();
3163
3423
  const dbName = metaUrl.getParam("name");
3164
- if (!dbName) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: name is required").AsError();
3165
- const remote = await RemoteMetaStore(loader.sthis, dbName, metaUrl, {
3424
+ if (!dbName) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: dbName is required").AsError();
3425
+ const gateway = rgateway.Ok();
3426
+ const remote = await RemoteMetaStore(loader.sthis, name, metaUrl, {
3166
3427
  gateway: gateway.gateway,
3167
- keybag: () => getKeyBag(loader.sthis, loader.ebOpts.keyBag),
3428
+ keybag: await loader.keyBag(),
3168
3429
  loader
3169
3430
  });
3170
3431
  this.loader.remoteMetaStore = remote;
@@ -3177,14 +3438,15 @@ var ConnectionBase = class {
3177
3438
  async connectStorage_X({ loader }) {
3178
3439
  if (!loader) throw this.logger.Error().Msg("connectStorage_X: loader is required").AsError();
3179
3440
  this.loader = loader;
3180
- const dataUrl = this.url.build().defParam("store", "data").URI();
3181
- const gateway = await getGatewayFromURL(dataUrl, this.loader.sthis);
3182
- if (!gateway) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: gateway is required").AsError();
3441
+ const dataUrl = this.url.build().defParam("store" /* STORE */, "data").URI();
3442
+ const rgateway = await getStartedGateway(loader.sthis, dataUrl);
3443
+ if (rgateway.isErr())
3444
+ throw this.logger.Error().Result("err", rgateway).Url(dataUrl).Msg("connectStorage_X: gateway is required").AsError();
3183
3445
  const name = dataUrl.getParam("name");
3184
3446
  if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: name is required").AsError;
3185
3447
  loader.remoteCarStore = await RemoteDataStore(loader.sthis, name, this.url, {
3186
- gateway: gateway.gateway,
3187
- keybag: () => getKeyBag(loader.sthis, this.loader?.ebOpts.keyBag)
3448
+ gateway: rgateway.Ok().gateway,
3449
+ keybag: await loader.keyBag()
3188
3450
  });
3189
3451
  loader.remoteFileStore = loader.remoteCarStore;
3190
3452
  }
@@ -3224,6 +3486,13 @@ var ConnectionBase = class {
3224
3486
  };
3225
3487
 
3226
3488
  // src/crdt-helpers.ts
3489
+ var import_link = require("multiformats/link");
3490
+ var import_sha25 = require("multiformats/hashes/sha2");
3491
+ var codec = __toESM(require("@ipld/dag-cbor"), 1);
3492
+ var import_crdt = require("@web3-storage/pail/crdt");
3493
+ var import_clock2 = require("@web3-storage/pail/clock");
3494
+ var Batch = __toESM(require("@web3-storage/pail/crdt/batch"), 1);
3495
+ init_types();
3227
3496
  function time(tag) {
3228
3497
  }
3229
3498
  function timeEnd(tag) {
@@ -3271,7 +3540,7 @@ async function writeDocContent(store, blocks, update, logger) {
3271
3540
  await processFiles(store, blocks, update.value, logger);
3272
3541
  value = { doc: update.value };
3273
3542
  }
3274
- const block = await encode({ value, hasher: import_sha25.sha256, codec });
3543
+ const block = await encode3({ value, hasher: import_sha25.sha256, codec });
3275
3544
  blocks.putSync(block.cid, block.bytes);
3276
3545
  return block.cid;
3277
3546
  }
@@ -3362,7 +3631,7 @@ function readFileset(blocks, files, isPublic = false) {
3362
3631
  async function getValueFromLink(blocks, link, logger) {
3363
3632
  const block = await blocks.get(link);
3364
3633
  if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
3365
- const { value } = await decode({ bytes: block.bytes, hasher: import_sha25.sha256, codec });
3634
+ const { value } = await decode3({ bytes: block.bytes, hasher: import_sha25.sha256, codec });
3366
3635
  const cvalue = {
3367
3636
  ...value,
3368
3637
  cid: link
@@ -3483,16 +3752,19 @@ async function doCompact(blockLog, head, logger) {
3483
3752
  async function getBlock(blocks, cidString) {
3484
3753
  const block = await blocks.get((0, import_link.parse)(cidString));
3485
3754
  if (!block) throw new Error(`Missing block ${cidString}`);
3486
- const { cid, value } = await decode({ bytes: block.bytes, codec, hasher: import_sha25.sha256 });
3755
+ const { cid, value } = await decode3({ bytes: block.bytes, codec, hasher: import_sha25.sha256 });
3487
3756
  return new Block({ cid, value, bytes: block.bytes });
3488
3757
  }
3489
3758
 
3759
+ // src/indexer.ts
3760
+ init_types();
3761
+
3490
3762
  // src/indexer-helpers.ts
3491
3763
  var import_sha26 = require("multiformats/hashes/sha2");
3492
3764
  var codec2 = __toESM(require("@ipld/dag-cbor"), 1);
3493
3765
  var import_charwise = __toESM(require("charwise"), 1);
3494
3766
  var DbIndex = __toESM(require("prolly-trees/db-index"), 1);
3495
- var import_utils16 = require("prolly-trees/utils");
3767
+ var import_utils15 = require("prolly-trees/utils");
3496
3768
  var import_cache = require("prolly-trees/cache");
3497
3769
  var IndexTree = class {
3498
3770
  };
@@ -3500,17 +3772,17 @@ function refCompare(aRef, bRef) {
3500
3772
  if (Number.isNaN(aRef)) return -1;
3501
3773
  if (Number.isNaN(bRef)) throw new Error("ref may not be Infinity or NaN");
3502
3774
  if (aRef === Infinity) return 1;
3503
- return (0, import_utils16.simpleCompare)(aRef, bRef);
3775
+ return (0, import_utils15.simpleCompare)(aRef, bRef);
3504
3776
  }
3505
3777
  function compare(a, b) {
3506
3778
  const [aKey, aRef] = a;
3507
3779
  const [bKey, bRef] = b;
3508
- const comp = (0, import_utils16.simpleCompare)(aKey, bKey);
3780
+ const comp = (0, import_utils15.simpleCompare)(aKey, bKey);
3509
3781
  if (comp !== 0) return comp;
3510
3782
  return refCompare(aRef, bRef);
3511
3783
  }
3512
- var byKeyOpts = { cache: import_cache.nocache, chunker: (0, import_utils16.bf)(30), codec: codec2, hasher: import_sha26.sha256, compare };
3513
- var byIdOpts = { cache: import_cache.nocache, chunker: (0, import_utils16.bf)(30), codec: codec2, hasher: import_sha26.sha256, compare: import_utils16.simpleCompare };
3784
+ var byKeyOpts = { cache: import_cache.nocache, chunker: (0, import_utils15.bf)(30), codec: codec2, hasher: import_sha26.sha256, compare };
3785
+ var byIdOpts = { cache: import_cache.nocache, chunker: (0, import_utils15.bf)(30), codec: codec2, hasher: import_sha26.sha256, compare: import_utils15.simpleCompare };
3514
3786
  function indexEntriesForChanges(changes, mapFn) {
3515
3787
  const indexEntries = [];
3516
3788
  changes.forEach(({ id: key, value, del }) => {
@@ -3541,7 +3813,8 @@ function makeProllyGetBlock(blocks) {
3541
3813
  return create({ cid, bytes, hasher: import_sha26.sha256, codec: codec2 });
3542
3814
  };
3543
3815
  }
3544
- async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
3816
+ async function bulkIndex(logger, tblocks, inIndex, indexEntries, opts) {
3817
+ logger.Debug().Msg("enter bulkIndex");
3545
3818
  if (!indexEntries.length) return inIndex;
3546
3819
  if (!inIndex.root) {
3547
3820
  if (!inIndex.cid) {
@@ -3558,18 +3831,22 @@ async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
3558
3831
  returnNode = node;
3559
3832
  }
3560
3833
  if (!returnNode || !returnRootBlock) throw new Error("failed to create index");
3834
+ logger.Debug().Msg("exit !root bulkIndex");
3561
3835
  return { root: returnNode, cid: returnRootBlock.cid };
3562
3836
  } else {
3563
3837
  inIndex.root = await DbIndex.load({ cid: inIndex.cid, get: makeProllyGetBlock(tblocks), ...opts });
3564
3838
  }
3565
3839
  }
3840
+ logger.Debug().Msg("pre bulk bulkIndex");
3566
3841
  const { root: root3, blocks: newBlocks } = await inIndex.root.bulk(indexEntries);
3567
3842
  if (root3) {
3843
+ logger.Debug().Msg("pre root put bulkIndex");
3568
3844
  for await (const block of newBlocks) {
3569
3845
  await tblocks.put(block.cid, block.bytes);
3570
3846
  }
3571
3847
  return { root: root3, cid: (await root3.block).cid };
3572
3848
  } else {
3849
+ logger.Debug().Msg("pre !root bulkIndex");
3573
3850
  return { root: void 0, cid: void 0 };
3574
3851
  }
3575
3852
  }
@@ -3610,17 +3887,17 @@ function encodeKey(key) {
3610
3887
 
3611
3888
  // src/indexer.ts
3612
3889
  init_utils();
3613
- function index(sthis, { _crdt }, name, mapFn, meta) {
3614
- if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
3615
- if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
3616
- if (_crdt.indexers.has(name)) {
3617
- const idx = _crdt.indexers.get(name);
3890
+ function index(refDb, name, mapFn, meta) {
3891
+ if (mapFn && meta) throw refDb.crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
3892
+ if (mapFn && mapFn.constructor.name !== "Function") throw refDb.crdt.logger.Error().Msg("mapFn must be a function").AsError();
3893
+ if (refDb.crdt.indexers.has(name)) {
3894
+ const idx = refDb.crdt.indexers.get(name);
3618
3895
  idx.applyMapFn(name, mapFn, meta);
3619
3896
  } else {
3620
- const idx = new Index(sthis, _crdt, name, mapFn, meta);
3621
- _crdt.indexers.set(name, idx);
3897
+ const idx = new Index(refDb.crdt.sthis, refDb.crdt, name, mapFn, meta);
3898
+ refDb.crdt.indexers.set(name, idx);
3622
3899
  }
3623
- return _crdt.indexers.get(name);
3900
+ return refDb.crdt.indexers.get(name);
3624
3901
  }
3625
3902
  var Index = class {
3626
3903
  constructor(sthis, crdt, name, mapFn, meta) {
@@ -3639,18 +3916,9 @@ var Index = class {
3639
3916
  return Promise.all([this.blockstore.ready(), this.crdt.ready()]).then(() => {
3640
3917
  });
3641
3918
  }
3642
- close() {
3643
- return Promise.all([this.blockstore.close(), this.crdt.close()]).then(() => {
3644
- });
3645
- }
3646
- destroy() {
3647
- return Promise.all([this.blockstore.destroy(), this.crdt.destroy()]).then(() => {
3648
- });
3649
- }
3650
3919
  applyMapFn(name, mapFn, meta) {
3651
3920
  if (mapFn && meta) throw this.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
3652
3921
  if (this.name && this.name !== name) throw this.logger.Error().Msg("cannot change name").AsError();
3653
- this.name = name;
3654
3922
  try {
3655
3923
  if (meta) {
3656
3924
  if (this.indexHead && this.indexHead.map((c) => c.toString()).join() !== meta.head.map((c) => c.toString()).join()) {
@@ -3698,9 +3966,13 @@ var Index = class {
3698
3966
  }
3699
3967
  }
3700
3968
  async query(opts = {}) {
3969
+ this.logger.Debug().Msg("enter query");
3701
3970
  await this.ready();
3971
+ this.logger.Debug().Msg("post ready query");
3702
3972
  await this._updateIndex();
3973
+ this.logger.Debug().Msg("post _updateIndex query");
3703
3974
  await this._hydrateIndex();
3975
+ this.logger.Debug().Msg("post _hydrateIndex query");
3704
3976
  if (!this.byKey.root) {
3705
3977
  return await applyQuery(this.crdt, { result: [] }, opts);
3706
3978
  }
@@ -3756,13 +4028,16 @@ var Index = class {
3756
4028
  }
3757
4029
  async _updateIndex() {
3758
4030
  await this.ready();
4031
+ this.logger.Debug().Msg("enter _updateIndex");
3759
4032
  if (this.initError) throw this.initError;
3760
4033
  if (!this.mapFn) throw this.logger.Error().Msg("No map function defined").AsError();
3761
4034
  let result, head;
3762
4035
  if (!this.indexHead || this.indexHead.length === 0) {
3763
4036
  ({ result, head } = await this.crdt.allDocs());
4037
+ this.logger.Debug().Msg("enter crdt.allDocs");
3764
4038
  } else {
3765
4039
  ({ result, head } = await this.crdt.changes(this.indexHead));
4040
+ this.logger.Debug().Msg("enter crdt.changes");
3766
4041
  }
3767
4042
  if (result.length === 0) {
3768
4043
  this.indexHead = head;
@@ -3795,9 +4070,22 @@ var Index = class {
3795
4070
  if (result.length === 0) {
3796
4071
  return indexerMeta;
3797
4072
  }
4073
+ this.logger.Debug().Msg("pre this.blockstore.transaction");
3798
4074
  const { meta } = await this.blockstore.transaction(async (tblocks) => {
3799
- this.byId = await bulkIndex(tblocks, this.byId, removeIdIndexEntries.concat(byIdIndexEntries), byIdOpts);
3800
- this.byKey = await bulkIndex(tblocks, this.byKey, staleKeyIndexEntries.concat(indexEntries), byKeyOpts);
4075
+ this.byId = await bulkIndex(
4076
+ this.logger,
4077
+ tblocks,
4078
+ this.byId,
4079
+ removeIdIndexEntries.concat(byIdIndexEntries),
4080
+ byIdOpts
4081
+ );
4082
+ this.byKey = await bulkIndex(
4083
+ this.logger,
4084
+ tblocks,
4085
+ this.byKey,
4086
+ staleKeyIndexEntries.concat(indexEntries),
4087
+ byKeyOpts
4088
+ );
3801
4089
  this.indexHead = head;
3802
4090
  if (this.byId.cid && this.byKey.cid) {
3803
4091
  const idxMeta = {
@@ -3809,8 +4097,10 @@ var Index = class {
3809
4097
  };
3810
4098
  indexerMeta.indexes?.set(this.name, idxMeta);
3811
4099
  }
4100
+ this.logger.Debug().Any("indexerMeta", new Array(indexerMeta.indexes?.entries())).Msg("exit this.blockstore.transaction fn");
3812
4101
  return indexerMeta;
3813
4102
  });
4103
+ this.logger.Debug().Msg("post this.blockstore.transaction");
3814
4104
  return meta;
3815
4105
  }
3816
4106
  };
@@ -3818,7 +4108,8 @@ var Index = class {
3818
4108
  // src/crdt-clock.ts
3819
4109
  var import_clock3 = require("@web3-storage/pail/clock");
3820
4110
  var import_crdt2 = require("@web3-storage/pail/crdt");
3821
- var import_cement14 = require("@adviser/cement");
4111
+ var import_cement16 = require("@adviser/cement");
4112
+ init_types();
3822
4113
 
3823
4114
  // src/apply-head-queue.ts
3824
4115
  function applyHeadQueue(worker, logger) {
@@ -3876,7 +4167,7 @@ var CRDTClock = class {
3876
4167
  this.zoomers = /* @__PURE__ */ new Set();
3877
4168
  this.watchers = /* @__PURE__ */ new Set();
3878
4169
  this.emptyWatchers = /* @__PURE__ */ new Set();
3879
- this._ready = new import_cement14.ResolveOnce();
4170
+ this._ready = new import_cement16.ResolveOnce();
3880
4171
  this.blockstore = blockstore;
3881
4172
  this.logger = ensureLogger(blockstore.sthis, "CRDTClock");
3882
4173
  this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
@@ -3939,21 +4230,23 @@ var CRDTClock = class {
3939
4230
  throw this.logger.Error().Msg("missing blockstore").AsError();
3940
4231
  }
3941
4232
  await validateBlocks(this.logger, newHead, this.blockstore);
3942
- const { meta } = await this.blockstore.transaction(
3943
- async (tblocks) => {
3944
- const advancedHead = await advanceBlocks(this.logger, newHead, tblocks, this.head);
3945
- const result = await (0, import_crdt2.root)(tblocks, advancedHead);
3946
- for (const { cid, bytes } of [
3947
- ...result.additions
3948
- // ...result.removals
3949
- ]) {
3950
- tblocks.putSync(cid, bytes);
3951
- }
3952
- return { head: advancedHead };
3953
- },
3954
- { noLoader, add: false }
3955
- );
3956
- this.setHead(meta.head);
4233
+ if (!this.transaction) {
4234
+ this.transaction = this.blockstore.openTransaction({ noLoader, add: false });
4235
+ }
4236
+ const tblocks = this.transaction;
4237
+ const advancedHead = await advanceBlocks(this.logger, newHead, tblocks, this.head);
4238
+ const result = await (0, import_crdt2.root)(tblocks, advancedHead);
4239
+ for (const { cid, bytes } of [
4240
+ ...result.additions
4241
+ // ...result.removals
4242
+ ]) {
4243
+ tblocks.putSync(cid, bytes);
4244
+ }
4245
+ if (!noLoader) {
4246
+ await this.blockstore.commitTransaction(tblocks, { head: advancedHead }, { add: false, noLoader });
4247
+ this.transaction = void 0;
4248
+ }
4249
+ this.setHead(advancedHead);
3957
4250
  }
3958
4251
  };
3959
4252
  function sortClockHead(clockHead) {
@@ -3986,15 +4279,13 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
3986
4279
  // src/crdt.ts
3987
4280
  init_utils();
3988
4281
  var CRDT = class {
3989
- constructor(sthis, name, opts = {}) {
4282
+ constructor(sthis, opts) {
3990
4283
  this.indexers = /* @__PURE__ */ new Map();
3991
- this.onceReady = new import_cement15.ResolveOnce();
4284
+ this.onceReady = new import_cement17.ResolveOnce();
3992
4285
  this.sthis = sthis;
3993
- this.name = name;
3994
4286
  this.logger = ensureLogger(sthis, "CRDT");
3995
4287
  this.opts = opts;
3996
4288
  this.blockstore = blockstoreFactory(sthis, {
3997
- name,
3998
4289
  applyMeta: async (meta) => {
3999
4290
  const crdtMeta = meta;
4000
4291
  if (!crdtMeta.head) throw this.logger.Error().Msg("missing head").AsError();
@@ -4004,23 +4295,27 @@ var CRDT = class {
4004
4295
  await doCompact(blocks, this.clock.head, this.logger);
4005
4296
  return { head: this.clock.head };
4006
4297
  },
4007
- autoCompact: this.opts.autoCompact || 100,
4008
- store: { ...this.opts.store, isIndex: void 0 },
4009
- public: this.opts.public,
4010
- meta: this.opts.meta,
4011
- threshold: this.opts.threshold
4298
+ // autoCompact: this.opts.autoCompact || 100,
4299
+ storeRuntime: toStoreRuntime(this.sthis, this.opts.storeEnDe),
4300
+ storeUrls: this.opts.storeUrls.data,
4301
+ keyBag: this.opts.keyBag,
4302
+ // public: this.opts.public,
4303
+ meta: this.opts.meta
4304
+ // threshold: this.opts.threshold,
4012
4305
  });
4013
4306
  this.indexBlockstore = blockstoreFactory(sthis, {
4014
- name,
4307
+ // name: opts.name,
4015
4308
  applyMeta: async (meta) => {
4016
4309
  const idxCarMeta = meta;
4017
4310
  if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
4018
- for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
4019
- index(this.sthis, { _crdt: this }, name2, void 0, idx);
4311
+ for (const [name, idx] of Object.entries(idxCarMeta.indexes)) {
4312
+ index({ crdt: this }, name, void 0, idx);
4020
4313
  }
4021
4314
  },
4022
- store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
4023
- public: this.opts.public
4315
+ storeRuntime: toStoreRuntime(this.sthis, this.opts.storeEnDe),
4316
+ storeUrls: this.opts.storeUrls.idx,
4317
+ keyBag: this.opts.keyBag
4318
+ // public: this.opts.public,
4024
4319
  });
4025
4320
  this.clock = new CRDTClock(this.blockstore);
4026
4321
  this.clock.onZoom(() => {
@@ -4102,52 +4397,166 @@ var CRDT = class {
4102
4397
  };
4103
4398
 
4104
4399
  // src/database.ts
4400
+ init_types();
4105
4401
  init_utils();
4106
- var Database = class {
4107
- constructor(name, opts) {
4108
- this.opts = {};
4402
+ var databases = new import_cement18.KeyedResolvOnce();
4403
+ function toSortedArray(set) {
4404
+ if (!set) return [];
4405
+ return Object.entries(set).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => ({ [k]: v }));
4406
+ }
4407
+ function keyConfigOpts(sthis, name, opts) {
4408
+ return JSON.stringify(
4409
+ toSortedArray({
4410
+ name,
4411
+ stores: toSortedArray(JSON.parse(JSON.stringify(toStoreURIRuntime(sthis, name, opts?.storeUrls))))
4412
+ })
4413
+ );
4414
+ }
4415
+ function isDatabase(db) {
4416
+ return db instanceof DatabaseImpl || db instanceof DatabaseShell;
4417
+ }
4418
+ function DatabaseFactory(name, opts) {
4419
+ const sthis = ensureSuperThis(opts);
4420
+ return new DatabaseShell(
4421
+ databases.get(keyConfigOpts(sthis, name, opts)).once((key) => {
4422
+ const db = new DatabaseImpl(sthis, {
4423
+ name,
4424
+ meta: opts?.meta,
4425
+ keyBag: defaultKeyBagOpts(sthis, opts?.keyBag),
4426
+ storeUrls: toStoreURIRuntime(sthis, name, opts?.storeUrls),
4427
+ storeEnDe: {
4428
+ encodeFile,
4429
+ decodeFile,
4430
+ ...opts?.storeEnDe
4431
+ }
4432
+ });
4433
+ db.onClosed(() => {
4434
+ databases.unget(key);
4435
+ });
4436
+ return db;
4437
+ })
4438
+ );
4439
+ }
4440
+ var DatabaseShell = class {
4441
+ constructor(ref) {
4442
+ this.ref = ref;
4443
+ ref.addShell(this);
4444
+ }
4445
+ get id() {
4446
+ return this.ref.id;
4447
+ }
4448
+ get logger() {
4449
+ return this.ref.logger;
4450
+ }
4451
+ get sthis() {
4452
+ return this.ref.sthis;
4453
+ }
4454
+ get crdt() {
4455
+ return this.ref.crdt;
4456
+ }
4457
+ name() {
4458
+ return this.ref.name();
4459
+ }
4460
+ onClosed(fn) {
4461
+ return this.ref.onClosed(fn);
4462
+ }
4463
+ close() {
4464
+ return this.ref.shellClose(this);
4465
+ }
4466
+ destroy() {
4467
+ return this.ref.destroy();
4468
+ }
4469
+ ready() {
4470
+ return this.ref.ready();
4471
+ }
4472
+ get(id) {
4473
+ return this.ref.get(id);
4474
+ }
4475
+ put(doc) {
4476
+ return this.ref.put(doc);
4477
+ }
4478
+ del(id) {
4479
+ return this.ref.del(id);
4480
+ }
4481
+ changes(since, opts) {
4482
+ return this.ref.changes(since, opts);
4483
+ }
4484
+ allDocs(opts) {
4485
+ return this.ref.allDocs(opts);
4486
+ }
4487
+ allDocuments() {
4488
+ return this.ref.allDocuments();
4489
+ }
4490
+ subscribe(listener, updates) {
4491
+ return this.ref.subscribe(listener, updates);
4492
+ }
4493
+ query(field, opts) {
4494
+ return this.ref.query(field, opts);
4495
+ }
4496
+ compact() {
4497
+ return this.ref.compact();
4498
+ }
4499
+ };
4500
+ var DatabaseImpl = class {
4501
+ constructor(sthis, opts) {
4109
4502
  this._listening = false;
4110
4503
  this._listeners = /* @__PURE__ */ new Set();
4111
4504
  this._noupdate_listeners = /* @__PURE__ */ new Set();
4112
- this._ready = new import_cement16.ResolveOnce();
4113
- this.name = name;
4114
- this.opts = opts || this.opts;
4115
- this.sthis = ensureSuperThis(this.opts);
4505
+ // readonly blockstore: BaseBlockstore;
4506
+ this.shells = /* @__PURE__ */ new Set();
4507
+ this._onClosedFns = /* @__PURE__ */ new Set();
4508
+ this._ready = new import_cement18.ResolveOnce();
4509
+ this.opts = opts;
4510
+ this.sthis = sthis;
4511
+ this.id = sthis.timeOrderedNextId().str;
4116
4512
  this.logger = ensureLogger(this.sthis, "Database");
4117
- this._crdt = new CRDT(this.sthis, name, this.opts);
4118
- this.blockstore = this._crdt.blockstore;
4513
+ this.crdt = new CRDT(this.sthis, this.opts);
4119
4514
  this._writeQueue = writeQueue(async (updates) => {
4120
- return await this._crdt.bulk(updates);
4515
+ return await this.crdt.bulk(updates);
4121
4516
  });
4122
- this._crdt.clock.onTock(() => {
4517
+ this.crdt.clock.onTock(() => {
4123
4518
  this._no_update_notify();
4124
4519
  });
4125
4520
  }
4126
- static {
4127
- this.databases = /* @__PURE__ */ new Map();
4521
+ addShell(shell) {
4522
+ this.shells.add(shell);
4523
+ }
4524
+ onClosed(fn) {
4525
+ this._onClosedFns.add(fn);
4128
4526
  }
4129
4527
  async close() {
4130
- await this.ready();
4131
- await this._crdt.close();
4132
- await this.blockstore.close();
4528
+ throw this.logger.Error().Str("db", this.name()).Msg(`use shellClose`).AsError();
4529
+ }
4530
+ async shellClose(db) {
4531
+ if (!this.shells.has(db)) {
4532
+ throw this.logger.Error().Str("db", this.name()).Msg(`Database Shell mismatch`).AsError();
4533
+ }
4534
+ this.shells.delete(db);
4535
+ if (this.shells.size === 0) {
4536
+ await this.ready();
4537
+ await this.crdt.close();
4538
+ this._onClosedFns.forEach((fn) => fn());
4539
+ }
4133
4540
  }
4134
4541
  async destroy() {
4135
4542
  await this.ready();
4136
- await this._crdt.destroy();
4137
- await this.blockstore.destroy();
4543
+ await this.crdt.destroy();
4138
4544
  }
4139
4545
  async ready() {
4140
- return this._ready.once(async () => {
4546
+ const ret = await this._ready.once(async () => {
4141
4547
  await this.sthis.start();
4142
- await this._crdt.ready();
4143
- await this.blockstore.ready();
4548
+ await this.crdt.ready();
4144
4549
  });
4550
+ return ret;
4551
+ }
4552
+ name() {
4553
+ return this.opts.storeUrls.data.data.getParam("name" /* NAME */) || "default";
4145
4554
  }
4146
4555
  async get(id) {
4147
- if (!id) throw this.logger.Error().Str("db", this.name).Msg(`Doc id is required`).AsError();
4556
+ if (!id) throw this.logger.Error().Str("db", this.name()).Msg(`Doc id is required`).AsError();
4148
4557
  await this.ready();
4149
4558
  this.logger.Debug().Str("id", id).Msg("get");
4150
- const got = await this._crdt.get(id).catch((e) => {
4559
+ const got = await this.crdt.get(id).catch((e) => {
4151
4560
  throw new NotFoundError(`Not found: ${id} - ${e.message}`);
4152
4561
  });
4153
4562
  if (!got) throw new NotFoundError(`Not found: ${id}`);
@@ -4166,34 +4575,34 @@ var Database = class {
4166
4575
  _id: docId
4167
4576
  }
4168
4577
  });
4169
- return { id: docId, clock: result?.head, name: this.name };
4578
+ return { id: docId, clock: result?.head, name: this.name() };
4170
4579
  }
4171
4580
  async del(id) {
4172
4581
  await this.ready();
4173
4582
  this.logger.Debug().Str("id", id).Msg("del");
4174
4583
  const result = await this._writeQueue.push({ id, del: true });
4175
- return { id, clock: result?.head, name: this.name };
4584
+ return { id, clock: result?.head, name: this.name() };
4176
4585
  }
4177
4586
  async changes(since = [], opts = {}) {
4178
4587
  await this.ready();
4179
4588
  this.logger.Debug().Any("since", since).Any("opts", opts).Msg("changes");
4180
- const { result, head } = await this._crdt.changes(since, opts);
4589
+ const { result, head } = await this.crdt.changes(since, opts);
4181
4590
  const rows = result.map(({ id: key, value, del, clock }) => ({
4182
4591
  key,
4183
4592
  value: del ? { _id: key, _deleted: true } : { _id: key, ...value },
4184
4593
  clock
4185
4594
  }));
4186
- return { rows, clock: head, name: this.name };
4595
+ return { rows, clock: head, name: this.name() };
4187
4596
  }
4188
4597
  async allDocs(opts = {}) {
4189
4598
  await this.ready();
4190
4599
  this.logger.Debug().Msg("allDocs");
4191
- const { result, head } = await this._crdt.allDocs();
4600
+ const { result, head } = await this.crdt.allDocs();
4192
4601
  const rows = result.map(({ id: key, value, del }) => ({
4193
4602
  key,
4194
4603
  value: del ? { _id: key, _deleted: true } : { _id: key, ...value }
4195
4604
  }));
4196
- return { rows, clock: head, name: this.name };
4605
+ return { rows, clock: head, name: this.name() };
4197
4606
  }
4198
4607
  async allDocuments() {
4199
4608
  return this.allDocs();
@@ -4203,7 +4612,7 @@ var Database = class {
4203
4612
  if (updates) {
4204
4613
  if (!this._listening) {
4205
4614
  this._listening = true;
4206
- this._crdt.clock.onTick((updates2) => {
4615
+ this.crdt.clock.onTick((updates2) => {
4207
4616
  void this._notify(updates2);
4208
4617
  });
4209
4618
  }
@@ -4222,13 +4631,13 @@ var Database = class {
4222
4631
  async query(field, opts = {}) {
4223
4632
  await this.ready();
4224
4633
  this.logger.Debug().Any("field", field).Any("opts", opts).Msg("query");
4225
- const _crdt = this._crdt;
4226
- const idx = typeof field === "string" ? index(this.sthis, { _crdt }, field) : index(this.sthis, { _crdt }, makeName(field.toString()), field);
4634
+ const _crdt = this.crdt;
4635
+ const idx = typeof field === "string" ? index({ crdt: _crdt }, field) : index({ crdt: _crdt }, makeName(field.toString()), field);
4227
4636
  return await idx.query(opts);
4228
4637
  }
4229
4638
  async compact() {
4230
4639
  await this.ready();
4231
- await this._crdt.compact();
4640
+ await this.crdt.compact();
4232
4641
  }
4233
4642
  async _notify(updates) {
4234
4643
  await this.ready();
@@ -4252,23 +4661,62 @@ var Database = class {
4252
4661
  }
4253
4662
  }
4254
4663
  };
4255
- function toSortedArray(set) {
4256
- if (!set) return [];
4257
- return Object.entries(set).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => ({ [k]: v }));
4664
+ function defaultURI(sthis, curi, uri, store, ctx) {
4665
+ ctx = ctx || {};
4666
+ const ret = (curi ? import_cement18.URI.from(curi) : uri).build().setParam("store" /* STORE */, store);
4667
+ if (!ret.hasParam("name" /* NAME */)) {
4668
+ const name = sthis.pathOps.basename(ret.URI().pathname);
4669
+ if (!name) {
4670
+ throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Database name is required").AsError();
4671
+ }
4672
+ ret.setParam("name" /* NAME */, name);
4673
+ }
4674
+ if (ctx.idx) {
4675
+ ret.defParam("index" /* INDEX */, "idx");
4676
+ ret.defParam("storekey" /* STORE_KEY */, `@${ret.getParam("name" /* NAME */)}-${store}-idx@`);
4677
+ } else {
4678
+ ret.defParam("storekey" /* STORE_KEY */, `@${ret.getParam("name" /* NAME */)}-${store}@`);
4679
+ }
4680
+ if (store === "data") {
4681
+ if (ctx.file) {
4682
+ } else {
4683
+ ret.defParam("suffix" /* SUFFIX */, ".car");
4684
+ }
4685
+ }
4686
+ return ret.URI();
4258
4687
  }
4259
- function fireproof(name, opts) {
4260
- const key = JSON.stringify(
4261
- toSortedArray({
4262
- name,
4263
- stores: toSortedArray(opts?.store?.stores)
4264
- })
4265
- );
4266
- let db = Database.databases.get(key);
4267
- if (!db) {
4268
- db = new Database(name, opts);
4269
- Database.databases.set(key, db);
4688
+ function toStoreURIRuntime(sthis, name, sopts) {
4689
+ sopts = sopts || {};
4690
+ if (!sopts.base) {
4691
+ const fp_env = sthis.env.get("FP_STORAGE_URL");
4692
+ if (fp_env) {
4693
+ sopts = { ...sopts, base: import_cement18.BuildURI.from(fp_env).setParam("urlGen" /* URL_GEN */, "fromEnv") };
4694
+ } else {
4695
+ sopts = { ...sopts, base: getDefaultURI(sthis).build().setParam("urlGen" /* URL_GEN */, "default") };
4696
+ }
4697
+ }
4698
+ const bbase = import_cement18.BuildURI.from(sopts.base);
4699
+ if (name) {
4700
+ bbase.setParam("name" /* NAME */, name);
4270
4701
  }
4271
- return db;
4702
+ const base = bbase.URI();
4703
+ return {
4704
+ idx: {
4705
+ data: defaultURI(sthis, sopts.idx?.data, base, "data", { idx: true }),
4706
+ file: defaultURI(sthis, sopts.idx?.data, base, "data", { file: true, idx: true }),
4707
+ meta: defaultURI(sthis, sopts.idx?.meta, base, "meta", { idx: true }),
4708
+ wal: defaultURI(sthis, sopts.idx?.wal, base, "wal", { idx: true })
4709
+ },
4710
+ data: {
4711
+ data: defaultURI(sthis, sopts.data?.data, base, "data"),
4712
+ file: defaultURI(sthis, sopts.data?.data, base, "data", { file: true }),
4713
+ meta: defaultURI(sthis, sopts.data?.meta, base, "meta"),
4714
+ wal: defaultURI(sthis, sopts.data?.wal, base, "wal")
4715
+ }
4716
+ };
4717
+ }
4718
+ function fireproof(name, opts) {
4719
+ return DatabaseFactory(name, opts);
4272
4720
  }
4273
4721
  function makeName(fnString) {
4274
4722
  const regex = /\(([^,()]+,\s*[^,()]+|\[[^\]]+\],\s*[^,()]+)\)/g;
@@ -4287,6 +4735,9 @@ function makeName(fnString) {
4287
4735
  }
4288
4736
  }
4289
4737
 
4738
+ // src/index.ts
4739
+ init_types();
4740
+
4290
4741
  // src/runtime/index.ts
4291
4742
  var runtime_exports = {};
4292
4743
  __export(runtime_exports, {
@@ -4299,7 +4750,7 @@ __export(runtime_exports, {
4299
4750
  kb: () => key_bag_exports,
4300
4751
  kc: () => keyed_crypto_exports,
4301
4752
  mf: () => wait_pr_multiformats_exports,
4302
- runtimeFn: () => import_cement17.runtimeFn,
4753
+ runtimeFn: () => import_cement19.runtimeFn,
4303
4754
  toArrayBuffer: () => toArrayBuffer
4304
4755
  });
4305
4756
  init_utils2();
@@ -4315,7 +4766,7 @@ __export(wait_pr_multiformats_exports, {
4315
4766
  var codec_interface_exports = {};
4316
4767
 
4317
4768
  // src/runtime/index.ts
4318
- var import_cement17 = require("@adviser/cement");
4769
+ var import_cement19 = require("@adviser/cement");
4319
4770
  init_version();
4320
4771
  init_version2();
4321
4772
 
@@ -4324,6 +4775,6 @@ init_utils();
4324
4775
 
4325
4776
  // src/version.ts
4326
4777
  var PACKAGE_VERSION = Object.keys({
4327
- "0.19.99": "xxxx"
4778
+ "0.19.101": "xxxx"
4328
4779
  })[0];
4329
4780
  //# sourceMappingURL=index.cjs.map