@fireproof/core 0.20.0-dev-preview-40 → 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.
Files changed (45) hide show
  1. package/README.md +6 -4
  2. package/deno/index.js +2 -2
  3. package/deno/index.js.map +1 -1
  4. package/deno.json +3 -2
  5. package/index.cjs +955 -599
  6. package/index.cjs.map +1 -1
  7. package/index.d.cts +334 -127
  8. package/index.d.ts +334 -127
  9. package/index.js +939 -582
  10. package/index.js.map +1 -1
  11. package/indexeddb/index.cjs.map +1 -1
  12. package/indexeddb/index.js.map +1 -1
  13. package/indexeddb/metafile-cjs.json +1 -1
  14. package/indexeddb/metafile-esm.json +1 -1
  15. package/metafile-cjs.json +1 -1
  16. package/metafile-esm.json +1 -1
  17. package/package.json +7 -5
  18. package/react/index.cjs +17 -9
  19. package/react/index.cjs.map +1 -1
  20. package/react/index.d.cts +5 -4
  21. package/react/index.d.ts +5 -4
  22. package/react/index.js +17 -9
  23. package/react/index.js.map +1 -1
  24. package/react/metafile-cjs.json +1 -1
  25. package/react/metafile-esm.json +1 -1
  26. package/tests/blockstore/interceptor-gateway.test.ts +15 -1
  27. package/tests/blockstore/keyed-crypto-indexeddb-file.test.ts +8 -18
  28. package/tests/blockstore/keyed-crypto.test.ts +31 -53
  29. package/tests/blockstore/loader.test.ts +21 -19
  30. package/tests/blockstore/store.test.ts +52 -56
  31. package/tests/blockstore/transaction.test.ts +13 -11
  32. package/tests/fireproof/all-gateway.test.ts +53 -50
  33. package/tests/fireproof/attachable.test.ts +356 -0
  34. package/tests/fireproof/crdt.test.ts +100 -60
  35. package/tests/fireproof/database.test.ts +95 -54
  36. package/tests/fireproof/fireproof.test.ts +58 -55
  37. package/tests/fireproof/hello.test.ts +4 -4
  38. package/tests/fireproof/indexer.test.ts +44 -44
  39. package/tests/fireproof/stable-cid.test.ts +69 -0
  40. package/tests/fireproof/utils.test.ts +21 -10
  41. package/tests/gateway/file/loader-config.test.ts +25 -25
  42. package/tests/gateway/fp-envelope-serialize.test.ts +8 -8
  43. package/tests/gateway/indexeddb/loader-config.test.ts +6 -6
  44. package/tests/helpers.ts +81 -2
  45. package/tests/react/useFireproof.test.tsx +59 -17
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 BuildURI2, KeyedResolvOnce as KeyedResolvOnce4, ResolveOnce as ResolveOnce7, URI as URI13 } 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) {
@@ -60,6 +66,18 @@ function falsyToUndef(value) {
60
66
  }
61
67
  return value;
62
68
  }
69
+ var DataAndMetaAndWalAndBaseStore = class {
70
+ constructor(dam) {
71
+ this.wal = dam.wal;
72
+ this.file = dam.file;
73
+ this.car = dam.car;
74
+ this.meta = dam.meta;
75
+ this.baseStores = [this.file, this.car, this.meta];
76
+ if (this.wal) {
77
+ this.baseStores.push(this.wal);
78
+ }
79
+ }
80
+ };
63
81
 
64
82
  // src/utils.ts
65
83
  import { base58btc } from "multiformats/bases/base58";
@@ -270,21 +288,25 @@ function ensureLogger(sthis, componentName, ctx) {
270
288
  return out;
271
289
  }
272
290
  function getStore(url, sthis, joiner) {
273
- const store = url.getParam(PARAM.STORE);
274
- switch (store) {
275
- 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;
276
298
  case "wal":
277
299
  case "meta":
300
+ pathPart = fromUrl;
278
301
  break;
279
302
  default:
280
303
  throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
281
- throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
282
304
  }
283
- let name = store;
305
+ let name = pathPart;
284
306
  if (url.hasParam("index")) {
285
307
  name = joiner(url.getParam(PARAM.INDEX) || "idx", name);
286
308
  }
287
- return { store, name };
309
+ return { pathPart, fromUrl, name };
288
310
  }
289
311
  function getKey(url, logger) {
290
312
  const result = url.getParam(PARAM.KEY);
@@ -386,6 +408,35 @@ function makeName(fnString) {
386
408
  return found[1];
387
409
  }
388
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
+ }
389
440
 
390
441
  // src/write-queue.ts
391
442
  import { Future } from "@adviser/cement";
