@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.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  CRDTImpl: () => CRDTImpl,
34
+ DataAndMetaAndWalAndBaseStore: () => DataAndMetaAndWalAndBaseStore,
34
35
  DatabaseImpl: () => DatabaseImpl,
35
36
  Index: () => Index,
36
37
  LedgerFactory: () => LedgerFactory,
@@ -47,6 +48,7 @@ __export(index_exports, {
47
48
  ensureLogger: () => ensureLogger,
48
49
  ensureSuperLog: () => ensureSuperLog,
49
50
  ensureSuperThis: () => ensureSuperThis,
51
+ ensureURIDefaults: () => ensureURIDefaults,
50
52
  exceptionWrapper: () => exceptionWrapper,
51
53
  falsyToUndef: () => falsyToUndef,
52
54
  fireproof: () => fireproof,
@@ -64,6 +66,7 @@ __export(index_exports, {
64
66
  onSuperThis: () => onSuperThis,
65
67
  rt: () => runtime_exports,
66
68
  runtime: () => runtime_exports,
69
+ storeType2DataMetaWal: () => storeType2DataMetaWal,
67
70
  throwFalsy: () => throwFalsy,
68
71
  toSortedArray: () => toSortedArray,
69
72
  toStoreURIRuntime: () => toStoreURIRuntime
@@ -100,8 +103,13 @@ var PARAM = {
100
103
  FRAG_LEN: "len",
101
104
  FRAG_HEAD: "headerSize",
102
105
  EXTRACTKEY: "extractKey",
103
- SELF_REFLECT: "selfReflect"
106
+ SELF_REFLECT: "selfReflect",
104
107
  // if no subscribe in Gateway see your own META updates
108
+ CAR_PARALLEL: "parallel",
109
+ CAR_CACHE_SIZE: "carCacheSize",
110
+ CAR_COMPACT_CACHE_SIZE: "carCompactCacheSize",
111
+ CAR_META_CACHE_SIZE: "carMetaCacheSize",
112
+ GENESIS_CID: "baembeiarootfireproofgenesisblockaaaafireproofgenesisblocka"
105
113
  // FS = "fs",
106
114
  };
107
115
  function throwFalsy(value) {
@@ -116,6 +124,18 @@ function falsyToUndef(value) {
116
124
  }
117
125
  return value;
118
126
  }
127
+ var DataAndMetaAndWalAndBaseStore = class {
128
+ constructor(dam) {
129
+ this.wal = dam.wal;
130
+ this.file = dam.file;
131
+ this.car = dam.car;
132
+ this.meta = dam.meta;
133
+ this.baseStores = [this.file, this.car, this.meta];
134
+ if (this.wal) {
135
+ this.baseStores.push(this.wal);
136
+ }
137
+ }
138
+ };
119
139
 
120
140
  // src/utils.ts
121
141
  var import_base58 = require("multiformats/bases/base58");
@@ -326,21 +346,25 @@ function ensureLogger(sthis, componentName, ctx) {
326
346
  return out;
327
347
  }
328
348
  function getStore(url, sthis, joiner) {
329
- const store = url.getParam(PARAM.STORE);
330
- switch (store) {
331
- case "data":
349
+ const fromUrl = url.getParam(PARAM.STORE);
350
+ let pathPart;
351
+ switch (fromUrl) {
352
+ case "car":
353
+ case "file":
354
+ pathPart = "data";
355
+ break;
332
356
  case "wal":
333
357
  case "meta":
358
+ pathPart = fromUrl;
334
359
  break;
335
360
  default:
336
361
  throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
337
- throw sthis.logger.Error().Url(url).Msg(`store not found`).AsError();
338
362
  }
339
- let name = store;
363
+ let name = pathPart;
340
364
  if (url.hasParam("index")) {
341
365
  name = joiner(url.getParam(PARAM.INDEX) || "idx", name);
342
366
  }
343
- return { store, name };
367
+ return { pathPart, fromUrl, name };
344
368
  }
345
369
  function getKey(url, logger) {
346
370
  const result = url.getParam(PARAM.KEY);
@@ -442,6 +466,35 @@ function makeName(fnString) {
442
466
  return found[1];
443
467
  }
444
468
  }
469
+ function storeType2DataMetaWal(store) {
470
+ switch (store) {
471
+ case "car":
472
+ case "file":
473
+ return "data";
474
+ case "meta":
475
+ case "wal":
476
+ return store;
477
+ default:
478
+ throw new Error(`unknown store ${store}`);
479
+ }
480
+ }
481
+ function ensureURIDefaults(sthis, name, curi, uri, store, ctx) {
482
+ ctx = ctx || {};
483
+ const ret = (curi ? import_cement.URI.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
484
+ if (!ret.hasParam(PARAM.NAME)) {
485
+ throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
486
+ }
487
+ if (ctx.idx) {
488
+ ret.defParam(PARAM.INDEX, "idx");
489
+ ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${storeType2DataMetaWal(store)}-idx@`);
490
+ } else {
491
+ ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${storeType2DataMetaWal(store)}@`);
492
+ }
493
+ if (store === "car") {
494
+ ret.defParam(PARAM.SUFFIX, ".car");
495
+ }
496
+ return ret.URI();
497
+ }
445
498
 
446
499
  // src/write-queue.ts
447
500
  var import_cement2 = require("@adviser/cement");
@@ -1123,8 +1176,10 @@ function getFileName(url, sthis) {
1123
1176
  const key = url.getParam("key");
1124
1177
  if (!key) throw sthis.logger.Error().Url(url).Msg(`key not found`).AsError();
1125
1178
  const res = (0, import_core.getStore)(url, sthis, (...a) => a.join("-"));
1126
- switch (res.store) {
1127
- case "data":
1179
+ switch (res.fromUrl) {
1180
+ case "file":
1181
+ return sthis.pathOps.join(res.name, key);
1182
+ case "car":
1128
1183
  return sthis.pathOps.join(res.name, key + ".car");
1129
1184
  case "wal":
1130
1185
  case "meta":
@@ -1272,8 +1327,13 @@ var MemoryGateway = class {
1272
1327
  close(baseUrl) {
1273
1328
  return Promise.resolve(import_cement6.Result.Ok(void 0));
1274
1329
  }
1275
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1276
1330
  destroy(baseUrl) {
1331
+ const keyUrl = baseUrl.toString();
1332
+ for (const key of this.memorys.keys()) {
1333
+ if (key.startsWith(keyUrl)) {
1334
+ this.memorys.delete(key);
1335
+ }
1336
+ }
1277
1337
  this.memorys.clear();
1278
1338
  return Promise.resolve(import_cement6.Result.Ok(void 0));
1279
1339
  }
@@ -1285,7 +1345,7 @@ var MemoryGateway = class {
1285
1345
  get(url) {
1286
1346
  const x = this.memorys.get(url.toString());
1287
1347
  if (!x) {
1288
- return Promise.resolve(import_cement6.Result.Err(new NotFoundError("not found")));
1348
+ return Promise.resolve(import_cement6.Result.Err(new NotFoundError(`not found: ${url.toString()}`)));
1289
1349
  }
1290
1350
  return Promise.resolve(import_cement6.Result.Ok(x));
1291
1351
  }
@@ -1391,9 +1451,6 @@ async function decode2DbMetaEvents(sthis, rserializedMeta) {
1391
1451
  if (!Array.isArray(serializedMeta)) {
1392
1452
  return sthis.logger.Debug().Any("metaEntries", serializedMeta).Msg("No data in MetaEntries").ResultError();
1393
1453
  }
1394
- if (!serializedMeta.length) {
1395
- return sthis.logger.Debug().Msg("No MetaEntries found").ResultError();
1396
- }
1397
1454
  return import_cement8.Result.Ok(
1398
1455
  await Promise.all(
1399
1456
  serializedMeta.map(async (metaEntry) => {
@@ -1458,10 +1515,9 @@ async function fpDeserialize(sthis, url, intoRaw, pdecoder) {
1458
1515
  ...pdecoder
1459
1516
  };
1460
1517
  switch (url.getParam(PARAM.STORE)) {
1461
- case "data":
1462
- if (url.getParam(PARAM.SUFFIX) === ".car") {
1463
- return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1464
- }
1518
+ case "car":
1519
+ return makeFPEnvelope(FPEnvelopeTypes.CAR, await decoder.car(sthis, raw2));
1520
+ case "file":
1465
1521
  return makeFPEnvelope(FPEnvelopeTypes.FILE, await decoder.file(sthis, raw2));
1466
1522
  case "wal":
1467
1523
  return makeFPEnvelope(FPEnvelopeTypes.WAL, await decode2WalState(sthis, await decoder.wal(sthis, raw2)));
@@ -1523,7 +1579,7 @@ var DefSerdeGateway = class {
1523
1579
  const urlWithoutKey = url.build().delParam(PARAM.KEY).delParam(PARAM.SELF_REFLECT).toString();
1524
1580
  this.subscribeFn.set(urlWithoutKey, rawCallback);
1525
1581
  return import_cement9.Result.Ok(() => {
1526
- this.subscribeFn.delete(url.toString());
1582
+ this.subscribeFn.delete(urlWithoutKey);
1527
1583
  });
1528
1584
  }
1529
1585
  const unreg = await this.gw.subscribe(url, rawCallback, sthis);
@@ -1652,37 +1708,49 @@ var Block = import_block.Block;
1652
1708
  async function decode({
1653
1709
  bytes,
1654
1710
  codec: codec3,
1655
- hasher: hasher7
1711
+ hasher: hasher6
1656
1712
  }) {
1657
1713
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1658
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1714
+ if (codec3 == null || hasher6 == null) throw new Error("Missing required argument: codec or hasher");
1659
1715
  const value = await Promise.resolve(codec3.decode(bytes));
1660
- const hash = await hasher7.digest(bytes);
1716
+ let toHash = bytes;
1717
+ if (codec3.valueToHashBytes) {
1718
+ toHash = await Promise.resolve(codec3.valueToHashBytes(value));
1719
+ }
1720
+ const hash = await hasher6.digest(toHash);
1661
1721
  const cid = import_multiformats2.CID.create(1, codec3.code, hash);
1662
- return new import_block.Block({ value, bytes, cid });
1722
+ return new import_block.Block({ value, bytes: toHash, cid });
1663
1723
  }
1664
1724
  async function encode({
1665
1725
  value,
1666
1726
  codec: codec3,
1667
- hasher: hasher7
1727
+ hasher: hasher6
1668
1728
  }) {
1669
1729
  if (typeof value === "undefined") throw new Error('Missing required argument "value"');
1670
- if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
1671
- const bytes = await Promise.resolve(codec3.encode(value));
1672
- const hash = await hasher7.digest(bytes);
1730
+ if (codec3 == null || hasher6 == null) throw new Error("Missing required argument: codec or hasher");
1731
+ let bytes;
1732
+ let hash;
1733
+ if (codec3.bytesToHash) {
1734
+ const hashable = await Promise.resolve(codec3.bytesToHash(value));
1735
+ hash = await hasher6.digest(hashable);
1736
+ bytes = await Promise.resolve(codec3.encode(value));
1737
+ } else {
1738
+ bytes = await Promise.resolve(codec3.encode(value));
1739
+ hash = await hasher6.digest(bytes);
1740
+ }
1673
1741
  const cid = import_multiformats2.CID.create(1, codec3.code, hash);
1674
- return new import_block.Block({ value, bytes, cid });
1742
+ return new Block({ value, bytes, cid });
1675
1743
  }
1676
1744
  async function create({
1677
1745
  bytes,
1678
1746
  cid,
1679
- hasher: hasher7,
1747
+ hasher: hasher6,
1680
1748
  codec: codec3
1681
1749
  }) {
1682
1750
  if (bytes == null) throw new Error('Missing required argument "bytes"');
1683
- if (hasher7 == null) throw new Error('Missing required argument "hasher"');
1751
+ if (hasher6 == null) throw new Error('Missing required argument "hasher"');
1684
1752
  const value = await Promise.resolve(codec3.decode(bytes));
1685
- const hash = await hasher7.digest(bytes);
1753
+ const hash = await hasher6.digest(bytes);
1686
1754
  if (!import_multiformats2.bytes.equals(cid.multihash.bytes, hash.bytes)) {
1687
1755
  throw new Error("CID hash does not match bytes");
1688
1756
  }
@@ -2080,6 +2148,9 @@ var DatabaseImpl = class {
2080
2148
  this.id = ledger.id;
2081
2149
  this.logger = ensureLogger(this.sthis, "Database");
2082
2150
  }
2151
+ attach(a) {
2152
+ return this.ledger.attach(a);
2153
+ }
2083
2154
  get name() {
2084
2155
  return this.ledger.name;
2085
2156
  }
@@ -2175,19 +2246,27 @@ var import_cement19 = require("@adviser/cement");
2175
2246
  // src/blockstore/index.ts
2176
2247
  var blockstore_exports = {};
2177
2248
  __export(blockstore_exports, {
2249
+ AttachedRemotesImpl: () => AttachedRemotesImpl,
2250
+ BaseActiveStore: () => BaseActiveStore,
2178
2251
  BaseBlockstoreImpl: () => BaseBlockstoreImpl,
2179
2252
  Car2FPMsg: () => Car2FPMsg,
2253
+ CarActiveStore: () => CarActiveStore,
2254
+ CarLog: () => CarLog,
2180
2255
  CarTransactionImpl: () => CarTransactionImpl,
2181
2256
  CompactionFetcher: () => CompactionFetcher,
2182
- ConnectionBase: () => ConnectionBase,
2183
2257
  DbMetaEventEqual: () => DbMetaEventEqual,
2184
2258
  DbMetaEventsEqual: () => DbMetaEventsEqual,
2185
2259
  EncryptedBlockstore: () => EncryptedBlockstore,
2186
2260
  FPEnvelopeTypes: () => FPEnvelopeTypes,
2187
2261
  File2FPMsg: () => File2FPMsg,
2262
+ FileActiveStore: () => FileActiveStore,
2188
2263
  InterceptorGateway: () => InterceptorGateway,
2189
2264
  Loader: () => Loader,
2265
+ MetaActiveStore: () => MetaActiveStore,
2190
2266
  PassThroughGateway: () => PassThroughGateway,
2267
+ TaskManager: () => TaskManager,
2268
+ WALActiveStore: () => WALActiveStore,
2269
+ createAttachedStores: () => createAttachedStores,
2191
2270
  createDbMetaEvent: () => createDbMetaEvent,
2192
2271
  defaultGatewayFactoryItem: () => defaultGatewayFactoryItem,
2193
2272
  ensureStoreEnDeFile: () => ensureStoreEnDeFile,
@@ -2201,6 +2280,37 @@ __export(blockstore_exports, {
2201
2280
  });
2202
2281
 
2203
2282
  // src/blockstore/types.ts
2283
+ var CarLog = class {
2284
+ constructor() {
2285
+ this._logs = [];
2286
+ }
2287
+ get length() {
2288
+ return this._logs.length;
2289
+ }
2290
+ last() {
2291
+ const x = [...this._logs[this._logs.length - 1]];
2292
+ Object.freeze(x);
2293
+ return x;
2294
+ }
2295
+ xunshift(logs) {
2296
+ this._logs.unshift(logs);
2297
+ }
2298
+ update(logs) {
2299
+ this._logs.length = 0;
2300
+ this._logs.push(...logs);
2301
+ }
2302
+ asArray() {
2303
+ const a = [
2304
+ ...this._logs.map((l) => {
2305
+ const x = [...l];
2306
+ Object.freeze(x);
2307
+ return x;
2308
+ })
2309
+ ];
2310
+ Object.freeze(a);
2311
+ return a;
2312
+ }
2313
+ };
2204
2314
  function toCIDBlock(block) {
2205
2315
  return block;
2206
2316
  }
@@ -2210,24 +2320,34 @@ function DbMetaEventEqual(a, b) {
2210
2320
  function DbMetaEventsEqual(a, b) {
2211
2321
  return a.length === b.length && a.every((e, i) => DbMetaEventEqual(e, b[i]));
2212
2322
  }
2323
+ var BaseActiveStore = class {
2324
+ };
2325
+ var CarActiveStore = class extends BaseActiveStore {
2326
+ };
2327
+ var FileActiveStore = class extends BaseActiveStore {
2328
+ };
2329
+ var MetaActiveStore = class extends BaseActiveStore {
2330
+ };
2331
+ var WALActiveStore = class extends BaseActiveStore {
2332
+ };
2213
2333
 
2214
2334
  // src/blockstore/store-factory.ts
2215
- var import_cement16 = require("@adviser/cement");
2335
+ var import_cement17 = require("@adviser/cement");
2216
2336
 
2217
2337
  // src/blockstore/store.ts
2218
- var import_cement15 = require("@adviser/cement");
2338
+ var import_cement16 = require("@adviser/cement");
2219
2339
 
2220
2340
  // src/blockstore/loader.ts
2221
2341
  var import_p_limit = __toESM(require("p-limit"), 1);
2222
2342
  var import_reader = require("@ipld/car/reader");
2223
- var import_cement13 = require("@adviser/cement");
2343
+ var import_cement14 = require("@adviser/cement");
2224
2344
 
2225
2345
  // src/blockstore/loader-helpers.ts
2226
2346
  var import_sha22 = require("multiformats/hashes/sha2");
2227
2347
  var dagCodec = __toESM(require("@ipld/dag-cbor"), 1);
2228
2348
  async function parseCarFile(reader, logger) {
2229
- const roots = await reader.getRoots();
2230
- const header = await reader.get(roots[0]);
2349
+ const roots = await reader.roots;
2350
+ const header = reader.blocks.find((i) => i.cid.equals(roots[0]));
2231
2351
  if (!header) throw logger.Error().Msg("missing header block").AsError();
2232
2352
  const dec = await decode({ bytes: header.bytes, hasher: import_sha22.sha256, codec: dagCodec });
2233
2353
  const fpvalue = dec.value;
@@ -2242,6 +2362,7 @@ var import_block4 = require("@fireproof/vendor/@web3-storage/pail/block");
2242
2362
  var import_cement11 = require("@adviser/cement");
2243
2363
  var CarTransactionImpl = class {
2244
2364
  #memblock = new import_block4.MemoryBlockstore();
2365
+ #hackUnshift;
2245
2366
  constructor(parent, opts = { add: true, noLoader: false }) {
2246
2367
  if (opts.add) {
2247
2368
  parent.transactions.add(this);
@@ -2249,7 +2370,7 @@ var CarTransactionImpl = class {
2249
2370
  this.parent = parent;
2250
2371
  }
2251
2372
  async get(cid) {
2252
- return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
2373
+ return await this.superGet(cid) ?? falsyToUndef(await this.parent.get(cid));
2253
2374
  }
2254
2375
  async superGet(cid) {
2255
2376
  return this.#memblock.get(cid);
@@ -2260,7 +2381,16 @@ var CarTransactionImpl = class {
2260
2381
  putSync(cid, bytes) {
2261
2382
  this.#memblock.putSync(cid, bytes);
2262
2383
  }
2384
+ unshift(cid, bytes) {
2385
+ if (this.#hackUnshift) {
2386
+ throw new Error("unshift already called");
2387
+ }
2388
+ this.#hackUnshift = { cid, bytes };
2389
+ }
2263
2390
  async *entries() {
2391
+ if (this.#hackUnshift) {
2392
+ yield this.#hackUnshift;
2393
+ }
2264
2394
  for await (const blk of this.#memblock.entries()) {
2265
2395
  yield blk;
2266
2396
  }
@@ -2286,6 +2416,11 @@ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
2286
2416
  keyBag: opts.keyBag || {},
2287
2417
  crypto: (0, import_cement11.toCryptoRuntime)(opts.crypto),
2288
2418
  storeUrls: opts.storeUrls,
2419
+ taskManager: {
2420
+ removeAfter: 3,
2421
+ retryTimeout: 50,
2422
+ ...opts.taskManager
2423
+ },
2289
2424
  // storeEnDeFile: ensureStoreEnDeFile(opts.storeEnDeFile),
2290
2425
  // store,
2291
2426
  storeRuntime: toStoreRuntime(sthis, ensureStoreEnDeFile(opts.storeEnDeFile))
@@ -2378,10 +2513,8 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2378
2513
  async get(cid) {
2379
2514
  const got = await super.get(cid);
2380
2515
  if (got) return got;
2381
- if (!this.loader) {
2382
- return;
2383
- }
2384
- return falsyToUndef(await this.loader.getBlock(cid));
2516
+ const ret = falsyToUndef(await this.loader.getBlock(cid, this.loader.attachedStores.local()));
2517
+ return ret;
2385
2518
  }
2386
2519
  async transaction(fn, opts = { noLoader: false }) {
2387
2520
  this.logger.Debug().Msg("enter transaction");
@@ -2401,11 +2534,8 @@ var EncryptedBlockstore = class extends BaseBlockstoreImpl {
2401
2534
  async getFile(car, cid) {
2402
2535
  await this.ready();
2403
2536
  if (!this.loader) throw this.logger.Error().Msg("loader required to get file, ledger must be named").AsError();
2404
- const reader = await this.loader.loadFileCar(
2405
- car
2406
- /*, isPublic */
2407
- );
2408
- const block = await reader.get(cid);
2537
+ const reader = await this.loader.loadFileCar(car, this.loader.attachedStores.local());
2538
+ const block = await reader.blocks.find((i) => i.cid.equals(cid));
2409
2539
  if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
2410
2540
  return block.bytes;
2411
2541
  }
@@ -2512,8 +2642,8 @@ var CommitQueue = class {
2512
2642
 
2513
2643
  // src/blockstore/commitor.ts
2514
2644
  var CBW = __toESM(require("@ipld/car/buffer-writer"), 1);
2515
- var import_sha23 = require("multiformats/hashes/sha2");
2516
2645
  var dagCodec2 = __toESM(require("@ipld/dag-cbor"), 1);
2646
+ var import_sha23 = require("multiformats/hashes/sha2");
2517
2647
  async function encodeCarFile(roots, t, codec3) {
2518
2648
  let size = 0;
2519
2649
  const headerSize = CBW.headerLength({ roots });
@@ -2538,7 +2668,7 @@ async function createCarFile(encoder, cid, t) {
2538
2668
  async function commitFiles(fileStore, walStore, t, done) {
2539
2669
  const { files: roots } = makeFileCarHeader(done);
2540
2670
  const cids = [];
2541
- const codec3 = (await fileStore.keyedCrypto()).codec();
2671
+ const codec3 = await fileStore.keyedCrypto().then((i) => i.codec());
2542
2672
  const cars = await prepareCarFilesFiles(codec3, roots, t);
2543
2673
  for (const car of cars) {
2544
2674
  const { cid, bytes } = car;
@@ -2564,7 +2694,7 @@ async function prepareCarFilesFiles(encoder, roots, t) {
2564
2694
  return [await encodeCarFile(roots, t, encoder)];
2565
2695
  }
2566
2696
  function makeCarHeader(meta, cars, compact = false) {
2567
- const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
2697
+ const coreHeader = compact ? { cars: [], compact: cars.asArray() } : { cars: cars.asArray(), compact: [] };
2568
2698
  return { ...coreHeader, meta };
2569
2699
  }
2570
2700
  async function encodeCarHeader(fp) {
@@ -2591,7 +2721,7 @@ async function commit(params, t, done, opts = { noLoader: false, compact: false
2591
2721
  }
2592
2722
  async function prepareCarFiles(encoder, threshold, rootBlock, t) {
2593
2723
  const carFiles = [];
2594
- threshold = threshold || 128e3 * 8;
2724
+ threshold = threshold || 16 * 65536;
2595
2725
  let clonedt = new CarTransactionImpl(t.parent, { add: false, noLoader: false });
2596
2726
  clonedt.putSync(rootBlock.cid, rootBlock.bytes);
2597
2727
  let newsize = CBW.blockLength(toCIDBlock(rootBlock));
@@ -2617,19 +2747,20 @@ var import_sha24 = require("multiformats/hashes/sha2");
2617
2747
 
2618
2748
  // src/blockstore/task-manager.ts
2619
2749
  var TaskManager = class {
2620
- constructor(sthis, callback) {
2750
+ constructor(sthis, callback, params) {
2621
2751
  // we need to remove the events after some time
2622
2752
  this.eventsWeHandled = /* @__PURE__ */ new Set();
2623
2753
  this.queue = [];
2624
2754
  this.isProcessing = false;
2625
2755
  this.logger = ensureLogger(sthis, "TaskManager");
2626
2756
  this.callback = callback;
2757
+ this.params = params;
2627
2758
  }
2628
- async handleEvent(cid, parents, dbMeta) {
2759
+ async handleEvent(cid, parents, dbMeta, store) {
2629
2760
  for (const parent of parents) {
2630
2761
  this.eventsWeHandled.add(parent.toString());
2631
2762
  }
2632
- this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
2763
+ this.queue.push({ cid: cid.toString(), dbMeta, retries: 0, store });
2633
2764
  this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
2634
2765
  void this.processQueue();
2635
2766
  }
@@ -2643,7 +2774,7 @@ var TaskManager = class {
2643
2774
  return;
2644
2775
  }
2645
2776
  try {
2646
- await this.callback(first.dbMeta);
2777
+ await this.callback(first.dbMeta, first.store);
2647
2778
  this.eventsWeHandled.add(first.cid);
2648
2779
  this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
2649
2780
  } catch (err) {
@@ -2652,7 +2783,7 @@ var TaskManager = class {
2652
2783
  this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
2653
2784
  }
2654
2785
  await new Promise((resolve) => setTimeout(resolve, 50));
2655
- throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
2786
+ this.logger.Warn().Err(err).Msg("retry to process event block");
2656
2787
  } finally {
2657
2788
  this.isProcessing = false;
2658
2789
  if (this.queue.length > 0) {
@@ -2662,13 +2793,299 @@ var TaskManager = class {
2662
2793
  }
2663
2794
  };
2664
2795
 
2796
+ // src/blockstore/attachable-store.ts
2797
+ var import_cement13 = require("@adviser/cement");
2798
+ var AttachedImpl = class {
2799
+ constructor(gws, stores, unreg) {
2800
+ this.gatewayUrls = gws;
2801
+ this.stores = new DataAndMetaAndWalAndBaseStore(stores);
2802
+ this.unreg = unreg;
2803
+ }
2804
+ async detach() {
2805
+ const toClose = [this.stores.car.close(), this.stores.file.close(), this.stores.meta.close()];
2806
+ if (this.stores.wal) {
2807
+ toClose.push(this.stores.wal.close());
2808
+ }
2809
+ await Promise.all(toClose);
2810
+ this.unreg();
2811
+ }
2812
+ status() {
2813
+ return "attached";
2814
+ }
2815
+ };
2816
+ var FileActiveStoreImpl = class extends FileActiveStore {
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.xattached.remotes();
2828
+ }
2829
+ };
2830
+ var CarActiveStoreImpl = class extends CarActiveStore {
2831
+ constructor(ref, active, attached) {
2832
+ super();
2833
+ this.ref = ref;
2834
+ this.active = active;
2835
+ this.xattached = attached;
2836
+ }
2837
+ local() {
2838
+ return this.xattached.local();
2839
+ }
2840
+ remotes() {
2841
+ return [this.active, ...this.xattached.remotes().filter((i) => i !== this.active)];
2842
+ }
2843
+ };
2844
+ var CarAttachedStoresImpl = class {
2845
+ constructor(attached) {
2846
+ this.attached = attached;
2847
+ }
2848
+ local() {
2849
+ return this.attached.local().active.car;
2850
+ }
2851
+ remotes() {
2852
+ return this.attached.remotes().map(({ active }) => active.car);
2853
+ }
2854
+ };
2855
+ var FileAttachedStoresImpl = class {
2856
+ constructor(attached) {
2857
+ this.attached = attached;
2858
+ }
2859
+ local() {
2860
+ return this.attached.local().active.file;
2861
+ }
2862
+ remotes() {
2863
+ return this.attached.remotes().map(({ active }) => active.file);
2864
+ }
2865
+ };
2866
+ var MetaActiveStoreImpl = class extends MetaActiveStore {
2867
+ constructor(ref, active, attached) {
2868
+ super();
2869
+ this.ref = ref;
2870
+ this.active = active;
2871
+ this.xattached = attached;
2872
+ }
2873
+ local() {
2874
+ return this.xattached.local();
2875
+ }
2876
+ remotes() {
2877
+ return [this.active, ...this.xattached.remotes().filter((i) => i !== this.active)];
2878
+ }
2879
+ };
2880
+ var MetaAttachedStoresImpl = class {
2881
+ constructor(attached) {
2882
+ this.attached = attached;
2883
+ }
2884
+ local() {
2885
+ return this.attached.local().active.meta;
2886
+ }
2887
+ remotes() {
2888
+ return this.attached.remotes().map(({ active }) => active.meta);
2889
+ }
2890
+ };
2891
+ var WALActiveStoreImpl = class extends WALActiveStore {
2892
+ constructor(ref, active, attached) {
2893
+ super();
2894
+ this.ref = ref;
2895
+ this.active = active;
2896
+ this.xattached = attached;
2897
+ }
2898
+ local() {
2899
+ return this.xattached.local();
2900
+ }
2901
+ remotes() {
2902
+ return this.xattached.remotes();
2903
+ }
2904
+ };
2905
+ var WALAttachedStoresImpl = class {
2906
+ constructor(attached) {
2907
+ this.attached = attached;
2908
+ }
2909
+ local() {
2910
+ return this.attached.local().active.wal;
2911
+ }
2912
+ remotes() {
2913
+ return this.attached.remotes().filter(({ active }) => active.wal).map(({ active }) => active.wal);
2914
+ }
2915
+ };
2916
+ var ActiveStoreImpl = class {
2917
+ constructor(active, attached) {
2918
+ this.active = active;
2919
+ this.xattached = attached;
2920
+ }
2921
+ local() {
2922
+ return this.xattached.local();
2923
+ }
2924
+ remotes() {
2925
+ return this.xattached.remotes();
2926
+ }
2927
+ baseStores() {
2928
+ const bs = [this.active.car, this.active.file, this.active.meta];
2929
+ if (this.active.wal) {
2930
+ bs.push(this.active.wal);
2931
+ }
2932
+ return bs;
2933
+ }
2934
+ carStore() {
2935
+ return new CarActiveStoreImpl(this, this.active.car, new CarAttachedStoresImpl(this.xattached));
2936
+ }
2937
+ fileStore() {
2938
+ return new FileActiveStoreImpl(this, this.active.file, new FileAttachedStoresImpl(this.xattached));
2939
+ }
2940
+ metaStore() {
2941
+ return new MetaActiveStoreImpl(this, this.active.meta, new MetaAttachedStoresImpl(this.xattached));
2942
+ }
2943
+ walStore() {
2944
+ if (!this.active.wal) {
2945
+ throw this.xattached.loadable.sthis.logger.Error().Msg("wal store not set").AsError();
2946
+ }
2947
+ return new WALActiveStoreImpl(this, this.active.wal, new WALAttachedStoresImpl(this.xattached));
2948
+ }
2949
+ };
2950
+ function isLoadable(unknown) {
2951
+ return !!unknown.sthis && !!unknown.attachedStores;
2952
+ }
2953
+ async function createAttachedStores(urlOrGup, arOrLoadable, name = "local") {
2954
+ let ar;
2955
+ if (!isLoadable(arOrLoadable)) {
2956
+ ar = arOrLoadable;
2957
+ } else {
2958
+ ar = arOrLoadable.attachedStores;
2959
+ }
2960
+ let gup;
2961
+ if (!urlOrGup) {
2962
+ throw new Error("urlOrGup is required");
2963
+ }
2964
+ if ((0, import_cement13.isCoerceURI)(urlOrGup)) {
2965
+ const url = urlOrGup;
2966
+ gup = {
2967
+ car: { url },
2968
+ file: { url },
2969
+ meta: { url },
2970
+ wal: { url }
2971
+ };
2972
+ } else {
2973
+ gup = urlOrGup;
2974
+ }
2975
+ return await ar.attach({
2976
+ name,
2977
+ prepare: async () => gup
2978
+ });
2979
+ }
2980
+ var AttachedRemotesImpl = class {
2981
+ constructor(loadable) {
2982
+ this._remotes = new import_cement13.KeyedResolvOnce();
2983
+ this.loadable = loadable;
2984
+ }
2985
+ forRemotes(action) {
2986
+ return Promise.all(this.remotes().map((i) => action(i))).then(() => void 0);
2987
+ }
2988
+ remotes() {
2989
+ return this._remotes.values().filter(({ value }) => value.isOk() && !value.Ok().stores.wal).map(({ value }) => value.Ok().stores).map((i) => this.activate(i));
2990
+ }
2991
+ local() {
2992
+ if (!this._local) {
2993
+ throw this.loadable.sthis.logger.Error().Msg("local store not set").AsError();
2994
+ }
2995
+ return new ActiveStoreImpl(this._local.stores, this);
2996
+ }
2997
+ activate(store) {
2998
+ if ((0, import_cement13.isCoerceURI)(store)) {
2999
+ const activateUrl = import_cement13.URI.from(store);
3000
+ let maxScore = 0;
3001
+ let maxStore;
3002
+ for (const { value } of this._remotes.values()) {
3003
+ if (value.isErr()) {
3004
+ continue;
3005
+ }
3006
+ for (const url of value.Ok().stores.baseStores.map((i) => i.url())) {
3007
+ const mr = url.match(activateUrl);
3008
+ if (mr.score > maxScore) {
3009
+ maxScore = mr.score;
3010
+ maxStore = value.Ok().stores;
3011
+ }
3012
+ }
3013
+ }
3014
+ if (!maxStore) {
3015
+ throw this.loadable.sthis.logger.Error().Url(activateUrl).Msg("no store found").AsError();
3016
+ }
3017
+ store = maxStore;
3018
+ }
3019
+ return new ActiveStoreImpl(store, this);
3020
+ }
3021
+ async detach() {
3022
+ await Promise.all(
3023
+ this._remotes.values().map(async ({ value: rvalue }) => {
3024
+ if (rvalue.isOk()) {
3025
+ await rvalue.Ok().detach();
3026
+ }
3027
+ })
3028
+ );
3029
+ }
3030
+ async attach(attached) {
3031
+ const gwp = await attached.prepare();
3032
+ const gws = {
3033
+ car: {
3034
+ ...gwp.car,
3035
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, gwp.car.url, import_cement13.URI.from(gwp.car.url), "car")
3036
+ },
3037
+ file: {
3038
+ ...gwp.file,
3039
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, import_cement13.URI.from(gwp.file.url), "file", { file: true })
3040
+ },
3041
+ meta: {
3042
+ ...gwp.meta,
3043
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, import_cement13.URI.from(gwp.meta.url), "meta")
3044
+ },
3045
+ wal: gwp.wal ? {
3046
+ ...gwp.wal,
3047
+ url: ensureURIDefaults(this.loadable.sthis, attached.name, void 0, import_cement13.URI.from(gwp.wal.url), "wal")
3048
+ } : void 0
3049
+ };
3050
+ const key = JSON.stringify(
3051
+ toSortedArray({
3052
+ carUrl: gws.car.url.toString(),
3053
+ filesUrl: gws.file.url.toString(),
3054
+ metaUrl: gws.meta.url.toString(),
3055
+ walUrl: gws.wal?.url.toString()
3056
+ })
3057
+ );
3058
+ return this._remotes.get(key).once(async () => {
3059
+ const rt = toStoreRuntime(this.loadable.sthis);
3060
+ const result = new AttachedImpl(
3061
+ gws,
3062
+ await rt.makeStores({
3063
+ byStore: gws,
3064
+ loader: this.loadable
3065
+ }),
3066
+ () => {
3067
+ this._remotes.unget(key);
3068
+ }
3069
+ );
3070
+ if (result.stores.wal) {
3071
+ if (this._local) {
3072
+ throw this.loadable.sthis.logger.Error().Msg("local store could only set once").AsError();
3073
+ }
3074
+ this._local = result;
3075
+ }
3076
+ return result;
3077
+ });
3078
+ }
3079
+ };
3080
+
2665
3081
  // src/blockstore/loader.ts
2666
3082
  function carLogIncludesGroup(list, cids) {
2667
- return list.some((arr) => {
2668
- return arr.toString() === cids.toString();
2669
- });
3083
+ const cidSet = cids.map((cid) => cid.toString()).sort().join(",");
3084
+ return list.some(
3085
+ (arr) => cidSet === arr.map((cid) => cid.toString()).sort().join(",")
3086
+ );
2670
3087
  }
2671
- function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
3088
+ function uniqueCids(list, remove = new import_cement14.LRUSet()) {
2672
3089
  const byString = /* @__PURE__ */ new Map();
2673
3090
  for (const cid of list) {
2674
3091
  if (remove.has(cid.toString())) continue;
@@ -2680,18 +3097,9 @@ var Loader = class {
2680
3097
  constructor(sthis, ebOpts) {
2681
3098
  this.commitQueue = new CommitQueue();
2682
3099
  this.isCompacting = false;
2683
- this.carReaders = /* @__PURE__ */ new Map();
2684
- this.seenCompacted = /* @__PURE__ */ new Set();
2685
- this.processedCars = /* @__PURE__ */ new Set();
2686
- this.carLog = [];
2687
- this.getBlockCache = /* @__PURE__ */ new Map();
2688
- this.seenMeta = /* @__PURE__ */ new Set();
2689
- this.writeLimit = (0, import_p_limit.default)(1);
2690
- this._carStore = new import_cement13.ResolveOnce();
2691
- this._fileStore = new import_cement13.ResolveOnce();
2692
- this._WALStore = new import_cement13.ResolveOnce();
2693
- this._metaStore = new import_cement13.ResolveOnce();
2694
- this.onceReady = new import_cement13.ResolveOnce();
3100
+ this.maxConcurrentWrite = (0, import_p_limit.default)(1);
3101
+ this.carLog = new CarLog();
3102
+ this.onceReady = new import_cement14.ResolveOnce();
2695
3103
  this.sthis = sthis;
2696
3104
  this.ebOpts = defaultedBlockstoreRuntime(
2697
3105
  sthis,
@@ -2701,76 +3109,75 @@ var Loader = class {
2701
3109
  },
2702
3110
  "Loader"
2703
3111
  );
2704
- this.logger = this.ebOpts.logger;
2705
- this.taskManager = new TaskManager(sthis, async (dbMeta) => {
2706
- await this.handleDbMetasFromStore([dbMeta]);
3112
+ this.logger = ensureLogger(sthis, "Loader");
3113
+ this.cidCache = new import_cement14.KeyedResolvOnce({
3114
+ lru: {
3115
+ maxEntries: parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_CACHE_SIZE, "1000"), 10)
3116
+ }
2707
3117
  });
2708
- }
2709
- async carStore() {
2710
- return this._carStore.once(
2711
- async () => this.ebOpts.storeRuntime.makeDataStore({
2712
- // sthis: this.sthis,
2713
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2714
- url: this.ebOpts.storeUrls.data,
2715
- // keybag: await this.keyBag(),
2716
- loader: this
2717
- })
2718
- );
2719
- }
2720
- async fileStore() {
2721
- return this._fileStore.once(
2722
- async () => this.ebOpts.storeRuntime.makeDataStore({
2723
- // sthis: this.sthis,
2724
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2725
- url: this.ebOpts.storeUrls.file,
2726
- // keybag: await this.keyBag(),
2727
- loader: this
2728
- })
2729
- );
2730
- }
2731
- async WALStore() {
2732
- return this._WALStore.once(
2733
- async () => this.ebOpts.storeRuntime.makeWALStore({
2734
- // sthis: this.sthis,
2735
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2736
- url: this.ebOpts.storeUrls.wal,
2737
- // keybag: await this.keyBag(),
2738
- loader: this
2739
- })
3118
+ this.seenMeta = new import_cement14.LRUSet({
3119
+ maxEntries: parseInt(this.ebOpts.storeUrls.meta.getParam(PARAM.CAR_META_CACHE_SIZE, "1000"), 10)
3120
+ });
3121
+ this.seenCompacted = new import_cement14.LRUSet({
3122
+ maxEntries: parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_COMPACT_CACHE_SIZE, "1000"), 10)
3123
+ });
3124
+ this.maxConcurrentCarReader = (0, import_p_limit.default)(parseInt(this.ebOpts.storeUrls.car.getParam(PARAM.CAR_PARALLEL, "5"), 10));
3125
+ this.taskManager = new TaskManager(
3126
+ sthis,
3127
+ async (dbMeta, activeStore) => {
3128
+ await this.handleDbMetasFromStore([dbMeta], activeStore);
3129
+ },
3130
+ this.ebOpts.taskManager
2740
3131
  );
3132
+ this.attachedStores = new AttachedRemotesImpl(this);
2741
3133
  }
2742
- async metaStore() {
2743
- return this._metaStore.once(
2744
- async () => this.ebOpts.storeRuntime.makeMetaStore({
2745
- // sthis: this.sthis,
2746
- gatewayInterceptor: this.ebOpts.gatewayInterceptor,
2747
- url: this.ebOpts.storeUrls.meta,
2748
- // keybag: await this.keyBag(),
2749
- loader: this
2750
- })
2751
- );
3134
+ async attach(attached) {
3135
+ const at = await this.attachedStores.attach(attached);
3136
+ if (!at.stores.wal) {
3137
+ try {
3138
+ const dbMeta = await at.stores.meta.load();
3139
+ if (!Array.isArray(dbMeta)) {
3140
+ throw this.logger.Error().Msg("missing dbMeta").AsError();
3141
+ }
3142
+ await this.handleDbMetasFromStore(dbMeta, this.attachedStores.activate(at.stores));
3143
+ } catch (e) {
3144
+ this.logger.Error().Err(e).Msg("error attaching store");
3145
+ at.detach();
3146
+ }
3147
+ }
3148
+ return at;
2752
3149
  }
2753
3150
  keyBag() {
2754
3151
  return getKeyBag(this.sthis, this.ebOpts.keyBag);
2755
3152
  }
2756
3153
  async ready() {
2757
3154
  return this.onceReady.once(async () => {
2758
- const metas = await (await this.metaStore()).load();
3155
+ await createAttachedStores(
3156
+ {
3157
+ car: { url: this.ebOpts.storeUrls.car, gatewayInterceptor: this.ebOpts.gatewayInterceptor },
3158
+ file: { url: this.ebOpts.storeUrls.file, gatewayInterceptor: this.ebOpts.gatewayInterceptor },
3159
+ meta: { url: this.ebOpts.storeUrls.meta, gatewayInterceptor: this.ebOpts.gatewayInterceptor },
3160
+ wal: { url: this.ebOpts.storeUrls.wal, gatewayInterceptor: this.ebOpts.gatewayInterceptor }
3161
+ },
3162
+ this.attachedStores
3163
+ );
3164
+ const local = this.attachedStores.local();
3165
+ const metas = await local.active.meta.load();
2759
3166
  if (this.ebOpts.meta) {
2760
- await this.handleDbMetasFromStore([this.ebOpts.meta]);
3167
+ await this.handleDbMetasFromStore([this.ebOpts.meta, ...metas || []], local);
2761
3168
  } else if (metas) {
2762
- await this.handleDbMetasFromStore(metas);
3169
+ await this.handleDbMetasFromStore(metas, local);
2763
3170
  }
2764
3171
  });
2765
3172
  }
2766
3173
  async close() {
2767
3174
  await this.commitQueue.waitIdle();
2768
- const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
2769
- await Promise.all(toClose.map((store) => store.close()));
3175
+ await this.attachedStores.detach();
2770
3176
  }
2771
3177
  async destroy() {
2772
- const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
2773
- await Promise.all(toDestroy.map((store) => store.destroy()));
3178
+ await Promise.all(
3179
+ this.attachedStores.local().baseStores().map((store) => store.destroy())
3180
+ );
2774
3181
  }
2775
3182
  // async snapToCar(carCid: AnyLink | string) {
2776
3183
  // await this.ready
@@ -2782,28 +3189,38 @@ var Loader = class {
2782
3189
  // await this.getMoreReaders(carHeader.cars)
2783
3190
  // await this._applyCarHeader(carHeader, true)
2784
3191
  // }
2785
- async handleDbMetasFromStore(metas) {
2786
- this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
3192
+ async handleDbMetasFromStore(metas, activeStore) {
3193
+ this.logger.Debug().Any("metas", metas).Url(activeStore.active.car.url()).Msg("handleDbMetasFromStore");
2787
3194
  for (const meta of metas) {
2788
- await this.writeLimit(async () => {
2789
- await this.mergeDbMetaIntoClock(meta);
3195
+ await this.maxConcurrentWrite(async () => {
3196
+ await this.mergeDbMetaIntoClock(meta, activeStore);
2790
3197
  });
2791
3198
  }
2792
3199
  }
2793
- async mergeDbMetaIntoClock(meta) {
3200
+ async mergeDbMetaIntoClock(meta, activeStore) {
2794
3201
  if (this.isCompacting) {
2795
3202
  throw this.logger.Error().Msg("cannot merge while compacting").AsError();
2796
3203
  }
2797
- if (this.seenMeta.has(meta.cars.toString())) return;
2798
- this.seenMeta.add(meta.cars.toString());
2799
- if (carLogIncludesGroup(this.carLog, meta.cars)) {
2800
- return;
3204
+ try {
3205
+ this.isCompacting = true;
3206
+ const metaKey = meta.cars.map((i) => i.toString()).sort().join(",");
3207
+ if (this.seenMeta.has(metaKey)) return;
3208
+ this.seenMeta.add(metaKey);
3209
+ if (carLogIncludesGroup(this.carLog.asArray(), meta.cars)) {
3210
+ return;
3211
+ }
3212
+ const carHeader = await this.loadCarHeaderFromMeta(meta, activeStore);
3213
+ carHeader.compact.map((c) => c.toString()).forEach((k) => this.seenCompacted.add(k), this.seenCompacted);
3214
+ try {
3215
+ await this.getMoreReaders(carHeader.cars.flat(), activeStore);
3216
+ } catch (e) {
3217
+ this.logger.Error().Err(e).Msg("error getting more readers");
3218
+ }
3219
+ this.carLog.update(uniqueCids([meta.cars, ...this.carLog.asArray(), ...carHeader.cars], this.seenCompacted));
3220
+ await this.ebOpts.applyMeta?.(carHeader.meta);
3221
+ } finally {
3222
+ this.isCompacting = false;
2801
3223
  }
2802
- const carHeader = await this.loadCarHeaderFromMeta(meta);
2803
- carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2804
- await this.getMoreReaders(carHeader.cars.flat());
2805
- this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
2806
- await this.ebOpts.applyMeta?.(carHeader.meta);
2807
3224
  }
2808
3225
  // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
2809
3226
  // const { key } = meta;
@@ -2811,8 +3228,8 @@ var Loader = class {
2811
3228
  // await this.setKey(key);
2812
3229
  // }
2813
3230
  // }
2814
- async loadCarHeaderFromMeta({ cars: cids }) {
2815
- const reader = await this.loadCar(cids[0]);
3231
+ async loadCarHeaderFromMeta(dbm, astore) {
3232
+ const reader = await this.loadCar(dbm.cars[0], astore);
2816
3233
  return await parseCarFile(reader, this.logger);
2817
3234
  }
2818
3235
  // async _getKey(): Promise<string | undefined> {
@@ -2825,22 +3242,22 @@ var Loader = class {
2825
3242
  // }
2826
3243
  async commitFiles(t, done) {
2827
3244
  await this.ready();
2828
- const fstore = await this.fileStore();
2829
- const wstore = await this.WALStore();
3245
+ const fstore = this.attachedStores.local().active.file;
3246
+ const wstore = this.attachedStores.local().active.wal;
2830
3247
  return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
2831
3248
  }
2832
- async loadFileCar(cid) {
2833
- return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
3249
+ async loadFileCar(cid, store) {
3250
+ return await this.storesLoadCar(cid, store.fileStore());
2834
3251
  }
2835
3252
  async commit(t, done, opts = { noLoader: false, compact: false }) {
2836
3253
  await this.ready();
2837
- const carStore = await this.carStore();
3254
+ const carStore = this.attachedStores.local().active.car;
2838
3255
  const params = {
2839
3256
  encoder: (await carStore.keyedCrypto()).codec(),
2840
3257
  carLog: this.carLog,
2841
3258
  carStore,
2842
- WALStore: await this.WALStore(),
2843
- metaStore: await this.metaStore(),
3259
+ WALStore: this.attachedStores.local().active.wal,
3260
+ metaStore: this.attachedStores.local().active.meta,
2844
3261
  threshold: this.ebOpts.threshold
2845
3262
  };
2846
3263
  return this.commitQueue.enqueue(async () => {
@@ -2850,41 +3267,54 @@ var Loader = class {
2850
3267
  return ret.cgrp;
2851
3268
  });
2852
3269
  }
2853
- async updateCarLog(cids, fp, compact) {
3270
+ async updateCarLog(cids, cHeader, compact) {
2854
3271
  if (compact) {
2855
- const previousCompactCid = fp.compact[fp.compact.length - 1];
2856
- fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2857
- this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
2858
- await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
3272
+ const previousCompactCid = cHeader.compact[cHeader.compact.length - 1];
3273
+ cHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
3274
+ this.carLog.update(uniqueCids([...this.carLog.asArray(), ...cHeader.cars, cids], this.seenCompacted));
3275
+ await this.removeCidsForCompact(previousCompactCid[0], this.attachedStores.local()).catch((e) => e);
2859
3276
  } else {
2860
- this.carLog.unshift(cids);
3277
+ this.carLog.xunshift(cids);
2861
3278
  }
2862
3279
  }
2863
3280
  async cacheTransaction(t) {
2864
3281
  for await (const block of t.entries()) {
2865
3282
  const sBlock = block.cid.toString();
2866
- if (!this.getBlockCache.has(sBlock)) {
2867
- this.getBlockCache.set(sBlock, block);
2868
- }
2869
- }
2870
- }
2871
- async cacheCarReader(carCidStr, reader) {
2872
- if (this.processedCars.has(carCidStr)) return;
2873
- this.processedCars.add(carCidStr);
2874
- for await (const block of reader.blocks()) {
2875
- const sBlock = block.cid.toString();
2876
- if (!this.getBlockCache.has(sBlock)) {
2877
- this.getBlockCache.set(sBlock, block);
2878
- }
3283
+ this.cidCache.get(sBlock).once(
3284
+ () => ({
3285
+ type: "block",
3286
+ cid: block.cid,
3287
+ blocks: [block],
3288
+ roots: []
3289
+ })
3290
+ );
2879
3291
  }
2880
3292
  }
2881
- async removeCidsForCompact(cid) {
2882
- const carHeader = await this.loadCarHeaderFromMeta({
2883
- cars: [cid]
2884
- });
3293
+ // /**
3294
+ // *
3295
+ // * @returns the list of blocks which was read from the car file
3296
+ // */
3297
+ // private async readCar(reader: CarReader): Promise<AnyBlock[]> {
3298
+ // const blocks: AnyBlock[] = [];
3299
+ // for await (const block of reader.blocks()) {
3300
+ // const sBlock = block.cid.toString();
3301
+ // this.cidCache.get(sBlock).once(() => {
3302
+ // blocks.push(block);
3303
+ // return [block];
3304
+ // });
3305
+ // }
3306
+ // return blocks;
3307
+ // }
3308
+ async removeCidsForCompact(cid, store) {
3309
+ const carHeader = await this.loadCarHeaderFromMeta(
3310
+ {
3311
+ cars: [cid]
3312
+ },
3313
+ store
3314
+ );
2885
3315
  for (const cids of carHeader.compact) {
2886
3316
  for (const cid2 of cids) {
2887
- await (await this.carStore()).remove(cid2);
3317
+ await this.attachedStores.local().active.car.remove(cid2);
2888
3318
  }
2889
3319
  }
2890
3320
  }
@@ -2896,148 +3326,134 @@ var Loader = class {
2896
3326
  // await this.remoteWAL!.enqueue(dbMeta, { public: false })
2897
3327
  // }
2898
3328
  // }
2899
- async *entries(cache2 = true) {
3329
+ async *entries() {
2900
3330
  await this.ready();
2901
- if (cache2) {
2902
- for (const [, block] of this.getBlockCache) {
2903
- yield block;
2904
- }
2905
- } else {
2906
- for (const [, block] of this.getBlockCache) {
2907
- yield block;
2908
- }
2909
- for (const cids of this.carLog) {
2910
- for (const cid of cids) {
2911
- const reader = await this.loadCar(cid);
2912
- if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
2913
- for await (const block of reader.blocks()) {
2914
- const sCid = block.cid.toString();
2915
- if (!this.getBlockCache.has(sCid)) {
2916
- yield block;
2917
- }
2918
- }
3331
+ const seen = /* @__PURE__ */ new Set();
3332
+ for (const carCids of this.carLog.asArray()) {
3333
+ for (const carCid of carCids) {
3334
+ const reader = await this.loadCar(carCid, this.attachedStores.local());
3335
+ if (!reader || reader.type !== "car") {
3336
+ throw this.logger.Error().Any("reader", reader.type).Str("cid", carCid.toString()).Msg("missing car reader").AsError();
3337
+ }
3338
+ for (const block of reader.blocks) {
3339
+ const cidStr = block.cid.toString();
3340
+ if (seen.has(cidStr)) continue;
3341
+ seen.add(cidStr);
3342
+ yield block;
2919
3343
  }
2920
3344
  }
2921
3345
  }
2922
3346
  }
2923
- async getBlock(cid) {
3347
+ async getBlock(cid, store) {
2924
3348
  await this.ready();
2925
- const sCid = cid.toString();
2926
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2927
- const getCarCid = async (carCid) => {
2928
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2929
- const reader = await this.loadCar(carCid);
2930
- if (!reader) {
2931
- throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
2932
- }
2933
- await this.cacheCarReader(carCid.toString(), reader).catch(() => {
2934
- return;
2935
- });
2936
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2937
- throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
2938
- };
2939
- const getCompactCarCids = async (carCid) => {
2940
- const reader = await this.loadCar(carCid);
2941
- if (!reader) {
2942
- throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
2943
- }
2944
- const header = await parseCarFile(reader, this.logger);
2945
- const compacts = header.compact;
2946
- let got2;
2947
- const batchSize2 = 5;
2948
- for (let i = 0; i < compacts.length; i += batchSize2) {
2949
- const promises = [];
2950
- for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
2951
- for (const cid2 of compacts[j]) {
2952
- promises.push(getCarCid(cid2));
3349
+ const cidStr = cid.toString();
3350
+ const ci = await this.cidCache.get(cidStr).once(async () => {
3351
+ const getCompactCarCids = async (carCid) => {
3352
+ const sCid = carCid.toString();
3353
+ const reader = await this.loadCar(carCid, store);
3354
+ const header = await parseCarFile(reader, this.logger);
3355
+ const compacts = header.compact;
3356
+ const got2 = await Promise.allSettled(compacts.map((compact) => compact.map((cid2) => this.loadCar(cid2, store)).flat()));
3357
+ got2.filter((result) => result.status === "rejected").forEach((result) => {
3358
+ this.logger.Error().Err(result.reason).Str("cid", sCid).Msg("error getting compacted block");
3359
+ });
3360
+ };
3361
+ let got;
3362
+ for (const carCids of this.carLog.asArray()) {
3363
+ for (const carCid of carCids) {
3364
+ const ci2 = await this.loadCar(carCid, store);
3365
+ if (!ci2) {
3366
+ this.logger.Error().Str("cid", carCid.toString()).Msg("missing CarCID");
3367
+ continue;
3368
+ }
3369
+ got = ci2.blocks.find((block) => block.cid.equals(cid));
3370
+ if (got) {
3371
+ break;
2953
3372
  }
2954
3373
  }
2955
- try {
2956
- got2 = await Promise.any(promises);
2957
- } catch {
2958
- }
2959
- if (got2) break;
2960
- }
2961
- if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2962
- throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
2963
- };
2964
- let got;
2965
- const batchSize = 5;
2966
- for (let i = 0; i < this.carLog.length; i += batchSize) {
2967
- const batch = this.carLog.slice(i, i + batchSize);
2968
- const promises = batch.flatMap((slice) => slice.map(getCarCid));
2969
- try {
2970
- got = await Promise.any(promises);
2971
- } catch {
2972
3374
  }
2973
- if (got) break;
2974
- }
2975
- if (!got) {
2976
- try {
2977
- got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
2978
- } catch {
3375
+ if (!got) {
3376
+ await getCompactCarCids(this.carLog.last()[0]);
2979
3377
  }
3378
+ return {
3379
+ type: "block",
3380
+ cid,
3381
+ blocks: got ? [got] : [],
3382
+ roots: []
3383
+ };
3384
+ });
3385
+ if (!(ci.type === "block" && ci.blocks.length === 1)) {
3386
+ throw this.logger.Error().Str("cid", cidStr).Any("block", ci).Msg("missing block").AsError();
2980
3387
  }
2981
- return got;
3388
+ return ci.blocks[0];
2982
3389
  }
2983
- async loadCar(cid) {
2984
- if (!this.carStore) {
2985
- throw this.logger.Error().Msg("car store not initialized").AsError();
2986
- }
2987
- const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
3390
+ async loadCar(cid, store) {
3391
+ const loaded = await this.storesLoadCar(cid, store.carStore());
2988
3392
  return loaded;
2989
3393
  }
2990
- async makeDecoderAndCarReader(cid, local, remote) {
2991
- const cidsString = cid.toString();
3394
+ async makeDecoderAndCarReader(carCid, store) {
3395
+ const carCidStr = carCid.toString();
2992
3396
  let loadedCar = void 0;
2993
- let activeStore = local;
3397
+ let activeStore = store.local();
2994
3398
  try {
2995
- this.logger.Debug().Any("cid", cidsString).Msg("loading car");
2996
- loadedCar = await local.load(cid);
3399
+ this.logger.Debug().Any("cid", carCidStr).Msg("loading car");
3400
+ loadedCar = await store.local().load(carCid);
2997
3401
  this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
2998
3402
  } catch (e) {
2999
- if (remote) {
3000
- const remoteCar = await remote.load(cid);
3001
- if (remoteCar) {
3002
- this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3003
- await local.save(remoteCar);
3004
- loadedCar = remoteCar;
3005
- activeStore = remote;
3403
+ if (!isNotFoundError(e)) {
3404
+ throw this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3405
+ }
3406
+ for (const remote of store.remotes()) {
3407
+ try {
3408
+ const remoteCar = await remote.load(carCid);
3409
+ if (remoteCar) {
3410
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3411
+ await store.local().save(remoteCar);
3412
+ loadedCar = remoteCar;
3413
+ activeStore = remote;
3414
+ break;
3415
+ } else {
3416
+ this.logger.Error().Str("cid", carCidStr).Err(e).Msg("loading car");
3417
+ }
3418
+ } catch (e2) {
3419
+ this.logger.Warn().Str("cid", carCidStr).Url(remote.url()).Err(e2).Msg("loading car");
3006
3420
  }
3007
- } else {
3008
- this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
3009
3421
  }
3010
3422
  }
3011
3423
  if (!loadedCar) {
3012
- throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
3424
+ throw this.logger.Error().Url(store.local().url()).Str("cid", carCidStr).Msg("missing car files").AsError();
3013
3425
  }
3014
3426
  const bytes = await decode({ bytes: loadedCar.bytes, hasher: import_sha24.sha256, codec: (await activeStore.keyedCrypto()).codec() });
3015
- const rawReader = await import_reader.CarReader.fromBytes(bytes.value);
3016
- const readerP = Promise.resolve(rawReader);
3017
- const cachedReaderP = readerP.then(async (reader) => {
3018
- await this.cacheCarReader(cidsString, reader).catch((e) => {
3019
- this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
3020
- return;
3021
- });
3022
- return reader;
3023
- });
3024
- this.carReaders.set(cidsString, cachedReaderP);
3025
- return readerP;
3427
+ const rawReader = await import_reader.CarReader.fromBytes(bytes.value.data);
3428
+ const blocks = [];
3429
+ for await (const block of rawReader.blocks()) {
3430
+ const sBlock = block.cid.toString();
3431
+ blocks.push(block);
3432
+ this.cidCache.get(sBlock).once(() => ({
3433
+ type: "block",
3434
+ cid: block.cid,
3435
+ blocks: [block],
3436
+ roots: []
3437
+ }));
3438
+ }
3439
+ return {
3440
+ type: "car",
3441
+ cid: carCid,
3442
+ blocks,
3443
+ roots: await rawReader.getRoots()
3444
+ };
3026
3445
  }
3027
3446
  //What if instead it returns an Array of CarHeader
3028
- async storesLoadCar(cid, local, remote) {
3029
- const cidsString = cid.toString();
3030
- let dacr = this.carReaders.get(cidsString);
3031
- if (!dacr) {
3032
- dacr = this.makeDecoderAndCarReader(cid, local, remote);
3033
- this.carReaders.set(cidsString, dacr);
3034
- }
3035
- return dacr;
3447
+ async storesLoadCar(carCid, store) {
3448
+ const carCidStr = carCid.toString();
3449
+ return this.cidCache.get(carCidStr).once(async () => {
3450
+ return this.maxConcurrentCarReader(() => this.makeDecoderAndCarReader(carCid, store));
3451
+ });
3036
3452
  }
3037
- async getMoreReaders(cids) {
3038
- const limit = (0, import_p_limit.default)(5);
3039
- const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
3040
- await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
3453
+ async getMoreReaders(cids, store) {
3454
+ for (const cid of cids) {
3455
+ await this.loadCar(cid, store);
3456
+ }
3041
3457
  }
3042
3458
  };
3043
3459
 
@@ -3082,12 +3498,21 @@ function getGenerateIVFn(url, opts) {
3082
3498
  }
3083
3499
  var BlockIvKeyIdCodec = class {
3084
3500
  constructor(ko, iv, opts) {
3085
- this.code = 3147065;
3501
+ this.code = 24;
3086
3502
  this.name = "Fireproof@encrypted-block:aes-gcm";
3087
3503
  this.ko = ko;
3088
3504
  this.iv = iv;
3089
3505
  this.opts = opts || {};
3090
3506
  }
3507
+ // hashAsBytes(data: IvKeyIdData): AsyncHashAsBytes<Uint8Array<ArrayBufferLike>> {
3508
+ // return data;
3509
+ // }
3510
+ valueToHashBytes(value) {
3511
+ return Promise.resolve(value.data);
3512
+ }
3513
+ bytesToHash(data) {
3514
+ return Promise.resolve(data);
3515
+ }
3091
3516
  async encode(data) {
3092
3517
  const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
3093
3518
  const { iv } = this.ko.algo(calcIv);
@@ -3119,11 +3544,16 @@ var BlockIvKeyIdCodec = class {
3119
3544
  if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
3120
3545
  throw this.ko.logger.Error().Msg("iv missmatch").AsError();
3121
3546
  }
3122
- return result;
3547
+ return {
3548
+ iv,
3549
+ keyId,
3550
+ data: result
3551
+ };
3123
3552
  }
3124
3553
  };
3125
3554
  var cryptoAction = class {
3126
3555
  constructor(url, key, cyopt, sthis) {
3556
+ this.code = 24;
3127
3557
  this.ivLength = 12;
3128
3558
  this.isEncrypting = true;
3129
3559
  this.logger = ensureLogger(sthis, "cryptoAction");
@@ -3157,14 +3587,19 @@ var cryptoAction = class {
3157
3587
  };
3158
3588
  var nullCodec = class {
3159
3589
  constructor() {
3160
- this.code = 0;
3590
+ this.code = 24;
3161
3591
  this.name = "Fireproof@unencrypted-block";
3592
+ this.empty = new Uint8Array();
3162
3593
  }
3163
- encode(data) {
3594
+ async encode(data) {
3164
3595
  return data;
3165
3596
  }
3166
- decode(data) {
3167
- return data;
3597
+ async decode(data) {
3598
+ return {
3599
+ iv: this.empty,
3600
+ keyId: this.empty,
3601
+ data
3602
+ };
3168
3603
  }
3169
3604
  };
3170
3605
  var noCrypto = class {
@@ -3232,39 +3667,39 @@ var import_p_retry = __toESM(require("p-retry"), 1);
3232
3667
  var import_p_map = __toESM(require("p-map"), 1);
3233
3668
 
3234
3669
  // src/blockstore/interceptor-gateway.ts
3235
- var import_cement14 = require("@adviser/cement");
3670
+ var import_cement15 = require("@adviser/cement");
3236
3671
  var PassThroughGateway = class {
3237
3672
  async buildUrl(ctx, url, key) {
3238
3673
  const op = { url, key };
3239
- return import_cement14.Result.Ok({ op });
3674
+ return import_cement15.Result.Ok({ op });
3240
3675
  }
3241
3676
  async start(ctx, url) {
3242
3677
  const op = { url };
3243
- return import_cement14.Result.Ok({ op });
3678
+ return import_cement15.Result.Ok({ op });
3244
3679
  }
3245
3680
  async close(ctx, url) {
3246
3681
  const op = { url };
3247
- return import_cement14.Result.Ok({ op });
3682
+ return import_cement15.Result.Ok({ op });
3248
3683
  }
3249
3684
  async delete(ctx, url) {
3250
3685
  const op = { url };
3251
- return import_cement14.Result.Ok({ op });
3686
+ return import_cement15.Result.Ok({ op });
3252
3687
  }
3253
3688
  async destroy(ctx, url) {
3254
3689
  const op = { url };
3255
- return import_cement14.Result.Ok({ op });
3690
+ return import_cement15.Result.Ok({ op });
3256
3691
  }
3257
3692
  async put(ctx, url, body) {
3258
3693
  const op = { url, body };
3259
- return import_cement14.Result.Ok({ op });
3694
+ return import_cement15.Result.Ok({ op });
3260
3695
  }
3261
3696
  async get(ctx, url) {
3262
3697
  const op = { url };
3263
- return import_cement14.Result.Ok({ op });
3698
+ return import_cement15.Result.Ok({ op });
3264
3699
  }
3265
3700
  async subscribe(ctx, url, callback) {
3266
3701
  const op = { url, callback };
3267
- return import_cement14.Result.Ok({ op });
3702
+ return import_cement15.Result.Ok({ op });
3268
3703
  }
3269
3704
  };
3270
3705
  var passThrougthGateway = new PassThroughGateway();
@@ -3276,7 +3711,7 @@ var InterceptorGateway = class {
3276
3711
  async buildUrl(ctx, baseUrl, key) {
3277
3712
  const rret = await this.interceptor.buildUrl(ctx, baseUrl, key);
3278
3713
  if (rret.isErr()) {
3279
- return import_cement14.Result.Err(rret.Err());
3714
+ return import_cement15.Result.Err(rret.Err());
3280
3715
  }
3281
3716
  const ret = rret.unwrap();
3282
3717
  if (ret.stop && ret.value) {
@@ -3287,7 +3722,7 @@ var InterceptorGateway = class {
3287
3722
  async destroy(ctx, iurl) {
3288
3723
  const rret = await this.interceptor.destroy(ctx, iurl);
3289
3724
  if (rret.isErr()) {
3290
- return import_cement14.Result.Err(rret.Err());
3725
+ return import_cement15.Result.Err(rret.Err());
3291
3726
  }
3292
3727
  const ret = rret.unwrap();
3293
3728
  if (ret.stop && ret.value) {
@@ -3298,7 +3733,7 @@ var InterceptorGateway = class {
3298
3733
  async start(ctx, url) {
3299
3734
  const rret = await this.interceptor.start(ctx, url);
3300
3735
  if (rret.isErr()) {
3301
- return import_cement14.Result.Err(rret.Err());
3736
+ return import_cement15.Result.Err(rret.Err());
3302
3737
  }
3303
3738
  const ret = rret.unwrap();
3304
3739
  if (ret.stop && ret.value) {
@@ -3309,7 +3744,7 @@ var InterceptorGateway = class {
3309
3744
  async close(ctx, url) {
3310
3745
  const rret = await this.interceptor.close(ctx, url);
3311
3746
  if (rret.isErr()) {
3312
- return import_cement14.Result.Err(rret.Err());
3747
+ return import_cement15.Result.Err(rret.Err());
3313
3748
  }
3314
3749
  const ret = rret.unwrap();
3315
3750
  if (ret.stop && ret.value) {
@@ -3320,7 +3755,7 @@ var InterceptorGateway = class {
3320
3755
  async put(ctx, url, fpEnv) {
3321
3756
  const rret = await this.interceptor.put(ctx, url, fpEnv);
3322
3757
  if (rret.isErr()) {
3323
- return import_cement14.Result.Err(rret.Err());
3758
+ return import_cement15.Result.Err(rret.Err());
3324
3759
  }
3325
3760
  const ret = rret.unwrap();
3326
3761
  if (ret.stop && ret.value) {
@@ -3331,7 +3766,7 @@ var InterceptorGateway = class {
3331
3766
  async get(ctx, url) {
3332
3767
  const rret = await this.interceptor.get(ctx, url);
3333
3768
  if (rret.isErr()) {
3334
- return import_cement14.Result.Err(rret.Err());
3769
+ return import_cement15.Result.Err(rret.Err());
3335
3770
  }
3336
3771
  const ret = rret.unwrap();
3337
3772
  if (ret.stop && ret.value) {
@@ -3341,11 +3776,11 @@ var InterceptorGateway = class {
3341
3776
  }
3342
3777
  async subscribe(ctx, url, callback) {
3343
3778
  if (!this.innerGW.subscribe) {
3344
- return import_cement14.Result.Err(ctx.loader.sthis.logger.Error().Url(url).Msg("subscribe not supported").AsError());
3779
+ return import_cement15.Result.Err(ctx.loader.sthis.logger.Error().Url(url).Msg("subscribe not supported").AsError());
3345
3780
  }
3346
3781
  const rret = await this.interceptor.subscribe(ctx, url, callback);
3347
3782
  if (rret.isErr()) {
3348
- return import_cement14.Result.Err(rret.Err());
3783
+ return import_cement15.Result.Err(rret.Err());
3349
3784
  }
3350
3785
  const ret = rret.unwrap();
3351
3786
  if (ret.stop && ret.value) {
@@ -3356,7 +3791,7 @@ var InterceptorGateway = class {
3356
3791
  async delete(ctx, url) {
3357
3792
  const rret = await this.interceptor.delete(ctx, url);
3358
3793
  if (rret.isErr()) {
3359
- return import_cement14.Result.Err(rret.Err());
3794
+ return import_cement15.Result.Err(rret.Err());
3360
3795
  }
3361
3796
  const ret = rret.unwrap();
3362
3797
  if (ret.stop && ret.value) {
@@ -3372,9 +3807,9 @@ var InterceptorGateway = class {
3372
3807
  // src/blockstore/store.ts
3373
3808
  function guardVersion(url) {
3374
3809
  if (!url.hasParam("version")) {
3375
- return import_cement15.Result.Err(`missing version: ${url.toString()}`);
3810
+ return import_cement16.Result.Err(`missing version: ${url.toString()}`);
3376
3811
  }
3377
- return import_cement15.Result.Ok(url);
3812
+ return import_cement16.Result.Ok(url);
3378
3813
  }
3379
3814
  var BaseStoreImpl = class {
3380
3815
  // readonly loader: Loadable;
@@ -3408,7 +3843,7 @@ var BaseStoreImpl = class {
3408
3843
  async keyedCrypto() {
3409
3844
  return keyedCryptoFactory(this._url, await this.loader.keyBag(), this.sthis);
3410
3845
  }
3411
- async start() {
3846
+ async start(dam) {
3412
3847
  this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
3413
3848
  this._url = this._url.build().setParam(PARAM.STORE, this.storeType).URI();
3414
3849
  const res = await this.gateway.start({ loader: this.loader }, this._url);
@@ -3419,13 +3854,8 @@ var BaseStoreImpl = class {
3419
3854
  this._url = res.Ok();
3420
3855
  const kb = await this.loader.keyBag();
3421
3856
  const skRes = await kb.ensureKeyFromUrl(this._url, () => {
3422
- const idx = this._url.getParam(PARAM.INDEX);
3423
- const storeKeyName = [this.url().getParam(PARAM.NAME)];
3424
- if (idx) {
3425
- storeKeyName.push(idx);
3426
- }
3427
- storeKeyName.push(this.storeType);
3428
- return storeKeyName.join(":");
3857
+ const key = this._url.getParam(PARAM.KEY);
3858
+ return key;
3429
3859
  });
3430
3860
  if (skRes.isErr()) {
3431
3861
  return skRes;
@@ -3439,13 +3869,13 @@ var BaseStoreImpl = class {
3439
3869
  }
3440
3870
  if (this.ready) {
3441
3871
  const fn = this.ready.bind(this);
3442
- const ready = await (0, import_cement15.exception2Result)(fn);
3872
+ const ready = await (0, import_cement16.exception2Result)(fn);
3443
3873
  if (ready.isErr()) {
3444
3874
  await this.close();
3445
3875
  return ready;
3446
3876
  }
3447
3877
  }
3448
- this._onStarted.forEach((fn) => fn());
3878
+ this._onStarted.forEach((fn) => fn(dam));
3449
3879
  this.logger.Debug().Msg("started");
3450
3880
  return version;
3451
3881
  }
@@ -3474,12 +3904,19 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3474
3904
  /*this.remote && */
3475
3905
  opts.gateway.subscribe
3476
3906
  ) {
3477
- this.onStarted(async () => {
3907
+ this.onStarted(async (dam) => {
3478
3908
  this.logger.Debug().Str("url", this.url().toString()).Msg("Subscribing to the gateway");
3479
3909
  opts.gateway.subscribe({ loader: this.loader }, this.url(), async ({ payload: dbMetas }) => {
3480
3910
  this.logger.Debug().Msg("Received message from gateway");
3481
3911
  await Promise.all(
3482
- dbMetas.map((dbMeta) => this.loader.taskManager?.handleEvent(dbMeta.eventCid, dbMeta.parents, dbMeta.dbMeta))
3912
+ dbMetas.map(
3913
+ (dbMetaEv) => this.loader.taskManager?.handleEvent(
3914
+ dbMetaEv.eventCid,
3915
+ dbMetaEv.parents,
3916
+ dbMetaEv.dbMeta,
3917
+ this.loader.attachedStores.activate(dam)
3918
+ )
3919
+ )
3483
3920
  );
3484
3921
  this.updateParentsFromDbMetas(dbMetas);
3485
3922
  });
@@ -3501,8 +3938,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3501
3938
  // }
3502
3939
  // return (rDbMeta.Ok() as FPEnvelopeMeta).payload;
3503
3940
  // }
3504
- async load() {
3505
- const branch = "main";
3941
+ async load(branch = "main") {
3506
3942
  const url = await this.gateway.buildUrl({ loader: this.loader }, this.url(), branch);
3507
3943
  if (url.isErr()) {
3508
3944
  throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
@@ -3514,10 +3950,11 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3514
3950
  }
3515
3951
  throw this.logger.Error().Url(url.Ok()).Err(rfpEnv).Msg("gateway get").AsError();
3516
3952
  }
3517
- const dbMetas = rfpEnv.Ok().payload;
3518
- await this.loader.handleDbMetasFromStore(dbMetas.map((m) => m.dbMeta));
3519
- this.updateParentsFromDbMetas(dbMetas);
3520
- return dbMetas.map((m) => m.dbMeta);
3953
+ const fpMeta = rfpEnv.Ok().payload;
3954
+ const dbMetas = fpMeta.map((m) => m.dbMeta);
3955
+ await this.loader.handleDbMetasFromStore(dbMetas, this.loader.attachedStores.activate(url.Ok()));
3956
+ this.updateParentsFromDbMetas(fpMeta);
3957
+ return dbMetas;
3521
3958
  }
3522
3959
  async save(meta, branch) {
3523
3960
  branch = branch || "main";
@@ -3539,7 +3976,7 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3539
3976
  async close() {
3540
3977
  await this.gateway.close({ loader: this.loader }, this.url());
3541
3978
  this._onClosed.forEach((fn) => fn());
3542
- return import_cement15.Result.Ok(void 0);
3979
+ return import_cement16.Result.Ok(void 0);
3543
3980
  }
3544
3981
  async destroy() {
3545
3982
  this.logger.Debug().Msg("destroy");
@@ -3547,9 +3984,8 @@ var MetaStoreImpl = class extends BaseStoreImpl {
3547
3984
  }
3548
3985
  };
3549
3986
  var DataStoreImpl = class extends BaseStoreImpl {
3550
- constructor(sthis, url, opts) {
3551
- super(sthis, url, { ...opts }, ensureLogger(sthis, "DataStoreImpl"));
3552
- this.storeType = "data";
3987
+ constructor(sthis, url, opts, logger) {
3988
+ super(sthis, url, { ...opts }, logger);
3553
3989
  }
3554
3990
  async load(cid) {
3555
3991
  this.logger.Debug().Any("cid", cid).Msg("loading");
@@ -3564,7 +4000,6 @@ var DataStoreImpl = class extends BaseStoreImpl {
3564
4000
  const fpenv = res.Ok();
3565
4001
  switch (fpenv.type) {
3566
4002
  case "car":
3567
- return { cid, bytes: fpenv.payload };
3568
4003
  case "file":
3569
4004
  return { cid, bytes: fpenv.payload };
3570
4005
  default:
@@ -3580,12 +4015,11 @@ var DataStoreImpl = class extends BaseStoreImpl {
3580
4015
  }
3581
4016
  let fpMsg;
3582
4017
  switch (url.Ok().getParam(PARAM.STORE)) {
3583
- case "data":
3584
- if (url.Ok().getParam(PARAM.SUFFIX)) {
3585
- fpMsg = Car2FPMsg(car.bytes);
3586
- } else {
3587
- fpMsg = File2FPMsg(car.bytes);
3588
- }
4018
+ case "car":
4019
+ fpMsg = Car2FPMsg(car.bytes);
4020
+ break;
4021
+ case "file":
4022
+ fpMsg = File2FPMsg(car.bytes);
3589
4023
  break;
3590
4024
  default:
3591
4025
  throw this.logger.Error().Str("store", url.Ok().getParam(PARAM.STORE)).Msg("unexpected store").AsError();
@@ -3609,20 +4043,32 @@ var DataStoreImpl = class extends BaseStoreImpl {
3609
4043
  async close() {
3610
4044
  await this.gateway.close({ loader: this.loader }, this.url());
3611
4045
  this._onClosed.forEach((fn) => fn());
3612
- return import_cement15.Result.Ok(void 0);
4046
+ return import_cement16.Result.Ok(void 0);
3613
4047
  }
3614
4048
  destroy() {
3615
4049
  this.logger.Debug().Msg("destroy");
3616
4050
  return this.gateway.destroy({ loader: this.loader }, this.url());
3617
4051
  }
3618
4052
  };
4053
+ var CarStoreImpl = class extends DataStoreImpl {
4054
+ constructor(sthis, url, opts) {
4055
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "CarStoreImpl"));
4056
+ this.storeType = "car";
4057
+ }
4058
+ };
4059
+ var FileStoreImpl = class extends DataStoreImpl {
4060
+ constructor(sthis, url, opts) {
4061
+ super(sthis, url, { ...opts }, ensureLogger(sthis, "FileStoreImpl"));
4062
+ this.storeType = "file";
4063
+ }
4064
+ };
3619
4065
  var WALStoreImpl = class extends BaseStoreImpl {
3620
4066
  constructor(sthis, url, opts) {
3621
4067
  super(sthis, url, { ...opts }, ensureLogger(sthis, "WALStoreImpl"));
3622
4068
  this.storeType = "wal";
3623
4069
  // readonly tag: string = "rwal-base";
3624
4070
  // readonly loader: Loadable;
3625
- this._ready = new import_cement15.ResolveOnce();
4071
+ this._ready = new import_cement16.ResolveOnce();
3626
4072
  this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
3627
4073
  this.processing = void 0;
3628
4074
  this.processQueue = new CommitQueue();
@@ -3663,7 +4109,6 @@ var WALStoreImpl = class extends BaseStoreImpl {
3663
4109
  }
3664
4110
  async process() {
3665
4111
  await this.ready();
3666
- if (!this.loader.remoteCarStore) return;
3667
4112
  await this.processQueue.enqueue(async () => {
3668
4113
  try {
3669
4114
  await this._doProcess();
@@ -3677,7 +4122,6 @@ var WALStoreImpl = class extends BaseStoreImpl {
3677
4122
  }
3678
4123
  async _doProcess() {
3679
4124
  if (!this.loader) return;
3680
- if (!this.loader.remoteCarStore) return;
3681
4125
  const operations = [...this.walState.operations];
3682
4126
  const noLoaderOps = [...this.walState.noLoaderOps];
3683
4127
  const fileOperations = [...this.walState.fileOperations];
@@ -3698,13 +4142,13 @@ var WALStoreImpl = class extends BaseStoreImpl {
3698
4142
  return;
3699
4143
  }
3700
4144
  for (const cid of dbMeta.cars) {
3701
- const car = await (await this.loader.carStore()).load(cid);
4145
+ const car = await this.loader.attachedStores.local().active.car.load(cid);
3702
4146
  if (!car) {
3703
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4147
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3704
4148
  throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
3705
4149
  }
3706
4150
  } else {
3707
- await throwFalsy(this.loader.remoteCarStore).save(car);
4151
+ await this.loader.attachedStores.forRemotes((x) => x.active.car.save(car));
3708
4152
  }
3709
4153
  }
3710
4154
  inplaceFilter(this.walState.noLoaderOps, (op) => op !== dbMeta);
@@ -3720,13 +4164,13 @@ var WALStoreImpl = class extends BaseStoreImpl {
3720
4164
  return;
3721
4165
  }
3722
4166
  for (const cid of dbMeta.cars) {
3723
- const car = await (await this.loader.carStore()).load(cid);
4167
+ const car = await this.loader.attachedStores.local().active.car.load(cid);
3724
4168
  if (!car) {
3725
- if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
4169
+ if (carLogIncludesGroup(this.loader.carLog.asArray(), dbMeta.cars)) {
3726
4170
  throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
3727
4171
  }
3728
4172
  } else {
3729
- await throwFalsy(this.loader.remoteCarStore).save(car);
4173
+ await this.loader.attachedStores.forRemotes((x) => x.active.car.save(car));
3730
4174
  }
3731
4175
  }
3732
4176
  inplaceFilter(this.walState.operations, (op) => op !== dbMeta);
@@ -3741,11 +4185,11 @@ var WALStoreImpl = class extends BaseStoreImpl {
3741
4185
  if (!this.loader) {
3742
4186
  return;
3743
4187
  }
3744
- const fileBlock = await (await this.loader.fileStore()).load(fileCid);
4188
+ const fileBlock = await this.loader.attachedStores.local().active.file.load(fileCid);
3745
4189
  if (!fileBlock) {
3746
4190
  throw this.logger.Error().Ref("cid", fileCid).Msg("missing file block").AsError();
3747
4191
  }
3748
- await this.loader.remoteFileStore?.save(fileBlock, { public: publicFile });
4192
+ await this.loader.attachedStores.forRemotes((x) => x.active.file.save(fileBlock, { public: publicFile }));
3749
4193
  inplaceFilter(this.walState.fileOperations, (op) => op.cid !== fileCid);
3750
4194
  }, `fileOperation with cid=${fileCid.toString()}`);
3751
4195
  },
@@ -3757,7 +4201,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3757
4201
  if (!this.loader) {
3758
4202
  return;
3759
4203
  }
3760
- await this.loader.remoteMetaStore?.save(lastOp);
4204
+ await this.loader.attachedStores.forRemotes((x) => x.active.meta.save(lastOp));
3761
4205
  }, `remoteMetaStore save with dbMeta.cars=${lastOp.cars.toString()}`);
3762
4206
  }
3763
4207
  } catch (error) {
@@ -3801,7 +4245,7 @@ var WALStoreImpl = class extends BaseStoreImpl {
3801
4245
  async close() {
3802
4246
  await this.gateway.close({ loader: this.loader }, this.url());
3803
4247
  this._onClosed.forEach((fn) => fn());
3804
- return import_cement15.Result.Ok(void 0);
4248
+ return import_cement16.Result.Ok(void 0);
3805
4249
  }
3806
4250
  destroy() {
3807
4251
  this.logger.Debug().Msg("destroy");
@@ -3810,71 +4254,86 @@ var WALStoreImpl = class extends BaseStoreImpl {
3810
4254
  };
3811
4255
 
3812
4256
  // src/blockstore/store-factory.ts
3813
- var onceGateway = new import_cement16.KeyedResolvOnce();
3814
- var gatewayInstances = new import_cement16.KeyedResolvOnce();
4257
+ var onceGateway = new import_cement17.KeyedResolvOnce();
4258
+ var gatewayInstances = new import_cement17.KeyedResolvOnce();
3815
4259
  async function getStartedGateway(ctx, url) {
3816
4260
  return onceGateway.get(url.toString()).once(async () => {
3817
4261
  const item = getGatewayFactoryItem(url.protocol);
3818
4262
  if (item) {
3819
4263
  const ret = {
3820
4264
  url,
3821
- ...await gatewayInstances.get(url.protocol).once(async () => ({})),
3822
- gateway: await item.serdegateway(ctx.loader.sthis)
4265
+ ...await gatewayInstances.get(url.protocol).once(async () => ({
4266
+ gateway: await item.serdegateway(ctx.loader.sthis)
4267
+ }))
3823
4268
  };
3824
4269
  const res = await ret.gateway.start(ctx, url);
3825
4270
  if (res.isErr()) {
3826
- return import_cement16.Result.Err(ctx.loader.sthis.logger.Error().Result("start", res).Msg("start failed").AsError());
4271
+ return import_cement17.Result.Err(ctx.loader.sthis.logger.Error().Result("start", res).Msg("start failed").AsError());
3827
4272
  }
3828
4273
  ret.url = res.Ok();
3829
- return import_cement16.Result.Ok(ret);
4274
+ return import_cement17.Result.Ok(ret);
3830
4275
  }
3831
- return import_cement16.Result.Err(ctx.loader.sthis.logger.Warn().Url(url).Msg("unsupported protocol").AsError());
4276
+ return import_cement17.Result.Err(ctx.loader.sthis.logger.Warn().Url(url).Msg("unsupported protocol").AsError());
4277
+ });
4278
+ }
4279
+ async function carStoreFactory(ctx, uai) {
4280
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "car").URI();
4281
+ const rgateway = await getStartedGateway(ctx, storeUrl);
4282
+ if (rgateway.isErr()) {
4283
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
4284
+ }
4285
+ const gateway = rgateway.Ok();
4286
+ const store = new CarStoreImpl(ctx.loader.sthis, gateway.url, {
4287
+ gateway: gateway.gateway,
4288
+ gatewayInterceptor: uai.gatewayInterceptor,
4289
+ loader: ctx.loader
3832
4290
  });
4291
+ return store;
3833
4292
  }
3834
- async function dataStoreFactory(sfi) {
3835
- const storeUrl = sfi.url.build().setParam(PARAM.STORE, "data").URI();
3836
- const rgateway = await getStartedGateway(sfi, storeUrl);
4293
+ async function fileStoreFactory(ctx, uai) {
4294
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "file").URI();
4295
+ const rgateway = await getStartedGateway(ctx, storeUrl);
3837
4296
  if (rgateway.isErr()) {
3838
- throw sfi.loader.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
4297
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
3839
4298
  }
3840
4299
  const gateway = rgateway.Ok();
3841
- const store = new DataStoreImpl(sfi.loader.sthis, gateway.url, {
4300
+ const store = new FileStoreImpl(ctx.loader.sthis, gateway.url, {
3842
4301
  gateway: gateway.gateway,
3843
- gatewayInterceptor: sfi.gatewayInterceptor,
3844
- loader: sfi.loader
4302
+ gatewayInterceptor: uai.gatewayInterceptor,
4303
+ loader: ctx.loader
3845
4304
  });
3846
4305
  return store;
3847
4306
  }
3848
- async function metaStoreFactory(sfi) {
3849
- const storeUrl = sfi.url.build().setParam(PARAM.STORE, "meta").URI();
3850
- const rgateway = await getStartedGateway(sfi, storeUrl);
4307
+ async function metaStoreFactory(ctx, uai) {
4308
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "meta").URI();
4309
+ const rgateway = await getStartedGateway(ctx, storeUrl);
3851
4310
  if (rgateway.isErr()) {
3852
- throw sfi.loader.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
4311
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
3853
4312
  }
3854
4313
  const gateway = rgateway.Ok();
3855
- const store = new MetaStoreImpl(sfi.loader.sthis, gateway.url, {
4314
+ const store = new MetaStoreImpl(ctx.loader.sthis, gateway.url, {
3856
4315
  gateway: gateway.gateway,
3857
- gatewayInterceptor: sfi.gatewayInterceptor,
3858
- loader: sfi.loader
4316
+ gatewayInterceptor: uai.gatewayInterceptor,
4317
+ loader: ctx.loader
3859
4318
  });
3860
4319
  return store;
3861
4320
  }
3862
- async function WALStoreFactory(sfi) {
3863
- const storeUrl = sfi.url.build().setParam(PARAM.STORE, "wal").URI();
3864
- const rgateway = await getStartedGateway(sfi, storeUrl);
4321
+ async function WALStoreFactory(ctx, uai) {
4322
+ const storeUrl = uai.url.build().setParam(PARAM.STORE, "wal").URI();
4323
+ const rgateway = await getStartedGateway(ctx, storeUrl);
3865
4324
  if (rgateway.isErr()) {
3866
- throw sfi.loader.sthis.logger.Error().Result("err", rgateway).Url(sfi.url).Msg("notfound").AsError();
4325
+ throw ctx.loader.sthis.logger.Error().Result("err", rgateway).Url(uai.url).Msg("notfound").AsError();
3867
4326
  }
3868
4327
  const gateway = rgateway.Ok();
3869
- const store = new WALStoreImpl(sfi.loader.sthis, gateway.url, {
4328
+ const store = new WALStoreImpl(ctx.loader.sthis, gateway.url, {
3870
4329
  gateway: gateway.gateway,
3871
- gatewayInterceptor: sfi.gatewayInterceptor,
3872
- loader: sfi.loader
4330
+ gatewayInterceptor: uai.gatewayInterceptor,
4331
+ loader: ctx.loader
3873
4332
  });
3874
4333
  return store;
3875
4334
  }
3876
- async function ensureStart(store) {
3877
- const ret = await store.start();
4335
+ async function ensureStart(store, damaw) {
4336
+ const ret = await store.start(damaw);
3878
4337
  if (ret.isErr()) {
3879
4338
  throw store.logger.Error().Result("start", ret).Msg("start failed").AsError();
3880
4339
  }
@@ -3890,191 +4349,32 @@ function ensureStoreEnDeFile(ende) {
3890
4349
  }
3891
4350
  function toStoreRuntime(sthis, endeOpts = {}) {
3892
4351
  return {
3893
- makeMetaStore: async (sfi) => ensureStart(await metaStoreFactory(sfi)),
3894
- // async (loader: Loadable) => {
3895
- // logger
3896
- // .Debug()
3897
- // .Str("fromOpts", "" + !!endeOpts.func?.makeMetaStore)
3898
- // .Msg("makeMetaStore");
3899
- // return ensureStart(await (endeOpts.func?.makeMetaStore || metaStoreFactory)(loader), logger);
3900
- // },
3901
- makeDataStore: async (sfi) => ensureStart(await dataStoreFactory(sfi)),
3902
- // async (loader: Loadable) => {
3903
- // logger
3904
- // .Debug()
3905
- // .Str("fromOpts", "" + !!endeOpts.func?.makeDataStore)
3906
- // .Msg("makeDataStore");
3907
- // return ensureStart(await (endeOpts.func?.makeDataStore || dataStoreFactory)(loader), logger);
3908
- // },
3909
- makeWALStore: async (sfi) => ensureStart(await WALStoreFactory(sfi)),
3910
- // async (loader: Loadable) => {
3911
- // logger
3912
- // .Debug()
3913
- // .Str("fromOpts", "" + !!endeOpts.func?.makeWALStore)
3914
- // .Msg("makeRemoteWAL");
3915
- // return ensureStart(await (endeOpts.func?.makeWALStore || remoteWalFactory)(loader), logger);
3916
- // },
4352
+ makeStores: async (sfi) => {
4353
+ const ctx = {
4354
+ loader: sfi.loader
4355
+ };
4356
+ const storeSet = {};
4357
+ storeSet.meta = await metaStoreFactory(ctx, sfi.byStore.meta);
4358
+ storeSet.car = await carStoreFactory(ctx, sfi.byStore.car);
4359
+ storeSet.file = await fileStoreFactory(ctx, sfi.byStore.file);
4360
+ if (sfi.byStore.wal) {
4361
+ storeSet.wal = await WALStoreFactory(ctx, sfi.byStore.wal);
4362
+ }
4363
+ await ensureStart(storeSet.meta, storeSet);
4364
+ await ensureStart(storeSet.car, storeSet);
4365
+ await ensureStart(storeSet.file, storeSet);
4366
+ if (storeSet.wal) {
4367
+ await ensureStart(storeSet.wal, storeSet);
4368
+ }
4369
+ return storeSet;
4370
+ },
4371
+ // makeMetaStore: async (sfi: StoreFactoryItem) => ensureStart(await metaStoreFactory(sfi)),
4372
+ // makeDataStore: async (sfi: StoreFactoryItem) => ensureStart(await dataStoreFactory(sfi)),
4373
+ // makeWALStore: async (sfi: StoreFactoryItem) => ensureStart(await WALStoreFactory(sfi)),
3917
4374
  ...ensureStoreEnDeFile(endeOpts)
3918
4375
  };
3919
4376
  }
3920
4377
 
3921
- // src/blockstore/connection-base.ts
3922
- var import_cement17 = require("@adviser/cement");
3923
-
3924
- // src/blockstore/store-remote.ts
3925
- async function RemoteDataStore(sthis, url, opts) {
3926
- const ds = new DataStoreImpl(sthis, url, opts);
3927
- await ds.start();
3928
- return ds;
3929
- }
3930
- async function RemoteMetaStore(sthis, url, opts) {
3931
- const ms = new MetaStoreImpl(sthis, url, opts);
3932
- await ms.start();
3933
- return ms;
3934
- }
3935
-
3936
- // src/context.ts
3937
- var Context = class {
3938
- constructor() {
3939
- this.ctx = /* @__PURE__ */ new Map();
3940
- }
3941
- set(key, value) {
3942
- this.ctx.set(key, value);
3943
- }
3944
- get(key) {
3945
- return this.ctx.get(key);
3946
- }
3947
- delete(key) {
3948
- this.ctx.delete(key);
3949
- }
3950
- };
3951
-
3952
- // src/blockstore/connection-base.ts
3953
- function coerceLoader(ref) {
3954
- const refl = ref;
3955
- if (refl.loader) {
3956
- return refl.loader;
3957
- }
3958
- const refb = ref;
3959
- if (refb.blockstore) {
3960
- return coerceLoader(refb.blockstore);
3961
- }
3962
- return void 0;
3963
- }
3964
- var ConnectionBase = class {
3965
- constructor(url, logger) {
3966
- // loaded: Promise<void> = Promise.resolve();
3967
- this.context = new Context();
3968
- this._loaded = /* @__PURE__ */ new Set();
3969
- this._metaIsLoading = false;
3970
- this.logger = logger;
3971
- this.url = url;
3972
- }
3973
- loaded() {
3974
- const f = new import_cement17.Future();
3975
- if (!this._metaIsLoading) {
3976
- f.resolve();
3977
- } else {
3978
- this._loaded.add(f);
3979
- }
3980
- return f;
3981
- }
3982
- async refresh() {
3983
- await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load();
3984
- await (await throwFalsy(this.loader).WALStore()).process();
3985
- }
3986
- async connect(refl) {
3987
- await this.connectMeta(refl);
3988
- await this.connectStorage(refl);
3989
- }
3990
- async connectMeta(refl) {
3991
- const loader = coerceLoader(refl);
3992
- if (!loader) throw this.logger.Error().Msg("connectMeta: loader is required").AsError();
3993
- this.loader = loader;
3994
- await this.onConnect();
3995
- const metaUrl = this.url.build().defParam(PARAM.STORE, "meta").URI();
3996
- const rgateway = await getStartedGateway({ loader }, metaUrl);
3997
- if (rgateway.isErr())
3998
- throw this.logger.Error().Result("err", rgateway).Url(metaUrl).Msg("connectMeta: gateway is required").AsError();
3999
- const dbName = metaUrl.getParam(PARAM.NAME);
4000
- if (!dbName) {
4001
- throw this.logger.Error().Url(metaUrl).Msg("connectMeta: dbName is required").AsError();
4002
- }
4003
- const gateway = rgateway.Ok();
4004
- const remote = await RemoteMetaStore(loader.sthis, metaUrl, {
4005
- gateway: gateway.gateway,
4006
- loader
4007
- });
4008
- this.loader.remoteMetaStore = remote;
4009
- this._metaIsLoading = true;
4010
- this.loader.ready().then(async () => {
4011
- return remote.load().then(async () => {
4012
- const res = await (0, import_cement17.exception2Result)(async () => {
4013
- return await (await throwFalsy(this.loader).WALStore()).process();
4014
- });
4015
- this._metaIsLoading = false;
4016
- for (const f of this._loaded) {
4017
- if (res.isErr()) {
4018
- f.reject(res.Err());
4019
- } else {
4020
- f.resolve();
4021
- }
4022
- }
4023
- this._loaded.clear();
4024
- });
4025
- });
4026
- }
4027
- async connectStorage(refl) {
4028
- const loader = coerceLoader(refl);
4029
- if (!loader) throw this.logger.Error().Msg("connectStorage: loader is required").AsError();
4030
- this.loader = loader;
4031
- const dataUrl = this.url.build().defParam(PARAM.STORE, "data").URI();
4032
- const rgateway = await getStartedGateway({ loader }, dataUrl);
4033
- if (rgateway.isErr())
4034
- throw this.logger.Error().Result("err", rgateway).Url(dataUrl).Msg("connectStorage: gateway is required").AsError();
4035
- const name = dataUrl.getParam(PARAM.NAME);
4036
- if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage: name is required").AsError;
4037
- loader.remoteCarStore = await RemoteDataStore(loader.sthis, this.url, {
4038
- gateway: rgateway.Ok().gateway,
4039
- loader
4040
- });
4041
- loader.remoteFileStore = loader.remoteCarStore;
4042
- }
4043
- // move this stuff to connect
4044
- // async getDashboardURL(compact = true) {
4045
- // const baseUrl = 'https://dashboard.fireproof.storage/'
4046
- // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
4047
- // // if (compact) {
4048
- // // await this.compact()
4049
- // // }
4050
- // const currents = await this.loader?.metaStore?.load()
4051
- // if (!currents) throw new Error("Can't sync empty ledger: save data first")
4052
- // if (currents.length > 1)
4053
- // throw new Error("Can't sync ledger with split heads: make an update first")
4054
- // const current = currents[0]
4055
- // const params = {
4056
- // car: current.car.toString()
4057
- // }
4058
- // if (current.key) {
4059
- // // @ts-ignore
4060
- // params.key = current.key.toString()
4061
- // }
4062
- // // @ts-ignore
4063
- // if (this.name) {
4064
- // // @ts-ignore
4065
- // params.name = this.name
4066
- // }
4067
- // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
4068
- // console.log('Import to dashboard: ' + url.toString())
4069
- // return url
4070
- // }
4071
- // openDashboard() {
4072
- // void this.getDashboardURL().then(url => {
4073
- // if (url) window.open(url.toString(), '_blank')
4074
- // })
4075
- // }
4076
- };
4077
-
4078
4378
  // src/crdt-helpers.ts
4079
4379
  var import_link2 = require("multiformats/link");
4080
4380
  var import_sha26 = require("multiformats/hashes/sha2");
@@ -4095,6 +4395,34 @@ function toString(key, logger) {
4095
4395
  throw logger.Error().Msg("Invalid key type").AsError();
4096
4396
  }
4097
4397
  }
4398
+ function sanitizeDocumentFields(obj) {
4399
+ if (Array.isArray(obj)) {
4400
+ return obj.map((item) => {
4401
+ if (typeof item === "object" && item !== null) {
4402
+ return sanitizeDocumentFields(item);
4403
+ }
4404
+ return item;
4405
+ });
4406
+ } else if (typeof obj === "object" && obj !== null) {
4407
+ const typedObj = obj;
4408
+ const result = {};
4409
+ for (const key in typedObj) {
4410
+ if (Object.hasOwnProperty.call(typedObj, key)) {
4411
+ const value = typedObj[key];
4412
+ if (value === null || !Number.isNaN(value) && value !== void 0) {
4413
+ if (typeof value === "object" && !key.startsWith("_")) {
4414
+ const sanitized = sanitizeDocumentFields(value);
4415
+ result[key] = sanitized;
4416
+ } else {
4417
+ result[key] = value;
4418
+ }
4419
+ }
4420
+ }
4421
+ }
4422
+ return result;
4423
+ }
4424
+ return obj;
4425
+ }
4098
4426
  async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
4099
4427
  let result = null;
4100
4428
  if (updates.length > 1) {
@@ -4184,7 +4512,8 @@ async function getValueFromCrdt(blocks, head, key, logger) {
4184
4512
  if (!head.length) throw logger.Debug().Msg("Getting from an empty ledger").AsError();
4185
4513
  const link = await (0, import_crdt.get)(blocks, head, key);
4186
4514
  if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
4187
- return await getValueFromLink(blocks, link, logger);
4515
+ const ret = await getValueFromLink(blocks, link, logger);
4516
+ return ret;
4188
4517
  }
4189
4518
  function readFiles(blocks, { doc }) {
4190
4519
  if (!doc) return;
@@ -4282,6 +4611,9 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4282
4611
  const { key, value } = ops[i];
4283
4612
  if (!keys.has(key)) {
4284
4613
  const docValue = await getValueFromLink(blocks, value, logger);
4614
+ if (key === PARAM.GENESIS_CID) {
4615
+ continue;
4616
+ }
4285
4617
  updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
4286
4618
  limit--;
4287
4619
  keys.add(key);
@@ -4295,8 +4627,10 @@ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], k
4295
4627
  }
4296
4628
  async function* getAllEntries(blocks, head, logger) {
4297
4629
  for await (const [key, link] of (0, import_crdt.entries)(blocks, head)) {
4298
- const docValue = await getValueFromLink(blocks, link, logger);
4299
- yield { id: key, value: docValue.doc, del: docValue.del };
4630
+ if (key !== PARAM.GENESIS_CID) {
4631
+ const docValue = await getValueFromLink(blocks, link, logger);
4632
+ yield { id: key, value: docValue.doc, del: docValue.del };
4633
+ }
4300
4634
  }
4301
4635
  }
4302
4636
  async function* clockVis(blocks, head) {
@@ -4441,6 +4775,10 @@ var CRDTClockImpl = class {
4441
4775
  this.notifyWatchers(internalUpdates || []);
4442
4776
  }
4443
4777
  notifyWatchers(updates) {
4778
+ updates = updates.filter((update) => update.id !== PARAM.GENESIS_CID);
4779
+ if (!updates.length) {
4780
+ return;
4781
+ }
4444
4782
  this.emptyWatchers.forEach((fn) => fn());
4445
4783
  this.watchers.forEach((fn) => fn(updates || []));
4446
4784
  }
@@ -4520,7 +4858,7 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
4520
4858
  try {
4521
4859
  head = await (0, import_clock4.advance)(tblocks, head, cid);
4522
4860
  } catch (e) {
4523
- logger.Debug().Err(e).Msg("failed to advance head");
4861
+ logger.Error().Err(e).Msg("failed to advance head");
4524
4862
  continue;
4525
4863
  }
4526
4864
  }
@@ -4579,6 +4917,17 @@ var CRDTImpl = class {
4579
4917
  }
4580
4918
  async bulk(updates) {
4581
4919
  await this.ready();
4920
+ updates = updates.map((dupdate) => ({
4921
+ ...dupdate,
4922
+ value: sanitizeDocumentFields(dupdate.value)
4923
+ }));
4924
+ if (this.clock.head.length === 0) {
4925
+ const value = { id: PARAM.GENESIS_CID, value: { _id: PARAM.GENESIS_CID } };
4926
+ await this._bulk([value]);
4927
+ }
4928
+ return await this._bulk(updates);
4929
+ }
4930
+ async _bulk(updates) {
4582
4931
  const prevHead = [...this.clock.head];
4583
4932
  const done = await this.blockstore.transaction(async (blocks) => {
4584
4933
  const { head } = await applyBulkUpdateToCrdt(
@@ -4649,6 +4998,22 @@ var CRDTImpl = class {
4649
4998
  }
4650
4999
  };
4651
5000
 
5001
+ // src/context.ts
5002
+ var Context = class {
5003
+ constructor() {
5004
+ this.ctx = /* @__PURE__ */ new Map();
5005
+ }
5006
+ set(key, value) {
5007
+ this.ctx.set(key, value);
5008
+ }
5009
+ get(key) {
5010
+ return this.ctx.get(key);
5011
+ }
5012
+ delete(key) {
5013
+ this.ctx.delete(key);
5014
+ }
5015
+ };
5016
+
4652
5017
  // src/ledger.ts
4653
5018
  var ledgers = new import_cement20.KeyedResolvOnce();
4654
5019
  function keyConfigOpts(sthis, name, opts) {
@@ -4693,6 +5058,9 @@ var LedgerShell = class {
4693
5058
  this.name = ref.name;
4694
5059
  ref.addShell(this);
4695
5060
  }
5061
+ attach(a) {
5062
+ return this.ref.attach(a);
5063
+ }
4696
5064
  get opts() {
4697
5065
  return this.ref.opts;
4698
5066
  }
@@ -4791,11 +5159,16 @@ var LedgerImpl = class {
4791
5159
  });
4792
5160
  return ret;
4793
5161
  }
5162
+ async attach(a) {
5163
+ await this.ready();
5164
+ return this.crdt.blockstore.loader.attach(a);
5165
+ }
4794
5166
  // readonly _asDb = new ResolveOnce<Database>();
4795
5167
  // asDB(): Database {
4796
5168
  // return this._asDb.once(() => new DatabaseImpl(this));
4797
5169
  // }
4798
5170
  subscribe(listener, updates) {
5171
+ this.ready();
4799
5172
  this.logger.Debug().Bool("updates", updates).Msg("subscribe");
4800
5173
  if (updates) {
4801
5174
  if (!this._listening) {
@@ -4837,26 +5210,6 @@ var LedgerImpl = class {
4837
5210
  }
4838
5211
  }
4839
5212
  };
4840
- function defaultURI2(sthis, name, curi, uri, store, ctx) {
4841
- ctx = ctx || {};
4842
- const ret = (curi ? import_cement20.URI.from(curi) : uri).build().setParam(PARAM.STORE, store).defParam(PARAM.NAME, name);
4843
- if (!ret.hasParam(PARAM.NAME)) {
4844
- throw sthis.logger.Error().Url(ret).Any("ctx", ctx).Msg("Ledger name is required").AsError();
4845
- }
4846
- if (ctx.idx) {
4847
- ret.defParam(PARAM.INDEX, "idx");
4848
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}-idx@`);
4849
- } else {
4850
- ret.defParam(PARAM.STORE_KEY, `@${ret.getParam(PARAM.NAME)}-${store}@`);
4851
- }
4852
- if (store === "data") {
4853
- if (ctx.file) {
4854
- } else {
4855
- ret.defParam(PARAM.SUFFIX, ".car");
4856
- }
4857
- }
4858
- return ret.URI();
4859
- }
4860
5213
  function toStoreURIRuntime(sthis, name, sopts) {
4861
5214
  sopts = sopts || {};
4862
5215
  if (!sopts.base) {
@@ -4870,16 +5223,19 @@ function toStoreURIRuntime(sthis, name, sopts) {
4870
5223
  const base = import_cement20.URI.from(sopts.base);
4871
5224
  return {
4872
5225
  idx: {
4873
- data: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { idx: true }),
4874
- file: defaultURI2(sthis, name, sopts.idx?.data, base, "data", { file: true, idx: true }),
4875
- meta: defaultURI2(sthis, name, sopts.idx?.meta, base, "meta", { idx: true }),
4876
- wal: defaultURI2(sthis, name, sopts.idx?.wal, base, "wal", { idx: true })
5226
+ car: ensureURIDefaults(sthis, name, sopts.idx?.car ?? sopts.data?.car, base, "car", { idx: true }),
5227
+ file: ensureURIDefaults(sthis, name, sopts.idx?.file ?? sopts.idx?.car ?? sopts.data?.file ?? sopts.data?.car, base, "file", {
5228
+ file: true,
5229
+ idx: true
5230
+ }),
5231
+ meta: ensureURIDefaults(sthis, name, sopts.idx?.meta ?? sopts.data?.meta, base, "meta", { idx: true }),
5232
+ wal: ensureURIDefaults(sthis, name, sopts.idx?.wal ?? sopts.data?.wal, base, "wal", { idx: true })
4877
5233
  },
4878
5234
  data: {
4879
- data: defaultURI2(sthis, name, sopts.data?.data, base, "data"),
4880
- file: defaultURI2(sthis, name, sopts.data?.data, base, "data", { file: true }),
4881
- meta: defaultURI2(sthis, name, sopts.data?.meta, base, "meta"),
4882
- wal: defaultURI2(sthis, name, sopts.data?.wal, base, "wal")
5235
+ car: ensureURIDefaults(sthis, name, sopts.data?.car, base, "car"),
5236
+ file: ensureURIDefaults(sthis, name, sopts.data?.file ?? sopts.data?.car, base, "file", { file: true }),
5237
+ meta: ensureURIDefaults(sthis, name, sopts.data?.meta, base, "meta"),
5238
+ wal: ensureURIDefaults(sthis, name, sopts.data?.wal, base, "wal")
4883
5239
  }
4884
5240
  };
4885
5241
  }
@@ -4943,6 +5299,6 @@ __export(file_exports, {
4943
5299
 
4944
5300
  // src/version.ts
4945
5301
  var PACKAGE_VERSION = Object.keys({
4946
- "0.20.0-dev-preview-40": "xxxx"
5302
+ "0.20.0-dev-preview-50": "xxxx"
4947
5303
  })[0];
4948
5304
  //# sourceMappingURL=index.cjs.map