@fireproof/core 0.20.0-dev-preview-41 → 0.20.0-dev-preview-50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -48,6 +48,7 @@ __export(index_exports, {
48
48
  ensureLogger: () => ensureLogger,
49
49
  ensureSuperLog: () => ensureSuperLog,
50
50
  ensureSuperThis: () => ensureSuperThis,
51
+ ensureURIDefaults: () => ensureURIDefaults,
51
52
  exceptionWrapper: () => exceptionWrapper,
52
53
  falsyToUndef: () => falsyToUndef,
53
54
  fireproof: () => fireproof,
@@ -65,6 +66,7 @@ __export(index_exports, {
65
66
  onSuperThis: () => onSuperThis,
66
67
  rt: () => runtime_exports,
67
68
  runtime: () => runtime_exports,
69
+ storeType2DataMetaWal: () => storeType2DataMetaWal,
68
70
  throwFalsy: () => throwFalsy,
69
71
  toSortedArray: () => toSortedArray,
70
72
  toStoreURIRuntime: () => toStoreURIRuntime
@@ -101,8 +103,13 @@ var PARAM = {
101
103
  FRAG_LEN: "len",
102
104
  FRAG_HEAD: "headerSize",
103
105
  EXTRACTKEY: "extractKey",
104
- SELF_REFLECT: "selfReflect"
106
+ SELF_REFLECT: "selfReflect",
105
107
  // if no subscribe in Gateway see your own META updates
108
+ CAR_PARALLEL: "parallel",
109
+ CAR_CACHE_SIZE: "carCacheSize",
110
+ CAR_COMPACT_CACHE_SIZE: "carCompactCacheSize",
111
+ CAR_META_CACHE_SIZE: "carMetaCacheSize",
112
+ GENESIS_CID: "baembeiarootfireproofgenesisblockaaaafireproofgenesisblocka"
106
113
  // FS = "fs",
107
114
  };
108
115
  function throwFalsy(value) {
@@ -339,21 +346,25 @@ function ensureLogger(sthis, componentName, ctx) {
339
346
  return out;
340
347
  }
341
348
  function getStore(url, sthis, joiner) {
342
- const store = url.getParam(PARAM.STORE);
343
- switch (store) {
344
- case "data":
349
+ const fromUrl = url.getParam(PARAM.STORE);
350
+ let pathPart;
351
+ switch (fromUrl) {
352
+ case "car":
353
+ case "file":
354
+ pathPart = "data";
355
+ break;
345
356
  case "wal":
346
357
  case "meta":
358
+ pathPart = fromUrl;
347
359
  break;
348
360
  default:
349
361
  throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
350
- throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
351
362
  }
352
- let name = store;
363
+ let name = pathPart;
353
364
  if (url.hasParam("index")) {
354
365
  name = joiner(url.getParam(PARAM.INDEX) || "idx", name);
355
366
  }
356
- return { store, name };
367
+ return { pathPart, fromUrl, name };
357
368
  }
358
369
  function getKey(url, logger) {
359
370
  const result = url.getParam(PARAM.KEY);
@@ -455,6 +466,35 @@ function makeName(fnString) {
455
466
  return found[1];
456
467
  }
457
468
  }
469
+ function storeType2DataMetaWal(store) {
470
+ switch (store) {
471
+ case "car":
472
+ case "file":
473
+ return "data";
474
+ case "meta":
475
+ case "wal":
476
+ return store;
477
+ default:
478
+ throw new Error(`unknown store ${store}`);
479
+ }
480
+ }
481
+ function ensureURIDefaults(sthis, name, curi, uri, store, ctx) {
482
+ ctx = ctx || {};
483
+ const ret = (curi ? import_cement.URI.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
484
+ if (!ret.hasParam(PARAM.NAME)) {
485
+ throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
486
+ }
487
+ if (ctx.idx) {
488
+ ret.defParam(PARAM.INDEX, "idx");
489
+ ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${storeType2DataMetaWal(store)}-idx@`);
490
+ } else {
491
+ ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${storeType2DataMetaWal(store)}@`);
492
+ }
493
+ if (store === "car") {
494
+ ret.defParam(PARAM.SUFFIX, ".car");
495
+ }
496
+ return ret.URI();
497
+ }
458
498
 
459
499
  // src/write-queue.ts
460
500
  var import_cement2 = require("@adviser/cement");
@@ -1136,8 +1176,10 @@ function getFileName(url, sthis) {
1136
1176
  const key = url.getParam("key");
1137
1177
  if (!key) throw sthis.logger.Error().Url(url).Msg(`key not found`).AsError();
1138
1178
  const res = (0, import_core.getStore)(url, sthis, (...a) => a.join("-"));
1139
- switch (res.store) {
1140
- case "data":
1179
+ switch (res.fromUrl) {
1180
+ case "file":
1181
+ return sthis.pathOps.join(res.name, key);
1182
+ case "car":
1141
1183
  return sthis.pathOps.join(res.name, key + ".car");
1142
1184
  case "wal":
1143
1185
  case "meta":
@@ -1285,8 +1327,13 @@ var MemoryGateway = class {
1285
1327
  close(baseUrl) {
1286
1328
  return Promise.resolve(import_cement6.Result.Ok(void 0));
1287
1329
  }
1288
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1289
1330
  destroy(baseUrl) {
1331
+ const keyUrl = baseUrl.toString();
1332
+ for (const key of this.memorys.keys()) {
1333
+ if (key.startsWith(keyUrl)) {
1334
+ this.memorys.delete(key);
1335
+ }
1336
+ }
1290
1337
  this.memorys.clear();
1291
1338
  return Promise.resolve(import_cement6.Result.Ok(void 0));
1292
1339
  }
@@ -1298,7 +1345,7 @@ var MemoryGateway = class {
1298
1345
  get(url) {
1299
1346
  const x = this.memorys.get(url.toString());
1300
1347
  if (!x) {
1301
- return Promise.resolve(import_cement6.Result.Err(new NotFoundError(`not found: ${url.getParam(PARAM.STORE)}`)));
1348
+ return Promise.resolve(import_cement6.Result.Err(new NotFoundError(`not found: ${url.toString()}`)));
1302
1349
  }
1303
1350
  return Promise.resolve(import_cement6.Result.Ok(x));
1304
1351
  }
@@ -1468,10 +1515,9 @@ async function fpDeserialize(sthis, url, intoRaw, pdecoder) {
1468
1515
  ...pdecoder
1469
1516
  };
1470
1517
  switch (url.getParam(PARAM.STORE)) {
1471
- case "data":
1472
- if (url.getParam(PARAM.SUFFIX) === ".car") {
1473
- return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1474
- }
1518
+ case "car":
1519
+ return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1520
+ case "file":
1475
1521
  return makeFPEnvelope(FPEnvelopeTypes.FILE, await decoder.file(sthis, raw2));
1476
1522
  case "wal":
1477
1523
  return makeFPEnvelope(FPEnvelopeTypes.WAL, await decode2WalState(sthis, await decoder.wal(sthis, raw2)));
@@ -1533,7 +1579,7 @@ var DefSerdeGateway = class {
1533
1579
  const urlWithoutKey = url.build().delParam(PARAM.KEY).delParam(PARAM.SELF_REFLECT).toString();
1534
1580
  this.subscribeFn.set(urlWithoutKey, rawCallback);
1535
1581
  return import_cement9.Result.Ok(() => {
1536
- this.subscribeFn.delete(url.toString());
1582
+ this.subscribeFn.delete(urlWithoutKey);
1537
1583
  });
1538
1584
  }
1539
1585
  const unreg = await this.gw.subscribe(url, rawCallback, sthis);
@@ -1662,37 +1708,49 @@ var Block = import_block.Block;
1662
1708
  async function decode({
1663
1709
  bytes,
1664
1710
  codec: codec3,
1665
- hasher: hasher7
1711
+ hasher: hasher6
1666
1712
  }) {
1667
1713
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1668
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1714
+ if (codec3 == null || hasher6 == null) throw new Error("Missing required argument: codec or hasher");
1669
1715
  const value = await Promise.resolve(codec3.decode(bytes));
1670
- const hash = await hasher7.digest(bytes);
1716
+ let toHash = bytes;
1717
+ if (codec3.valueToHashBytes) {
1718
+ toHash = await Promise.resolve(codec3.valueToHashBytes(value));
1719
+ }
1720
+ const hash = await hasher6.digest(toHash);
1671
1721
  const cid = import_multiformats2.CID.create(1, codec3.code, hash);
1672
- return new import_block.Block({ value, bytes, cid });
1722
+ return new import_block.Block({ value, bytes: toHash, cid });
1673
1723
  }
1674
1724
  async function encode({
1675
1725
  value,
1676
1726
  codec: codec3,
1677
- hasher: hasher7
1727
+ hasher: hasher6
1678
1728
  }) {
1679
1729
  if (typeof value === "undefined") throw new Error('Missing required argument "value"');
1680
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1681
- const bytes = await Promise.resolve(codec3.encode(value));
1682
- const hash = await hasher7.digest(bytes);
1730
+ if (codec3 == null || hasher6 == null) throw new Error("Missing required argument: codec or hasher");
1731
+ let bytes;
1732
+ let hash;
1733
+ if (codec3.bytesToHash) {
1734
+ const hashable = await Promise.resolve(codec3.bytesToHash(value));
1735
+ hash = await hasher6.digest(hashable);
1736
+ bytes = await Promise.resolve(codec3.encode(value));
1737
+ } else {
1738
+ bytes = await Promise.resolve(codec3.encode(value));
1739
+ hash = await hasher6.digest(bytes);
1740
+ }
1683
1741
  const cid = import_multiformats2.CID.create(1, codec3.code, hash);
1684
- return new import_block.Block({ value, bytes, cid });
1742
+ return new Block({ value, bytes, cid });
1685
1743
  }
1686
1744
  async function create({
1687
1745
  bytes,
1688
1746
  cid,
1689
- hasher: hasher7,
1747
+ hasher: hasher6,
1690
1748
  codec: codec3
1691
1749
  }) {
1692
1750
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1693
- if (hasher7 == null) throw new Error('Missing required argument "hasher"');
1751
+ if (hasher6 == null) throw new Error('Missing required argument "hasher"');
1694
1752
  const value = await Promise.resolve(codec3.decode(bytes));
1695
- const hash = await hasher7.digest(bytes);
1753
+ const hash = await hasher6.digest(bytes);
1696
1754
  if (!import_multiformats2.bytes.equals(cid.multihash.bytes, hash.bytes)) {
1697
1755
  throw new Error("CID hash does not match bytes");
1698
1756
  }
@@ -2189,8 +2247,11 @@ var import_cement19 = require("@adviser/cement");
2189
2247
  var blockstore_exports = {};
2190
2248
  __export(blockstore_exports, {
2191
2249
  AttachedRemotesImpl: () => AttachedRemotesImpl,
2250
+ BaseActiveStore: () => BaseActiveStore,
2192
2251
  BaseBlockstoreImpl: () => BaseBlockstoreImpl,
2193
2252
  Car2FPMsg: () => Car2FPMsg,
2253
+ CarActiveStore: () => CarActiveStore,
2254
+ CarLog: () => CarLog,
2194
2255
  CarTransactionImpl: () => CarTransactionImpl,
2195
2256
  CompactionFetcher: () => CompactionFetcher,
2196
2257
  DbMetaEventEqual: () => DbMetaEventEqual,
@@ -2198,9 +2259,13 @@ __export(blockstore_exports, {
2198
2259
  EncryptedBlockstore: () => EncryptedBlockstore,
2199
2260
  FPEnvelopeTypes: () => FPEnvelopeTypes,
2200
2261
  File2FPMsg: () => File2FPMsg,
2262
+ FileActiveStore: () => FileActiveStore,
2201
2263
  InterceptorGateway: () => InterceptorGateway,
2202
2264
  Loader: () => Loader,
2265
+ MetaActiveStore: () => MetaActiveStore,
2203
2266
  PassThroughGateway: () => PassThroughGateway,
2267
+ TaskManager: () => TaskManager,
2268
+ WALActiveStore: () => WALActiveStore,
2204
2269
  createAttachedStores: () => createAttachedStores,
2205
2270
  createDbMetaEvent: () => createDbMetaEvent,
2206
2271
  defaultGatewayFactoryItem: () => defaultGatewayFactoryItem,
@@ -2215,6 +2280,37 @@ __export(blockstore_exports, {
2215
2280
  });
2216
2281
 
2217
2282
  // src/blockstore/types.ts
2283
+ var CarLog = class {
2284
+ constructor() {
2285
+ this._logs = [];
2286
+ }
2287
+ get length() {
2288
+ return this._logs.length;
2289
+ }
2290
+ last() {
2291
+ const x = [...this._logs[this._logs.length - 1]];
2292
+ Object.freeze(x);
2293
+ return x;
2294
+ }
2295
+ xunshift(logs) {
2296
+ this._logs.unshift(logs);
2297
+ }
2298
+ update(logs) {
2299
+ this._logs.length = 0;
2300
+ this._logs.push(...logs);
2301
+ }
2302
+ asArray() {
2303
+ const a = [
2304
+ ...this._logs.map((l) => {
2305
+ const x = [...l];
2306
+ Object.freeze(x);
2307
+ return x;
2308
+ })
2309
+ ];
2310
+ Object.freeze(a);
2311
+ return a;
2312
+ }
2313
+ };
2218
2314
  function toCIDBlock(block) {
2219
2315
  return block;
2220
2316
  }
@@ -2224,6 +2320,16 @@ function DbMetaEventEqual(a, b) {
2224
2320
  function DbMetaEventsEqual(a, b) {
2225
2321
  return a.length === b.length && a.every((e, i) => DbMetaEventEqual(e, b[i]));
2226
2322
  }
2323
+ var BaseActiveStore = class {
2324
+ };
2325
+ var CarActiveStore = class extends BaseActiveStore {
2326
+ };
2327
+ var FileActiveStore = class extends BaseActiveStore {
2328
+ };
2329
+ var MetaActiveStore = class extends BaseActiveStore {
2330
+ };
2331
+ var WALActiveStore = class extends BaseActiveStore {
2332
+ };
2227
2333
 
2228
2334
  // src/blockstore/store-factory.ts
2229
2335
  var import_cement17 = require("@adviser/cement");
@@ -2240,8 +2346,8 @@ var import_cement14 = require("@adviser/cement");
2240
2346
  var import_sha22 = require("multiformats/hashes/sha2");
2241
2347
  var dagCodec = __toESM(require("@ipld/dag-cbor"), 1);
2242
2348
  async function parseCarFile(reader, logger) {
2243
- const roots = await reader.getRoots();
2244
- const header = await reader.get(roots[0]);
2349
+ const roots = await reader.roots;
2350
+ const header = reader.blocks.find((i) => i.cid.equals(roots[0]));
2245
2351
  if (!header) throw logger.Error().Msg("missing header block").AsError();
2246
2352
  const dec = await decode({ bytes: header.bytes, hasher: import_sha22.sha256, codec: dagCodec });
2247
2353
  const fpvalue = dec.value;
@@ -2256,6 +2362,7 @@ var import_block4 = require("@fireproof/vendor/@web3-storage/pail/block");
2256
2362
  var import_cement11 = require("@adviser/cement");
2257
2363
  var CarTransactionImpl = class {
2258
2364
  #memblock = new import_block4.MemoryBlockstore();
2365
+ #hackUnshift;
2259
2366
  constructor(parent, opts = { add: true, noLoader: false }) {
2260
2367
  if (opts.add) {
2261
2368
  parent.transactions.add(this);
@@ -2263,7 +2370,7 @@ var CarTransactionImpl = class {
2263
2370
  this.parent = parent;
2264
2371
  }
2265
2372
  async get(cid) {
2266
- return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
2373
+ return await this.superGet(cid) ?? falsyToUndef(await this.parent.get(cid));
2267
2374
  }
2268
2375
  async superGet(cid) {
2269
2376
  return this.#memblock.get(cid);
@@ -2274,7 +2381,16 @@ var CarTransactionImpl = class {
2274
2381
  putSync(cid, bytes) {
2275
2382
  this.#memblock.putSync(cid, bytes);
2276
2383
  }
2384
+ unshift(cid, bytes) {
2385
+ if (this.#hackUnshift) {
2386
+ throw new Error("unshift already called");
2387
+ }
2388
+ this.#hackUnshift = { cid, bytes };
2389
+ }
2277
2390
  async *entries() {
2391
+ if (this.#hackUnshift) {
2392
+ yield this.#hackUnshift;
2393
+ }
2278
2394
  for await (const blk of this.#memblock.entries()) {
2279
2395
  yield blk;
2280
2396
  }
@@ -2300,6 +2416,11 @@ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
2300
2416
  keyBag: opts.keyBag || {},
2301
2417
  crypto: (0, import_cement11.toCryptoRuntime)(opts.crypto),
2302
2418
  storeUrls: opts.storeUrls,
2419
+ taskManager: {
2420
+ removeAfter: 3,
2421
+ retryTimeout: 50,
2422
+ ...opts.taskManager
2423
+ },
2303
2424
  // storeEnDeFile: ensureStoreEnDeFile(opts.storeEnDeFile),
2304
2425
  // store,
2305
2426
  storeRuntime: toStoreRuntime(sthis, ensureStoreEnDeFile(opts.storeEnDeFile))
@@ -2392,10 +2513,8 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2392
2513
  async get(cid) {
2393
2514
  const got = await super.get(cid);
2394
2515
  if (got) return got;
2395
- if (!this.loader) {
2396
- return;
2397
- }
2398
- return falsyToUndef(await this.loader.getBlock(cid, this.loader.attachedStores.local()));
2516
+ const ret = falsyToUndef(await this.loader.getBlock(cid, this.loader.attachedStores.local()));
2517
+ return ret;
2399
2518
  }
2400
2519
  async transaction(fn, opts = { noLoader: false }) {
2401
2520
  this.logger.Debug().Msg("enter transaction");
@@ -2416,7 +2535,7 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2416
2535
  await this.ready();
2417
2536
  if (!this.loader) throw this.logger.Error().Msg("loader required to get file, ledger must be named").AsError();
2418
2537
  const reader = await this.loader.loadFileCar(car, this.loader.attachedStores.local());
2419
- const block = await reader.get(cid);
2538
+ const block = await reader.blocks.find((i) => i.cid.equals(cid));
2420
2539
  if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
2421
2540
  return block.bytes;
2422
2541
  }
@@ -2523,8 +2642,8 @@ var CommitQueue = class {
2523
2642
 
2524
2643
  // src/blockstore/commitor.ts
2525
2644
  var CBW = __toESM(require("@ipld/car/buffer-writer"), 1);
2526
- var import_sha23 = require("multiformats/hashes/sha2");
2527
2645
  var dagCodec2 = __toESM(require("@ipld/dag-cbor"), 1);
2646
+ var import_sha23 = require("multiformats/hashes/sha2");
2528
2647
  async function encodeCarFile(roots, t, codec3) {
2529
2648
  let size = 0;
2530
2649
  const headerSize = CBW.headerLength({ roots });
@@ -2549,7 +2668,7 @@ async function createCarFile(encoder, cid, t) {
2549
2668
  async function commitFiles(fileStore, walStore, t, done) {
2550
2669
  const { files: roots } = makeFileCarHeader(done);
2551
2670
  const cids = [];
2552
- const codec3 = (await fileStore.keyedCrypto()).codec();
2671
+ const codec3 = await fileStore.keyedCrypto().then((i) => i.codec());
2553
2672
  const cars = await prepareCarFilesFiles(codec3, roots, t);
2554
2673
  for (const car of cars) {
2555
2674
  const { cid, bytes } = car;
@@ -2575,7 +2694,7 @@ async function prepareCarFilesFiles(encoder, roots, t) {
2575
2694
  return [await encodeCarFile(roots, t, encoder)];
2576
2695
  }
2577
2696
  function makeCarHeader(meta, cars, compact = false) {
2578
- const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
2697
+ const coreHeader = compact ? { cars: [], compact: cars.asArray() } : { cars: cars.asArray(), compact: [] };
2579
2698
  return { ...coreHeader, meta };
2580
2699
  }
2581
2700
  async function encodeCarHeader(fp) {
@@ -2602,7 +2721,7 @@ async function commit(params, t, done, opts = { noLoader: false, compact: false
2602
2721
  }
2603
2722
  async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2604
2723
  const carFiles = [];
2605
- threshold = threshold || 128e3 * 8;
2724
+ threshold = threshold || 16 * 65536;
2606
2725
  let clonedt = new CarTransactionImpl(t.parent, { add: false, noLoader: false });
2607
2726
  clonedt.putSync(rootBlock.cid, rootBlock.bytes);
2608
2727
  let newsize = CBW.blockLength(toCIDBlock(rootBlock));
@@ -2628,13 +2747,14 @@ var import_sha24 = require("multiformats/hashes/sha2");
2628
2747
 
2629
2748
  // src/blockstore/task-manager.ts
2630
2749
  var TaskManager = class {
2631
- constructor(sthis, callback) {
2750
+ constructor(sthis, callback, params) {
2632
2751
  // we need to remove the events after some time
2633
2752
  this.eventsWeHandled = /* @__PURE__ */ new Set();
2634
2753
  this.queue = [];
2635
2754
  this.isProcessing = false;
2636
2755
  this.logger = ensureLogger(sthis, "TaskManager");
2637
2756
  this.callback = callback;
2757
+ this.params = params;
2638
2758
  }
2639
2759
  async handleEvent(cid, parents, dbMeta, store) {
2640
2760
  for (const parent of parents) {
@@ -2663,7 +2783,7 @@ var TaskManager = class {
2663
2783
  this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
2664
2784
  }
2665
2785
  await new Promise((resolve) => setTimeout(resolve, 50));
2666
- throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
2786
+ this.logger.Warn().Err(err).Msg("retry to process event block");
2667
2787
  } finally {
2668
2788
  this.isProcessing = false;
2669
2789
  if (this.queue.length > 0) {
@@ -2693,11 +2813,32 @@ var AttachedImpl = class {
2693
2813
  return "attached";
2694
2814
  }
2695
2815
  };
2696
- var DataActiveStoreImpl = class {
2816
+ var FileActiveStoreImpl = class extends FileActiveStore {
2697
2817
  constructor(ref, active, attached) {
2818
+ super();
2698
2819
  this.ref = ref;
2699
2820
  this.active = active;
2700
- this.attached = attached;
2821
+ this.xattached = attached;
2822
+ }
2823
+ local() {
2824
+ return this.xattached.local();
2825
+ }
2826
+ remotes() {
2827
+ return this.xattached.remotes();
2828
+ }
2829
+ };
2830
+ var CarActiveStoreImpl = class extends CarActiveStore {
2831
+ constructor(ref, active, attached) {
2832
+ super();
2833
+ this.ref = ref;
2834
+ this.active = active;
2835
+ this.xattached = attached;
2836
+ }
2837
+ local() {
2838
+ return this.xattached.local();
2839
+ }
2840
+ remotes() {
2841
+ return [this.active, ...this.xattached.remotes().filter((i) => i !== this.active)];
2701
2842
  }
2702
2843
  };
2703
2844
  var CarAttachedStoresImpl = class {
@@ -2722,11 +2863,18 @@ var FileAttachedStoresImpl = class {
2722
2863
  return this.attached.remotes().map(({ active }) => active.file);
2723
2864
  }
2724
2865
  };
2725
- var MetaActiveStoreImpl = class {
2866
+ var MetaActiveStoreImpl = class extends MetaActiveStore {
2726
2867
  constructor(ref, active, attached) {
2868
+ super();
2727
2869
  this.ref = ref;
2728
2870
  this.active = active;
2729
- this.attached = attached;
2871
+ this.xattached = attached;
2872
+ }
2873
+ local() {
2874
+ return this.xattached.local();
2875
+ }
2876
+ remotes() {
2877
+ return [this.active, ...this.xattached.remotes().filter((i) => i !== this.active)];
2730
2878
  }
2731
2879
  };
2732
2880
  var MetaAttachedStoresImpl = class {
@@ -2740,11 +2888,18 @@ var MetaAttachedStoresImpl = class {
2740
2888
  return this.attached.remotes().map(({ active }) => active.meta);
2741
2889
  }
2742
2890
  };
2743
- var WALActiveStoreImpl = class {
2891
+ var WALActiveStoreImpl = class extends WALActiveStore {
2744
2892
  constructor(ref, active, attached) {
2893
+ super();
2745
2894
  this.ref = ref;
2746
2895
  this.active = active;
2747
- this.attached = attached;
2896
+ this.xattached = attached;
2897
+ }
2898
+ local() {
2899
+ return this.xattached.local();
2900
+ }
2901
+ remotes() {
2902
+ return this.xattached.remotes();
2748
2903
  }
2749
2904
  };
2750
2905
  var WALAttachedStoresImpl = class {
@@ -2761,7 +2916,13 @@ var WALAttachedStoresImpl = class {
2761
2916
  var ActiveStoreImpl = class {
2762
2917
  constructor(active, attached) {
2763
2918
  this.active = active;
2764
- this.attached = attached;
2919
+ this.xattached = attached;
2920
+ }
2921
+ local() {
2922
+ return this.xattached.local();
2923
+ }
2924
+ remotes() {
2925
+ return this.xattached.remotes();
2765
2926
  }
2766
2927
  baseStores() {
2767
2928
  const bs = [this.active.car, this.active.file, this.active.meta];
@@ -2771,19 +2932,19 @@ var ActiveStoreImpl = class {
2771
2932
  return bs;
2772
2933
  }
2773
2934
  carStore() {
2774
- return new DataActiveStoreImpl(this, this.active.car, new CarAttachedStoresImpl(this.attached));
2935
+ return new CarActiveStoreImpl(this, this.active.car, new CarAttachedStoresImpl(this.xattached));
2775
2936
  }
2776
2937
  fileStore() {
2777
- return new DataActiveStoreImpl(this, this.active.file, new FileAttachedStoresImpl(this.attached));
2938
+ return new FileActiveStoreImpl(this, this.active.file, new FileAttachedStoresImpl(this.xattached));
2778
2939
  }
2779
2940
  metaStore() {
2780
- return new MetaActiveStoreImpl(this, this.active.meta, new MetaAttachedStoresImpl(this.attached));
2941
+ return new MetaActiveStoreImpl(this, this.active.meta, new MetaAttachedStoresImpl(this.xattached));
2781
2942
  }
2782
2943
  walStore() {
2783
2944
  if (!this.active.wal) {
2784
- throw this.attached.loadable.sthis.logger.Error().Msg("wal store not set").AsError();
2945
+ throw this.xattached.loadable.sthis.logger.Error().Msg("wal store not set").AsError();
2785
2946
  }
2786
- return new WALActiveStoreImpl(this, this.active.wal, new WALAttachedStoresImpl(this.attached));
2947
+ return new WALActiveStoreImpl(this, this.active.wal, new WALAttachedStoresImpl(this.xattached));
2787
2948
  }
2788
2949
  };
2789
2950
  function isLoadable(unknown) {
@@ -2835,7 +2996,25 @@ var AttachedRemotesImpl = class {
2835
2996
  }
2836
2997
  activate(store) {
2837
2998
  if ((0, import_cement13.isCoerceURI)(store)) {
2838
- throw this.loadable.sthis.logger.Error().Msg("store must be an object").AsError();
2999
+ const activateUrl = import_cement13.URI.from(store);
3000
+ let maxScore = 0;
3001
+ let maxStore;
3002
+ for (const { value } of this._remotes.values()) {
3003
+ if (value.isErr()) {
3004
+ continue;
3005
+ }
3006
+ for (const url of value.Ok().stores.baseStores.map((i) => i.url())) {
3007
+ const mr = url.match(activateUrl);
3008
+ if (mr.score > maxScore) {
3009
+ maxScore = mr.score;
3010
+ maxStore = value.Ok().stores;
3011
+ }
3012
+ }
3013
+ }
3014
+ if (!maxStore) {
3015
+ throw this.loadable.sthis.logger.Error().Url(activateUrl).Msg("no store found").AsError();
3016
+ }
3017
+ store = maxStore;
2839
3018
  }
2840
3019
  return new ActiveStoreImpl(store, this);
2841
3020
  }
@@ -2853,19 +3032,19 @@ var AttachedRemotesImpl = class {
2853
3032
  const gws = {
2854
3033
  car: {
2855
3034
  ...gwp.car,
2856
- url: import_cement13.BuildURI.from(gwp.car.url).defParam("name", attached.name).URI()
3035
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, gwp.car.url, import_cement13.URI.from(gwp.car.url), "car")
2857
3036
  },
2858
3037
  file: {
2859
3038
  ...gwp.file,
2860
- url: import_cement13.BuildURI.from(gwp.file.url).defParam("name", attached.name).URI()
3039
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, import_cement13.URI.from(gwp.file.url), "file", { file: true })
2861
3040
  },
2862
3041
  meta: {
2863
3042
  ...gwp.meta,
2864
- url: import_cement13.BuildURI.from(gwp.meta.url).defParam("name", attached.name).URI()
3043
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, import_cement13.URI.from(gwp.meta.url), "meta")
2865
3044
  },
2866
3045
  wal: gwp.wal ? {
2867
3046
  ...gwp.wal,
2868
- url: import_cement13.BuildURI.from(gwp.wal.url).defParam("name", attached.name).URI()
3047
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, import_cement13.URI.from(gwp.wal.url), "wal")
2869
3048
  } : void 0
2870
3049
  };
2871
3050
  const key = JSON.stringify(
@@ -2884,11 +3063,6 @@ var AttachedRemotesImpl = class {
2884
3063
  byStore: gws,
2885
3064
  loader: this.loadable
2886
3065
  }),
2887
- // {
2888
- // car: await rt.makeDataStore({ url: gws.carUrl, loader: this.loadable }),
2889
- // file: await rt.makeDataStore({ url: gws.filesUrl, loader: this.loadable }),
2890
- // meta: await rt.makeMetaStore({ url: gws.metaUrl, loader: this.loadable }),
2891
- // },
2892
3066
  () => {
2893
3067
  this._remotes.unget(key);
2894
3068
  }
@@ -2906,11 +3080,12 @@ var AttachedRemotesImpl = class {
2906
3080
 
2907
3081
  // src/blockstore/loader.ts
2908
3082
  function carLogIncludesGroup(list, cids) {
2909
- return list.some((arr) => {
2910
- return arr.toString() === cids.toString();
2911
- });
3083
+ const cidSet = cids.map((cid) => cid.toString()).sort().join(",");
3084
+ return list.some(
3085
+ (arr) => cidSet === arr.map((cid) => cid.toString()).sort().join(",")
3086
+ );
2912
3087
  }
2913
- function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
3088
+ function uniqueCids(list, remove = new import_cement14.LRUSet()) {
2914
3089
  const byString = /* @__PURE__ */ new Map();
2915
3090
  for (const cid of list) {
2916
3091
  if (remove.has(cid.toString())) continue;
@@ -2922,13 +3097,8 @@ var Loader = class {
2922
3097
  constructor(sthis, ebOpts) {
2923
3098
  this.commitQueue = new CommitQueue();
2924
3099
  this.isCompacting = false;
2925
- this.carReaders = /* @__PURE__ */ new Map();
2926
- this.seenCompacted = /* @__PURE__ */ new Set();
2927
- this.processedCars = /* @__PURE__ */ new Set();
2928
- this.carLog = [];
2929
- this.getBlockCache = /* @__PURE__ */ new Map();
2930
- this.seenMeta = /* @__PURE__ */ new Set();
2931
- this.writeLimit = (0, import_p_limit.default)(1);
3100
+ this.maxConcurrentWrite = (0, import_p_limit.default)(1);
3101
+ this.carLog = new CarLog();
2932
3102
  this.onceReady = new import_cement14.ResolveOnce();
2933
3103
  this.sthis = sthis;
2934
3104
  this.ebOpts = defaultedBlockstoreRuntime(
@@ -2939,63 +3109,44 @@ var Loader = class {
2939
3109
  },
2940
3110
  "Loader"
2941
3111
  );
2942
- this.logger = this.ebOpts.logger;
2943
- this.taskManager = new TaskManager(sthis, async (dbMeta, activeStore) => {
2944
- await this.handleDbMetasFromStore([dbMeta], activeStore);
3112
+ this.logger = ensureLogger(sthis, "Loader");
3113
+ this.cidCache = new import_cement14.KeyedResolvOnce({
3114
+ lru: {
3115
+ maxEntries: parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_CACHE_SIZE, "1000"), 10)
3116
+ }
3117
+ });
3118
+ this.seenMeta = new import_cement14.LRUSet({
3119
+ maxEntries: parseInt(this.ebOpts.storeUrls.meta.getParam(PARAM.CAR_META_CACHE_SIZE, "1000"), 10)
3120
+ });
3121
+ this.seenCompacted = new import_cement14.LRUSet({
3122
+ maxEntries: parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_COMPACT_CACHE_SIZE, "1000"), 10)
2945
3123
  });
3124
+ this.maxConcurrentCarReader = (0, import_p_limit.default)(parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_PARALLEL, "5"), 10));
3125
+ this.taskManager = new TaskManager(
3126
+ sthis,
3127
+ async (dbMeta, activeStore) => {
3128
+ await this.handleDbMetasFromStore([dbMeta], activeStore);
3129
+ },
3130
+ this.ebOpts.taskManager
3131
+ );
2946
3132
  this.attachedStores = new AttachedRemotesImpl(this);
2947
3133
  }
2948
- attach(attached) {
2949
- return this.attachedStores.attach(attached);
2950
- }
2951
- // private readonly _carStore = new ResolveOnce<DataStore>();
2952
- // async carStore(): Promise<DataStore> {
2953
- // return this._carStore.once(async () =>
2954
- // this.ebOpts.storeRuntime.makeDataStore({
2955
- // // sthis: this.sthis,
2956
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2957
- // url: this.ebOpts.storeUrls.data,
2958
- // // keybag: await this.keyBag(),
2959
- // loader: this,
2960
- // }),
2961
- // );
2962
- // }
2963
- // private readonly _fileStore = new ResolveOnce<DataStore>();
2964
- // async fileStore(): Promise<DataStore> {
2965
- // return this._fileStore.once(async () =>
2966
- // this.ebOpts.storeRuntime.makeDataStore({
2967
- // // sthis: this.sthis,
2968
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2969
- // url: this.ebOpts.storeUrls.file,
2970
- // // keybag: await this.keyBag(),
2971
- // loader: this,
2972
- // }),
2973
- // );
2974
- // }
2975
- // private readonly _WALStore = new ResolveOnce<WALStore>();
2976
- // async WALStore(): Promise<WALStore> {
2977
- // return this._WALStore.once(async () =>
2978
- // this.ebOpts.storeRuntime.makeWALStore({
2979
- // // sthis: this.sthis,
2980
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2981
- // url: this.ebOpts.storeUrls.wal,
2982
- // // keybag: await this.keyBag(),
2983
- // loader: this,
2984
- // }),
2985
- // );
2986
- // }
2987
- // private readonly _metaStore = new ResolveOnce<MetaStore>();
2988
- // async metaStore(): Promise<MetaStore> {
2989
- // return this._metaStore.once(async () =>
2990
- // this.ebOpts.storeRuntime.makeMetaStore({
2991
- // // sthis: this.sthis,
2992
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2993
- // url: this.ebOpts.storeUrls.meta,
2994
- // // keybag: await this.keyBag(),
2995
- // loader: this,
2996
- // }),
2997
- // );
2998
- // }
3134
+ async attach(attached) {
3135
+ const at = await this.attachedStores.attach(attached);
3136
+ if (!at.stores.wal) {
3137
+ try {
3138
+ const dbMeta = await at.stores.meta.load();
3139
+ if (!Array.isArray(dbMeta)) {
3140
+ throw this.logger.Error().Msg("missing dbMeta").AsError();
3141
+ }
3142
+ await this.handleDbMetasFromStore(dbMeta, this.attachedStores.activate(at.stores));
3143
+ } catch (e) {
3144
+ this.logger.Error().Err(e).Msg("error attaching store");
3145
+ at.detach();
3146
+ }
3147
+ }
3148
+ return at;
3149
+ }
2999
3150
  keyBag() {
3000
3151
  return getKeyBag(this.sthis, this.ebOpts.keyBag);
3001
3152
  }
@@ -3039,9 +3190,9 @@ var Loader = class {
3039
3190
  // await this._applyCarHeader(carHeader, true)
3040
3191
  // }
3041
3192
  async handleDbMetasFromStore(metas, activeStore) {
3042
- this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
3193
+ this.logger.Debug().Any("metas", metas).Url(activeStore.active.car.url()).Msg("handleDbMetasFromStore");
3043
3194
  for (const meta of metas) {
3044
- await this.writeLimit(async () => {
3195
+ await this.maxConcurrentWrite(async () => {
3045
3196
  await this.mergeDbMetaIntoClock(meta, activeStore);
3046
3197
  });
3047
3198
  }
@@ -3050,16 +3201,26 @@ var Loader = class {
3050
3201
  if (this.isCompacting) {
3051
3202
  throw this.logger.Error().Msg("cannot merge while compacting").AsError();
3052
3203
  }
3053
- if (this.seenMeta.has(meta.cars.toString())) return;
3054
- this.seenMeta.add(meta.cars.toString());
3055
- if (carLogIncludesGroup(this.carLog, meta.cars)) {
3056
- return;
3204
+ try {
3205
+ this.isCompacting = true;
3206
+ const metaKey = meta.cars.map((i) => i.toString()).sort().join(",");
3207
+ if (this.seenMeta.has(metaKey)) return;
3208
+ this.seenMeta.add(metaKey);
3209
+ if (carLogIncludesGroup(this.carLog.asArray(), meta.cars)) {
3210
+ return;
3211
+ }
3212
+ const carHeader = await this.loadCarHeaderFromMeta(meta, activeStore);
3213
+ carHeader.compact.map((c) => c.toString()).forEach((k) => this.seenCompacted.add(k), this.seenCompacted);
3214
+ try {
3215
+ await this.getMoreReaders(carHeader.cars.flat(), activeStore);
3216
+ } catch (e) {
3217
+ this.logger.Error().Err(e).Msg("error getting more readers");
3218
+ }
3219
+ this.carLog.update(uniqueCids([meta.cars, ...this.carLog.asArray(), ...carHeader.cars], this.seenCompacted));
3220
+ await this.ebOpts.applyMeta?.(carHeader.meta);
3221
+ } finally {
3222
+ this.isCompacting = false;
3057
3223
  }
3058
- const carHeader = await this.loadCarHeaderFromMeta(meta, activeStore);
3059
- carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3060
- await this.getMoreReaders(carHeader.cars.flat(), activeStore);
3061
- this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
3062
- await this.ebOpts.applyMeta?.(carHeader.meta);
3063
3224
  }
3064
3225
  // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
3065
3226
  // const { key } = meta;
@@ -3090,13 +3251,13 @@ var Loader = class {
3090
3251
  }
3091
3252
  async commit(t, done, opts = { noLoader: false, compact: false }) {
3092
3253
  await this.ready();
3093
- const carStore = await this.attachedStores.local().active.car;
3254
+ const carStore = this.attachedStores.local().active.car;
3094
3255
  const params = {
3095
3256
  encoder: (await carStore.keyedCrypto()).codec(),
3096
3257
  carLog: this.carLog,
3097
3258
  carStore,
3098
- WALStore: await this.attachedStores.local().active.wal,
3099
- metaStore: await this.attachedStores.local().active.meta,
3259
+ WALStore: this.attachedStores.local().active.wal,
3260
+ metaStore: this.attachedStores.local().active.meta,
3100
3261
  threshold: this.ebOpts.threshold
3101
3262
  };
3102
3263
  return this.commitQueue.enqueue(async () => {
@@ -3106,34 +3267,44 @@ var Loader = class {
3106
3267
  return ret.cgrp;
3107
3268
  });
3108
3269
  }
3109
- async updateCarLog(cids, fp, compact) {
3270
+ async updateCarLog(cids, cHeader, compact) {
3110
3271
  if (compact) {
3111
- const previousCompactCid = fp.compact[fp.compact.length - 1];
3112
- fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3113
- this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
3272
+ const previousCompactCid = cHeader.compact[cHeader.compact.length - 1];
3273
+ cHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3274
+ this.carLog.update(uniqueCids([...this.carLog.asArray(), ...cHeader.cars, cids], this.seenCompacted));
3114
3275
  await this.removeCidsForCompact(previousCompactCid[0], this.attachedStores.local()).catch((e) => e);
3115
3276
  } else {
3116
- this.carLog.unshift(cids);
3277
+ this.carLog.xunshift(cids);
3117
3278
  }
3118
3279
  }
3119
3280
  async cacheTransaction(t) {
3120
3281
  for await (const block of t.entries()) {
3121
3282
  const sBlock = block.cid.toString();
3122
- if (!this.getBlockCache.has(sBlock)) {
3123
- this.getBlockCache.set(sBlock, block);
3124
- }
3125
- }
3126
- }
3127
- async cacheCarReader(carCidStr, reader) {
3128
- if (this.processedCars.has(carCidStr)) return;
3129
- this.processedCars.add(carCidStr);
3130
- for await (const block of reader.blocks()) {
3131
- const sBlock = block.cid.toString();
3132
- if (!this.getBlockCache.has(sBlock)) {
3133
- this.getBlockCache.set(sBlock, block);
3134
- }
3283
+ this.cidCache.get(sBlock).once(
3284
+ () => ({
3285
+ type: "block",
3286
+ cid: block.cid,
3287
+ blocks: [block],
3288
+ roots: []
3289
+ })
3290
+ );
3135
3291
  }
3136
3292
  }
3293
+ // /**
3294
+ // *
3295
+ // * @returns the list of blocks which was read from the car file
3296
+ // */
3297
+ // private async readCar(reader: CarReader): Promise<AnyBlock[]> {
3298
+ // const blocks: AnyBlock[] = [];
3299
+ // for await (const block of reader.blocks()) {
3300
+ // const sBlock = block.cid.toString();
3301
+ // this.cidCache.get(sBlock).once(() => {
3302
+ // blocks.push(block);
3303
+ // return [block];
3304
+ // });
3305
+ // }
3306
+ // return blocks;
3307
+ // }
3137
3308
  async removeCidsForCompact(cid, store) {
3138
3309
  const carHeader = await this.loadCarHeaderFromMeta(
3139
3310
  {
@@ -3155,149 +3326,134 @@ var Loader = class {
3155
3326
  // await this.remoteWAL!.enqueue(dbMeta, { public: false })
3156
3327
  // }
3157
3328
  // }
3158
- async *entries(cache2 = true) {
3329
+ async *entries() {
3159
3330
  await this.ready();
3160
- if (cache2) {
3161
- for (const [, block] of this.getBlockCache) {
3162
- yield block;
3163
- }
3164
- } else {
3165
- for (const [, block] of this.getBlockCache) {
3166
- yield block;
3167
- }
3168
- for (const cids of this.carLog) {
3169
- for (const cid of cids) {
3170
- const reader = await this.loadCar(cid, this.attachedStores.local());
3171
- if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
3172
- for await (const block of reader.blocks()) {
3173
- const sCid = block.cid.toString();
3174
- if (!this.getBlockCache.has(sCid)) {
3175
- yield block;
3176
- }
3177
- }
3331
+ const seen = /* @__PURE__ */ new Set();
3332
+ for (const carCids of this.carLog.asArray()) {
3333
+ for (const carCid of carCids) {
3334
+ const reader = await this.loadCar(carCid, this.attachedStores.local());
3335
+ if (!reader || reader.type !== "car") {
3336
+ throw this.logger.Error().Any("reader", reader.type).Str("cid", carCid.toString()).Msg("missing car reader").AsError();
3337
+ }
3338
+ for (const block of reader.blocks) {
3339
+ const cidStr = block.cid.toString();
3340
+ if (seen.has(cidStr)) continue;
3341
+ seen.add(cidStr);
3342
+ yield block;
3178
3343
  }
3179
3344
  }
3180
3345
  }
3181
3346
  }
3182
3347
  async getBlock(cid, store) {
3183
3348
  await this.ready();
3184
- const sCid = cid.toString();
3185
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3186
- const getCarCid = async (carCid) => {
3187
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3188
- const reader = await this.loadCar(carCid, store);
3189
- if (!reader) {
3190
- throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
3191
- }
3192
- await this.cacheCarReader(carCid.toString(), reader).catch(() => {
3193
- return;
3194
- });
3195
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3196
- throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
3197
- };
3198
- const getCompactCarCids = async (carCid) => {
3199
- const reader = await this.loadCar(carCid, store);
3200
- if (!reader) {
3201
- throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
3202
- }
3203
- const header = await parseCarFile(reader, this.logger);
3204
- const compacts = header.compact;
3205
- let got2;
3206
- const batchSize2 = 5;
3207
- for (let i = 0; i < compacts.length; i += batchSize2) {
3208
- const promises = [];
3209
- for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
3210
- for (const cid2 of compacts[j]) {
3211
- promises.push(getCarCid(cid2));
3349
+ const cidStr = cid.toString();
3350
+ const ci = await this.cidCache.get(cidStr).once(async () => {
3351
+ const getCompactCarCids = async (carCid) => {
3352
+ const sCid = carCid.toString();
3353
+ const reader = await this.loadCar(carCid, store);
3354
+ const header = await parseCarFile(reader, this.logger);
3355
+ const compacts = header.compact;
3356
+ const got2 = await Promise.allSettled(compacts.map((compact) => compact.map((cid2) => this.loadCar(cid2, store)).flat()));
3357
+ got2.filter((result) => result.status === "rejected").forEach((result) => {
3358
+ this.logger.Error().Err(result.reason).Str("cid", sCid).Msg("error getting compacted block");
3359
+ });
3360
+ };
3361
+ let got;
3362
+ for (const carCids of this.carLog.asArray()) {
3363
+ for (const carCid of carCids) {
3364
+ const ci2 = await this.loadCar(carCid, store);
3365
+ if (!ci2) {
3366
+ this.logger.Error().Str("cid", carCid.toString()).Msg("missing CarCID");
3367
+ continue;
3368
+ }
3369
+ got = ci2.blocks.find((block) => block.cid.equals(cid));
3370
+ if (got) {
3371
+ break;
3212
3372
  }
3213
3373
  }
3214
- try {
3215
- got2 = await Promise.any(promises);
3216
- } catch {
3217
- }
3218
- if (got2) break;
3219
3374
  }
3220
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3221
- throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
3222
- };
3223
- let got;
3224
- const batchSize = 5;
3225
- for (let i = 0; i < this.carLog.length; i += batchSize) {
3226
- const batch = this.carLog.slice(i, i + batchSize);
3227
- const promises = batch.flatMap((slice) => slice.map(getCarCid));
3228
- try {
3229
- got = await Promise.any(promises);
3230
- } catch {
3231
- }
3232
- if (got) break;
3233
- }
3234
- if (!got) {
3235
- try {
3236
- got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
3237
- } catch {
3375
+ if (!got) {
3376
+ await getCompactCarCids(this.carLog.last()[0]);
3238
3377
  }
3378
+ return {
3379
+ type: "block",
3380
+ cid,
3381
+ blocks: got ? [got] : [],
3382
+ roots: []
3383
+ };
3384
+ });
3385
+ if (!(ci.type === "block" && ci.blocks.length === 1)) {
3386
+ throw this.logger.Error().Str("cid", cidStr).Any("block", ci).Msg("missing block").AsError();
3239
3387
  }
3240
- return got;
3388
+ return ci.blocks[0];
3241
3389
  }
3242
3390
  async loadCar(cid, store) {
3243
3391
  const loaded = await this.storesLoadCar(cid, store.carStore());
3244
3392
  return loaded;
3245
3393
  }
3246
- async makeDecoderAndCarReader(cid, store) {
3247
- const cidsString = cid.toString();
3394
+ async makeDecoderAndCarReader(carCid, store) {
3395
+ const carCidStr = carCid.toString();
3248
3396
  let loadedCar = void 0;
3249
- let activeStore = store.attached.local();
3397
+ let activeStore = store.local();
3250
3398
  try {
3251
- this.logger.Debug().Any("cid", cidsString).Msg("loading car");
3252
- loadedCar = await store.attached.local().load(cid);
3399
+ this.logger.Debug().Any("cid", carCidStr).Msg("loading car");
3400
+ loadedCar = await store.local().load(carCid);
3253
3401
  this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
3254
3402
  } catch (e) {
3255
3403
  if (!isNotFoundError(e)) {
3256
- throw this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
3404
+ throw this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3257
3405
  }
3258
- for (const remote of store.attached.remotes()) {
3259
- const remoteCar = await remote.load(cid);
3260
- if (remoteCar) {
3261
- this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3262
- await store.attached.local().save(remoteCar);
3263
- loadedCar = remoteCar;
3264
- activeStore = remote;
3265
- break;
3266
- } else {
3267
- this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
3406
+ for (const remote of store.remotes()) {
3407
+ try {
3408
+ const remoteCar = await remote.load(carCid);
3409
+ if (remoteCar) {
3410
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3411
+ await store.local().save(remoteCar);
3412
+ loadedCar = remoteCar;
3413
+ activeStore = remote;
3414
+ break;
3415
+ } else {
3416
+ this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3417
+ }
3418
+ } catch (e2) {
3419
+ this.logger.Warn().Str("cid", carCidStr).Url(remote.url()).Err(e2).Msg("loading car");
3268
3420
  }
3269
3421
  }
3270
3422
  }
3271
3423
  if (!loadedCar) {
3272
- throw this.logger.Error().Url(store.attached.local().url()).Str("cid", cidsString).Msg("missing car files").AsError();
3424
+ throw this.logger.Error().Url(store.local().url()).Str("cid", carCidStr).Msg("missing car files").AsError();
3273
3425
  }
3274
3426
  const bytes = await decode({ bytes: loadedCar.bytes, hasher: import_sha24.sha256, codec: (await activeStore.keyedCrypto()).codec() });
3275
- const rawReader = await import_reader.CarReader.fromBytes(bytes.value);
3276
- const readerP = Promise.resolve(rawReader);
3277
- const cachedReaderP = readerP.then(async (reader) => {
3278
- await this.cacheCarReader(cidsString, reader).catch((e) => {
3279
- this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
3280
- return;
3281
- });
3282
- return reader;
3283
- });
3284
- this.carReaders.set(cidsString, cachedReaderP);
3285
- return readerP;
3427
+ const rawReader = await import_reader.CarReader.fromBytes(bytes.value.data);
3428
+ const blocks = [];
3429
+ for await (const block of rawReader.blocks()) {
3430
+ const sBlock = block.cid.toString();
3431
+ blocks.push(block);
3432
+ this.cidCache.get(sBlock).once(() => ({
3433
+ type: "block",
3434
+ cid: block.cid,
3435
+ blocks: [block],
3436
+ roots: []
3437
+ }));
3438
+ }
3439
+ return {
3440
+ type: "car",
3441
+ cid: carCid,
3442
+ blocks,
3443
+ roots: await rawReader.getRoots()
3444
+ };
3286
3445
  }
3287
3446
  //What if instead it returns an Array of CarHeader
3288
- async storesLoadCar(cid, store) {
3289
- const cidsString = cid.toString();
3290
- let dacr = this.carReaders.get(cidsString);
3291
- if (!dacr) {
3292
- dacr = this.makeDecoderAndCarReader(cid, store);
3293
- this.carReaders.set(cidsString, dacr);
3294
- }
3295
- return dacr;
3447
+ async storesLoadCar(carCid, store) {
3448
+ const carCidStr = carCid.toString();
3449
+ return this.cidCache.get(carCidStr).once(async () => {
3450
+ return this.maxConcurrentCarReader(() => this.makeDecoderAndCarReader(carCid, store));
3451
+ });
3296
3452
  }
3297
3453
  async getMoreReaders(cids, store) {
3298
- const limit = (0, import_p_limit.default)(5);
3299
- const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
3300
- await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid, store))));
3454
+ for (const cid of cids) {
3455
+ await this.loadCar(cid, store);
3456
+ }
3301
3457
  }
3302
3458
  };
3303
3459
 
@@ -3342,12 +3498,21 @@ function getGenerateIVFn(url, opts) {
3342
3498
  }
3343
3499
  var BlockIvKeyIdCodec = class {
3344
3500
  constructor(ko, iv, opts) {
3345
- this.code = 3147065;
3501
+ this.code = 24;
3346
3502
  this.name = "Fireproof@encrypted-block:aes-gcm";
3347
3503
  this.ko = ko;
3348
3504
  this.iv = iv;
3349
3505
  this.opts = opts || {};
3350
3506
  }
3507
+ // hashAsBytes(data: IvKeyIdData): AsyncHashAsBytes<Uint8Array<ArrayBufferLike>> {
3508
+ // return data;
3509
+ // }
3510
+ valueToHashBytes(value) {
3511
+ return Promise.resolve(value.data);
3512
+ }
3513
+ bytesToHash(data) {
3514
+ return Promise.resolve(data);
3515
+ }
3351
3516
  async encode(data) {
3352
3517
  const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
3353
3518
  const { iv } = this.ko.algo(calcIv);
@@ -3379,11 +3544,16 @@ var BlockIvKeyIdCodec = class {
3379
3544
  if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
3380
3545
  throw this.ko.logger.Error().Msg("iv missmatch").AsError();
3381
3546
  }
3382
- return result;
3547
+ return {
3548
+ iv,
3549
+ keyId,
3550
+ data: result
3551
+ };
3383
3552
  }
3384
3553
  };
3385
3554
  var cryptoAction = class {
3386
3555
  constructor(url, key, cyopt, sthis) {
3556
+ this.code = 24;
3387
3557
  this.ivLength = 12;
3388
3558
  this.isEncrypting = true;
3389
3559
  this.logger = ensureLogger(sthis, "cryptoAction");
@@ -3417,14 +3587,19 @@ var cryptoAction = class {
3417
3587
  };
3418
3588
  var nullCodec = class {
3419
3589
  constructor() {
3420
- this.code = 0;
3590
+ this.code = 24;
3421
3591
  this.name = "Fireproof@unencrypted-block";
3592
+ this.empty = new Uint8Array();
3422
3593
  }
3423
- encode(data) {
3594
+ async encode(data) {
3424
3595
  return data;
3425
3596
  }
3426
- decode(data) {
3427
- return data;
3597
+ async decode(data) {
3598
+ return {
3599
+ iv: this.empty,
3600
+ keyId: this.empty,
3601
+ data
3602
+ };
3428
3603
  }
3429
3604
  };
3430
3605
  var noCrypto = class {
@@ -3679,13 +3854,8 @@ var BaseStoreImpl = class {
3679
3854
  this._url = res.Ok();
3680
3855
  const kb = await this.loader.keyBag();
3681
3856
  const skRes = await kb.ensureKeyFromUrl(this._url, () => {
3682
- const idx = this._url.getParam(PARAM.INDEX);
3683
- const storeKeyName = [this.url().getParam(PARAM.NAME)];
3684
- if (idx) {
3685
- storeKeyName.push(idx);
3686
- }
3687
- storeKeyName.push(this.storeType);
3688
- return storeKeyName.join(":");
3857
+ const key = this._url.getParam(PARAM.KEY);
3858
+ return key;
3689
3859
  });
3690
3860
  if (skRes.isErr()) {
3691
3861
  return skRes;
@@ -3768,8 +3938,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3768
3938
  // }
3769
3939
  // return (rDbMeta.Ok() as FPEnvelopeMeta).payload;
3770
3940
  // }
3771
- async load() {
3772
- const branch = "main";
3941
+ async load(branch = "main") {
3773
3942
  const url = await this.gateway.buildUrl({ loader: this.loader }, this.url(), branch);
3774
3943
  if (url.isErr()) {
3775
3944
  throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
@@ -3783,7 +3952,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3783
3952
  }
3784
3953
  const fpMeta = rfpEnv.Ok().payload;
3785
3954
  const dbMetas = fpMeta.map((m) => m.dbMeta);
3786
- await this.loader.handleDbMetasFromStore(dbMetas, this.loader.attachedStores.local());
3955
+ await this.loader.handleDbMetasFromStore(dbMetas, this.loader.attachedStores.activate(url.Ok()));
3787
3956
  this.updateParentsFromDbMetas(fpMeta);
3788
3957
  return dbMetas;
3789
3958
  }
@@ -3815,9 +3984,8 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3815
3984
  }
3816
3985
  };
3817
3986
  var DataStoreImpl = class extends BaseStoreImpl {
3818
- constructor(sthis, url, opts) {
3819
- super(sthis, url, { ...opts }, ensureLogger(sthis, "DataStoreImpl"));
3820
- this.storeType = "data";
3987
+ constructor(sthis, url, opts, logger) {
3988
+ super(sthis, url, { ...opts }, logger);
3821
3989
  }
3822
3990
  async load(cid) {
3823
3991
  this.logger.Debug().Any("cid", cid).Msg("loading");
@@ -3832,7 +4000,6 @@ var DataStoreImpl = class extends BaseStoreImpl {
3832
4000
  const fpenv = res.Ok();
3833
4001
  switch (fpenv.type) {
3834
4002
  case "car":
3835
- return { cid, bytes: fpenv.payload };
3836
4003
  case "file":
3837
4004
  return { cid, bytes: fpenv.payload };
3838
4005
  default:
@@ -3848,12 +4015,11 @@ var DataStoreImpl = class extends BaseStoreImpl {
3848
4015
  }
3849
4016
  let fpMsg;
3850
4017
  switch (url.Ok().getParam(PARAM.STORE)) {
3851
- case "data":
3852
- if (url.Ok().getParam(PARAM.SUFFIX)) {
3853
- fpMsg = Car2FPMsg(car.bytes);
3854
- } else {
3855
- fpMsg = File2FPMsg(car.bytes);
3856
- }
4018
+ case "car":
4019
+ fpMsg = Car2FPMsg(car.bytes);
4020
+ break;
4021
+ case "file":
4022
+ fpMsg = File2FPMsg(car.bytes);
3857
4023
  break;
3858
4024
  default:
3859
4025
  throw this.logger.Error().Str("store", url.Ok().getParam(PARAM.STORE)).Msg("unexpected store").AsError();
@@ -3884,6 +4050,18 @@ var DataStoreImpl = class extends BaseStoreImpl {
3884
4050
  return this.gateway.destroy({ loader: this.loader }, this.url());
3885
4051
  }
3886
4052
  };
4053
+ var CarStoreImpl = class extends DataStoreImpl {
4054
+ constructor(sthis, url, opts) {
4055
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "CarStoreImpl"));
4056
+ this.storeType = "car";
4057
+ }
4058
+ };
4059
+ var FileStoreImpl = class extends DataStoreImpl {
4060
+ constructor(sthis, url, opts) {
4061
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "FileStoreImpl"));
4062
+ this.storeType = "file";
4063
+ }
4064
+ };
3887
4065
  var WALStoreImpl = class extends BaseStoreImpl {
3888
4066
  constructor(sthis, url, opts) {
3889
4067
  super(sthis, url, { ...opts }, ensureLogger(sthis, "WALStoreImpl"));
@@ -3966,7 +4144,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3966
4144
  for (const cid of dbMeta.cars) {
3967
4145
  const car = await this.loader.attachedStores.local().active.car.load(cid);
3968
4146
  if (!car) {
3969
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4147
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3970
4148
  throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
3971
4149
  }
3972
4150
  } else {
@@ -3988,7 +4166,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3988
4166
  for (const cid of dbMeta.cars) {
3989
4167
  const car = await this.loader.attachedStores.local().active.car.load(cid);
3990
4168
  if (!car) {
3991
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4169
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3992
4170
  throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
3993
4171
  }
3994
4172
  } else {
@@ -4084,8 +4262,9 @@ async function getStartedGateway(ctx, url) {
4084
4262
  if (item) {
4085
4263
  const ret = {
4086
4264
  url,
4087
- ...await gatewayInstances.get(url.protocol).once(async () => ({})),
4088
- gateway: await item.serdegateway(ctx.loader.sthis)
4265
+ ...await gatewayInstances.get(url.protocol).once(async () => ({
4266
+ gateway: await item.serdegateway(ctx.loader.sthis)
4267
+ }))
4089
4268
  };
4090
4269
  const res = await ret.gateway.start(ctx, url);
4091
4270
  if (res.isErr()) {
@@ -4097,14 +4276,28 @@ async function getStartedGateway(ctx, url) {
4097
4276
  return import_cement17.Result.Err(ctx.loader.sthis.logger.Warn().Url(url).Msg("unsupported protocol").AsError());
4098
4277
  });
4099
4278
  }
4100
- async function dataStoreFactory(ctx, uai) {
4101
- const storeUrl = uai.url.build().setParam(PARAM.STORE, "data").URI();
4279
+ async function carStoreFactory(ctx, uai) {
4280
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "car").URI();
4102
4281
  const rgateway = await getStartedGateway(ctx, storeUrl);
4103
4282
  if (rgateway.isErr()) {
4104
4283
  throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
4105
4284
  }
4106
4285
  const gateway = rgateway.Ok();
4107
- const store = new DataStoreImpl(ctx.loader.sthis, gateway.url, {
4286
+ const store = new CarStoreImpl(ctx.loader.sthis, gateway.url, {
4287
+ gateway: gateway.gateway,
4288
+ gatewayInterceptor: uai.gatewayInterceptor,
4289
+ loader: ctx.loader
4290
+ });
4291
+ return store;
4292
+ }
4293
+ async function fileStoreFactory(ctx, uai) {
4294
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "file").URI();
4295
+ const rgateway = await getStartedGateway(ctx, storeUrl);
4296
+ if (rgateway.isErr()) {
4297
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
4298
+ }
4299
+ const gateway = rgateway.Ok();
4300
+ const store = new FileStoreImpl(ctx.loader.sthis, gateway.url, {
4108
4301
  gateway: gateway.gateway,
4109
4302
  gatewayInterceptor: uai.gatewayInterceptor,
4110
4303
  loader: ctx.loader
@@ -4162,8 +4355,8 @@ function toStoreRuntime(sthis, endeOpts = {}) {
4162
4355
  };
4163
4356
  const storeSet = {};
4164
4357
  storeSet.meta = await metaStoreFactory(ctx, sfi.byStore.meta);
4165
- storeSet.car = await dataStoreFactory(ctx, sfi.byStore.car);
4166
- storeSet.file = await dataStoreFactory(ctx, sfi.byStore.file);
4358
+ storeSet.car = await carStoreFactory(ctx, sfi.byStore.car);
4359
+ storeSet.file = await fileStoreFactory(ctx, sfi.byStore.file);
4167
4360
  if (sfi.byStore.wal) {
4168
4361
  storeSet.wal = await WALStoreFactory(ctx, sfi.byStore.wal);
4169
4362
  }
@@ -4202,6 +4395,34 @@ function toString(key, logger) {
4202
4395
  throw logger.Error().Msg("Invalid key type").AsError();
4203
4396
  }
4204
4397
  }
4398
+ function sanitizeDocumentFields(obj) {
4399
+ if (Array.isArray(obj)) {
4400
+ return obj.map((item) => {
4401
+ if (typeof item === "object" && item !== null) {
4402
+ return sanitizeDocumentFields(item);
4403
+ }
4404
+ return item;
4405
+ });
4406
+ } else if (typeof obj === "object" && obj !== null) {
4407
+ const typedObj = obj;
4408
+ const result = {};
4409
+ for (const key in typedObj) {
4410
+ if (Object.hasOwnProperty.call(typedObj, key)) {
4411
+ const value = typedObj[key];
4412
+ if (value === null || !Number.isNaN(value) && value !== void 0) {
4413
+ if (typeof value === "object" && !key.startsWith("_")) {
4414
+ const sanitized = sanitizeDocumentFields(value);
4415
+ result[key] = sanitized;
4416
+ } else {
4417
+ result[key] = value;
4418
+ }
4419
+ }
4420
+ }
4421
+ }
4422
+ return result;
4423
+ }
4424
+ return obj;
4425
+ }
4205
4426
  async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
4206
4427
  let result = null;
4207
4428
  if (updates.length > 1) {
@@ -4291,7 +4512,8 @@ async function getValueFromCrdt(blocks, head, key, logger) {
4291
4512
  if (!head.length) throw logger.Debug().Msg("Getting from an empty ledger").AsError();
4292
4513
  const link = await (0, import_crdt.get)(blocks, head, key);
4293
4514
  if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
4294
- return await getValueFromLink(blocks, link, logger);
4515
+ const ret = await getValueFromLink(blocks, link, logger);
4516
+ return ret;
4295
4517
  }
4296
4518
  function readFiles(blocks, { doc }) {
4297
4519
  if (!doc) return;
@@ -4389,6 +4611,9 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4389
4611
  const { key, value } = ops[i];
4390
4612
  if (!keys.has(key)) {
4391
4613
  const docValue = await getValueFromLink(blocks, value, logger);
4614
+ if (key === PARAM.GENESIS_CID) {
4615
+ continue;
4616
+ }
4392
4617
  updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
4393
4618
  limit--;
4394
4619
  keys.add(key);
@@ -4402,8 +4627,10 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4402
4627
  }
4403
4628
  async function* getAllEntries(blocks, head, logger) {
4404
4629
  for await (const [key, link] of (0, import_crdt.entries)(blocks, head)) {
4405
- const docValue = await getValueFromLink(blocks, link, logger);
4406
- yield { id: key, value: docValue.doc, del: docValue.del };
4630
+ if (key !== PARAM.GENESIS_CID) {
4631
+ const docValue = await getValueFromLink(blocks, link, logger);
4632
+ yield { id: key, value: docValue.doc, del: docValue.del };
4633
+ }
4407
4634
  }
4408
4635
  }
4409
4636
  async function* clockVis(blocks, head) {
@@ -4548,6 +4775,10 @@ var CRDTClockImpl = class {
4548
4775
  this.notifyWatchers(internalUpdates || []);
4549
4776
  }
4550
4777
  notifyWatchers(updates) {
4778
+ updates = updates.filter((update) => update.id !== PARAM.GENESIS_CID);
4779
+ if (!updates.length) {
4780
+ return;
4781
+ }
4551
4782
  this.emptyWatchers.forEach((fn) => fn());
4552
4783
  this.watchers.forEach((fn) => fn(updates || []));
4553
4784
  }
@@ -4627,7 +4858,7 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
4627
4858
  try {
4628
4859
  head = await (0, import_clock4.advance)(tblocks, head, cid);
4629
4860
  } catch (e) {
4630
- logger.Debug().Err(e).Msg("failed to advance head");
4861
+ logger.Error().Err(e).Msg("failed to advance head");
4631
4862
  continue;
4632
4863
  }
4633
4864
  }
@@ -4686,6 +4917,17 @@ var CRDTImpl = class {
4686
4917
  }
4687
4918
  async bulk(updates) {
4688
4919
  await this.ready();
4920
+ updates = updates.map((dupdate) => ({
4921
+ ...dupdate,
4922
+ value: sanitizeDocumentFields(dupdate.value)
4923
+ }));
4924
+ if (this.clock.head.length === 0) {
4925
+ const value = { id: PARAM.GENESIS_CID, value: { _id: PARAM.GENESIS_CID } };
4926
+ await this._bulk([value]);
4927
+ }
4928
+ return await this._bulk(updates);
4929
+ }
4930
+ async _bulk(updates) {
4689
4931
  const prevHead = [...this.clock.head];
4690
4932
  const done = await this.blockstore.transaction(async (blocks) => {
4691
4933
  const { head } = await applyBulkUpdateToCrdt(
@@ -4917,7 +5159,8 @@ var LedgerImpl = class {
4917
5159
  });
4918
5160
  return ret;
4919
5161
  }
4920
- attach(a) {
5162
+ async attach(a) {
5163
+ await this.ready();
4921
5164
  return this.crdt.blockstore.loader.attach(a);
4922
5165
  }
4923
5166
  // readonly _asDb = new ResolveOnce<Database>();
@@ -4925,6 +5168,7 @@ var LedgerImpl = class {
4925
5168
  // return this._asDb.once(() => new DatabaseImpl(this));
4926
5169
  // }
4927
5170
  subscribe(listener, updates) {
5171
+ this.ready();
4928
5172
  this.logger.Debug().Bool("updates", updates).Msg("subscribe");
4929
5173
  if (updates) {
4930
5174
  if (!this._listening) {
@@ -4966,26 +5210,6 @@ var LedgerImpl = class {
4966
5210
  }
4967
5211
  }
4968
5212
  };
4969
- function defaultURI2(sthis, name, curi, uri, store, ctx) {
4970
- ctx = ctx || {};
4971
- const ret = (curi ? import_cement20.URI.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
4972
- if (!ret.hasParam(PARAM.NAME)) {
4973
- throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
4974
- }
4975
- if (ctx.idx) {
4976
- ret.defParam(PARAM.INDEX, "idx");
4977
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}-idx@`);
4978
- } else {
4979
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}@`);
4980
- }
4981
- if (store === "data") {
4982
- if (ctx.file) {
4983
- } else {
4984
- ret.defParam(PARAM.SUFFIX, ".car");
4985
- }
4986
- }
4987
- return ret.URI();
4988
- }
4989
5213
  function toStoreURIRuntime(sthis, name, sopts) {
4990
5214
  sopts = sopts || {};
4991
5215
  if (!sopts.base) {
@@ -4999,16 +5223,19 @@ function toStoreURIRuntime(sthis, name, sopts) {
4999
5223
  const base = import_cement20.URI.from(sopts.base);
5000
5224
  return {
5001
5225
  idx: {
5002
- car: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { idx: true }),
5003
- file: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { file: true, idx: true }),
5004
- meta: defaultURI2(sthis, name, sopts.idx?.meta, base, "meta", { idx: true }),
5005
- wal: defaultURI2(sthis, name, sopts.idx?.wal, base, "wal", { idx: true })
5226
+ car: ensureURIDefaults(sthis, name, sopts.idx?.car ?? sopts.data?.car, base, "car", { idx: true }),
5227
+ file: ensureURIDefaults(sthis, name, sopts.idx?.file ?? sopts.idx?.car ?? sopts.data?.file ?? sopts.data?.car, base, "file", {
5228
+ file: true,
5229
+ idx: true
5230
+ }),
5231
+ meta: ensureURIDefaults(sthis, name, sopts.idx?.meta ?? sopts.data?.meta, base, "meta", { idx: true }),
5232
+ wal: ensureURIDefaults(sthis, name, sopts.idx?.wal ?? sopts.data?.wal, base, "wal", { idx: true })
5006
5233
  },
5007
5234
  data: {
5008
- car: defaultURI2(sthis, name, sopts.data?.data, base, "data"),
5009
- file: defaultURI2(sthis, name, sopts.data?.data, base, "data", { file: true }),
5010
- meta: defaultURI2(sthis, name, sopts.data?.meta, base, "meta"),
5011
- wal: defaultURI2(sthis, name, sopts.data?.wal, base, "wal")
5235
+ car: ensureURIDefaults(sthis, name, sopts.data?.car, base, "car"),
5236
+ file: ensureURIDefaults(sthis, name, sopts.data?.file ?? sopts.data?.car, base, "file", { file: true }),
5237
+ meta: ensureURIDefaults(sthis, name, sopts.data?.meta, base, "meta"),
5238
+ wal: ensureURIDefaults(sthis, name, sopts.data?.wal, base, "wal")
5012
5239
  }
5013
5240
  };
5014
5241
  }
@@ -5072,6 +5299,6 @@ __export(file_exports, {
5072
5299
 
5073
5300
  // src/version.ts
5074
5301
  var PACKAGE_VERSION = Object.keys({
5075
- "0.20.0-dev-preview-41": "xxxx"
5302
+ "0.20.0-dev-preview-50": "xxxx"
5076
5303
  })[0];
5077
5304
  //# sourceMappingURL=index.cjs.map