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

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.js CHANGED
@@ -5,7 +5,7 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/ledger.ts
8
- import { BuildURI as BuildURI3, KeyedResolvOnce as KeyedResolvOnce5, ResolveOnce as ResolveOnce7, URI as URI12 } from "@adviser/cement";
8
+ import { BuildURI as BuildURI2, KeyedResolvOnce as KeyedResolvOnce6, ResolveOnce as ResolveOnce7, URI as URI13 } from "@adviser/cement";
9
9
 
10
10
  // src/utils.ts
11
11
  import {
@@ -14,6 +14,7 @@ import {
14
14
  Result,
15
15
  ResolveOnce,
16
16
  isURL,
17
+ URI,
17
18
  envFactory,
18
19
  toCryptoRuntime,
19
20
  JSONFormatter,
@@ -44,8 +45,13 @@ var PARAM = {
44
45
  FRAG_LEN: "len",
45
46
  FRAG_HEAD: "headerSize",
46
47
  EXTRACTKEY: "extractKey",
47
- SELF_REFLECT: "selfReflect"
48
+ SELF_REFLECT: "selfReflect",
48
49
  // if no subscribe in Gateway see your own META updates
50
+ CAR_PARALLEL: "parallel",
51
+ CAR_CACHE_SIZE: "carCacheSize",
52
+ CAR_COMPACT_CACHE_SIZE: "carCompactCacheSize",
53
+ CAR_META_CACHE_SIZE: "carMetaCacheSize",
54
+ GENESIS_CID: "baembeiarootfireproofgenesisblockaaaafireproofgenesisblocka"
49
55
  // FS = "fs",
50
56
  };
51
57
  function throwFalsy(value) {
@@ -282,21 +288,25 @@ function ensureLogger(sthis, componentName, ctx) {
282
288
  return out;
283
289
  }
284
290
  function getStore(url, sthis, joiner) {
285
- const store = url.getParam(PARAM.STORE);
286
- switch (store) {
287
- case "data":
291
+ const fromUrl = url.getParam(PARAM.STORE);
292
+ let pathPart;
293
+ switch (fromUrl) {
294
+ case "car":
295
+ case "file":
296
+ pathPart = "data";
297
+ break;
288
298
  case "wal":
289
299
  case "meta":
300
+ pathPart = fromUrl;
290
301
  break;
291
302
  default:
292
303
  throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
293
- throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
294
304
  }
295
- let name = store;
305
+ let name = pathPart;
296
306
  if (url.hasParam("index")) {
297
307
  name = joiner(url.getParam(PARAM.INDEX) || "idx", name);
298
308
  }
299
- return { store, name };
309
+ return { pathPart, fromUrl, name };
300
310
  }
301
311
  function getKey(url, logger) {
302
312
  const result = url.getParam(PARAM.KEY);
@@ -398,6 +408,35 @@ function makeName(fnString) {
398
408
  return found[1];
399
409
  }
400
410
  }
411
+ function storeType2DataMetaWal(store) {
412
+ switch (store) {
413
+ case "car":
414
+ case "file":
415
+ return "data";
416
+ case "meta":
417
+ case "wal":
418
+ return store;
419
+ default:
420
+ throw new Error(`unknown store ${store}`);
421
+ }
422
+ }
423
+ function ensureURIDefaults(sthis, name, curi, uri, store, ctx) {
424
+ ctx = ctx || {};
425
+ const ret = (curi ? URI.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
426
+ if (!ret.hasParam(PARAM.NAME)) {
427
+ throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
428
+ }
429
+ if (ctx.idx) {
430
+ ret.defParam(PARAM.INDEX, "idx");
431
+ ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${storeType2DataMetaWal(store)}-idx@`);
432
+ } else {
433
+ ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${storeType2DataMetaWal(store)}@`);
434
+ }
435
+ if (store === "car") {
436
+ ret.defParam(PARAM.SUFFIX, ".car");
437
+ }
438
+ return ret.URI();
439
+ }
401
440
 
402
441
  // src/write-queue.ts
403
442
  import { Future } from "@adviser/cement";
@@ -1087,8 +1126,10 @@ function getFileName(url, sthis) {
1087
1126
  const key = url.getParam("key");
1088
1127
  if (!key) throw sthis.logger.Error().Url(url).Msg(`key not found`).AsError();
1089
1128
  const res = getStore2(url, sthis, (...a) => a.join("-"));
1090
- switch (res.store) {
1091
- case "data":
1129
+ switch (res.fromUrl) {
1130
+ case "file":
1131
+ return sthis.pathOps.join(res.name, key);
1132
+ case "car":
1092
1133
  return sthis.pathOps.join(res.name, key + ".car");
1093
1134
  case "wal":
1094
1135
  case "meta":
@@ -1236,8 +1277,13 @@ var MemoryGateway = class {
1236
1277
  close(baseUrl) {
1237
1278
  return Promise.resolve(Result4.Ok(void 0));
1238
1279
  }
1239
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1240
1280
  destroy(baseUrl) {
1281
+ const keyUrl = baseUrl.toString();
1282
+ for (const key of this.memorys.keys()) {
1283
+ if (key.startsWith(keyUrl)) {
1284
+ this.memorys.delete(key);
1285
+ }
1286
+ }
1241
1287
  this.memorys.clear();
1242
1288
  return Promise.resolve(Result4.Ok(void 0));
1243
1289
  }
@@ -1249,7 +1295,7 @@ var MemoryGateway = class {
1249
1295
  get(url) {
1250
1296
  const x = this.memorys.get(url.toString());
1251
1297
  if (!x) {
1252
- return Promise.resolve(Result4.Err(new NotFoundError(`not found: ${url.getParam(PARAM.STORE)}`)));
1298
+ return Promise.resolve(Result4.Err(new NotFoundError(`not found: ${url.toString()}`)));
1253
1299
  }
1254
1300
  return Promise.resolve(Result4.Ok(x));
1255
1301
  }
@@ -1419,10 +1465,9 @@ async function fpDeserialize(sthis, url, intoRaw, pdecoder) {
1419
1465
  ...pdecoder
1420
1466
  };
1421
1467
  switch (url.getParam(PARAM.STORE)) {
1422
- case "data":
1423
- if (url.getParam(PARAM.SUFFIX) === ".car") {
1424
- return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1425
- }
1468
+ case "car":
1469
+ return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1470
+ case "file":
1426
1471
  return makeFPEnvelope(FPEnvelopeTypes.FILE, await decoder.file(sthis, raw2));
1427
1472
  case "wal":
1428
1473
  return makeFPEnvelope(FPEnvelopeTypes.WAL, await decode2WalState(sthis, await decoder.wal(sthis, raw2)));
@@ -1484,7 +1529,7 @@ var DefSerdeGateway = class {
1484
1529
  const urlWithoutKey = url.build().delParam(PARAM.KEY).delParam(PARAM.SELF_REFLECT).toString();
1485
1530
  this.subscribeFn.set(urlWithoutKey, rawCallback);
1486
1531
  return Result7.Ok(() => {
1487
- this.subscribeFn.delete(url.toString());
1532
+ this.subscribeFn.delete(urlWithoutKey);
1488
1533
  });
1489
1534
  }
1490
1535
  const unreg = await this.gw.subscribe(url, rawCallback, sthis);
@@ -1613,37 +1658,49 @@ var Block = mfBlock;
1613
1658
  async function decode({
1614
1659
  bytes,
1615
1660
  codec: codec3,
1616
- hasher: hasher7
1661
+ hasher: hasher6
1617
1662
  }) {
1618
1663
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1619
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1664
+ if (codec3 == null || hasher6 == null) throw new Error("Missing required argument: codec or hasher");
1620
1665
  const value = await Promise.resolve(codec3.decode(bytes));
1621
- const hash = await hasher7.digest(bytes);
1666
+ let toHash = bytes;
1667
+ if (codec3.valueToHashBytes) {
1668
+ toHash = await Promise.resolve(codec3.valueToHashBytes(value));
1669
+ }
1670
+ const hash = await hasher6.digest(toHash);
1622
1671
  const cid = CID2.create(1, codec3.code, hash);
1623
- return new mfBlock({ value, bytes, cid });
1672
+ return new mfBlock({ value, bytes: toHash, cid });
1624
1673
  }
1625
1674
  async function encode({
1626
1675
  value,
1627
1676
  codec: codec3,
1628
- hasher: hasher7
1677
+ hasher: hasher6
1629
1678
  }) {
1630
1679
  if (typeof value === "undefined") throw new Error('Missing required argument "value"');
1631
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1632
- const bytes = await Promise.resolve(codec3.encode(value));
1633
- const hash = await hasher7.digest(bytes);
1680
+ if (codec3 == null || hasher6 == null) throw new Error("Missing required argument: codec or hasher");
1681
+ let bytes;
1682
+ let hash;
1683
+ if (codec3.bytesToHash) {
1684
+ const hashable = await Promise.resolve(codec3.bytesToHash(value));
1685
+ hash = await hasher6.digest(hashable);
1686
+ bytes = await Promise.resolve(codec3.encode(value));
1687
+ } else {
1688
+ bytes = await Promise.resolve(codec3.encode(value));
1689
+ hash = await hasher6.digest(bytes);
1690
+ }
1634
1691
  const cid = CID2.create(1, codec3.code, hash);
1635
- return new mfBlock({ value, bytes, cid });
1692
+ return new Block({ value, bytes, cid });
1636
1693
  }
1637
1694
  async function create({
1638
1695
  bytes,
1639
1696
  cid,
1640
- hasher: hasher7,
1697
+ hasher: hasher6,
1641
1698
  codec: codec3
1642
1699
  }) {
1643
1700
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1644
- if (hasher7 == null) throw new Error('Missing required argument "hasher"');
1701
+ if (hasher6 == null) throw new Error('Missing required argument "hasher"');
1645
1702
  const value = await Promise.resolve(codec3.decode(bytes));
1646
- const hash = await hasher7.digest(bytes);
1703
+ const hash = await hasher6.digest(bytes);
1647
1704
  if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
1648
1705
  throw new Error("CID hash does not match bytes");
1649
1706
  }
@@ -1855,7 +1912,7 @@ var Index = class {
1855
1912
  if (this.mapFn) {
1856
1913
  if (mapFn) {
1857
1914
  if (this.mapFn.toString() !== mapFn.toString()) {
1858
- throw this.logger.Error().Msg("cannot apply different mapFn app2").AsError();
1915
+ this.logger.Error().Msg("cannot apply different mapFn app2");
1859
1916
  }
1860
1917
  }
1861
1918
  } else {
@@ -1864,7 +1921,7 @@ var Index = class {
1864
1921
  }
1865
1922
  if (this.mapFnString) {
1866
1923
  if (this.mapFnString !== mapFn.toString()) {
1867
- throw this.logger.Error().Str("mapFnString", this.mapFnString).Str("mapFn", mapFn.toString()).Msg("cannot apply different mapFn app").AsError();
1924
+ this.logger.Error().Str("mapFnString", this.mapFnString).Str("mapFn", mapFn.toString()).Msg("cannot apply different mapFn app");
1868
1925
  }
1869
1926
  } else {
1870
1927
  this.mapFnString = mapFn.toString();
@@ -2140,8 +2197,11 @@ import { ResolveOnce as ResolveOnce6 } from "@adviser/cement";
2140
2197
  var blockstore_exports = {};
2141
2198
  __export(blockstore_exports, {
2142
2199
  AttachedRemotesImpl: () => AttachedRemotesImpl,
2200
+ BaseActiveStore: () => BaseActiveStore,
2143
2201
  BaseBlockstoreImpl: () => BaseBlockstoreImpl,
2144
2202
  Car2FPMsg: () => Car2FPMsg,
2203
+ CarActiveStore: () => CarActiveStore,
2204
+ CarLog: () => CarLog,
2145
2205
  CarTransactionImpl: () => CarTransactionImpl,
2146
2206
  CompactionFetcher: () => CompactionFetcher,
2147
2207
  DbMetaEventEqual: () => DbMetaEventEqual,
@@ -2149,9 +2209,13 @@ __export(blockstore_exports, {
2149
2209
  EncryptedBlockstore: () => EncryptedBlockstore,
2150
2210
  FPEnvelopeTypes: () => FPEnvelopeTypes,
2151
2211
  File2FPMsg: () => File2FPMsg,
2212
+ FileActiveStore: () => FileActiveStore,
2152
2213
  InterceptorGateway: () => InterceptorGateway,
2153
2214
  Loader: () => Loader,
2215
+ MetaActiveStore: () => MetaActiveStore,
2154
2216
  PassThroughGateway: () => PassThroughGateway,
2217
+ TaskManager: () => TaskManager,
2218
+ WALActiveStore: () => WALActiveStore,
2155
2219
  createAttachedStores: () => createAttachedStores,
2156
2220
  createDbMetaEvent: () => createDbMetaEvent,
2157
2221
  defaultGatewayFactoryItem: () => defaultGatewayFactoryItem,
@@ -2166,6 +2230,37 @@ __export(blockstore_exports, {
2166
2230
  });
2167
2231
 
2168
2232
  // src/blockstore/types.ts
2233
+ var CarLog = class {
2234
+ constructor() {
2235
+ this._logs = [];
2236
+ }
2237
+ get length() {
2238
+ return this._logs.length;
2239
+ }
2240
+ last() {
2241
+ const x = [...this._logs[this._logs.length - 1]];
2242
+ Object.freeze(x);
2243
+ return x;
2244
+ }
2245
+ xunshift(logs) {
2246
+ this._logs.unshift(logs);
2247
+ }
2248
+ update(logs) {
2249
+ this._logs.length = 0;
2250
+ this._logs.push(...logs);
2251
+ }
2252
+ asArray() {
2253
+ const a = [
2254
+ ...this._logs.map((l) => {
2255
+ const x = [...l];
2256
+ Object.freeze(x);
2257
+ return x;
2258
+ })
2259
+ ];
2260
+ Object.freeze(a);
2261
+ return a;
2262
+ }
2263
+ };
2169
2264
  function toCIDBlock(block) {
2170
2265
  return block;
2171
2266
  }
@@ -2175,9 +2270,19 @@ function DbMetaEventEqual(a, b) {
2175
2270
  function DbMetaEventsEqual(a, b) {
2176
2271
  return a.length === b.length && a.every((e, i) => DbMetaEventEqual(e, b[i]));
2177
2272
  }
2273
+ var BaseActiveStore = class {
2274
+ };
2275
+ var CarActiveStore = class extends BaseActiveStore {
2276
+ };
2277
+ var FileActiveStore = class extends BaseActiveStore {
2278
+ };
2279
+ var MetaActiveStore = class extends BaseActiveStore {
2280
+ };
2281
+ var WALActiveStore = class extends BaseActiveStore {
2282
+ };
2178
2283
 
2179
2284
  // src/blockstore/store-factory.ts
2180
- import { KeyedResolvOnce as KeyedResolvOnce4, Result as Result10 } from "@adviser/cement";
2285
+ import { KeyedResolvOnce as KeyedResolvOnce5, Result as Result10 } from "@adviser/cement";
2181
2286
 
2182
2287
  // src/blockstore/store.ts
2183
2288
  import { exception2Result as exception2Result3, ResolveOnce as ResolveOnce4, Result as Result9 } from "@adviser/cement";
@@ -2185,14 +2290,14 @@ import { exception2Result as exception2Result3, ResolveOnce as ResolveOnce4, Res
2185
2290
  // src/blockstore/loader.ts
2186
2291
  import pLimit from "p-limit";
2187
2292
  import { CarReader } from "@ipld/car/reader";
2188
- import { ResolveOnce as ResolveOnce3 } from "@adviser/cement";
2293
+ import { KeyedResolvOnce as KeyedResolvOnce4, LRUSet, ResolveOnce as ResolveOnce3 } from "@adviser/cement";
2189
2294
 
2190
2295
  // src/blockstore/loader-helpers.ts
2191
2296
  import { sha256 as hasher2 } from "multiformats/hashes/sha2";
2192
2297
  import * as dagCodec from "@ipld/dag-cbor";
2193
2298
  async function parseCarFile(reader, logger) {
2194
- const roots = await reader.getRoots();
2195
- const header = await reader.get(roots[0]);
2299
+ const roots = await reader.roots;
2300
+ const header = reader.blocks.find((i) => i.cid.equals(roots[0]));
2196
2301
  if (!header) throw logger.Error().Msg("missing header block").AsError();
2197
2302
  const dec = await decode({ bytes: header.bytes, hasher: hasher2, codec: dagCodec });
2198
2303
  const fpvalue = dec.value;
@@ -2207,6 +2312,7 @@ import { MemoryBlockstore } from "@fireproof/vendor/@web3-storage/pail/block";
2207
2312
  import { toCryptoRuntime as toCryptoRuntime3 } from "@adviser/cement";
2208
2313
  var CarTransactionImpl = class {
2209
2314
  #memblock = new MemoryBlockstore();
2315
+ #hackUnshift;
2210
2316
  constructor(parent, opts = { add: true, noLoader: false }) {
2211
2317
  if (opts.add) {
2212
2318
  parent.transactions.add(this);
@@ -2214,7 +2320,7 @@ var CarTransactionImpl = class {
2214
2320
  this.parent = parent;
2215
2321
  }
2216
2322
  async get(cid) {
2217
- return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
2323
+ return await this.superGet(cid) ?? falsyToUndef(await this.parent.get(cid));
2218
2324
  }
2219
2325
  async superGet(cid) {
2220
2326
  return this.#memblock.get(cid);
@@ -2225,7 +2331,16 @@ var CarTransactionImpl = class {
2225
2331
  putSync(cid, bytes) {
2226
2332
  this.#memblock.putSync(cid, bytes);
2227
2333
  }
2334
+ unshift(cid, bytes) {
2335
+ if (this.#hackUnshift) {
2336
+ throw new Error("unshift already called");
2337
+ }
2338
+ this.#hackUnshift = { cid, bytes };
2339
+ }
2228
2340
  async *entries() {
2341
+ if (this.#hackUnshift) {
2342
+ yield this.#hackUnshift;
2343
+ }
2229
2344
  for await (const blk of this.#memblock.entries()) {
2230
2345
  yield blk;
2231
2346
  }
@@ -2251,6 +2366,11 @@ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
2251
2366
  keyBag: opts.keyBag || {},
2252
2367
  crypto: toCryptoRuntime3(opts.crypto),
2253
2368
  storeUrls: opts.storeUrls,
2369
+ taskManager: {
2370
+ removeAfter: 3,
2371
+ retryTimeout: 50,
2372
+ ...opts.taskManager
2373
+ },
2254
2374
  // storeEnDeFile: ensureStoreEnDeFile(opts.storeEnDeFile),
2255
2375
  // store,
2256
2376
  storeRuntime: toStoreRuntime(sthis, ensureStoreEnDeFile(opts.storeEnDeFile))
@@ -2343,10 +2463,8 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2343
2463
  async get(cid) {
2344
2464
  const got = await super.get(cid);
2345
2465
  if (got) return got;
2346
- if (!this.loader) {
2347
- return;
2348
- }
2349
- return falsyToUndef(await this.loader.getBlock(cid, this.loader.attachedStores.local()));
2466
+ const ret = falsyToUndef(await this.loader.getBlock(cid, this.loader.attachedStores.local()));
2467
+ return ret;
2350
2468
  }
2351
2469
  async transaction(fn, opts = { noLoader: false }) {
2352
2470
  this.logger.Debug().Msg("enter transaction");
@@ -2367,7 +2485,7 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2367
2485
  await this.ready();
2368
2486
  if (!this.loader) throw this.logger.Error().Msg("loader required to get file, ledger must be named").AsError();
2369
2487
  const reader = await this.loader.loadFileCar(car, this.loader.attachedStores.local());
2370
- const block = await reader.get(cid);
2488
+ const block = await reader.blocks.find((i) => i.cid.equals(cid));
2371
2489
  if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
2372
2490
  return block.bytes;
2373
2491
  }
@@ -2474,8 +2592,8 @@ var CommitQueue = class {
2474
2592
 
2475
2593
  // src/blockstore/commitor.ts
2476
2594
  import * as CBW from "@ipld/car/buffer-writer";
2477
- import { sha256 as hasher3 } from "multiformats/hashes/sha2";
2478
2595
  import * as dagCodec2 from "@ipld/dag-cbor";
2596
+ import { sha256 } from "multiformats/hashes/sha2";
2479
2597
  async function encodeCarFile(roots, t, codec3) {
2480
2598
  let size = 0;
2481
2599
  const headerSize = CBW.headerLength({ roots });
@@ -2492,7 +2610,7 @@ async function encodeCarFile(roots, t, codec3) {
2492
2610
  writer.write({ cid, bytes });
2493
2611
  }
2494
2612
  writer.close();
2495
- return await encode({ value: writer.bytes, hasher: hasher3, codec: codec3 });
2613
+ return await encode({ value: writer.bytes, hasher: sha256, codec: codec3 });
2496
2614
  }
2497
2615
  async function createCarFile(encoder, cid, t) {
2498
2616
  return encodeCarFile([cid], t, encoder);
@@ -2500,7 +2618,7 @@ async function createCarFile(encoder, cid, t) {
2500
2618
  async function commitFiles(fileStore, walStore, t, done) {
2501
2619
  const { files: roots } = makeFileCarHeader(done);
2502
2620
  const cids = [];
2503
- const codec3 = (await fileStore.keyedCrypto()).codec();
2621
+ const codec3 = await fileStore.keyedCrypto().then((i) => i.codec());
2504
2622
  const cars = await prepareCarFilesFiles(codec3, roots, t);
2505
2623
  for (const car of cars) {
2506
2624
  const { cid, bytes } = car;
@@ -2526,13 +2644,13 @@ async function prepareCarFilesFiles(encoder, roots, t) {
2526
2644
  return [await encodeCarFile(roots, t, encoder)];
2527
2645
  }
2528
2646
  function makeCarHeader(meta, cars, compact = false) {
2529
- const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
2647
+ const coreHeader = compact ? { cars: [], compact: cars.asArray() } : { cars: cars.asArray(), compact: [] };
2530
2648
  return { ...coreHeader, meta };
2531
2649
  }
2532
2650
  async function encodeCarHeader(fp) {
2533
2651
  return await encode({
2534
2652
  value: { fp },
2535
- hasher: hasher3,
2653
+ hasher: sha256,
2536
2654
  codec: dagCodec2
2537
2655
  });
2538
2656
  }
@@ -2553,7 +2671,7 @@ async function commit(params, t, done, opts = { noLoader: false, compact: false
2553
2671
  }
2554
2672
  async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2555
2673
  const carFiles = [];
2556
- threshold = threshold || 128e3 * 8;
2674
+ threshold = threshold || 16 * 65536;
2557
2675
  let clonedt = new CarTransactionImpl(t.parent, { add: false, noLoader: false });
2558
2676
  clonedt.putSync(rootBlock.cid, rootBlock.bytes);
2559
2677
  let newsize = CBW.blockLength(toCIDBlock(rootBlock));
@@ -2575,17 +2693,18 @@ async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2575
2693
  }
2576
2694
 
2577
2695
  // src/blockstore/loader.ts
2578
- import { sha256 as hasher4 } from "multiformats/hashes/sha2";
2696
+ import { sha256 as hasher3 } from "multiformats/hashes/sha2";
2579
2697
 
2580
2698
  // src/blockstore/task-manager.ts
2581
2699
  var TaskManager = class {
2582
- constructor(sthis, callback) {
2700
+ constructor(sthis, callback, params) {
2583
2701
  // we need to remove the events after some time
2584
2702
  this.eventsWeHandled = /* @__PURE__ */ new Set();
2585
2703
  this.queue = [];
2586
2704
  this.isProcessing = false;
2587
2705
  this.logger = ensureLogger(sthis, "TaskManager");
2588
2706
  this.callback = callback;
2707
+ this.params = params;
2589
2708
  }
2590
2709
  async handleEvent(cid, parents, dbMeta, store) {
2591
2710
  for (const parent of parents) {
@@ -2614,7 +2733,7 @@ var TaskManager = class {
2614
2733
  this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
2615
2734
  }
2616
2735
  await new Promise((resolve) => setTimeout(resolve, 50));
2617
- throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
2736
+ this.logger.Warn().Err(err).Msg("retry to process event block");
2618
2737
  } finally {
2619
2738
  this.isProcessing = false;
2620
2739
  if (this.queue.length > 0) {
@@ -2625,7 +2744,7 @@ var TaskManager = class {
2625
2744
  };
2626
2745
 
2627
2746
  // src/blockstore/attachable-store.ts
2628
- import { KeyedResolvOnce as KeyedResolvOnce3, BuildURI as BuildURI2, isCoerceURI } from "@adviser/cement";
2747
+ import { KeyedResolvOnce as KeyedResolvOnce3, isCoerceURI, URI as URI9 } from "@adviser/cement";
2629
2748
  var AttachedImpl = class {
2630
2749
  constructor(gws, stores, unreg) {
2631
2750
  this.gatewayUrls = gws;
@@ -2644,11 +2763,32 @@ var AttachedImpl = class {
2644
2763
  return "attached";
2645
2764
  }
2646
2765
  };
2647
- var DataActiveStoreImpl = class {
2766
+ var FileActiveStoreImpl = class extends FileActiveStore {
2648
2767
  constructor(ref, active, attached) {
2768
+ super();
2649
2769
  this.ref = ref;
2650
2770
  this.active = active;
2651
- this.attached = attached;
2771
+ this.xattached = attached;
2772
+ }
2773
+ local() {
2774
+ return this.xattached.local();
2775
+ }
2776
+ remotes() {
2777
+ return this.xattached.remotes();
2778
+ }
2779
+ };
2780
+ var CarActiveStoreImpl = class extends CarActiveStore {
2781
+ constructor(ref, active, attached) {
2782
+ super();
2783
+ this.ref = ref;
2784
+ this.active = active;
2785
+ this.xattached = attached;
2786
+ }
2787
+ local() {
2788
+ return this.xattached.local();
2789
+ }
2790
+ remotes() {
2791
+ return [this.active, ...this.xattached.remotes().filter((i) => i !== this.active)];
2652
2792
  }
2653
2793
  };
2654
2794
  var CarAttachedStoresImpl = class {
@@ -2673,11 +2813,18 @@ var FileAttachedStoresImpl = class {
2673
2813
  return this.attached.remotes().map(({ active }) => active.file);
2674
2814
  }
2675
2815
  };
2676
- var MetaActiveStoreImpl = class {
2816
+ var MetaActiveStoreImpl = class extends MetaActiveStore {
2677
2817
  constructor(ref, active, attached) {
2818
+ super();
2678
2819
  this.ref = ref;
2679
2820
  this.active = active;
2680
- this.attached = attached;
2821
+ this.xattached = attached;
2822
+ }
2823
+ local() {
2824
+ return this.xattached.local();
2825
+ }
2826
+ remotes() {
2827
+ return [this.active, ...this.xattached.remotes().filter((i) => i !== this.active)];
2681
2828
  }
2682
2829
  };
2683
2830
  var MetaAttachedStoresImpl = class {
@@ -2691,11 +2838,18 @@ var MetaAttachedStoresImpl = class {
2691
2838
  return this.attached.remotes().map(({ active }) => active.meta);
2692
2839
  }
2693
2840
  };
2694
- var WALActiveStoreImpl = class {
2841
+ var WALActiveStoreImpl = class extends WALActiveStore {
2695
2842
  constructor(ref, active, attached) {
2843
+ super();
2696
2844
  this.ref = ref;
2697
2845
  this.active = active;
2698
- this.attached = attached;
2846
+ this.xattached = attached;
2847
+ }
2848
+ local() {
2849
+ return this.xattached.local();
2850
+ }
2851
+ remotes() {
2852
+ return this.xattached.remotes();
2699
2853
  }
2700
2854
  };
2701
2855
  var WALAttachedStoresImpl = class {
@@ -2712,7 +2866,13 @@ var WALAttachedStoresImpl = class {
2712
2866
  var ActiveStoreImpl = class {
2713
2867
  constructor(active, attached) {
2714
2868
  this.active = active;
2715
- this.attached = attached;
2869
+ this.xattached = attached;
2870
+ }
2871
+ local() {
2872
+ return this.xattached.local();
2873
+ }
2874
+ remotes() {
2875
+ return this.xattached.remotes();
2716
2876
  }
2717
2877
  baseStores() {
2718
2878
  const bs = [this.active.car, this.active.file, this.active.meta];
@@ -2722,19 +2882,19 @@ var ActiveStoreImpl = class {
2722
2882
  return bs;
2723
2883
  }
2724
2884
  carStore() {
2725
- return new DataActiveStoreImpl(this, this.active.car, new CarAttachedStoresImpl(this.attached));
2885
+ return new CarActiveStoreImpl(this, this.active.car, new CarAttachedStoresImpl(this.xattached));
2726
2886
  }
2727
2887
  fileStore() {
2728
- return new DataActiveStoreImpl(this, this.active.file, new FileAttachedStoresImpl(this.attached));
2888
+ return new FileActiveStoreImpl(this, this.active.file, new FileAttachedStoresImpl(this.xattached));
2729
2889
  }
2730
2890
  metaStore() {
2731
- return new MetaActiveStoreImpl(this, this.active.meta, new MetaAttachedStoresImpl(this.attached));
2891
+ return new MetaActiveStoreImpl(this, this.active.meta, new MetaAttachedStoresImpl(this.xattached));
2732
2892
  }
2733
2893
  walStore() {
2734
2894
  if (!this.active.wal) {
2735
- throw this.attached.loadable.sthis.logger.Error().Msg("wal store not set").AsError();
2895
+ throw this.xattached.loadable.sthis.logger.Error().Msg("wal store not set").AsError();
2736
2896
  }
2737
- return new WALActiveStoreImpl(this, this.active.wal, new WALAttachedStoresImpl(this.attached));
2897
+ return new WALActiveStoreImpl(this, this.active.wal, new WALAttachedStoresImpl(this.xattached));
2738
2898
  }
2739
2899
  };
2740
2900
  function isLoadable(unknown) {
@@ -2786,7 +2946,25 @@ var AttachedRemotesImpl = class {
2786
2946
  }
2787
2947
  activate(store) {
2788
2948
  if (isCoerceURI(store)) {
2789
- throw this.loadable.sthis.logger.Error().Msg("store must be an object").AsError();
2949
+ const activateUrl = URI9.from(store);
2950
+ let maxScore = 0;
2951
+ let maxStore;
2952
+ for (const { value } of this._remotes.values()) {
2953
+ if (value.isErr()) {
2954
+ continue;
2955
+ }
2956
+ for (const url of value.Ok().stores.baseStores.map((i) => i.url())) {
2957
+ const mr = url.match(activateUrl);
2958
+ if (mr.score > maxScore) {
2959
+ maxScore = mr.score;
2960
+ maxStore = value.Ok().stores;
2961
+ }
2962
+ }
2963
+ }
2964
+ if (!maxStore) {
2965
+ throw this.loadable.sthis.logger.Error().Url(activateUrl).Msg("no store found").AsError();
2966
+ }
2967
+ store = maxStore;
2790
2968
  }
2791
2969
  return new ActiveStoreImpl(store, this);
2792
2970
  }
@@ -2804,19 +2982,19 @@ var AttachedRemotesImpl = class {
2804
2982
  const gws = {
2805
2983
  car: {
2806
2984
  ...gwp.car,
2807
- url: BuildURI2.from(gwp.car.url).defParam("name", attached.name).URI()
2985
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, gwp.car.url, URI9.from(gwp.car.url), "car")
2808
2986
  },
2809
2987
  file: {
2810
2988
  ...gwp.file,
2811
- url: BuildURI2.from(gwp.file.url).defParam("name", attached.name).URI()
2989
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, URI9.from(gwp.file.url), "file", { file: true })
2812
2990
  },
2813
2991
  meta: {
2814
2992
  ...gwp.meta,
2815
- url: BuildURI2.from(gwp.meta.url).defParam("name", attached.name).URI()
2993
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, URI9.from(gwp.meta.url), "meta")
2816
2994
  },
2817
2995
  wal: gwp.wal ? {
2818
2996
  ...gwp.wal,
2819
- url: BuildURI2.from(gwp.wal.url).defParam("name", attached.name).URI()
2997
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, URI9.from(gwp.wal.url), "wal")
2820
2998
  } : void 0
2821
2999
  };
2822
3000
  const key = JSON.stringify(
@@ -2835,11 +3013,6 @@ var AttachedRemotesImpl = class {
2835
3013
  byStore: gws,
2836
3014
  loader: this.loadable
2837
3015
  }),
2838
- // {
2839
- // car: await rt.makeDataStore({ url: gws.carUrl, loader: this.loadable }),
2840
- // file: await rt.makeDataStore({ url: gws.filesUrl, loader: this.loadable }),
2841
- // meta: await rt.makeMetaStore({ url: gws.metaUrl, loader: this.loadable }),
2842
- // },
2843
3016
  () => {
2844
3017
  this._remotes.unget(key);
2845
3018
  }
@@ -2857,11 +3030,12 @@ var AttachedRemotesImpl = class {
2857
3030
 
2858
3031
  // src/blockstore/loader.ts
2859
3032
  function carLogIncludesGroup(list, cids) {
2860
- return list.some((arr) => {
2861
- return arr.toString() === cids.toString();
2862
- });
3033
+ const cidSet = cids.map((cid) => cid.toString()).sort().join(",");
3034
+ return list.some(
3035
+ (arr) => cidSet === arr.map((cid) => cid.toString()).sort().join(",")
3036
+ );
2863
3037
  }
2864
- function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
3038
+ function uniqueCids(list, remove = new LRUSet()) {
2865
3039
  const byString = /* @__PURE__ */ new Map();
2866
3040
  for (const cid of list) {
2867
3041
  if (remove.has(cid.toString())) continue;
@@ -2873,13 +3047,8 @@ var Loader = class {
2873
3047
  constructor(sthis, ebOpts) {
2874
3048
  this.commitQueue = new CommitQueue();
2875
3049
  this.isCompacting = false;
2876
- this.carReaders = /* @__PURE__ */ new Map();
2877
- this.seenCompacted = /* @__PURE__ */ new Set();
2878
- this.processedCars = /* @__PURE__ */ new Set();
2879
- this.carLog = [];
2880
- this.getBlockCache = /* @__PURE__ */ new Map();
2881
- this.seenMeta = /* @__PURE__ */ new Set();
2882
- this.writeLimit = pLimit(1);
3050
+ this.maxConcurrentWrite = pLimit(1);
3051
+ this.carLog = new CarLog();
2883
3052
  this.onceReady = new ResolveOnce3();
2884
3053
  this.sthis = sthis;
2885
3054
  this.ebOpts = defaultedBlockstoreRuntime(
@@ -2890,63 +3059,44 @@ var Loader = class {
2890
3059
  },
2891
3060
  "Loader"
2892
3061
  );
2893
- this.logger = this.ebOpts.logger;
2894
- this.taskManager = new TaskManager(sthis, async (dbMeta, activeStore) => {
2895
- await this.handleDbMetasFromStore([dbMeta], activeStore);
3062
+ this.logger = ensureLogger(sthis, "Loader");
3063
+ this.cidCache = new KeyedResolvOnce4({
3064
+ lru: {
3065
+ maxEntries: parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_CACHE_SIZE, "1000"), 10)
3066
+ }
2896
3067
  });
3068
+ this.seenMeta = new LRUSet({
3069
+ maxEntries: parseInt(this.ebOpts.storeUrls.meta.getParam(PARAM.CAR_META_CACHE_SIZE, "1000"), 10)
3070
+ });
3071
+ this.seenCompacted = new LRUSet({
3072
+ maxEntries: parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_COMPACT_CACHE_SIZE, "1000"), 10)
3073
+ });
3074
+ this.maxConcurrentCarReader = pLimit(parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_PARALLEL, "5"), 10));
3075
+ this.taskManager = new TaskManager(
3076
+ sthis,
3077
+ async (dbMeta, activeStore) => {
3078
+ await this.handleDbMetasFromStore([dbMeta], activeStore);
3079
+ },
3080
+ this.ebOpts.taskManager
3081
+ );
2897
3082
  this.attachedStores = new AttachedRemotesImpl(this);
2898
3083
  }
2899
- attach(attached) {
2900
- return this.attachedStores.attach(attached);
2901
- }
2902
- // private readonly _carStore = new ResolveOnce<DataStore>();
2903
- // async carStore(): Promise<DataStore> {
2904
- // return this._carStore.once(async () =>
2905
- // this.ebOpts.storeRuntime.makeDataStore({
2906
- // // sthis: this.sthis,
2907
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2908
- // url: this.ebOpts.storeUrls.data,
2909
- // // keybag: await this.keyBag(),
2910
- // loader: this,
2911
- // }),
2912
- // );
2913
- // }
2914
- // private readonly _fileStore = new ResolveOnce<DataStore>();
2915
- // async fileStore(): Promise<DataStore> {
2916
- // return this._fileStore.once(async () =>
2917
- // this.ebOpts.storeRuntime.makeDataStore({
2918
- // // sthis: this.sthis,
2919
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2920
- // url: this.ebOpts.storeUrls.file,
2921
- // // keybag: await this.keyBag(),
2922
- // loader: this,
2923
- // }),
2924
- // );
2925
- // }
2926
- // private readonly _WALStore = new ResolveOnce<WALStore>();
2927
- // async WALStore(): Promise<WALStore> {
2928
- // return this._WALStore.once(async () =>
2929
- // this.ebOpts.storeRuntime.makeWALStore({
2930
- // // sthis: this.sthis,
2931
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2932
- // url: this.ebOpts.storeUrls.wal,
2933
- // // keybag: await this.keyBag(),
2934
- // loader: this,
2935
- // }),
2936
- // );
2937
- // }
2938
- // private readonly _metaStore = new ResolveOnce<MetaStore>();
2939
- // async metaStore(): Promise<MetaStore> {
2940
- // return this._metaStore.once(async () =>
2941
- // this.ebOpts.storeRuntime.makeMetaStore({
2942
- // // sthis: this.sthis,
2943
- // gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2944
- // url: this.ebOpts.storeUrls.meta,
2945
- // // keybag: await this.keyBag(),
2946
- // loader: this,
2947
- // }),
2948
- // );
2949
- // }
3084
+ async attach(attached) {
3085
+ const at = await this.attachedStores.attach(attached);
3086
+ if (!at.stores.wal) {
3087
+ try {
3088
+ const dbMeta = await at.stores.meta.load();
3089
+ if (!Array.isArray(dbMeta)) {
3090
+ throw this.logger.Error().Msg("missing dbMeta").AsError();
3091
+ }
3092
+ await this.handleDbMetasFromStore(dbMeta, this.attachedStores.activate(at.stores));
3093
+ } catch (e) {
3094
+ this.logger.Error().Err(e).Msg("error attaching store");
3095
+ at.detach();
3096
+ }
3097
+ }
3098
+ return at;
3099
+ }
2950
3100
  keyBag() {
2951
3101
  return getKeyBag(this.sthis, this.ebOpts.keyBag);
2952
3102
  }
@@ -2990,9 +3140,9 @@ var Loader = class {
2990
3140
  // await this._applyCarHeader(carHeader, true)
2991
3141
  // }
2992
3142
  async handleDbMetasFromStore(metas, activeStore) {
2993
- this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
3143
+ this.logger.Debug().Any("metas", metas).Url(activeStore.active.car.url()).Msg("handleDbMetasFromStore");
2994
3144
  for (const meta of metas) {
2995
- await this.writeLimit(async () => {
3145
+ await this.maxConcurrentWrite(async () => {
2996
3146
  await this.mergeDbMetaIntoClock(meta, activeStore);
2997
3147
  });
2998
3148
  }
@@ -3001,16 +3151,26 @@ var Loader = class {
3001
3151
  if (this.isCompacting) {
3002
3152
  throw this.logger.Error().Msg("cannot merge while compacting").AsError();
3003
3153
  }
3004
- if (this.seenMeta.has(meta.cars.toString())) return;
3005
- this.seenMeta.add(meta.cars.toString());
3006
- if (carLogIncludesGroup(this.carLog, meta.cars)) {
3007
- return;
3154
+ try {
3155
+ this.isCompacting = true;
3156
+ const metaKey = meta.cars.map((i) => i.toString()).sort().join(",");
3157
+ if (this.seenMeta.has(metaKey)) return;
3158
+ this.seenMeta.add(metaKey);
3159
+ if (carLogIncludesGroup(this.carLog.asArray(), meta.cars)) {
3160
+ return;
3161
+ }
3162
+ const carHeader = await this.loadCarHeaderFromMeta(meta, activeStore);
3163
+ carHeader.compact.map((c) => c.toString()).forEach((k) => this.seenCompacted.add(k), this.seenCompacted);
3164
+ try {
3165
+ await this.getMoreReaders(carHeader.cars.flat(), activeStore);
3166
+ } catch (e) {
3167
+ this.logger.Error().Err(e).Msg("error getting more readers");
3168
+ }
3169
+ this.carLog.update(uniqueCids([meta.cars, ...this.carLog.asArray(), ...carHeader.cars], this.seenCompacted));
3170
+ await this.ebOpts.applyMeta?.(carHeader.meta);
3171
+ } finally {
3172
+ this.isCompacting = false;
3008
3173
  }
3009
- const carHeader = await this.loadCarHeaderFromMeta(meta, activeStore);
3010
- carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3011
- await this.getMoreReaders(carHeader.cars.flat(), activeStore);
3012
- this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
3013
- await this.ebOpts.applyMeta?.(carHeader.meta);
3014
3174
  }
3015
3175
  // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
3016
3176
  // const { key } = meta;
@@ -3041,13 +3201,13 @@ var Loader = class {
3041
3201
  }
3042
3202
  async commit(t, done, opts = { noLoader: false, compact: false }) {
3043
3203
  await this.ready();
3044
- const carStore = await this.attachedStores.local().active.car;
3204
+ const carStore = this.attachedStores.local().active.car;
3045
3205
  const params = {
3046
3206
  encoder: (await carStore.keyedCrypto()).codec(),
3047
3207
  carLog: this.carLog,
3048
3208
  carStore,
3049
- WALStore: await this.attachedStores.local().active.wal,
3050
- metaStore: await this.attachedStores.local().active.meta,
3209
+ WALStore: this.attachedStores.local().active.wal,
3210
+ metaStore: this.attachedStores.local().active.meta,
3051
3211
  threshold: this.ebOpts.threshold
3052
3212
  };
3053
3213
  return this.commitQueue.enqueue(async () => {
@@ -3057,34 +3217,44 @@ var Loader = class {
3057
3217
  return ret.cgrp;
3058
3218
  });
3059
3219
  }
3060
- async updateCarLog(cids, fp, compact) {
3220
+ async updateCarLog(cids, cHeader, compact) {
3061
3221
  if (compact) {
3062
- const previousCompactCid = fp.compact[fp.compact.length - 1];
3063
- fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3064
- this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
3222
+ const previousCompactCid = cHeader.compact[cHeader.compact.length - 1];
3223
+ cHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3224
+ this.carLog.update(uniqueCids([...this.carLog.asArray(), ...cHeader.cars, cids], this.seenCompacted));
3065
3225
  await this.removeCidsForCompact(previousCompactCid[0], this.attachedStores.local()).catch((e) => e);
3066
3226
  } else {
3067
- this.carLog.unshift(cids);
3227
+ this.carLog.xunshift(cids);
3068
3228
  }
3069
3229
  }
3070
3230
  async cacheTransaction(t) {
3071
3231
  for await (const block of t.entries()) {
3072
3232
  const sBlock = block.cid.toString();
3073
- if (!this.getBlockCache.has(sBlock)) {
3074
- this.getBlockCache.set(sBlock, block);
3075
- }
3076
- }
3077
- }
3078
- async cacheCarReader(carCidStr, reader) {
3079
- if (this.processedCars.has(carCidStr)) return;
3080
- this.processedCars.add(carCidStr);
3081
- for await (const block of reader.blocks()) {
3082
- const sBlock = block.cid.toString();
3083
- if (!this.getBlockCache.has(sBlock)) {
3084
- this.getBlockCache.set(sBlock, block);
3085
- }
3233
+ this.cidCache.get(sBlock).once(
3234
+ () => ({
3235
+ type: "block",
3236
+ cid: block.cid,
3237
+ blocks: [block],
3238
+ roots: []
3239
+ })
3240
+ );
3086
3241
  }
3087
3242
  }
3243
+ // /**
3244
+ // *
3245
+ // * @returns the list of blocks which was read from the car file
3246
+ // */
3247
+ // private async readCar(reader: CarReader): Promise<AnyBlock[]> {
3248
+ // const blocks: AnyBlock[] = [];
3249
+ // for await (const block of reader.blocks()) {
3250
+ // const sBlock = block.cid.toString();
3251
+ // this.cidCache.get(sBlock).once(() => {
3252
+ // blocks.push(block);
3253
+ // return [block];
3254
+ // });
3255
+ // }
3256
+ // return blocks;
3257
+ // }
3088
3258
  async removeCidsForCompact(cid, store) {
3089
3259
  const carHeader = await this.loadCarHeaderFromMeta(
3090
3260
  {
@@ -3106,149 +3276,134 @@ var Loader = class {
3106
3276
  // await this.remoteWAL!.enqueue(dbMeta, { public: false })
3107
3277
  // }
3108
3278
  // }
3109
- async *entries(cache2 = true) {
3279
+ async *entries() {
3110
3280
  await this.ready();
3111
- if (cache2) {
3112
- for (const [, block] of this.getBlockCache) {
3113
- yield block;
3114
- }
3115
- } else {
3116
- for (const [, block] of this.getBlockCache) {
3117
- yield block;
3118
- }
3119
- for (const cids of this.carLog) {
3120
- for (const cid of cids) {
3121
- const reader = await this.loadCar(cid, this.attachedStores.local());
3122
- if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
3123
- for await (const block of reader.blocks()) {
3124
- const sCid = block.cid.toString();
3125
- if (!this.getBlockCache.has(sCid)) {
3126
- yield block;
3127
- }
3128
- }
3281
+ const seen = /* @__PURE__ */ new Set();
3282
+ for (const carCids of this.carLog.asArray()) {
3283
+ for (const carCid of carCids) {
3284
+ const reader = await this.loadCar(carCid, this.attachedStores.local());
3285
+ if (!reader || reader.type !== "car") {
3286
+ throw this.logger.Error().Any("reader", reader.type).Str("cid", carCid.toString()).Msg("missing car reader").AsError();
3287
+ }
3288
+ for (const block of reader.blocks) {
3289
+ const cidStr = block.cid.toString();
3290
+ if (seen.has(cidStr)) continue;
3291
+ seen.add(cidStr);
3292
+ yield block;
3129
3293
  }
3130
3294
  }
3131
3295
  }
3132
3296
  }
3133
3297
  async getBlock(cid, store) {
3134
3298
  await this.ready();
3135
- const sCid = cid.toString();
3136
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3137
- const getCarCid = async (carCid) => {
3138
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3139
- const reader = await this.loadCar(carCid, store);
3140
- if (!reader) {
3141
- throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
3142
- }
3143
- await this.cacheCarReader(carCid.toString(), reader).catch(() => {
3144
- return;
3145
- });
3146
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3147
- throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
3148
- };
3149
- const getCompactCarCids = async (carCid) => {
3150
- const reader = await this.loadCar(carCid, store);
3151
- if (!reader) {
3152
- throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
3153
- }
3154
- const header = await parseCarFile(reader, this.logger);
3155
- const compacts = header.compact;
3156
- let got2;
3157
- const batchSize2 = 5;
3158
- for (let i = 0; i < compacts.length; i += batchSize2) {
3159
- const promises = [];
3160
- for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
3161
- for (const cid2 of compacts[j]) {
3162
- promises.push(getCarCid(cid2));
3299
+ const cidStr = cid.toString();
3300
+ const ci = await this.cidCache.get(cidStr).once(async () => {
3301
+ const getCompactCarCids = async (carCid) => {
3302
+ const sCid = carCid.toString();
3303
+ const reader = await this.loadCar(carCid, store);
3304
+ const header = await parseCarFile(reader, this.logger);
3305
+ const compacts = header.compact;
3306
+ const got2 = await Promise.allSettled(compacts.map((compact) => compact.map((cid2) => this.loadCar(cid2, store)).flat()));
3307
+ got2.filter((result) => result.status === "rejected").forEach((result) => {
3308
+ this.logger.Error().Err(result.reason).Str("cid", sCid).Msg("error getting compacted block");
3309
+ });
3310
+ };
3311
+ let got;
3312
+ for (const carCids of this.carLog.asArray()) {
3313
+ for (const carCid of carCids) {
3314
+ const ci2 = await this.loadCar(carCid, store);
3315
+ if (!ci2) {
3316
+ this.logger.Error().Str("cid", carCid.toString()).Msg("missing CarCID");
3317
+ continue;
3318
+ }
3319
+ got = ci2.blocks.find((block) => block.cid.equals(cid));
3320
+ if (got) {
3321
+ break;
3163
3322
  }
3164
3323
  }
3165
- try {
3166
- got2 = await Promise.any(promises);
3167
- } catch {
3168
- }
3169
- if (got2) break;
3170
- }
3171
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
3172
- throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
3173
- };
3174
- let got;
3175
- const batchSize = 5;
3176
- for (let i = 0; i < this.carLog.length; i += batchSize) {
3177
- const batch = this.carLog.slice(i, i + batchSize);
3178
- const promises = batch.flatMap((slice) => slice.map(getCarCid));
3179
- try {
3180
- got = await Promise.any(promises);
3181
- } catch {
3182
3324
  }
3183
- if (got) break;
3184
- }
3185
- if (!got) {
3186
- try {
3187
- got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
3188
- } catch {
3325
+ if (!got) {
3326
+ await getCompactCarCids(this.carLog.last()[0]);
3189
3327
  }
3328
+ return {
3329
+ type: "block",
3330
+ cid,
3331
+ blocks: got ? [got] : [],
3332
+ roots: []
3333
+ };
3334
+ });
3335
+ if (!(ci.type === "block" && ci.blocks.length === 1)) {
3336
+ throw this.logger.Error().Str("cid", cidStr).Any("block", ci).Msg("missing block").AsError();
3190
3337
  }
3191
- return got;
3338
+ return ci.blocks[0];
3192
3339
  }
3193
3340
  async loadCar(cid, store) {
3194
3341
  const loaded = await this.storesLoadCar(cid, store.carStore());
3195
3342
  return loaded;
3196
3343
  }
3197
- async makeDecoderAndCarReader(cid, store) {
3198
- const cidsString = cid.toString();
3344
+ async makeDecoderAndCarReader(carCid, store) {
3345
+ const carCidStr = carCid.toString();
3199
3346
  let loadedCar = void 0;
3200
- let activeStore = store.attached.local();
3347
+ let activeStore = store.local();
3201
3348
  try {
3202
- this.logger.Debug().Any("cid", cidsString).Msg("loading car");
3203
- loadedCar = await store.attached.local().load(cid);
3349
+ this.logger.Debug().Any("cid", carCidStr).Msg("loading car");
3350
+ loadedCar = await store.local().load(carCid);
3204
3351
  this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
3205
3352
  } catch (e) {
3206
3353
  if (!isNotFoundError(e)) {
3207
- throw this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
3354
+ throw this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3208
3355
  }
3209
- for (const remote of store.attached.remotes()) {
3210
- const remoteCar = await remote.load(cid);
3211
- if (remoteCar) {
3212
- this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3213
- await store.attached.local().save(remoteCar);
3214
- loadedCar = remoteCar;
3215
- activeStore = remote;
3216
- break;
3217
- } else {
3218
- this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
3356
+ for (const remote of store.remotes()) {
3357
+ try {
3358
+ const remoteCar = await remote.load(carCid);
3359
+ if (remoteCar) {
3360
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3361
+ await store.local().save(remoteCar);
3362
+ loadedCar = remoteCar;
3363
+ activeStore = remote;
3364
+ break;
3365
+ } else {
3366
+ this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3367
+ }
3368
+ } catch (e2) {
3369
+ this.logger.Warn().Str("cid", carCidStr).Url(remote.url()).Err(e2).Msg("loading car");
3219
3370
  }
3220
3371
  }
3221
3372
  }
3222
3373
  if (!loadedCar) {
3223
- throw this.logger.Error().Url(store.attached.local().url()).Str("cid", cidsString).Msg("missing car files").AsError();
3224
- }
3225
- const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher4, codec: (await activeStore.keyedCrypto()).codec() });
3226
- const rawReader = await CarReader.fromBytes(bytes.value);
3227
- const readerP = Promise.resolve(rawReader);
3228
- const cachedReaderP = readerP.then(async (reader) => {
3229
- await this.cacheCarReader(cidsString, reader).catch((e) => {
3230
- this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
3231
- return;
3232
- });
3233
- return reader;
3234
- });
3235
- this.carReaders.set(cidsString, cachedReaderP);
3236
- return readerP;
3374
+ throw this.logger.Error().Url(store.local().url()).Str("cid", carCidStr).Msg("missing car files").AsError();
3375
+ }
3376
+ const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher3, codec: (await activeStore.keyedCrypto()).codec() });
3377
+ const rawReader = await CarReader.fromBytes(bytes.value.data);
3378
+ const blocks = [];
3379
+ for await (const block of rawReader.blocks()) {
3380
+ const sBlock = block.cid.toString();
3381
+ blocks.push(block);
3382
+ this.cidCache.get(sBlock).once(() => ({
3383
+ type: "block",
3384
+ cid: block.cid,
3385
+ blocks: [block],
3386
+ roots: []
3387
+ }));
3388
+ }
3389
+ return {
3390
+ type: "car",
3391
+ cid: carCid,
3392
+ blocks,
3393
+ roots: await rawReader.getRoots()
3394
+ };
3237
3395
  }
3238
3396
  //What if instead it returns an Array of CarHeader
3239
- async storesLoadCar(cid, store) {
3240
- const cidsString = cid.toString();
3241
- let dacr = this.carReaders.get(cidsString);
3242
- if (!dacr) {
3243
- dacr = this.makeDecoderAndCarReader(cid, store);
3244
- this.carReaders.set(cidsString, dacr);
3245
- }
3246
- return dacr;
3397
+ async storesLoadCar(carCid, store) {
3398
+ const carCidStr = carCid.toString();
3399
+ return this.cidCache.get(carCidStr).once(async () => {
3400
+ return this.maxConcurrentCarReader(() => this.makeDecoderAndCarReader(carCid, store));
3401
+ });
3247
3402
  }
3248
3403
  async getMoreReaders(cids, store) {
3249
- const limit = pLimit(5);
3250
- const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
3251
- await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid, store))));
3404
+ for (const cid of cids) {
3405
+ await this.loadCar(cid, store);
3406
+ }
3252
3407
  }
3253
3408
  };
3254
3409
 
@@ -3259,7 +3414,7 @@ __export(keyed_crypto_exports, {
3259
3414
  keyedCryptoFactory: () => keyedCryptoFactory
3260
3415
  });
3261
3416
  import { base58btc as base58btc3 } from "multiformats/bases/base58";
3262
- import { sha256 as hasher5 } from "multiformats/hashes/sha2";
3417
+ import { sha256 as hasher4 } from "multiformats/hashes/sha2";
3263
3418
  import * as CBOR from "cborg";
3264
3419
  var generateIV = {
3265
3420
  random: {
@@ -3274,7 +3429,7 @@ var generateIV = {
3274
3429
  },
3275
3430
  hash: {
3276
3431
  calc: async (ko, crypto, data) => {
3277
- const hash = await hasher5.digest(data);
3432
+ const hash = await hasher4.digest(data);
3278
3433
  const hashBytes = new Uint8Array(hash.bytes);
3279
3434
  const hashArray = new Uint8Array(ko.ivLength);
3280
3435
  for (let i = 0; i < hashBytes.length; i++) {
@@ -3293,12 +3448,21 @@ function getGenerateIVFn(url, opts) {
3293
3448
  }
3294
3449
  var BlockIvKeyIdCodec = class {
3295
3450
  constructor(ko, iv, opts) {
3296
- this.code = 3147065;
3451
+ this.code = 24;
3297
3452
  this.name = "Fireproof@encrypted-block:aes-gcm";
3298
3453
  this.ko = ko;
3299
3454
  this.iv = iv;
3300
3455
  this.opts = opts || {};
3301
3456
  }
3457
+ // hashAsBytes(data: IvKeyIdData): AsyncHashAsBytes<Uint8Array<ArrayBufferLike>> {
3458
+ // return data;
3459
+ // }
3460
+ valueToHashBytes(value) {
3461
+ return Promise.resolve(value.data);
3462
+ }
3463
+ bytesToHash(data) {
3464
+ return Promise.resolve(data);
3465
+ }
3302
3466
  async encode(data) {
3303
3467
  const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
3304
3468
  const { iv } = this.ko.algo(calcIv);
@@ -3330,11 +3494,16 @@ var BlockIvKeyIdCodec = class {
3330
3494
  if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
3331
3495
  throw this.ko.logger.Error().Msg("iv missmatch").AsError();
3332
3496
  }
3333
- return result;
3497
+ return {
3498
+ iv,
3499
+ keyId,
3500
+ data: result
3501
+ };
3334
3502
  }
3335
3503
  };
3336
3504
  var cryptoAction = class {
3337
3505
  constructor(url, key, cyopt, sthis) {
3506
+ this.code = 24;
3338
3507
  this.ivLength = 12;
3339
3508
  this.isEncrypting = true;
3340
3509
  this.logger = ensureLogger(sthis, "cryptoAction");
@@ -3368,14 +3537,19 @@ var cryptoAction = class {
3368
3537
  };
3369
3538
  var nullCodec = class {
3370
3539
  constructor() {
3371
- this.code = 0;
3540
+ this.code = 24;
3372
3541
  this.name = "Fireproof@unencrypted-block";
3542
+ this.empty = new Uint8Array();
3373
3543
  }
3374
- encode(data) {
3544
+ async encode(data) {
3375
3545
  return data;
3376
3546
  }
3377
- decode(data) {
3378
- return data;
3547
+ async decode(data) {
3548
+ return {
3549
+ iv: this.empty,
3550
+ keyId: this.empty,
3551
+ data
3552
+ };
3379
3553
  }
3380
3554
  };
3381
3555
  var noCrypto = class {
@@ -3630,13 +3804,8 @@ var BaseStoreImpl = class {
3630
3804
  this._url = res.Ok();
3631
3805
  const kb = await this.loader.keyBag();
3632
3806
  const skRes = await kb.ensureKeyFromUrl(this._url, () => {
3633
- const idx = this._url.getParam(PARAM.INDEX);
3634
- const storeKeyName = [this.url().getParam(PARAM.NAME)];
3635
- if (idx) {
3636
- storeKeyName.push(idx);
3637
- }
3638
- storeKeyName.push(this.storeType);
3639
- return storeKeyName.join(":");
3807
+ const key = this._url.getParam(PARAM.KEY);
3808
+ return key;
3640
3809
  });
3641
3810
  if (skRes.isErr()) {
3642
3811
  return skRes;
@@ -3719,8 +3888,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3719
3888
  // }
3720
3889
  // return (rDbMeta.Ok() as FPEnvelopeMeta).payload;
3721
3890
  // }
3722
- async load() {
3723
- const branch = "main";
3891
+ async load(branch = "main") {
3724
3892
  const url = await this.gateway.buildUrl({ loader: this.loader }, this.url(), branch);
3725
3893
  if (url.isErr()) {
3726
3894
  throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
@@ -3734,7 +3902,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3734
3902
  }
3735
3903
  const fpMeta = rfpEnv.Ok().payload;
3736
3904
  const dbMetas = fpMeta.map((m) => m.dbMeta);
3737
- await this.loader.handleDbMetasFromStore(dbMetas, this.loader.attachedStores.local());
3905
+ await this.loader.handleDbMetasFromStore(dbMetas, this.loader.attachedStores.activate(url.Ok()));
3738
3906
  this.updateParentsFromDbMetas(fpMeta);
3739
3907
  return dbMetas;
3740
3908
  }
@@ -3766,9 +3934,8 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3766
3934
  }
3767
3935
  };
3768
3936
  var DataStoreImpl = class extends BaseStoreImpl {
3769
- constructor(sthis, url, opts) {
3770
- super(sthis, url, { ...opts }, ensureLogger(sthis, "DataStoreImpl"));
3771
- this.storeType = "data";
3937
+ constructor(sthis, url, opts, logger) {
3938
+ super(sthis, url, { ...opts }, logger);
3772
3939
  }
3773
3940
  async load(cid) {
3774
3941
  this.logger.Debug().Any("cid", cid).Msg("loading");
@@ -3783,7 +3950,6 @@ var DataStoreImpl = class extends BaseStoreImpl {
3783
3950
  const fpenv = res.Ok();
3784
3951
  switch (fpenv.type) {
3785
3952
  case "car":
3786
- return { cid, bytes: fpenv.payload };
3787
3953
  case "file":
3788
3954
  return { cid, bytes: fpenv.payload };
3789
3955
  default:
@@ -3799,12 +3965,11 @@ var DataStoreImpl = class extends BaseStoreImpl {
3799
3965
  }
3800
3966
  let fpMsg;
3801
3967
  switch (url.Ok().getParam(PARAM.STORE)) {
3802
- case "data":
3803
- if (url.Ok().getParam(PARAM.SUFFIX)) {
3804
- fpMsg = Car2FPMsg(car.bytes);
3805
- } else {
3806
- fpMsg = File2FPMsg(car.bytes);
3807
- }
3968
+ case "car":
3969
+ fpMsg = Car2FPMsg(car.bytes);
3970
+ break;
3971
+ case "file":
3972
+ fpMsg = File2FPMsg(car.bytes);
3808
3973
  break;
3809
3974
  default:
3810
3975
  throw this.logger.Error().Str("store", url.Ok().getParam(PARAM.STORE)).Msg("unexpected store").AsError();
@@ -3835,6 +4000,18 @@ var DataStoreImpl = class extends BaseStoreImpl {
3835
4000
  return this.gateway.destroy({ loader: this.loader }, this.url());
3836
4001
  }
3837
4002
  };
4003
+ var CarStoreImpl = class extends DataStoreImpl {
4004
+ constructor(sthis, url, opts) {
4005
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "CarStoreImpl"));
4006
+ this.storeType = "car";
4007
+ }
4008
+ };
4009
+ var FileStoreImpl = class extends DataStoreImpl {
4010
+ constructor(sthis, url, opts) {
4011
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "FileStoreImpl"));
4012
+ this.storeType = "file";
4013
+ }
4014
+ };
3838
4015
  var WALStoreImpl = class extends BaseStoreImpl {
3839
4016
  constructor(sthis, url, opts) {
3840
4017
  super(sthis, url, { ...opts }, ensureLogger(sthis, "WALStoreImpl"));
@@ -3917,7 +4094,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3917
4094
  for (const cid of dbMeta.cars) {
3918
4095
  const car = await this.loader.attachedStores.local().active.car.load(cid);
3919
4096
  if (!car) {
3920
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4097
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3921
4098
  throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
3922
4099
  }
3923
4100
  } else {
@@ -3939,7 +4116,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3939
4116
  for (const cid of dbMeta.cars) {
3940
4117
  const car = await this.loader.attachedStores.local().active.car.load(cid);
3941
4118
  if (!car) {
3942
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4119
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3943
4120
  throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
3944
4121
  }
3945
4122
  } else {
@@ -4027,16 +4204,17 @@ var WALStoreImpl = class extends BaseStoreImpl {
4027
4204
  };
4028
4205
 
4029
4206
  // src/blockstore/store-factory.ts
4030
- var onceGateway = new KeyedResolvOnce4();
4031
- var gatewayInstances = new KeyedResolvOnce4();
4207
+ var onceGateway = new KeyedResolvOnce5();
4208
+ var gatewayInstances = new KeyedResolvOnce5();
4032
4209
  async function getStartedGateway(ctx, url) {
4033
4210
  return onceGateway.get(url.toString()).once(async () => {
4034
4211
  const item = getGatewayFactoryItem(url.protocol);
4035
4212
  if (item) {
4036
4213
  const ret = {
4037
4214
  url,
4038
- ...await gatewayInstances.get(url.protocol).once(async () => ({})),
4039
- gateway: await item.serdegateway(ctx.loader.sthis)
4215
+ ...await gatewayInstances.get(url.protocol).once(async () => ({
4216
+ gateway: await item.serdegateway(ctx.loader.sthis)
4217
+ }))
4040
4218
  };
4041
4219
  const res = await ret.gateway.start(ctx, url);
4042
4220
  if (res.isErr()) {
@@ -4048,14 +4226,28 @@ async function getStartedGateway(ctx, url) {
4048
4226
  return Result10.Err(ctx.loader.sthis.logger.Warn().Url(url).Msg("unsupported protocol").AsError());
4049
4227
  });
4050
4228
  }
4051
- async function dataStoreFactory(ctx, uai) {
4052
- const storeUrl = uai.url.build().setParam(PARAM.STORE, "data").URI();
4229
+ async function carStoreFactory(ctx, uai) {
4230
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "car").URI();
4053
4231
  const rgateway = await getStartedGateway(ctx, storeUrl);
4054
4232
  if (rgateway.isErr()) {
4055
4233
  throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
4056
4234
  }
4057
4235
  const gateway = rgateway.Ok();
4058
- const store = new DataStoreImpl(ctx.loader.sthis, gateway.url, {
4236
+ const store = new CarStoreImpl(ctx.loader.sthis, gateway.url, {
4237
+ gateway: gateway.gateway,
4238
+ gatewayInterceptor: uai.gatewayInterceptor,
4239
+ loader: ctx.loader
4240
+ });
4241
+ return store;
4242
+ }
4243
+ async function fileStoreFactory(ctx, uai) {
4244
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "file").URI();
4245
+ const rgateway = await getStartedGateway(ctx, storeUrl);
4246
+ if (rgateway.isErr()) {
4247
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
4248
+ }
4249
+ const gateway = rgateway.Ok();
4250
+ const store = new FileStoreImpl(ctx.loader.sthis, gateway.url, {
4059
4251
  gateway: gateway.gateway,
4060
4252
  gatewayInterceptor: uai.gatewayInterceptor,
4061
4253
  loader: ctx.loader
@@ -4113,8 +4305,8 @@ function toStoreRuntime(sthis, endeOpts = {}) {
4113
4305
  };
4114
4306
  const storeSet = {};
4115
4307
  storeSet.meta = await metaStoreFactory(ctx, sfi.byStore.meta);
4116
- storeSet.car = await dataStoreFactory(ctx, sfi.byStore.car);
4117
- storeSet.file = await dataStoreFactory(ctx, sfi.byStore.file);
4308
+ storeSet.car = await carStoreFactory(ctx, sfi.byStore.car);
4309
+ storeSet.file = await fileStoreFactory(ctx, sfi.byStore.file);
4118
4310
  if (sfi.byStore.wal) {
4119
4311
  storeSet.wal = await WALStoreFactory(ctx, sfi.byStore.wal);
4120
4312
  }
@@ -4135,7 +4327,7 @@ function toStoreRuntime(sthis, endeOpts = {}) {
4135
4327
 
4136
4328
  // src/crdt-helpers.ts
4137
4329
  import { parse as parse2 } from "multiformats/link";
4138
- import { sha256 as hasher6 } from "multiformats/hashes/sha2";
4330
+ import { sha256 as hasher5 } from "multiformats/hashes/sha2";
4139
4331
  import * as codec2 from "@ipld/dag-cbor";
4140
4332
  import { put, get, entries, root } from "@fireproof/vendor/@web3-storage/pail/crdt";
4141
4333
  import { EventFetcher, vis } from "@fireproof/vendor/@web3-storage/pail/clock";
@@ -4153,6 +4345,41 @@ function toString(key, logger) {
4153
4345
  throw logger.Error().Msg("Invalid key type").AsError();
4154
4346
  }
4155
4347
  }
4348
+ function sanitizeDocumentFields(obj) {
4349
+ if (Array.isArray(obj)) {
4350
+ return obj.map((item) => {
4351
+ if (typeof item === "object" && item !== null) {
4352
+ return sanitizeDocumentFields(item);
4353
+ }
4354
+ return item;
4355
+ });
4356
+ } else if (typeof obj === "object" && obj !== null) {
4357
+ if (obj instanceof Date) {
4358
+ return obj.toISOString();
4359
+ }
4360
+ const typedObj = obj;
4361
+ const result = {};
4362
+ for (const key in typedObj) {
4363
+ if (Object.hasOwnProperty.call(typedObj, key)) {
4364
+ const value = typedObj[key];
4365
+ if (value === null || !Number.isNaN(value) && value !== void 0) {
4366
+ if (typeof value === "object" && !key.startsWith("_")) {
4367
+ if (value instanceof Date) {
4368
+ result[key] = value.toISOString();
4369
+ } else {
4370
+ const sanitized = sanitizeDocumentFields(value);
4371
+ result[key] = sanitized;
4372
+ }
4373
+ } else {
4374
+ result[key] = value;
4375
+ }
4376
+ }
4377
+ }
4378
+ }
4379
+ return result;
4380
+ }
4381
+ return obj;
4382
+ }
4156
4383
  async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
4157
4384
  let result = null;
4158
4385
  if (updates.length > 1) {
@@ -4187,7 +4414,7 @@ async function writeDocContent(store, blocks, update, logger) {
4187
4414
  await processFiles(store, blocks, update.value, logger);
4188
4415
  value = { doc: update.value };
4189
4416
  }
4190
- const block = await encode({ value, hasher: hasher6, codec: codec2 });
4417
+ const block = await encode({ value, hasher: hasher5, codec: codec2 });
4191
4418
  blocks.putSync(block.cid, block.bytes);
4192
4419
  return block.cid;
4193
4420
  }
@@ -4242,7 +4469,8 @@ async function getValueFromCrdt(blocks, head, key, logger) {
4242
4469
  if (!head.length) throw logger.Debug().Msg("Getting from an empty ledger").AsError();
4243
4470
  const link = await get(blocks, head, key);
4244
4471
  if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
4245
- return await getValueFromLink(blocks, link, logger);
4472
+ const ret = await getValueFromLink(blocks, link, logger);
4473
+ return ret;
4246
4474
  }
4247
4475
  function readFiles(blocks, { doc }) {
4248
4476
  if (!doc) return;
@@ -4278,7 +4506,7 @@ function readFileset(blocks, files, isPublic = false) {
4278
4506
  async function getValueFromLink(blocks, link, logger) {
4279
4507
  const block = await blocks.get(link);
4280
4508
  if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
4281
- const { value } = await decode({ bytes: block.bytes, hasher: hasher6, codec: codec2 });
4509
+ const { value } = await decode({ bytes: block.bytes, hasher: hasher5, codec: codec2 });
4282
4510
  const cvalue = {
4283
4511
  ...value,
4284
4512
  cid: link
@@ -4340,6 +4568,9 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4340
4568
  const { key, value } = ops[i];
4341
4569
  if (!keys.has(key)) {
4342
4570
  const docValue = await getValueFromLink(blocks, value, logger);
4571
+ if (key === PARAM.GENESIS_CID) {
4572
+ continue;
4573
+ }
4343
4574
  updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
4344
4575
  limit--;
4345
4576
  keys.add(key);
@@ -4353,8 +4584,10 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4353
4584
  }
4354
4585
  async function* getAllEntries(blocks, head, logger) {
4355
4586
  for await (const [key, link] of entries(blocks, head)) {
4356
- const docValue = await getValueFromLink(blocks, link, logger);
4357
- yield { id: key, value: docValue.doc, del: docValue.del };
4587
+ if (key !== PARAM.GENESIS_CID) {
4588
+ const docValue = await getValueFromLink(blocks, link, logger);
4589
+ yield { id: key, value: docValue.doc, del: docValue.del };
4590
+ }
4358
4591
  }
4359
4592
  }
4360
4593
  async function* clockVis(blocks, head) {
@@ -4399,7 +4632,7 @@ async function doCompact(blockLog, head, logger) {
4399
4632
  async function getBlock(blocks, cidString) {
4400
4633
  const block = await blocks.get(parse2(cidString));
4401
4634
  if (!block) throw new Error(`Missing block ${cidString}`);
4402
- const { cid, value } = await decode({ bytes: block.bytes, codec: codec2, hasher: hasher6 });
4635
+ const { cid, value } = await decode({ bytes: block.bytes, codec: codec2, hasher: hasher5 });
4403
4636
  return new Block({ cid, value, bytes: block.bytes });
4404
4637
  }
4405
4638
 
@@ -4499,6 +4732,10 @@ var CRDTClockImpl = class {
4499
4732
  this.notifyWatchers(internalUpdates || []);
4500
4733
  }
4501
4734
  notifyWatchers(updates) {
4735
+ updates = updates.filter((update) => update.id !== PARAM.GENESIS_CID);
4736
+ if (!updates.length) {
4737
+ return;
4738
+ }
4502
4739
  this.emptyWatchers.forEach((fn) => fn());
4503
4740
  this.watchers.forEach((fn) => fn(updates || []));
4504
4741
  }
@@ -4578,7 +4815,7 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
4578
4815
  try {
4579
4816
  head = await advance(tblocks, head, cid);
4580
4817
  } catch (e) {
4581
- logger.Debug().Err(e).Msg("failed to advance head");
4818
+ logger.Error().Err(e).Msg("failed to advance head");
4582
4819
  continue;
4583
4820
  }
4584
4821
  }
@@ -4637,6 +4874,17 @@ var CRDTImpl = class {
4637
4874
  }
4638
4875
  async bulk(updates) {
4639
4876
  await this.ready();
4877
+ updates = updates.map((dupdate) => ({
4878
+ ...dupdate,
4879
+ value: sanitizeDocumentFields(dupdate.value)
4880
+ }));
4881
+ if (this.clock.head.length === 0) {
4882
+ const value = { id: PARAM.GENESIS_CID, value: { _id: PARAM.GENESIS_CID } };
4883
+ await this._bulk([value]);
4884
+ }
4885
+ return await this._bulk(updates);
4886
+ }
4887
+ async _bulk(updates) {
4640
4888
  const prevHead = [...this.clock.head];
4641
4889
  const done = await this.blockstore.transaction(async (blocks) => {
4642
4890
  const { head } = await applyBulkUpdateToCrdt(
@@ -4724,7 +4972,7 @@ var Context = class {
4724
4972
  };
4725
4973
 
4726
4974
  // src/ledger.ts
4727
- var ledgers = new KeyedResolvOnce5();
4975
+ var ledgers = new KeyedResolvOnce6();
4728
4976
  function keyConfigOpts(sthis, name, opts) {
4729
4977
  return JSON.stringify(
4730
4978
  toSortedArray({
@@ -4868,7 +5116,8 @@ var LedgerImpl = class {
4868
5116
  });
4869
5117
  return ret;
4870
5118
  }
4871
- attach(a) {
5119
+ async attach(a) {
5120
+ await this.ready();
4872
5121
  return this.crdt.blockstore.loader.attach(a);
4873
5122
  }
4874
5123
  // readonly _asDb = new ResolveOnce<Database>();
@@ -4876,6 +5125,7 @@ var LedgerImpl = class {
4876
5125
  // return this._asDb.once(() => new DatabaseImpl(this));
4877
5126
  // }
4878
5127
  subscribe(listener, updates) {
5128
+ this.ready();
4879
5129
  this.logger.Debug().Bool("updates", updates).Msg("subscribe");
4880
5130
  if (updates) {
4881
5131
  if (!this._listening) {
@@ -4917,49 +5167,32 @@ var LedgerImpl = class {
4917
5167
  }
4918
5168
  }
4919
5169
  };
4920
- function defaultURI2(sthis, name, curi, uri, store, ctx) {
4921
- ctx = ctx || {};
4922
- const ret = (curi ? URI12.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
4923
- if (!ret.hasParam(PARAM.NAME)) {
4924
- throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
4925
- }
4926
- if (ctx.idx) {
4927
- ret.defParam(PARAM.INDEX, "idx");
4928
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}-idx@`);
4929
- } else {
4930
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}@`);
4931
- }
4932
- if (store === "data") {
4933
- if (ctx.file) {
4934
- } else {
4935
- ret.defParam(PARAM.SUFFIX, ".car");
4936
- }
4937
- }
4938
- return ret.URI();
4939
- }
4940
5170
  function toStoreURIRuntime(sthis, name, sopts) {
4941
5171
  sopts = sopts || {};
4942
5172
  if (!sopts.base) {
4943
5173
  const fp_env = sthis.env.get("FP_STORAGE_URL");
4944
5174
  if (fp_env) {
4945
- sopts = { ...sopts, base: BuildURI3.from(fp_env).setParam(PARAM.URL_GEN, "fromEnv") };
5175
+ sopts = { ...sopts, base: BuildURI2.from(fp_env).setParam(PARAM.URL_GEN, "fromEnv") };
4946
5176
  } else {
4947
5177
  sopts = { ...sopts, base: getDefaultURI(sthis).build().setParam(PARAM.URL_GEN, "default") };
4948
5178
  }
4949
5179
  }
4950
- const base = URI12.from(sopts.base);
5180
+ const base = URI13.from(sopts.base);
4951
5181
  return {
4952
5182
  idx: {
4953
- car: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { idx: true }),
4954
- file: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { file: true, idx: true }),
4955
- meta: defaultURI2(sthis, name, sopts.idx?.meta, base, "meta", { idx: true }),
4956
- wal: defaultURI2(sthis, name, sopts.idx?.wal, base, "wal", { idx: true })
5183
+ car: ensureURIDefaults(sthis, name, sopts.idx?.car ?? sopts.data?.car, base, "car", { idx: true }),
5184
+ file: ensureURIDefaults(sthis, name, sopts.idx?.file ?? sopts.idx?.car ?? sopts.data?.file ?? sopts.data?.car, base, "file", {
5185
+ file: true,
5186
+ idx: true
5187
+ }),
5188
+ meta: ensureURIDefaults(sthis, name, sopts.idx?.meta ?? sopts.data?.meta, base, "meta", { idx: true }),
5189
+ wal: ensureURIDefaults(sthis, name, sopts.idx?.wal ?? sopts.data?.wal, base, "wal", { idx: true })
4957
5190
  },
4958
5191
  data: {
4959
- car: defaultURI2(sthis, name, sopts.data?.data, base, "data"),
4960
- file: defaultURI2(sthis, name, sopts.data?.data, base, "data", { file: true }),
4961
- meta: defaultURI2(sthis, name, sopts.data?.meta, base, "meta"),
4962
- wal: defaultURI2(sthis, name, sopts.data?.wal, base, "wal")
5192
+ car: ensureURIDefaults(sthis, name, sopts.data?.car, base, "car"),
5193
+ file: ensureURIDefaults(sthis, name, sopts.data?.file ?? sopts.data?.car, base, "file", { file: true }),
5194
+ meta: ensureURIDefaults(sthis, name, sopts.data?.meta, base, "meta"),
5195
+ wal: ensureURIDefaults(sthis, name, sopts.data?.wal, base, "wal")
4963
5196
  }
4964
5197
  };
4965
5198
  }
@@ -5023,7 +5256,7 @@ __export(file_exports, {
5023
5256
 
5024
5257
  // src/version.ts
5025
5258
  var PACKAGE_VERSION = Object.keys({
5026
- "0.20.0-dev-preview-41": "xxxx"
5259
+ "0.20.0-dev-preview-51": "xxxx"
5027
5260
  })[0];
5028
5261
  export {
5029
5262
  CRDTImpl,
@@ -5044,6 +5277,7 @@ export {
5044
5277
  ensureLogger,
5045
5278
  ensureSuperLog,
5046
5279
  ensureSuperThis,
5280
+ ensureURIDefaults,
5047
5281
  exceptionWrapper,
5048
5282
  falsyToUndef,
5049
5283
  fireproof,
@@ -5061,6 +5295,7 @@ export {
5061
5295
  onSuperThis,
5062
5296
  runtime_exports as rt,
5063
5297
  runtime_exports as runtime,
5298
+ storeType2DataMetaWal,
5064
5299
  throwFalsy,
5065
5300
  toSortedArray,
5066
5301
  toStoreURIRuntime