@@ -1075,8 +1126,10 @@ function getFileName(url, sthis) {
1075
1126
  const key = url.getParam("key");
1076
1127
  if (!key) throw sthis.logger.Error().Url(url).Msg(`key not found`).AsError();
1077
1128
  const res = getStore2(url, sthis, (...a) => a.join("-"));
1078
- switch (res.store) {
1079
- case "data":
1129
+ switch (res.fromUrl) {
1130
+ case "file":
1131
+ return sthis.pathOps.join(res.name, key);
1132
+ case "car":
1080
1133
  return sthis.pathOps.join(res.name, key + ".car");
1081
1134
  case "wal":
1082
1135
  case "meta":
@@ -1224,8 +1277,13 @@ var MemoryGateway = class {
1224
1277
  close(baseUrl) {
1225
1278
  return Promise.resolve(Result4.Ok(void 0));
1226
1279
  }
1227
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1228
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
+ }
1229
1287
  this.memorys.clear();
1230
1288
  return Promise.resolve(Result4.Ok(void 0));
1231
1289
  }
@@ -1237,7 +1295,7 @@ var MemoryGateway = class {
1237
1295
  get(url) {
1238
1296
  const x = this.memorys.get(url.toString());
1239
1297
  if (!x) {
1240
- return Promise.resolve(Result4.Err(new NotFoundError("not found")));
1298
+ return Promise.resolve(Result4.Err(new NotFoundError(`not found: ${url.toString()}`)));
1241
1299
  }
1242
1300
  return Promise.resolve(Result4.Ok(x));
1243
1301
  }
@@ -1343,9 +1401,6 @@ async function decode2DbMetaEvents(sthis, rserializedMeta) {
1343
1401
  if (!Array.isArray(serializedMeta)) {
1344
1402
  return sthis.logger.Debug().Any("metaEntries", serializedMeta).Msg("No data in MetaEntries").ResultError();
1345
1403
  }
1346
- if (!serializedMeta.length) {
1347
- return sthis.logger.Debug().Msg("No MetaEntries found").ResultError();
1348
- }
1349
1404
  return Result6.Ok(
1350
1405
  await Promise.all(
1351
1406
  serializedMeta.map(async (metaEntry) => {
@@ -1410,10 +1465,9 @@ async function fpDeserialize(sthis, url, intoRaw, pdecoder) {
1410
1465
  ...pdecoder
1411
1466
  };
1412
1467
  switch (url.getParam(PARAM.STORE)) {
1413
- case "data":
1414
- if (url.getParam(PARAM.SUFFIX) === ".car") {
1415
- return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1416
- }
1468
+ case "car":
1469
+ return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1470
+ case "file":
1417
1471
  return makeFPEnvelope(FPEnvelopeTypes.FILE, await decoder.file(sthis, raw2));
1418
1472
  case "wal":
1419
1473
  return makeFPEnvelope(FPEnvelopeTypes.WAL, await decode2WalState(sthis, await decoder.wal(sthis, raw2)));
@@ -1475,7 +1529,7 @@ var DefSerdeGateway = class {
1475
1529
  const urlWithoutKey = url.build().delParam(PARAM.KEY).delParam(PARAM.SELF_REFLECT).toString();
1476
1530
  this.subscribeFn.set(urlWithoutKey, rawCallback);
1477
1531
  return Result7.Ok(() => {
1478
- this.subscribeFn.delete(url.toString());
1532
+ this.subscribeFn.delete(urlWithoutKey);
1479
1533
  });
1480
1534
  }
1481
1535
  const unreg = await this.gw.subscribe(url, rawCallback, sthis);
@@ -1604,37 +1658,49 @@ var Block = mfBlock;
1604
1658
  async function decode({
1605
1659
  bytes,
1606
1660
  codec: codec3,
1607
- hasher: hasher7
1661
+ hasher: hasher6
1608
1662
  }) {
1609
1663
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1610
- 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");
1611
1665
  const value = await Promise.resolve(codec3.decode(bytes));
1612
- 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);
1613
1671
  const cid = CID2.create(1, codec3.code, hash);
1614
- return new mfBlock({ value, bytes, cid });
1672
+ return new mfBlock({ value, bytes: toHash, cid });
1615
1673
  }
1616
1674
  async function encode({
1617
1675
  value,
1618
1676
  codec: codec3,
1619
- hasher: hasher7
1677
+ hasher: hasher6
1620
1678
  }) {
1621
1679
  if (typeof value === "undefined") throw new Error('Missing required argument "value"');
1622
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1623
- const bytes = await Promise.resolve(codec3.encode(value));
1624
- 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
+ }
1625
1691
  const cid = CID2.create(1, codec3.code, hash);
1626
- return new mfBlock({ value, bytes, cid });
1692
+ return new Block({ value, bytes, cid });
1627
1693
  }
1628
1694
  async function create({
1629
1695
  bytes,
1630
1696
  cid,
1631
- hasher: hasher7,
1697
+ hasher: hasher6,
1632
1698
  codec: codec3
1633
1699
  }) {
1634
1700
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1635
- if (hasher7 == null) throw new Error('Missing required argument "hasher"');
1701
+ if (hasher6 == null) throw new Error('Missing required argument "hasher"');
1636
1702
  const value = await Promise.resolve(codec3.decode(bytes));
1637
- const hash = await hasher7.digest(bytes);
1703
+ const hash = await hasher6.digest(bytes);
1638
1704
  if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
1639
1705
  throw new Error("CID hash does not match bytes");
1640
1706
  }
@@ -2032,6 +2098,9 @@ var DatabaseImpl = class {
2032
2098
  this.id = ledger.id;
2033
2099
  this.logger = ensureLogger(this.sthis, "Database");
2034
2100
  }
2101
+ attach(a) {
2102
+ return this.ledger.attach(a);
2103
+ }
2035
2104
  get name() {
2036
2105
  return this.ledger.name;
2037
2106
  }
@@ -2127,19 +2196,27 @@ import { ResolveOnce as ResolveOnce6 } from "@adviser/cement";
2127
2196
  // src/blockstore/index.ts
2128
2197
  var blockstore_exports = {};
2129
2198
  __export(blockstore_exports, {
2199
+ AttachedRemotesImpl: () => AttachedRemotesImpl,
2200
+ BaseActiveStore: () => BaseActiveStore,
2130
2201
  BaseBlockstoreImpl: () => BaseBlockstoreImpl,
2131
2202
  Car2FPMsg: () => Car2FPMsg,
2203
+ CarActiveStore: () => CarActiveStore,
2204
+ CarLog: () => CarLog,
2132
2205
  CarTransactionImpl: () => CarTransactionImpl,
2133
2206
  CompactionFetcher: () => CompactionFetcher,
2134
- ConnectionBase: () => ConnectionBase,
2135
2207
  DbMetaEventEqual: () => DbMetaEventEqual,
2136
2208
  DbMetaEventsEqual: () => DbMetaEventsEqual,
2137
2209
  EncryptedBlockstore: () => EncryptedBlockstore,
2138
2210
  FPEnvelopeTypes: () => FPEnvelopeTypes,
2139
2211
  File2FPMsg: () => File2FPMsg,
2212
+ FileActiveStore: () => FileActiveStore,
2140
2213
  InterceptorGateway: () => InterceptorGateway,
2141
2214
  Loader: () => Loader,
2215
+ MetaActiveStore: () => MetaActiveStore,
2142
2216
  PassThroughGateway: () => PassThroughGateway,
2217
+ TaskManager: () => TaskManager,
2218
+ WALActiveStore: () => WALActiveStore,
2219
+ createAttachedStores: () => createAttachedStores,
2143
2220
  createDbMetaEvent: () => createDbMetaEvent,
2144
2221
  defaultGatewayFactoryItem: () => defaultGatewayFactoryItem,
2145
2222
  ensureStoreEnDeFile: () => ensureStoreEnDeFile,
@@ -2153,6 +2230,37 @@ __export(blockstore_exports, {
2153
2230
  });
2154
2231
 
2155
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
+ };
2156
2264
  function toCIDBlock(block) {
2157
2265
  return block;
2158
2266
  }
@@ -2162,9 +2270,19 @@ function DbMetaEventEqual(a, b) {
2162
2270
  function DbMetaEventsEqual(a, b) {
2163
2271
  return a.length === b.length && a.every((e, i) => DbMetaEventEqual(e, b[i]));
2164
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
+ };
2165
2283
 
2166
2284
  // src/blockstore/store-factory.ts
2167
- import { KeyedResolvOnce as KeyedResolvOnce3, Result as Result10 } from "@adviser/cement";
2285
+ import { KeyedResolvOnce as KeyedResolvOnce5, Result as Result10 } from "@adviser/cement";
2168
2286
 
2169
2287
  // src/blockstore/store.ts
2170
2288
  import { exception2Result as exception2Result3, ResolveOnce as ResolveOnce4, Result as Result9 } from "@adviser/cement";
@@ -2172,14 +2290,14 @@ import { exception2Result as exception2Result3, ResolveOnce as ResolveOnce4, Res
2172
2290
  // src/blockstore/loader.ts
2173
2291
  import pLimit from "p-limit";
2174
2292
  import { CarReader } from "@ipld/car/reader";
2175
- import { ResolveOnce as ResolveOnce3 } from "@adviser/cement";
2293
+ import { KeyedResolvOnce as KeyedResolvOnce4, LRUSet, ResolveOnce as ResolveOnce3 } from "@adviser/cement";
2176
2294
 
2177
2295
  // src/blockstore/loader-helpers.ts
2178
2296
  import { sha256 as hasher2 } from "multiformats/hashes/sha2";
2179
2297
  import * as dagCodec from "@ipld/dag-cbor";
2180
2298
  async function parseCarFile(reader, logger) {
2181
- const roots = await reader.getRoots();
2182
- 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]));
2183
2301
  if (!header) throw logger.Error().Msg("missing header block").AsError();
2184
2302
  const dec = await decode({ bytes: header.bytes, hasher: hasher2, codec: dagCodec });
2185
2303
  const fpvalue = dec.value;
@@ -2194,6 +2312,7 @@ import { MemoryBlockstore } from "@fireproof/vendor/@web3-storage/pail/block";
2194
2312
  import { toCryptoRuntime as toCryptoRuntime3 } from "@adviser/cement";
2195
2313
  var CarTransactionImpl = class {
2196
2314
  #memblock = new MemoryBlockstore();
2315
+ #hackUnshift;
2197
2316
  constructor(parent, opts = { add: true, noLoader: false }) {
2198
2317
  if (opts.add) {
2199
2318
  parent.transactions.add(this);
@@ -2201,7 +2320,7 @@ var CarTransactionImpl = class {
2201
2320
  this.parent = parent;
2202
2321
  }
2203
2322
  async get(cid) {
2204
- return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
2323
+ return await this.superGet(cid) ?? falsyToUndef(await this.parent.get(cid));
2205
2324
  }
2206
2325
  async superGet(cid) {
2207
2326
  return this.#memblock.get(cid);
@@ -2212,7 +2331,16 @@ var CarTransactionImpl = class {
2212
2331
  putSync(cid, bytes) {
2213
2332
  this.#memblock.putSync(cid, bytes);
2214
2333
  }
2334
+ unshift(cid, bytes) {
2335
+ if (this.#hackUnshift) {
2336
+ throw new Error("unshift already called");
2337
+ }
2338
+ this.#hackUnshift = { cid, bytes };
2339
+ }
2215
2340
  async *entries() {
2341
+ if (this.#hackUnshift) {
2342
+ yield this.#hackUnshift;
2343
+ }
2216
2344
  for await (const blk of this.#memblock.entries()) {
2217
2345
  yield blk;
2218
2346
  }
@@ -2238,6 +2366,11 @@ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
2238
2366
  keyBag: opts.keyBag || {},
2239
2367
  crypto: toCryptoRuntime3(opts.crypto),
2240
2368
  storeUrls: opts.storeUrls,
2369
+ taskManager: {
2370
+ removeAfter: 3,
2371
+ retryTimeout: 50,
2372
+ ...opts.taskManager
2373
+ },
2241
2374
  // storeEnDeFile: ensureStoreEnDeFile(opts.storeEnDeFile),
2242
2375
  // store,
2243
2376
  storeRuntime: toStoreRuntime(sthis, ensureStoreEnDeFile(opts.storeEnDeFile))
@@ -2330,10 +2463,8 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2330
2463
  async get(cid) {
2331
2464
  const got = await super.get(cid);
2332
2465
  if (got) return got;
2333
- if (!this.loader) {
2334
- return;
2335
- }
2336
- return falsyToUndef(await this.loader.getBlock(cid));
2466
+ const ret = falsyToUndef(await this.loader.getBlock(cid, this.loader.attachedStores.local()));
2467
+ return ret;
2337
2468
  }
2338
2469
  async transaction(fn, opts = { noLoader: false }) {
2339
2470
  this.logger.Debug().Msg("enter transaction");
@@ -2353,11 +2484,8 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2353
2484
  async getFile(car, cid) {
2354
2485
  await this.ready();
2355
2486
  if (!this.loader) throw this.logger.Error().Msg("loader required to get file, ledger must be named").AsError();
2356
- const reader = await this.loader.loadFileCar(
2357
- car
2358
- /*, isPublic */
2359
- );
2360
- const block = await reader.get(cid);
2487
+ const reader = await this.loader.loadFileCar(car, this.loader.attachedStores.local());
2488
+ const block = await reader.blocks.find((i) => i.cid.equals(cid));
2361
2489
  if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
2362
2490
  return block.bytes;
2363
2491
  }
@@ -2464,8 +2592,8 @@ var CommitQueue = class {
2464
2592
 
2465
2593
  // src/blockstore/commitor.ts
2466
2594
  import * as CBW from "@ipld/car/buffer-writer";
2467
- import { sha256 as hasher3 } from "multiformats/hashes/sha2";
2468
2595
  import * as dagCodec2 from "@ipld/dag-cbor";
2596
+ import { sha256 } from "multiformats/hashes/sha2";
2469
2597
  async function encodeCarFile(roots, t, codec3) {
2470
2598
  let size = 0;
2471
2599
  const headerSize = CBW.headerLength({ roots });
@@ -2482,7 +2610,7 @@ async function encodeCarFile(roots, t, codec3) {
2482
2610
  writer.write({ cid, bytes });
2483
2611
  }
2484
2612
  writer.close();
2485
- return await encode({ value: writer.bytes, hasher: hasher3, codec: codec3 });
2613
+ return await encode({ value: writer.bytes, hasher: sha256, codec: codec3 });
2486
2614
  }
2487
2615
  async function createCarFile(encoder, cid, t) {
2488
2616
  return encodeCarFile([cid], t, encoder);
@@ -2490,7 +2618,7 @@ async function createCarFile(encoder, cid, t) {
2490
2618
  async function commitFiles(fileStore, walStore, t, done) {
2491
2619
  const { files: roots } = makeFileCarHeader(done);
2492
2620
  const cids = [];
2493
- const codec3 = (await fileStore.keyedCrypto()).codec();
2621
+ const codec3 = await fileStore.keyedCrypto().then((i) => i.codec());
2494
2622
  const cars = await prepareCarFilesFiles(codec3, roots, t);
2495
2623
  for (const car of cars) {
2496
2624
  const { cid, bytes } = car;
@@ -2516,13 +2644,13 @@ async function prepareCarFilesFiles(encoder, roots, t) {
2516
2644
  return [await encodeCarFile(roots, t, encoder)];
2517
2645
  }
2518
2646
  function makeCarHeader(meta, cars, compact = false) {
2519
- const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
2647
+ const coreHeader = compact ? { cars: [], compact: cars.asArray() } : { cars: cars.asArray(), compact: [] };
2520
2648
  return { ...coreHeader, meta };
2521
2649
  }
2522
2650
  async function encodeCarHeader(fp) {
2523
2651
  return await encode({
2524
2652
  value: { fp },
2525
- hasher: hasher3,
2653
+ hasher: sha256,
2526
2654
  codec: dagCodec2
2527
2655
  });
2528
2656
  }
@@ -2543,7 +2671,7 @@ async function commit(params, t, done, opts = { noLoader: false, compact: false
2543
2671
  }
2544
2672
  async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2545
2673
  const carFiles = [];
2546
- threshold = threshold || 128e3 * 8;
2674
+ threshold = threshold || 16 * 65536;
2547
2675
  let clonedt = new CarTransactionImpl(t.parent, { add: false, noLoader: false });
2548
2676
  clonedt.putSync(rootBlock.cid, rootBlock.bytes);
2549
2677
  let newsize = CBW.blockLength(toCIDBlock(rootBlock));
@@ -2565,23 +2693,24 @@ async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2565
2693
  }
2566
2694
 
2567
2695
  // src/blockstore/loader.ts
2568
- import { sha256 as hasher4 } from "multiformats/hashes/sha2";
2696
+ import { sha256 as hasher3 } from "multiformats/hashes/sha2";
2569
2697
 
2570
2698
  // src/blockstore/task-manager.ts
2571
2699
  var TaskManager = class {
2572
- constructor(sthis, callback) {
2700
+ constructor(sthis, callback, params) {
2573
2701
  // we need to remove the events after some time
2574
2702
  this.eventsWeHandled = /* @__PURE__ */ new Set();
2575
2703
  this.queue = [];
2576
2704
  this.isProcessing = false;
2577
2705
  this.logger = ensureLogger(sthis, "TaskManager");
2578
2706
  this.callback = callback;
2707
+ this.params = params;
2579
2708
  }
2580
- async handleEvent(cid, parents, dbMeta) {
2709
+ async handleEvent(cid, parents, dbMeta, store) {
2581
2710
  for (const parent of parents) {
2582
2711
  this.eventsWeHandled.add(parent.toString());
2583
2712
  }
2584
- this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
2713
+ this.queue.push({ cid: cid.toString(), dbMeta, retries: 0, store });
2585
2714
  this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
2586
2715
  void this.processQueue();
2587
2716
  }
@@ -2595,7 +2724,7 @@ var TaskManager = class {
2595
2724
  return;
2596
2725
  }
2597
2726
  try {
2598
- await this.callback(first.dbMeta);
2727
+ await this.callback(first.dbMeta, first.store);
2599
2728
  this.eventsWeHandled.add(first.cid);
2600
2729
  this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
2601
2730
  } catch (err) {
@@ -2604,7 +2733,7 @@ var TaskManager = class {
2604
2733
  this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
2605
2734
  }
2606
2735
  await new Promise((resolve) => setTimeout(resolve, 50));
2607
- 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");
2608
2737
  } finally {
2609
2738
  this.isProcessing = false;
2610
2739
  if (this.queue.length > 0) {
@@ -2614,13 +2743,299 @@ var TaskManager = class {
2614
2743
  }
2615
2744
  };
2616
2745
 
2746
+ // src/blockstore/attachable-store.ts
2747
+ import { KeyedResolvOnce as KeyedResolvOnce3, isCoerceURI, URI as URI9 } from "@adviser/cement";
2748
+ var AttachedImpl = class {
2749
+ constructor(gws, stores, unreg) {
2750
+ this.gatewayUrls = gws;
2751
+ this.stores = new DataAndMetaAndWalAndBaseStore(stores);
2752
+ this.unreg = unreg;
2753
+ }
2754
+ async detach() {
2755
+ const toClose = [this.stores.car.close(), this.stores.file.close(), this.stores.meta.close()];
2756
+ if (this.stores.wal) {
2757
+ toClose.push(this.stores.wal.close());
2758
+ }
2759
+ await Promise.all(toClose);
2760
+ this.unreg();
2761
+ }
2762
+ status() {
2763
+ return "attached";
2764
+ }
2765
+ };
2766
+ var FileActiveStoreImpl = class extends FileActiveStore {
2767
+ constructor(ref, active, attached) {
2768
+ super();
2769
+ this.ref = ref;
2770
+ this.active = active;
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)];
2792
+ }
2793
+ };
2794
+ var CarAttachedStoresImpl = class {
2795
+ constructor(attached) {
2796
+ this.attached = attached;
2797
+ }
2798
+ local() {
2799
+ return this.attached.local().active.car;
2800
+ }
2801
+ remotes() {
2802
+ return this.attached.remotes().map(({ active }) => active.car);
2803
+ }
2804
+ };
2805
+ var FileAttachedStoresImpl = class {
2806
+ constructor(attached) {
2807
+ this.attached = attached;
2808
+ }
2809
+ local() {
2810
+ return this.attached.local().active.file;
2811
+ }
2812
+ remotes() {
2813
+ return this.attached.remotes().map(({ active }) => active.file);
2814
+ }
2815
+ };
2816
+ var MetaActiveStoreImpl = class extends MetaActiveStore {
2817
+ constructor(ref, active, attached) {
2818
+ super();
2819
+ this.ref = ref;
2820
+ this.active = active;
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)];
2828
+ }
2829
+ };
2830
+ var MetaAttachedStoresImpl = class {
2831
+ constructor(attached) {
2832
+ this.attached = attached;
2833
+ }
2834
+ local() {
2835
+ return this.attached.local().active.meta;
2836
+ }
2837
+ remotes() {
2838
+ return this.attached.remotes().map(({ active }) => active.meta);
2839
+ }
2840
+ };
2841
+ var WALActiveStoreImpl = class extends WALActiveStore {
2842
+ constructor(ref, active, attached) {
2843
+ super();
2844
+ this.ref = ref;
2845
+ this.active = active;
2846
+ this.xattached = attached;
2847
+ }
2848
+ local() {
2849
+ return this.xattached.local();
2850
+ }
2851
+ remotes() {
2852
+ return this.xattached.remotes();
2853
+ }
2854
+ };
2855
+ var WALAttachedStoresImpl = class {
2856
+ constructor(attached) {
2857
+ this.attached = attached;
2858
+ }
2859
+ local() {
2860
+ return this.attached.local().active.wal;
2861
+ }
2862
+ remotes() {
2863
+ return this.attached.remotes().filter(({ active }) => active.wal).map(({ active }) => active.wal);
2864
+ }
2865
+ };
2866
+ var ActiveStoreImpl = class {
2867
+ constructor(active, attached) {
2868
+ this.active = active;
2869
+ this.xattached = attached;
2870
+ }
2871
+ local() {
2872
+ return this.xattached.local();
2873
+ }
2874
+ remotes() {
2875
+ return this.xattached.remotes();
2876
+ }
2877
+ baseStores() {
2878
+ const bs = [this.active.car, this.active.file, this.active.meta];
2879
+ if (this.active.wal) {
2880
+ bs.push(this.active.wal);
2881
+ }
2882
+ return bs;
2883
+ }
2884
+ carStore() {
2885
+ return new CarActiveStoreImpl(this, this.active.car, new CarAttachedStoresImpl(this.xattached));
2886
+ }
2887
+ fileStore() {
2888
+ return new FileActiveStoreImpl(this, this.active.file, new FileAttachedStoresImpl(this.xattached));
2889
+ }
2890
+ metaStore() {
2891
+ return new MetaActiveStoreImpl(this, this.active.meta, new MetaAttachedStoresImpl(this.xattached));
2892
+ }
2893
+ walStore() {
2894
+ if (!this.active.wal) {
2895
+ throw this.xattached.loadable.sthis.logger.Error().Msg("wal store not set").AsError();
2896
+ }
2897
+ return new WALActiveStoreImpl(this, this.active.wal, new WALAttachedStoresImpl(this.xattached));
2898
+ }
2899
+ };
2900
+ function isLoadable(unknown) {
2901
+ return !!unknown.sthis && !!unknown.attachedStores;
2902
+ }
2903
+ async function createAttachedStores(urlOrGup, arOrLoadable, name = "local") {
2904
+ let ar;
2905
+ if (!isLoadable(arOrLoadable)) {
2906
+ ar = arOrLoadable;
2907
+ } else {
2908
+ ar = arOrLoadable.attachedStores;
2909
+ }
2910
+ let gup;
2911
+ if (!urlOrGup) {
2912
+ throw new Error("urlOrGup is required");
2913
+ }
2914
+ if (isCoerceURI(urlOrGup)) {
2915
+ const url = urlOrGup;
2916
+ gup = {
2917
+ car: { url },
2918
+ file: { url },
2919
+ meta: { url },
2920
+ wal: { url }
2921
+ };
2922
+ } else {
2923
+ gup = urlOrGup;
2924
+ }
2925
+ return await ar.attach({
2926
+ name,
2927
+ prepare: async () => gup
2928
+ });
2929
+ }
2930
+ var AttachedRemotesImpl = class {
2931
+ constructor(loadable) {
2932
+ this._remotes = new KeyedResolvOnce3();
2933
+ this.loadable = loadable;
2934
+ }
2935
+ forRemotes(action) {
2936
+ return Promise.all(this.remotes().map((i) => action(i))).then(() => void 0);
2937
+ }
2938
+ remotes() {
2939
+ return this._remotes.values().filter(({ value }) => value.isOk() && !value.Ok().stores.wal).map(({ value }) => value.Ok().stores).map((i) => this.activate(i));
2940
+ }
2941
+ local() {
2942
+ if (!this._local) {
2943
+ throw this.loadable.sthis.logger.Error().Msg("local store not set").AsError();
2944
+ }
2945
+ return new ActiveStoreImpl(this._local.stores, this);
2946
+ }
2947
+ activate(store) {
2948
+ if (isCoerceURI(store)) {
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;
2968
+ }
2969
+ return new ActiveStoreImpl(store, this);
2970
+ }
2971
+ async detach() {
2972
+ await Promise.all(
2973
+ this._remotes.values().map(async ({ value: rvalue }) => {
2974
+ if (rvalue.isOk()) {
2975
+ await rvalue.Ok().detach();
2976
+ }
2977
+ })
2978
+ );
2979
+ }
2980
+ async attach(attached) {
2981
+ const gwp = await attached.prepare();
2982
+ const gws = {
2983
+ car: {
2984
+ ...gwp.car,
2985
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, gwp.car.url, URI9.from(gwp.car.url), "car")
2986
+ },
2987
+ file: {
2988
+ ...gwp.file,
2989
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, URI9.from(gwp.file.url), "file", { file: true })
2990
+ },
2991
+ meta: {
2992
+ ...gwp.meta,
2993
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, URI9.from(gwp.meta.url), "meta")
2994
+ },
2995
+ wal: gwp.wal ? {
2996
+ ...gwp.wal,
2997
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, URI9.from(gwp.wal.url), "wal")
2998
+ } : void 0
2999
+ };
3000
+ const key = JSON.stringify(
3001
+ toSortedArray({
3002
+ carUrl: gws.car.url.toString(),
3003
+ filesUrl: gws.file.url.toString(),
3004
+ metaUrl: gws.meta.url.toString(),
3005
+ walUrl: gws.wal?.url.toString()
3006
+ })
3007
+ );
3008
+ return this._remotes.get(key).once(async () => {
3009
+ const rt = toStoreRuntime(this.loadable.sthis);
3010
+ const result = new AttachedImpl(
3011
+ gws,
3012
+ await rt.makeStores({
3013
+ byStore: gws,
3014
+ loader: this.loadable
3015
+ }),
3016
+ () => {
3017
+ this._remotes.unget(key);
3018
+ }
3019
+ );
3020
+ if (result.stores.wal) {
3021
+ if (this._local) {
3022
+ throw this.loadable.sthis.logger.Error().Msg("local store could only set once").AsError();
3023
+ }
3024
+ this._local = result;
3025
+ }
3026
+ return result;
3027
+ });
3028
+ }
3029
+ };
3030
+
2617
3031
  // src/blockstore/loader.ts
2618
3032
  function carLogIncludesGroup(list, cids) {
2619
- return list.some((arr) => {
2620
- return arr.toString() === cids.toString();
2621
- });
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
+ );
2622
3037
  }
2623
- function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
3038
+ function uniqueCids(list, remove = new LRUSet()) {
2624
3039
  const byString = /* @__PURE__ */ new Map();
2625
3040
  for (const cid of list) {
2626
3041
  if (remove.has(cid.toString())) continue;
@@ -2632,17 +3047,8 @@ var Loader = class {
2632
3047
  constructor(sthis, ebOpts) {
2633
3048
  this.commitQueue = new CommitQueue();
2634
3049
  this.isCompacting = false;
2635
- this.carReaders = /* @__PURE__ */ new Map();
2636
- this.seenCompacted = /* @__PURE__ */ new Set();
2637
- this.processedCars = /* @__PURE__ */ new Set();
2638
- this.carLog = [];
2639
- this.getBlockCache = /* @__PURE__ */ new Map();
2640
- this.seenMeta = /* @__PURE__ */ new Set();
2641
- this.writeLimit = pLimit(1);
2642
- this._carStore = new ResolveOnce3();
2643
- this._fileStore = new ResolveOnce3();
2644
- this._WALStore = new ResolveOnce3();
2645
- this._metaStore = new ResolveOnce3();
3050
+ this.maxConcurrentWrite = pLimit(1);
3051
+ this.carLog = new CarLog();
2646
3052
  this.onceReady = new ResolveOnce3();
2647
3053
  this.sthis = sthis;
2648
3054
  this.ebOpts = defaultedBlockstoreRuntime(
@@ -2653,76 +3059,75 @@ var Loader = class {
2653
3059
  },
2654
3060
  "Loader"
2655
3061
  );
2656
- this.logger = this.ebOpts.logger;
2657
- this.taskManager = new TaskManager(sthis, async (dbMeta) => {
2658
- await this.handleDbMetasFromStore([dbMeta]);
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
+ }
2659
3067
  });
2660
- }
2661
- async carStore() {
2662
- return this._carStore.once(
2663
- async () => this.ebOpts.storeRuntime.makeDataStore({
2664
- // sthis: this.sthis,
2665
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2666
- url: this.ebOpts.storeUrls.data,
2667
- // keybag: await this.keyBag(),
2668
- loader: this
2669
- })
2670
- );
2671
- }
2672
- async fileStore() {
2673
- return this._fileStore.once(
2674
- async () => this.ebOpts.storeRuntime.makeDataStore({
2675
- // sthis: this.sthis,
2676
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2677
- url: this.ebOpts.storeUrls.file,
2678
- // keybag: await this.keyBag(),
2679
- loader: this
2680
- })
2681
- );
2682
- }
2683
- async WALStore() {
2684
- return this._WALStore.once(
2685
- async () => this.ebOpts.storeRuntime.makeWALStore({
2686
- // sthis: this.sthis,
2687
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2688
- url: this.ebOpts.storeUrls.wal,
2689
- // keybag: await this.keyBag(),
2690
- loader: this
2691
- })
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
2692
3081
  );
3082
+ this.attachedStores = new AttachedRemotesImpl(this);
2693
3083
  }
2694
- async metaStore() {
2695
- return this._metaStore.once(
2696
- async () => this.ebOpts.storeRuntime.makeMetaStore({
2697
- // sthis: this.sthis,
2698
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2699
- url: this.ebOpts.storeUrls.meta,
2700
- // keybag: await this.keyBag(),
2701
- loader: this
2702
- })
2703
- );
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;
2704
3099
  }
2705
3100
  keyBag() {
2706
3101
  return getKeyBag(this.sthis, this.ebOpts.keyBag);
2707
3102
  }
2708
3103
  async ready() {
2709
3104
  return this.onceReady.once(async () => {
2710
- const metas = await (await this.metaStore()).load();
3105
+ await createAttachedStores(
3106
+ {
3107
+ car: { url: this.ebOpts.storeUrls.car, gatewayInterceptor: this.ebOpts.gatewayInterceptor },
3108
+ file: { url: this.ebOpts.storeUrls.file, gatewayInterceptor: this.ebOpts.gatewayInterceptor },
3109
+ meta: { url: this.ebOpts.storeUrls.meta, gatewayInterceptor: this.ebOpts.gatewayInterceptor },
3110
+ wal: { url: this.ebOpts.storeUrls.wal, gatewayInterceptor: this.ebOpts.gatewayInterceptor }
3111
+ },
3112
+ this.attachedStores
3113
+ );
3114
+ const local = this.attachedStores.local();
3115
+ const metas = await local.active.meta.load();
2711
3116
  if (this.ebOpts.meta) {
2712
- await this.handleDbMetasFromStore([this.ebOpts.meta]);
3117
+ await this.handleDbMetasFromStore([this.ebOpts.meta, ...metas || []], local);
2713
3118
  } else if (metas) {
2714
- await this.handleDbMetasFromStore(metas);
3119
+ await this.handleDbMetasFromStore(metas, local);
2715
3120
  }
2716
3121
  });
2717
3122
  }
2718
3123
  async close() {
2719
3124
  await this.commitQueue.waitIdle();
2720
- const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
2721
- await Promise.all(toClose.map((store) => store.close()));
3125
+ await this.attachedStores.detach();
2722
3126
  }
2723
3127
  async destroy() {
2724
- const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
2725
- await Promise.all(toDestroy.map((store) => store.destroy()));
3128
+ await Promise.all(
3129
+ this.attachedStores.local().baseStores().map((store) => store.destroy())
3130
+ );
2726
3131
  }
2727
3132
  // async snapToCar(carCid: AnyLink | string) {
2728
3133
  // await this.ready
@@ -2734,28 +3139,38 @@ var Loader = class {
2734
3139
  // await this.getMoreReaders(carHeader.cars)
2735
3140
  // await this._applyCarHeader(carHeader, true)
2736
3141
  // }
2737
- async handleDbMetasFromStore(metas) {
2738
- this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
3142
+ async handleDbMetasFromStore(metas, activeStore) {
3143
+ this.logger.Debug().Any("metas", metas).Url(activeStore.active.car.url()).Msg("handleDbMetasFromStore");
2739
3144
  for (const meta of metas) {
2740
- await this.writeLimit(async () => {
2741
- await this.mergeDbMetaIntoClock(meta);
3145
+ await this.maxConcurrentWrite(async () => {
3146
+ await this.mergeDbMetaIntoClock(meta, activeStore);
2742
3147
  });
2743
3148
  }
2744
3149
  }
2745
- async mergeDbMetaIntoClock(meta) {
3150
+ async mergeDbMetaIntoClock(meta, activeStore) {
2746
3151
  if (this.isCompacting) {
2747
3152
  throw this.logger.Error().Msg("cannot merge while compacting").AsError();
2748
3153
  }
2749
- if (this.seenMeta.has(meta.cars.toString())) return;
2750
- this.seenMeta.add(meta.cars.toString());
2751
- if (carLogIncludesGroup(this.carLog, meta.cars)) {
2752
- 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;
2753
3173
  }
2754
- const carHeader = await this.loadCarHeaderFromMeta(meta);
2755
- carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2756
- await this.getMoreReaders(carHeader.cars.flat());
2757
- this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
2758
- await this.ebOpts.applyMeta?.(carHeader.meta);
2759
3174
  }
2760
3175
  // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
2761
3176
  // const { key } = meta;
@@ -2763,8 +3178,8 @@ var Loader = class {
2763
3178
  // await this.setKey(key);
2764
3179
  // }
2765
3180
  // }
2766
- async loadCarHeaderFromMeta({ cars: cids }) {
2767
- const reader = await this.loadCar(cids[0]);
3181
+ async loadCarHeaderFromMeta(dbm, astore) {
3182
+ const reader = await this.loadCar(dbm.cars[0], astore);
2768
3183
  return await parseCarFile(reader, this.logger);
2769
3184
  }
2770
3185
  // async _getKey(): Promise<string | undefined> {
@@ -2777,22 +3192,22 @@ var Loader = class {
2777
3192
  // }
2778
3193
  async commitFiles(t, done) {
2779
3194
  await this.ready();
2780
- const fstore = await this.fileStore();
2781
- const wstore = await this.WALStore();
3195
+ const fstore = this.attachedStores.local().active.file;
3196
+ const wstore = this.attachedStores.local().active.wal;
2782
3197
  return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
2783
3198
  }
2784
- async loadFileCar(cid) {
2785
- return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
3199
+ async loadFileCar(cid, store) {
3200
+ return await this.storesLoadCar(cid, store.fileStore());
2786
3201
  }
2787
3202
  async commit(t, done, opts = { noLoader: false, compact: false }) {
2788
3203
  await this.ready();
2789
- const carStore = await this.carStore();
3204
+ const carStore = this.attachedStores.local().active.car;
2790
3205
  const params = {
2791
3206
  encoder: (await carStore.keyedCrypto()).codec(),
2792
3207
  carLog: this.carLog,
2793
3208
  carStore,
2794
- WALStore: await this.WALStore(),
2795
- metaStore: await this.metaStore(),
3209
+ WALStore: this.attachedStores.local().active.wal,
3210
+ metaStore: this.attachedStores.local().active.meta,
2796
3211
  threshold: this.ebOpts.threshold
2797
3212
  };
2798
3213
  return this.commitQueue.enqueue(async () => {
@@ -2802,41 +3217,54 @@ var Loader = class {
2802
3217
  return ret.cgrp;
2803
3218
  });
2804
3219
  }
2805
- async updateCarLog(cids, fp, compact) {
3220
+ async updateCarLog(cids, cHeader, compact) {
2806
3221
  if (compact) {
2807
- const previousCompactCid = fp.compact[fp.compact.length - 1];
2808
- fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2809
- this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
2810
- await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
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));
3225
+ await this.removeCidsForCompact(previousCompactCid[0], this.attachedStores.local()).catch((e) => e);
2811
3226
  } else {
2812
- this.carLog.unshift(cids);
3227
+ this.carLog.xunshift(cids);
2813
3228
  }
2814
3229
  }
2815
3230
  async cacheTransaction(t) {
2816
3231
  for await (const block of t.entries()) {
2817
3232
  const sBlock = block.cid.toString();
2818
- if (!this.getBlockCache.has(sBlock)) {
2819
- this.getBlockCache.set(sBlock, block);
2820
- }
2821
- }
2822
- }
2823
- async cacheCarReader(carCidStr, reader) {
2824
- if (this.processedCars.has(carCidStr)) return;
2825
- this.processedCars.add(carCidStr);
2826
- for await (const block of reader.blocks()) {
2827
- const sBlock = block.cid.toString();
2828
- if (!this.getBlockCache.has(sBlock)) {
2829
- this.getBlockCache.set(sBlock, block);
2830
- }
3233
+ this.cidCache.get(sBlock).once(
3234
+ () => ({
3235
+ type: "block",
3236
+ cid: block.cid,
3237
+ blocks: [block],
3238
+ roots: []
3239
+ })
3240
+ );
2831
3241
  }
2832
3242
  }
2833
- async removeCidsForCompact(cid) {
2834
- const carHeader = await this.loadCarHeaderFromMeta({
2835
- cars: [cid]
2836
- });
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
+ // }
3258
+ async removeCidsForCompact(cid, store) {
3259
+ const carHeader = await this.loadCarHeaderFromMeta(
3260
+ {
3261
+ cars: [cid]
3262
+ },
3263
+ store
3264
+ );
2837
3265
  for (const cids of carHeader.compact) {
2838
3266
  for (const cid2 of cids) {
2839
- await (await this.carStore()).remove(cid2);
3267
+ await this.attachedStores.local().active.car.remove(cid2);
2840
3268
  }
2841
3269
  }
2842
3270
  }
@@ -2848,148 +3276,134 @@ var Loader = class {
2848
3276
  // await this.remoteWAL!.enqueue(dbMeta, { public: false })
2849
3277
  // }
2850
3278
  // }
2851
- async *entries(cache2 = true) {
3279
+ async *entries() {
2852
3280
  await this.ready();
2853
- if (cache2) {
2854
- for (const [, block] of this.getBlockCache) {
2855
- yield block;
2856
- }
2857
- } else {
2858
- for (const [, block] of this.getBlockCache) {
2859
- yield block;
2860
- }
2861
- for (const cids of this.carLog) {
2862
- for (const cid of cids) {
2863
- const reader = await this.loadCar(cid);
2864
- if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
2865
- for await (const block of reader.blocks()) {
2866
- const sCid = block.cid.toString();
2867
- if (!this.getBlockCache.has(sCid)) {
2868
- yield block;
2869
- }
2870
- }
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;
2871
3293
  }
2872
3294
  }
2873
3295
  }
2874
3296
  }
2875
- async getBlock(cid) {
3297
+ async getBlock(cid, store) {
2876
3298
  await this.ready();
2877
- const sCid = cid.toString();
2878
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2879
- const getCarCid = async (carCid) => {
2880
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2881
- const reader = await this.loadCar(carCid);
2882
- if (!reader) {
2883
- throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
2884
- }
2885
- await this.cacheCarReader(carCid.toString(), reader).catch(() => {
2886
- return;
2887
- });
2888
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2889
- throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
2890
- };
2891
- const getCompactCarCids = async (carCid) => {
2892
- const reader = await this.loadCar(carCid);
2893
- if (!reader) {
2894
- throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
2895
- }
2896
- const header = await parseCarFile(reader, this.logger);
2897
- const compacts = header.compact;
2898
- let got2;
2899
- const batchSize2 = 5;
2900
- for (let i = 0; i < compacts.length; i += batchSize2) {
2901
- const promises = [];
2902
- for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
2903
- for (const cid2 of compacts[j]) {
2904
- 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;
2905
3322
  }
2906
3323
  }
2907
- try {
2908
- got2 = await Promise.any(promises);
2909
- } catch {
2910
- }
2911
- if (got2) break;
2912
- }
2913
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2914
- throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
2915
- };
2916
- let got;
2917
- const batchSize = 5;
2918
- for (let i = 0; i < this.carLog.length; i += batchSize) {
2919
- const batch = this.carLog.slice(i, i + batchSize);
2920
- const promises = batch.flatMap((slice) => slice.map(getCarCid));
2921
- try {
2922
- got = await Promise.any(promises);
2923
- } catch {
2924
3324
  }
2925
- if (got) break;
2926
- }
2927
- if (!got) {
2928
- try {
2929
- got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
2930
- } catch {
3325
+ if (!got) {
3326
+ await getCompactCarCids(this.carLog.last()[0]);
2931
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();
2932
3337
  }
2933
- return got;
3338
+ return ci.blocks[0];
2934
3339
  }
2935
- async loadCar(cid) {
2936
- if (!this.carStore) {
2937
- throw this.logger.Error().Msg("car store not initialized").AsError();
2938
- }
2939
- const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
3340
+ async loadCar(cid, store) {
3341
+ const loaded = await this.storesLoadCar(cid, store.carStore());
2940
3342
  return loaded;
2941
3343
  }
2942
- async makeDecoderAndCarReader(cid, local, remote) {
2943
- const cidsString = cid.toString();
3344
+ async makeDecoderAndCarReader(carCid, store) {
3345
+ const carCidStr = carCid.toString();
2944
3346
  let loadedCar = void 0;
2945
- let activeStore = local;
3347
+ let activeStore = store.local();
2946
3348
  try {
2947
- this.logger.Debug().Any("cid", cidsString).Msg("loading car");
2948
- loadedCar = await local.load(cid);
3349
+ this.logger.Debug().Any("cid", carCidStr).Msg("loading car");
3350
+ loadedCar = await store.local().load(carCid);
2949
3351
  this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
2950
3352
  } catch (e) {
2951
- if (remote) {
2952
- const remoteCar = await remote.load(cid);
2953
- if (remoteCar) {
2954
- this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
2955
- await local.save(remoteCar);
2956
- loadedCar = remoteCar;
2957
- activeStore = remote;
3353
+ if (!isNotFoundError(e)) {
3354
+ throw this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3355
+ }
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");
2958
3370
  }
2959
- } else {
2960
- this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
2961
3371
  }
2962
3372
  }
2963
3373
  if (!loadedCar) {
2964
- throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
2965
- }
2966
- const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher4, codec: (await activeStore.keyedCrypto()).codec() });
2967
- const rawReader = await CarReader.fromBytes(bytes.value);
2968
- const readerP = Promise.resolve(rawReader);
2969
- const cachedReaderP = readerP.then(async (reader) => {
2970
- await this.cacheCarReader(cidsString, reader).catch((e) => {
2971
- this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
2972
- return;
2973
- });
2974
- return reader;
2975
- });
2976
- this.carReaders.set(cidsString, cachedReaderP);
2977
- 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
+ };
2978
3395
  }
2979
3396
  //What if instead it returns an Array of CarHeader
2980
- async storesLoadCar(cid, local, remote) {
2981
- const cidsString = cid.toString();
2982
- let dacr = this.carReaders.get(cidsString);
2983
- if (!dacr) {
2984
- dacr = this.makeDecoderAndCarReader(cid, local, remote);
2985
- this.carReaders.set(cidsString, dacr);
2986
- }
2987
- 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
+ });
2988
3402
  }
2989
- async getMoreReaders(cids) {
2990
- const limit = pLimit(5);
2991
- const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
2992
- await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
3403
+ async getMoreReaders(cids, store) {
3404
+ for (const cid of cids) {
3405
+ await this.loadCar(cid, store);
3406
+ }
2993
3407
  }
2994
3408
  };
2995
3409
 
@@ -3000,7 +3414,7 @@ __export(keyed_crypto_exports, {
3000
3414
  keyedCryptoFactory: () => keyedCryptoFactory
3001
3415
  });
3002
3416
  import { base58btc as base58btc3 } from "multiformats/bases/base58";
3003
- import { sha256 as hasher5 } from "multiformats/hashes/sha2";
3417
+ import { sha256 as hasher4 } from "multiformats/hashes/sha2";
3004
3418
  import * as CBOR from "cborg";
3005
3419
  var generateIV = {
3006
3420
  random: {
@@ -3015,7 +3429,7 @@ var generateIV = {
3015
3429
  },
3016
3430
  hash: {
3017
3431
  calc: async (ko, crypto, data) => {
3018
- const hash = await hasher5.digest(data);
3432
+ const hash = await hasher4.digest(data);
3019
3433
  const hashBytes = new Uint8Array(hash.bytes);
3020
3434
  const hashArray = new Uint8Array(ko.ivLength);
3021
3435
  for (let i = 0; i < hashBytes.length; i++) {
@@ -3034,12 +3448,21 @@ function getGenerateIVFn(url, opts) {
3034
3448
  }
3035
3449
  var BlockIvKeyIdCodec = class {
3036
3450
  constructor(ko, iv, opts) {
3037
- this.code = 3147065;
3451
+ this.code = 24;
3038
3452
  this.name = "Fireproof@encrypted-block:aes-gcm";
3039
3453
  this.ko = ko;
3040
3454
  this.iv = iv;
3041
3455
  this.opts = opts || {};
3042
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
+ }
3043
3466
  async encode(data) {
3044
3467
  const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
3045
3468
  const { iv } = this.ko.algo(calcIv);
@@ -3071,11 +3494,16 @@ var BlockIvKeyIdCodec = class {
3071
3494
  if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
3072
3495
  throw this.ko.logger.Error().Msg("iv missmatch").AsError();
3073
3496
  }
3074
- return result;
3497
+ return {
3498
+ iv,
3499
+ keyId,
3500
+ data: result
3501
+ };
3075
3502
  }
3076
3503
  };
3077
3504
  var cryptoAction = class {
3078
3505
  constructor(url, key, cyopt, sthis) {
3506
+ this.code = 24;
3079
3507
  this.ivLength = 12;
3080
3508
  this.isEncrypting = true;
3081
3509
  this.logger = ensureLogger(sthis, "cryptoAction");
@@ -3109,14 +3537,19 @@ var cryptoAction = class {
3109
3537
  };
3110
3538
  var nullCodec = class {
3111
3539
  constructor() {
3112
- this.code = 0;
3540
+ this.code = 24;
3113
3541
  this.name = "Fireproof@unencrypted-block";
3542
+ this.empty = new Uint8Array();
3114
3543
  }
3115
- encode(data) {
3544
+ async encode(data) {
3116
3545
  return data;
3117
3546
  }
3118
- decode(data) {
3119
- return data;
3547
+ async decode(data) {
3548
+ return {
3549
+ iv: this.empty,
3550
+ keyId: this.empty,
3551
+ data
3552
+ };
3120
3553
  }
3121
3554
  };
3122
3555
  var noCrypto = class {
@@ -3360,7 +3793,7 @@ var BaseStoreImpl = class {
3360
3793
  async keyedCrypto() {
3361
3794
  return keyedCryptoFactory(this._url, await this.loader.keyBag(), this.sthis);
3362
3795
  }
3363
- async start() {
3796
+ async start(dam) {
3364
3797
  this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
3365
3798
  this._url = this._url.build().setParam(PARAM.STORE, this.storeType).URI();
3366
3799
  const res = await this.gateway.start({ loader: this.loader }, this._url);
@@ -3371,13 +3804,8 @@ var BaseStoreImpl = class {
3371
3804
  this._url = res.Ok();
3372
3805
  const kb = await this.loader.keyBag();
3373
3806
  const skRes = await kb.ensureKeyFromUrl(this._url, () => {
3374
- const idx = this._url.getParam(PARAM.INDEX);
3375
- const storeKeyName = [this.url().getParam(PARAM.NAME)];
3376
- if (idx) {
3377
- storeKeyName.push(idx);
3378
- }
3379
- storeKeyName.push(this.storeType);
3380
- return storeKeyName.join(":");
3807
+ const key = this._url.getParam(PARAM.KEY);
3808
+ return key;
3381
3809
  });
3382
3810
  if (skRes.isErr()) {
3383
3811
  return skRes;
@@ -3397,7 +3825,7 @@ var BaseStoreImpl = class {
3397
3825
  return ready;
3398
3826
  }
3399
3827
  }
3400
- this._onStarted.forEach((fn) => fn());
3828
+ this._onStarted.forEach((fn) => fn(dam));
3401
3829
  this.logger.Debug().Msg("started");
3402
3830
  return version;
3403
3831
  }
@@ -3426,12 +3854,19 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3426
3854
  /*this.remote && */
3427
3855
  opts.gateway.subscribe
3428
3856
  ) {
3429
- this.onStarted(async () => {
3857
+ this.onStarted(async (dam) => {
3430
3858
  this.logger.Debug().Str("url", this.url().toString()).Msg("Subscribing to the gateway");
3431
3859
  opts.gateway.subscribe({ loader: this.loader }, this.url(), async ({ payload: dbMetas }) => {
3432
3860
  this.logger.Debug().Msg("Received message from gateway");
3433
3861
  await Promise.all(
3434
- dbMetas.map((dbMeta) => this.loader.taskManager?.handleEvent(dbMeta.eventCid, dbMeta.parents, dbMeta.dbMeta))
3862
+ dbMetas.map(
3863
+ (dbMetaEv) => this.loader.taskManager?.handleEvent(
3864
+ dbMetaEv.eventCid,
3865
+ dbMetaEv.parents,
3866
+ dbMetaEv.dbMeta,
3867
+ this.loader.attachedStores.activate(dam)
3868
+ )
3869
+ )
3435
3870
  );
3436
3871
  this.updateParentsFromDbMetas(dbMetas);
3437
3872
  });
@@ -3453,8 +3888,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3453
3888
  // }
3454
3889
  // return (rDbMeta.Ok() as FPEnvelopeMeta).payload;
3455
3890
  // }
3456
- async load() {
3457
- const branch = "main";
3891
+ async load(branch = "main") {
3458
3892
  const url = await this.gateway.buildUrl({ loader: this.loader }, this.url(), branch);
3459
3893
  if (url.isErr()) {
3460
3894
  throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
@@ -3466,10 +3900,11 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3466
3900
  }
3467
3901
  throw this.logger.Error().Url(url.Ok()).Err(rfpEnv).Msg("gateway get").AsError();
3468
3902
  }
3469
- const dbMetas = rfpEnv.Ok().payload;
3470
- await this.loader.handleDbMetasFromStore(dbMetas.map((m) => m.dbMeta));
3471
- this.updateParentsFromDbMetas(dbMetas);
3472
- return dbMetas.map((m) => m.dbMeta);
3903
+ const fpMeta = rfpEnv.Ok().payload;
3904
+ const dbMetas = fpMeta.map((m) => m.dbMeta);
3905
+ await this.loader.handleDbMetasFromStore(dbMetas, this.loader.attachedStores.activate(url.Ok()));
3906
+ this.updateParentsFromDbMetas(fpMeta);
3907
+ return dbMetas;
3473
3908
  }
3474
3909
  async save(meta, branch) {
3475
3910
  branch = branch || "main";
@@ -3499,9 +3934,8 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3499
3934
  }
3500
3935
  };
3501
3936
  var DataStoreImpl = class extends BaseStoreImpl {
3502
- constructor(sthis, url, opts) {
3503
- super(sthis, url, { ...opts }, ensureLogger(sthis, "DataStoreImpl"));
3504
- this.storeType = "data";
3937
+ constructor(sthis, url, opts, logger) {
3938
+ super(sthis, url, { ...opts }, logger);
3505
3939
  }
3506
3940
  async load(cid) {
3507
3941
  this.logger.Debug().Any("cid", cid).Msg("loading");
@@ -3516,7 +3950,6 @@ var DataStoreImpl = class extends BaseStoreImpl {
3516
3950
  const fpenv = res.Ok();
3517
3951
  switch (fpenv.type) {
3518
3952
  case "car":
3519
- return { cid, bytes: fpenv.payload };
3520
3953
  case "file":
3521
3954
  return { cid, bytes: fpenv.payload };
3522
3955
  default:
@@ -3532,12 +3965,11 @@ var DataStoreImpl = class extends BaseStoreImpl {
3532
3965
  }
3533
3966
  let fpMsg;
3534
3967
  switch (url.Ok().getParam(PARAM.STORE)) {
3535
- case "data":
3536
- if (url.Ok().getParam(PARAM.SUFFIX)) {
3537
- fpMsg = Car2FPMsg(car.bytes);
3538
- } else {
3539
- fpMsg = File2FPMsg(car.bytes);
3540
- }
3968
+ case "car":
3969
+ fpMsg = Car2FPMsg(car.bytes);
3970
+ break;
3971
+ case "file":
3972
+ fpMsg = File2FPMsg(car.bytes);
3541
3973
  break;
3542
3974
  default:
3543
3975
  throw this.logger.Error().Str("store", url.Ok().getParam(PARAM.STORE)).Msg("unexpected store").AsError();
@@ -3568,6 +4000,18 @@ var DataStoreImpl = class extends BaseStoreImpl {
3568
4000
  return this.gateway.destroy({ loader: this.loader }, this.url());
3569
4001
  }
3570
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
+ };
3571
4015
  var WALStoreImpl = class extends BaseStoreImpl {
3572
4016
  constructor(sthis, url, opts) {
3573
4017
  super(sthis, url, { ...opts }, ensureLogger(sthis, "WALStoreImpl"));
@@ -3615,7 +4059,6 @@ var WALStoreImpl = class extends BaseStoreImpl {
3615
4059
  }
3616
4060
  async process() {
3617
4061
  await this.ready();
3618
- if (!this.loader.remoteCarStore) return;
3619
4062
  await this.processQueue.enqueue(async () => {
3620
4063
  try {
3621
4064
  await this._doProcess();
@@ -3629,7 +4072,6 @@ var WALStoreImpl = class extends BaseStoreImpl {
3629
4072
  }
3630
4073
  async _doProcess() {
3631
4074
  if (!this.loader) return;
3632
- if (!this.loader.remoteCarStore) return;
3633
4075
  const operations = [...this.walState.operations];
3634
4076
  const noLoaderOps = [...this.walState.noLoaderOps];
3635
4077
  const fileOperations = [...this.walState.fileOperations];
@@ -3650,13 +4092,13 @@ var WALStoreImpl = class extends BaseStoreImpl {
3650
4092
  return;
3651
4093
  }
3652
4094
  for (const cid of dbMeta.cars) {
3653
- const car = await (await this.loader.carStore()).load(cid);
4095
+ const car = await this.loader.attachedStores.local().active.car.load(cid);
3654
4096
  if (!car) {
3655
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4097
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3656
4098
  throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
3657
4099
  }
3658
4100
  } else {
3659
- await throwFalsy(this.loader.remoteCarStore).save(car);
4101
+ await this.loader.attachedStores.forRemotes((x) => x.active.car.save(car));
3660
4102
  }
3661
4103
  }
3662
4104
  inplaceFilter(this.walState.noLoaderOps, (op) => op !== dbMeta);
@@ -3672,13 +4114,13 @@ var WALStoreImpl = class extends BaseStoreImpl {
3672
4114
  return;
3673
4115
  }
3674
4116
  for (const cid of dbMeta.cars) {
3675
- const car = await (await this.loader.carStore()).load(cid);
4117
+ const car = await this.loader.attachedStores.local().active.car.load(cid);
3676
4118
  if (!car) {
3677
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4119
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3678
4120
  throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
3679
4121
  }
3680
4122
  } else {
3681
- await throwFalsy(this.loader.remoteCarStore).save(car);
4123
+ await this.loader.attachedStores.forRemotes((x) => x.active.car.save(car));
3682
4124
  }
3683
4125
  }
3684
4126
  inplaceFilter(this.walState.operations, (op) => op !== dbMeta);
@@ -3693,11 +4135,11 @@ var WALStoreImpl = class extends BaseStoreImpl {
3693
4135
  if (!this.loader) {
3694
4136
  return;
3695
4137
  }
3696
- const fileBlock = await (await this.loader.fileStore()).load(fileCid);
4138
+ const fileBlock = await this.loader.attachedStores.local().active.file.load(fileCid);
3697
4139
  if (!fileBlock) {
3698
4140
  throw this.logger.Error().Ref("cid", fileCid).Msg("missing file block").AsError();
3699
4141
  }
3700
- await this.loader.remoteFileStore?.save(fileBlock, { public: publicFile });
4142
+ await this.loader.attachedStores.forRemotes((x) => x.active.file.save(fileBlock, { public: publicFile }));
3701
4143
  inplaceFilter(this.walState.fileOperations, (op) => op.cid !== fileCid);
3702
4144
  }, `fileOperation with cid=${fileCid.toString()}`);
3703
4145
  },
@@ -3709,7 +4151,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3709
4151
  if (!this.loader) {
3710
4152
  return;
3711
4153
  }
3712
- await this.loader.remoteMetaStore?.save(lastOp);
4154
+ await this.loader.attachedStores.forRemotes((x) => x.active.meta.save(lastOp));
3713
4155
  }, `remoteMetaStore save with dbMeta.cars=${lastOp.cars.toString()}`);
3714
4156
  }
3715
4157
  } catch (error) {
@@ -3762,16 +4204,17 @@ var WALStoreImpl = class extends BaseStoreImpl {
3762
4204
  };
3763
4205
 
3764
4206
  // src/blockstore/store-factory.ts
3765
- var onceGateway = new KeyedResolvOnce3();
3766
- var gatewayInstances = new KeyedResolvOnce3();
4207
+ var onceGateway = new KeyedResolvOnce5();
4208
+ var gatewayInstances = new KeyedResolvOnce5();
3767
4209
  async function getStartedGateway(ctx, url) {
3768
4210
  return onceGateway.get(url.toString()).once(async () => {
3769
4211
  const item = getGatewayFactoryItem(url.protocol);
3770
4212
  if (item) {
3771
4213
  const ret = {
3772
4214
  url,
3773
- ...await gatewayInstances.get(url.protocol).once(async () => ({})),
3774
- gateway: await item.serdegateway(ctx.loader.sthis)
4215
+ ...await gatewayInstances.get(url.protocol).once(async () => ({
4216
+ gateway: await item.serdegateway(ctx.loader.sthis)
4217
+ }))
3775
4218
  };
3776
4219
  const res = await ret.gateway.start(ctx, url);
3777
4220
  if (res.isErr()) {
@@ -3783,50 +4226,64 @@ async function getStartedGateway(ctx, url) {
3783
4226
  return Result10.Err(ctx.loader.sthis.logger.Warn().Url(url).Msg("unsupported protocol").AsError());
3784
4227
  });
3785
4228
  }
3786
- async function dataStoreFactory(sfi) {
3787
- const storeUrl = sfi.url.build().setParam(PARAM.STORE, "data").URI();
3788
- const rgateway = await getStartedGateway(sfi, storeUrl);
4229
+ async function carStoreFactory(ctx, uai) {
4230
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "car").URI();
4231
+ const rgateway = await getStartedGateway(ctx, storeUrl);
3789
4232
  if (rgateway.isErr()) {
3790
- throw sfi.loader.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
4233
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
3791
4234
  }
3792
4235
  const gateway = rgateway.Ok();
3793
- const store = new DataStoreImpl(sfi.loader.sthis, gateway.url, {
4236
+ const store = new CarStoreImpl(ctx.loader.sthis, gateway.url, {
3794
4237
  gateway: gateway.gateway,
3795
- gatewayInterceptor: sfi.gatewayInterceptor,
3796
- loader: sfi.loader
4238
+ gatewayInterceptor: uai.gatewayInterceptor,
4239
+ loader: ctx.loader
3797
4240
  });
3798
4241
  return store;
3799
4242
  }
3800
- async function metaStoreFactory(sfi) {
3801
- const storeUrl = sfi.url.build().setParam(PARAM.STORE, "meta").URI();
3802
- const rgateway = await getStartedGateway(sfi, storeUrl);
4243
+ async function fileStoreFactory(ctx, uai) {
4244
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "file").URI();
4245
+ const rgateway = await getStartedGateway(ctx, storeUrl);
3803
4246
  if (rgateway.isErr()) {
3804
- throw sfi.loader.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
4247
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
3805
4248
  }
3806
4249
  const gateway = rgateway.Ok();
3807
- const store = new MetaStoreImpl(sfi.loader.sthis, gateway.url, {
4250
+ const store = new FileStoreImpl(ctx.loader.sthis, gateway.url, {
3808
4251
  gateway: gateway.gateway,
3809
- gatewayInterceptor: sfi.gatewayInterceptor,
3810
- loader: sfi.loader
4252
+ gatewayInterceptor: uai.gatewayInterceptor,
4253
+ loader: ctx.loader
3811
4254
  });
3812
4255
  return store;
3813
4256
  }
3814
- async function WALStoreFactory(sfi) {
3815
- const storeUrl = sfi.url.build().setParam(PARAM.STORE, "wal").URI();
3816
- const rgateway = await getStartedGateway(sfi, storeUrl);
4257
+ async function metaStoreFactory(ctx, uai) {
4258
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "meta").URI();
4259
+ const rgateway = await getStartedGateway(ctx, storeUrl);
3817
4260
  if (rgateway.isErr()) {
3818
- throw sfi.loader.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
4261
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
3819
4262
  }
3820
4263
  const gateway = rgateway.Ok();
3821
- const store = new WALStoreImpl(sfi.loader.sthis, gateway.url, {
4264
+ const store = new MetaStoreImpl(ctx.loader.sthis, gateway.url, {
3822
4265
  gateway: gateway.gateway,
3823
- gatewayInterceptor: sfi.gatewayInterceptor,
3824
- loader: sfi.loader
4266
+ gatewayInterceptor: uai.gatewayInterceptor,
4267
+ loader: ctx.loader
3825
4268
  });
3826
4269
  return store;
3827
4270
  }
3828
- async function ensureStart(store) {
3829
- const ret = await store.start();
4271
+ async function WALStoreFactory(ctx, uai) {
4272
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "wal").URI();
4273
+ const rgateway = await getStartedGateway(ctx, storeUrl);
4274
+ if (rgateway.isErr()) {
4275
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
4276
+ }
4277
+ const gateway = rgateway.Ok();
4278
+ const store = new WALStoreImpl(ctx.loader.sthis, gateway.url, {
4279
+ gateway: gateway.gateway,
4280
+ gatewayInterceptor: uai.gatewayInterceptor,
4281
+ loader: ctx.loader
4282
+ });
4283
+ return store;
4284
+ }
4285
+ async function ensureStart(store, damaw) {
4286
+ const ret = await store.start(damaw);
3830
4287
  if (ret.isErr()) {
3831
4288
  throw store.logger.Error().Result("start", ret).Msg("start failed").AsError();
3832
4289
  }
@@ -3842,194 +4299,35 @@ function ensureStoreEnDeFile(ende) {
3842
4299
  }
3843
4300
  function toStoreRuntime(sthis, endeOpts = {}) {
3844
4301
  return {
3845
- makeMetaStore: async (sfi) => ensureStart(await metaStoreFactory(sfi)),
3846
- // async (loader: Loadable) => {
3847
- // logger
3848
- // .Debug()
3849
- // .Str("fromOpts", "" + !!endeOpts.func?.makeMetaStore)
3850
- // .Msg("makeMetaStore");
3851
- // return ensureStart(await (endeOpts.func?.makeMetaStore || metaStoreFactory)(loader), logger);
3852
- // },
3853
- makeDataStore: async (sfi) => ensureStart(await dataStoreFactory(sfi)),
3854
- // async (loader: Loadable) => {
3855
- // logger
3856
- // .Debug()
3857
- // .Str("fromOpts", "" + !!endeOpts.func?.makeDataStore)
3858
- // .Msg("makeDataStore");
3859
- // return ensureStart(await (endeOpts.func?.makeDataStore || dataStoreFactory)(loader), logger);
3860
- // },
3861
- makeWALStore: async (sfi) => ensureStart(await WALStoreFactory(sfi)),
3862
- // async (loader: Loadable) => {
3863
- // logger
3864
- // .Debug()
3865
- // .Str("fromOpts", "" + !!endeOpts.func?.makeWALStore)
3866
- // .Msg("makeRemoteWAL");
3867
- // return ensureStart(await (endeOpts.func?.makeWALStore || remoteWalFactory)(loader), logger);
3868
- // },
4302
+ makeStores: async (sfi) => {
4303
+ const ctx = {
4304
+ loader: sfi.loader
4305
+ };
4306
+ const storeSet = {};
4307
+ storeSet.meta = await metaStoreFactory(ctx, sfi.byStore.meta);
4308
+ storeSet.car = await carStoreFactory(ctx, sfi.byStore.car);
4309
+ storeSet.file = await fileStoreFactory(ctx, sfi.byStore.file);
4310
+ if (sfi.byStore.wal) {
4311
+ storeSet.wal = await WALStoreFactory(ctx, sfi.byStore.wal);
4312
+ }
4313
+ await ensureStart(storeSet.meta, storeSet);
4314
+ await ensureStart(storeSet.car, storeSet);
4315
+ await ensureStart(storeSet.file, storeSet);
4316
+ if (storeSet.wal) {
4317
+ await ensureStart(storeSet.wal, storeSet);
4318
+ }
4319
+ return storeSet;
4320
+ },
4321
+ // makeMetaStore: async (sfi: StoreFactoryItem) => ensureStart(await metaStoreFactory(sfi)),
4322
+ // makeDataStore: async (sfi: StoreFactoryItem) => ensureStart(await dataStoreFactory(sfi)),
4323
+ // makeWALStore: async (sfi: StoreFactoryItem) => ensureStart(await WALStoreFactory(sfi)),
3869
4324
  ...ensureStoreEnDeFile(endeOpts)
3870
4325
  };
3871
4326
  }
3872
4327
 
3873
- // src/blockstore/connection-base.ts
3874
- import { exception2Result as exception2Result4, Future as Future3 } from "@adviser/cement";
3875
-
3876
- // src/blockstore/store-remote.ts
3877
- async function RemoteDataStore(sthis, url, opts) {
3878
- const ds = new DataStoreImpl(sthis, url, opts);
3879
- await ds.start();
3880
- return ds;
3881
- }
3882
- async function RemoteMetaStore(sthis, url, opts) {
3883
- const ms = new MetaStoreImpl(sthis, url, opts);
3884
- await ms.start();
3885
- return ms;
3886
- }
3887
-
3888
- // src/context.ts
3889
- var Context = class {
3890
- constructor() {
3891
- this.ctx = /* @__PURE__ */ new Map();
3892
- }
3893
- set(key, value) {
3894
- this.ctx.set(key, value);
3895
- }
3896
- get(key) {
3897
- return this.ctx.get(key);
3898
- }
3899
- delete(key) {
3900
- this.ctx.delete(key);
3901
- }
3902
- };
3903
-
3904
- // src/blockstore/connection-base.ts
3905
- function coerceLoader(ref) {
3906
- const refl = ref;
3907
- if (refl.loader) {
3908
- return refl.loader;
3909
- }
3910
- const refb = ref;
3911
- if (refb.blockstore) {
3912
- return coerceLoader(refb.blockstore);
3913
- }
3914
- return void 0;
3915
- }
3916
- var ConnectionBase = class {
3917
- constructor(url, logger) {
3918
- // loaded: Promise<void> = Promise.resolve();
3919
- this.context = new Context();
3920
- this._loaded = /* @__PURE__ */ new Set();
3921
- this._metaIsLoading = false;
3922
- this.logger = logger;
3923
- this.url = url;
3924
- }
3925
- loaded() {
3926
- const f = new Future3();
3927
- if (!this._metaIsLoading) {
3928
- f.resolve();
3929
- } else {
3930
- this._loaded.add(f);
3931
- }
3932
- return f;
3933
- }
3934
- async refresh() {
3935
- await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load();
3936
- await (await throwFalsy(this.loader).WALStore()).process();
3937
- }
3938
- async connect(refl) {
3939
- await this.connectMeta(refl);
3940
- await this.connectStorage(refl);
3941
- }
3942
- async connectMeta(refl) {
3943
- const loader = coerceLoader(refl);
3944
- if (!loader) throw this.logger.Error().Msg("connectMeta: loader is required").AsError();
3945
- this.loader = loader;
3946
- await this.onConnect();
3947
- const metaUrl = this.url.build().defParam(PARAM.STORE, "meta").URI();
3948
- const rgateway = await getStartedGateway({ loader }, metaUrl);
3949
- if (rgateway.isErr())
3950
- throw this.logger.Error().Result("err", rgateway).Url(metaUrl).Msg("connectMeta: gateway is required").AsError();
3951
- const dbName = metaUrl.getParam(PARAM.NAME);
3952
- if (!dbName) {
3953
- throw this.logger.Error().Url(metaUrl).Msg("connectMeta: dbName is required").AsError();
3954
- }
3955
- const gateway = rgateway.Ok();
3956
- const remote = await RemoteMetaStore(loader.sthis, metaUrl, {
3957
- gateway: gateway.gateway,
3958
- loader
3959
- });
3960
- this.loader.remoteMetaStore = remote;
3961
- this._metaIsLoading = true;
3962
- this.loader.ready().then(async () => {
3963
- return remote.load().then(async () => {
3964
- const res = await exception2Result4(async () => {
3965
- return await (await throwFalsy(this.loader).WALStore()).process();
3966
- });
3967
- this._metaIsLoading = false;
3968
- for (const f of this._loaded) {
3969
- if (res.isErr()) {
3970
- f.reject(res.Err());
3971
- } else {
3972
- f.resolve();
3973
- }
3974
- }
3975
- this._loaded.clear();
3976
- });
3977
- });
3978
- }
3979
- async connectStorage(refl) {
3980
- const loader = coerceLoader(refl);
3981
- if (!loader) throw this.logger.Error().Msg("connectStorage: loader is required").AsError();
3982
- this.loader = loader;
3983
- const dataUrl = this.url.build().defParam(PARAM.STORE, "data").URI();
3984
- const rgateway = await getStartedGateway({ loader }, dataUrl);
3985
- if (rgateway.isErr())
3986
- throw this.logger.Error().Result("err", rgateway).Url(dataUrl).Msg("connectStorage: gateway is required").AsError();
3987
- const name = dataUrl.getParam(PARAM.NAME);
3988
- if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage: name is required").AsError;
3989
- loader.remoteCarStore = await RemoteDataStore(loader.sthis, this.url, {
3990
- gateway: rgateway.Ok().gateway,
3991
- loader
3992
- });
3993
- loader.remoteFileStore = loader.remoteCarStore;
3994
- }
3995
- // move this stuff to connect
3996
- // async getDashboardURL(compact = true) {
3997
- // const baseUrl = 'https://dashboard.fireproof.storage/'
3998
- // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
3999
- // // if (compact) {
4000
- // // await this.compact()
4001
- // // }
4002
- // const currents = await this.loader?.metaStore?.load()
4003
- // if (!currents) throw new Error("Can't sync empty ledger: save data first")
4004
- // if (currents.length > 1)
4005
- // throw new Error("Can't sync ledger with split heads: make an update first")
4006
- // const current = currents[0]
4007
- // const params = {
4008
- // car: current.car.toString()
4009
- // }
4010
- // if (current.key) {
4011
- // // @ts-ignore
4012
- // params.key = current.key.toString()
4013
- // }
4014
- // // @ts-ignore
4015
- // if (this.name) {
4016
- // // @ts-ignore
4017
- // params.name = this.name
4018
- // }
4019
- // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
4020
- // console.log('Import to dashboard: ' + url.toString())
4021
- // return url
4022
- // }
4023
- // openDashboard() {
4024
- // void this.getDashboardURL().then(url => {
4025
- // if (url) window.open(url.toString(), '_blank')
4026
- // })
4027
- // }
4028
- };
4029
-
4030
4328
  // src/crdt-helpers.ts
4031
4329
  import { parse as parse2 } from "multiformats/link";
4032
- import { sha256 as hasher6 } from "multiformats/hashes/sha2";
4330
+ import { sha256 as hasher5 } from "multiformats/hashes/sha2";
4033
4331
  import * as codec2 from "@ipld/dag-cbor";
4034
4332
  import { put, get, entries, root } from "@fireproof/vendor/@web3-storage/pail/crdt";
4035
4333
  import { EventFetcher, vis } from "@fireproof/vendor/@web3-storage/pail/clock";
@@ -4047,6 +4345,34 @@ function toString(key, logger) {
4047
4345
  throw logger.Error().Msg("Invalid key type").AsError();
4048
4346
  }
4049
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
+ const typedObj = obj;
4358
+ const result = {};
4359
+ for (const key in typedObj) {
4360
+ if (Object.hasOwnProperty.call(typedObj, key)) {
4361
+ const value = typedObj[key];
4362
+ if (value === null || !Number.isNaN(value) && value !== void 0) {
4363
+ if (typeof value === "object" && !key.startsWith("_")) {
4364
+ const sanitized = sanitizeDocumentFields(value);
4365
+ result[key] = sanitized;
4366
+ } else {
4367
+ result[key] = value;
4368
+ }
4369
+ }
4370
+ }
4371
+ }
4372
+ return result;
4373
+ }
4374
+ return obj;
4375
+ }
4050
4376
  async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
4051
4377
  let result = null;
4052
4378
  if (updates.length > 1) {
@@ -4081,7 +4407,7 @@ async function writeDocContent(store, blocks, update, logger) {
4081
4407
  await processFiles(store, blocks, update.value, logger);
4082
4408
  value = { doc: update.value };
4083
4409
  }
4084
- const block = await encode({ value, hasher: hasher6, codec: codec2 });
4410
+ const block = await encode({ value, hasher: hasher5, codec: codec2 });
4085
4411
  blocks.putSync(block.cid, block.bytes);
4086
4412
  return block.cid;
4087
4413
  }
@@ -4136,7 +4462,8 @@ async function getValueFromCrdt(blocks, head, key, logger) {
4136
4462
  if (!head.length) throw logger.Debug().Msg("Getting from an empty ledger").AsError();
4137
4463
  const link = await get(blocks, head, key);
4138
4464
  if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
4139
- return await getValueFromLink(blocks, link, logger);
4465
+ const ret = await getValueFromLink(blocks, link, logger);
4466
+ return ret;
4140
4467
  }
4141
4468
  function readFiles(blocks, { doc }) {
4142
4469
  if (!doc) return;
@@ -4172,7 +4499,7 @@ function readFileset(blocks, files, isPublic = false) {
4172
4499
  async function getValueFromLink(blocks, link, logger) {
4173
4500
  const block = await blocks.get(link);
4174
4501
  if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
4175
- const { value } = await decode({ bytes: block.bytes, hasher: hasher6, codec: codec2 });
4502
+ const { value } = await decode({ bytes: block.bytes, hasher: hasher5, codec: codec2 });
4176
4503
  const cvalue = {
4177
4504
  ...value,
4178
4505
  cid: link
@@ -4234,6 +4561,9 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4234
4561
  const { key, value } = ops[i];
4235
4562
  if (!keys.has(key)) {
4236
4563
  const docValue = await getValueFromLink(blocks, value, logger);
4564
+ if (key === PARAM.GENESIS_CID) {
4565
+ continue;
4566
+ }
4237
4567
  updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
4238
4568
  limit--;
4239
4569
  keys.add(key);
@@ -4247,8 +4577,10 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4247
4577
  }
4248
4578
  async function* getAllEntries(blocks, head, logger) {
4249
4579
  for await (const [key, link] of entries(blocks, head)) {
4250
- const docValue = await getValueFromLink(blocks, link, logger);
4251
- yield { id: key, value: docValue.doc, del: docValue.del };
4580
+ if (key !== PARAM.GENESIS_CID) {
4581
+ const docValue = await getValueFromLink(blocks, link, logger);
4582
+ yield { id: key, value: docValue.doc, del: docValue.del };
4583
+ }
4252
4584
  }
4253
4585
  }
4254
4586
  async function* clockVis(blocks, head) {
@@ -4293,7 +4625,7 @@ async function doCompact(blockLog, head, logger) {
4293
4625
  async function getBlock(blocks, cidString) {
4294
4626
  const block = await blocks.get(parse2(cidString));
4295
4627
  if (!block) throw new Error(`Missing block ${cidString}`);
4296
- const { cid, value } = await decode({ bytes: block.bytes, codec: codec2, hasher: hasher6 });
4628
+ const { cid, value } = await decode({ bytes: block.bytes, codec: codec2, hasher: hasher5 });
4297
4629
  return new Block({ cid, value, bytes: block.bytes });
4298
4630
  }
4299
4631
 
@@ -4393,6 +4725,10 @@ var CRDTClockImpl = class {
4393
4725
  this.notifyWatchers(internalUpdates || []);
4394
4726
  }
4395
4727
  notifyWatchers(updates) {
4728
+ updates = updates.filter((update) => update.id !== PARAM.GENESIS_CID);
4729
+ if (!updates.length) {
4730
+ return;
4731
+ }
4396
4732
  this.emptyWatchers.forEach((fn) => fn());
4397
4733
  this.watchers.forEach((fn) => fn(updates || []));
4398
4734
  }
@@ -4472,7 +4808,7 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
4472
4808
  try {
4473
4809
  head = await advance(tblocks, head, cid);
4474
4810
  } catch (e) {
4475
- logger.Debug().Err(e).Msg("failed to advance head");
4811
+ logger.Error().Err(e).Msg("failed to advance head");
4476
4812
  continue;
4477
4813
  }
4478
4814
  }
@@ -4531,6 +4867,17 @@ var CRDTImpl = class {
4531
4867
  }
4532
4868
  async bulk(updates) {
4533
4869
  await this.ready();
4870
+ updates = updates.map((dupdate) => ({
4871
+ ...dupdate,
4872
+ value: sanitizeDocumentFields(dupdate.value)
4873
+ }));
4874
+ if (this.clock.head.length === 0) {
4875
+ const value = { id: PARAM.GENESIS_CID, value: { _id: PARAM.GENESIS_CID } };
4876
+ await this._bulk([value]);
4877
+ }
4878
+ return await this._bulk(updates);
4879
+ }
4880
+ async _bulk(updates) {
4534
4881
  const prevHead = [...this.clock.head];
4535
4882
  const done = await this.blockstore.transaction(async (blocks) => {
4536
4883
  const { head } = await applyBulkUpdateToCrdt(
@@ -4601,8 +4948,24 @@ var CRDTImpl = class {
4601
4948
  }
4602
4949
  };
4603
4950
 
4951
+ // src/context.ts
4952
+ var Context = class {
4953
+ constructor() {
4954
+ this.ctx = /* @__PURE__ */ new Map();
4955
+ }
4956
+ set(key, value) {
4957
+ this.ctx.set(key, value);
4958
+ }
4959
+ get(key) {
4960
+ return this.ctx.get(key);
4961
+ }
4962
+ delete(key) {
4963
+ this.ctx.delete(key);
4964
+ }
4965
+ };
4966
+
4604
4967
  // src/ledger.ts
4605
- var ledgers = new KeyedResolvOnce4();
4968
+ var ledgers = new KeyedResolvOnce6();
4606
4969
  function keyConfigOpts(sthis, name, opts) {
4607
4970
  return JSON.stringify(
4608
4971
  toSortedArray({
@@ -4645,6 +5008,9 @@ var LedgerShell = class {
4645
5008
  this.name = ref.name;
4646
5009
  ref.addShell(this);
4647
5010
  }
5011
+ attach(a) {
5012
+ return this.ref.attach(a);
5013
+ }
4648
5014
  get opts() {
4649
5015
  return this.ref.opts;
4650
5016
  }
@@ -4743,11 +5109,16 @@ var LedgerImpl = class {
4743
5109
  });
4744
5110
  return ret;
4745
5111
  }
5112
+ async attach(a) {
5113
+ await this.ready();
5114
+ return this.crdt.blockstore.loader.attach(a);
5115
+ }
4746
5116
  // readonly _asDb = new ResolveOnce<Database>();
4747
5117
  // asDB(): Database {
4748
5118
  // return this._asDb.once(() => new DatabaseImpl(this));
4749
5119
  // }
4750
5120
  subscribe(listener, updates) {
5121
+ this.ready();
4751
5122
  this.logger.Debug().Bool("updates", updates).Msg("subscribe");
4752
5123
  if (updates) {
4753
5124
  if (!this._listening) {
@@ -4789,26 +5160,6 @@ var LedgerImpl = class {
4789
5160
  }
4790
5161
  }
4791
5162
  };
4792
- function defaultURI2(sthis, name, curi, uri, store, ctx) {
4793
- ctx = ctx || {};
4794
- const ret = (curi ? URI13.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
4795
- if (!ret.hasParam(PARAM.NAME)) {
4796
- throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
4797
- }
4798
- if (ctx.idx) {
4799
- ret.defParam(PARAM.INDEX, "idx");
4800
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}-idx@`);
4801
- } else {
4802
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}@`);
4803
- }
4804
- if (store === "data") {
4805
- if (ctx.file) {
4806
- } else {
4807
- ret.defParam(PARAM.SUFFIX, ".car");
4808
- }
4809
- }
4810
- return ret.URI();
4811
- }
4812
5163
  function toStoreURIRuntime(sthis, name, sopts) {
4813
5164
  sopts = sopts || {};
4814
5165
  if (!sopts.base) {
@@ -4822,16 +5173,19 @@ function toStoreURIRuntime(sthis, name, sopts) {
4822
5173
  const base = URI13.from(sopts.base);
4823
5174
  return {
4824
5175
  idx: {
4825
- data: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { idx: true }),
4826
- file: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { file: true, idx: true }),
4827
- meta: defaultURI2(sthis, name, sopts.idx?.meta, base, "meta", { idx: true }),
4828
- wal: defaultURI2(sthis, name, sopts.idx?.wal, base, "wal", { idx: true })
5176
+ car: ensureURIDefaults(sthis, name, sopts.idx?.car ?? sopts.data?.car, base, "car", { idx: true }),
5177
+ file: ensureURIDefaults(sthis, name, sopts.idx?.file ?? sopts.idx?.car ?? sopts.data?.file ?? sopts.data?.car, base, "file", {
5178
+ file: true,
5179
+ idx: true
5180
+ }),
5181
+ meta: ensureURIDefaults(sthis, name, sopts.idx?.meta ?? sopts.data?.meta, base, "meta", { idx: true }),
5182
+ wal: ensureURIDefaults(sthis, name, sopts.idx?.wal ?? sopts.data?.wal, base, "wal", { idx: true })
4829
5183
  },
4830
5184
  data: {
4831
- data: defaultURI2(sthis, name, sopts.data?.data, base, "data"),
4832
- file: defaultURI2(sthis, name, sopts.data?.data, base, "data", { file: true }),
4833
- meta: defaultURI2(sthis, name, sopts.data?.meta, base, "meta"),
4834
- wal: defaultURI2(sthis, name, sopts.data?.wal, base, "wal")
5185
+ car: ensureURIDefaults(sthis, name, sopts.data?.car, base, "car"),
5186
+ file: ensureURIDefaults(sthis, name, sopts.data?.file ?? sopts.data?.car, base, "file", { file: true }),
5187
+ meta: ensureURIDefaults(sthis, name, sopts.data?.meta, base, "meta"),
5188
+ wal: ensureURIDefaults(sthis, name, sopts.data?.wal, base, "wal")
4835
5189
  }
4836
5190
  };
4837
5191
  }
@@ -4895,10 +5249,11 @@ __export(file_exports, {
4895
5249
 
4896
5250
  // src/version.ts
4897
5251
  var PACKAGE_VERSION = Object.keys({
4898
- "0.20.0-dev-preview-40": "xxxx"
5252
+ "0.20.0-dev-preview-50": "xxxx"
4899
5253
  })[0];
4900
5254
  export {
4901
5255
  CRDTImpl,
5256
+ DataAndMetaAndWalAndBaseStore,
4902
5257
  DatabaseImpl,
4903
5258
  Index,
4904
5259
  LedgerFactory,
@@ -4915,6 +5270,7 @@ export {
4915
5270
  ensureLogger,
4916
5271
  ensureSuperLog,
4917
5272
  ensureSuperThis,
5273
+ ensureURIDefaults,
4918
5274
  exceptionWrapper,
4919
5275
  falsyToUndef,
4920
5276
  fireproof,
@@ -4932,6 +5288,7 @@ export {
4932
5288
  onSuperThis,
4933
5289
  runtime_exports as rt,
4934
5290
  runtime_exports as runtime,
5291
+ storeType2DataMetaWal,
4935
5292
  throwFalsy,
4936
5293
  toSortedArray,
4937
5294
  toStoreURIRuntime