@fireproof/core 0.19.8-dev-global → 0.19.8-dev-series-2

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 (64) hide show
  1. package/README.md +34 -0
  2. package/chunk-7EWIAXTM.js +7 -0
  3. package/chunk-7EWIAXTM.js.map +1 -0
  4. package/chunk-OFGPKRCM.js +290 -0
  5. package/chunk-OFGPKRCM.js.map +1 -0
  6. package/chunk-PB4BKL4O.js +7 -0
  7. package/chunk-PB4BKL4O.js.map +1 -0
  8. package/chunk-WS3YRPIA.js +75 -0
  9. package/chunk-WS3YRPIA.js.map +1 -0
  10. package/deno.json +22 -0
  11. package/gateway-5FCWPX5W.js +144 -0
  12. package/gateway-5FCWPX5W.js.map +1 -0
  13. package/{store-indexdb-WLRSICCB.js → gateway-H7UD6TNB.js} +49 -82
  14. package/gateway-H7UD6TNB.js.map +1 -0
  15. package/index.cjs +2365 -1875
  16. package/index.cjs.map +1 -1
  17. package/index.d.cts +663 -535
  18. package/index.d.ts +663 -535
  19. package/index.global.js +26742 -20717
  20. package/index.global.js.map +1 -1
  21. package/index.js +1691 -1094
  22. package/index.js.map +1 -1
  23. package/key-bag-file-WADZBHYG.js +54 -0
  24. package/key-bag-file-WADZBHYG.js.map +1 -0
  25. package/key-bag-indexdb-PGVAI3FJ.js +50 -0
  26. package/key-bag-indexdb-PGVAI3FJ.js.map +1 -0
  27. package/mem-filesystem-YPPJV7Q2.js +41 -0
  28. package/mem-filesystem-YPPJV7Q2.js.map +1 -0
  29. package/metafile-cjs.json +1 -1
  30. package/metafile-esm.json +1 -1
  31. package/metafile-iife.json +1 -1
  32. package/node-filesystem-INX4ZTHE.js +45 -0
  33. package/node-filesystem-INX4ZTHE.js.map +1 -0
  34. package/package.json +14 -8
  35. package/tests/blockstore/fragment-gateway.test.ts +107 -0
  36. package/tests/blockstore/keyed-crypto.test.ts +332 -0
  37. package/tests/blockstore/loader.test.ts +24 -19
  38. package/tests/blockstore/store.test.ts +51 -40
  39. package/tests/blockstore/transaction.test.ts +19 -15
  40. package/tests/fireproof/all-gateway.test.ts +394 -0
  41. package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.car +0 -0
  42. package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.ts +316 -0
  43. package/tests/fireproof/config.test.ts +94 -78
  44. package/tests/fireproof/crdt.test.ts +34 -28
  45. package/tests/fireproof/database.test.ts +22 -14
  46. package/tests/fireproof/fireproof.test.fixture.ts +133 -0
  47. package/tests/fireproof/fireproof.test.ts +331 -219
  48. package/tests/fireproof/hello.test.ts +34 -18
  49. package/tests/fireproof/indexer.test.ts +34 -27
  50. package/tests/fireproof/utils.test.ts +84 -0
  51. package/tests/helpers.ts +28 -57
  52. package/tests/www/todo-local.html +1 -1
  53. package/tests/www/todo.html +12 -15
  54. package/utils-QO2HIWGI.js +14 -0
  55. package/utils-QO2HIWGI.js.map +1 -0
  56. package/chunk-BNL4PVBF.js +0 -314
  57. package/chunk-BNL4PVBF.js.map +0 -1
  58. package/chunk-JW2QT6BF.js +0 -184
  59. package/chunk-JW2QT6BF.js.map +0 -1
  60. package/node-sys-container-MIEX6ELJ.js +0 -29
  61. package/node-sys-container-MIEX6ELJ.js.map +0 -1
  62. package/store-file-VJ6BI4II.js +0 -191
  63. package/store-file-VJ6BI4II.js.map +0 -1
  64. package/store-indexdb-WLRSICCB.js.map +0 -1
package/index.js CHANGED
@@ -1,30 +1,33 @@
1
+ import {
2
+ FILESTORE_VERSION
3
+ } from "./chunk-7EWIAXTM.js";
4
+ import {
5
+ getFileName,
6
+ getFileSystem,
7
+ getPath,
8
+ toArrayBuffer
9
+ } from "./chunk-WS3YRPIA.js";
10
+ import {
11
+ INDEXDB_VERSION
12
+ } from "./chunk-PB4BKL4O.js";
1
13
  import {
2
14
  NotFoundError,
3
15
  Result,
16
+ UInt8ArrayEqual,
17
+ __export,
4
18
  dataDir,
5
- decodeFile,
6
- encodeFile,
7
19
  ensureLogger,
8
- exception2Result,
20
+ ensureSuperLog,
21
+ ensureSuperThis,
9
22
  exceptionWrapper,
10
23
  getKey,
11
24
  getName,
12
25
  getStore,
13
- isNotFoundError,
14
- runtime_exports,
15
- toCryptoOpts
16
- } from "./chunk-BNL4PVBF.js";
17
- import {
18
- SysContainer,
19
- __export,
20
- falsyToUndef,
21
- isFalsy,
22
- throwFalsy
23
- } from "./chunk-JW2QT6BF.js";
26
+ isNotFoundError
27
+ } from "./chunk-OFGPKRCM.js";
24
28
 
25
29
  // src/database.ts
26
- import { uuidv7 } from "uuidv7";
27
- import { ResolveOnce as ResolveOnce5 } from "@adviser/cement";
30
+ import { ResolveOnce as ResolveOnce6 } from "@adviser/cement";
28
31
 
29
32
  // src/write-queue.ts
30
33
  function writeQueue(worker, payload = Infinity, unbounded = false) {
@@ -67,13 +70,83 @@ function writeQueue(worker, payload = Infinity, unbounded = false) {
67
70
  }
68
71
 
69
72
  // src/crdt.ts
70
- import { ResolveOnce as ResolveOnce4 } from "@adviser/cement";
73
+ import { ResolveOnce as ResolveOnce5 } from "@adviser/cement";
74
+
75
+ // src/runtime/wait-pr-multiformats/block.ts
76
+ var block_exports = {};
77
+ __export(block_exports, {
78
+ Block: () => Block,
79
+ create: () => create,
80
+ createUnsafe: () => createUnsafe,
81
+ decode: () => decode,
82
+ encode: () => encode
83
+ });
84
+ import { bytes as binary, CID } from "multiformats";
85
+ import { Block as mfBlock } from "multiformats/block";
86
+ var Block = mfBlock;
87
+ async function decode({
88
+ bytes,
89
+ codec: codec3,
90
+ hasher: hasher7
91
+ }) {
92
+ if (bytes == null) throw new Error('Missing required argument "bytes"');
93
+ if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
94
+ const value = await Promise.resolve(codec3.decode(bytes));
95
+ const hash = await hasher7.digest(bytes);
96
+ const cid = CID.create(1, codec3.code, hash);
97
+ return new mfBlock({ value, bytes, cid });
98
+ }
99
+ async function encode({
100
+ value,
101
+ codec: codec3,
102
+ hasher: hasher7
103
+ }) {
104
+ if (typeof value === "undefined") throw new Error('Missing required argument "value"');
105
+ if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
106
+ const bytes = await Promise.resolve(codec3.encode(value));
107
+ const hash = await hasher7.digest(bytes);
108
+ const cid = CID.create(1, codec3.code, hash);
109
+ return new mfBlock({ value, bytes, cid });
110
+ }
111
+ async function create({
112
+ bytes,
113
+ cid,
114
+ hasher: hasher7,
115
+ codec: codec3
116
+ }) {
117
+ if (bytes == null) throw new Error('Missing required argument "bytes"');
118
+ if (hasher7 == null) throw new Error('Missing required argument "hasher"');
119
+ const value = await Promise.resolve(codec3.decode(bytes));
120
+ const hash = await hasher7.digest(bytes);
121
+ if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
122
+ throw new Error("CID hash does not match bytes");
123
+ }
124
+ return createUnsafe({
125
+ bytes,
126
+ cid,
127
+ value,
128
+ codec: codec3
129
+ });
130
+ }
131
+ async function createUnsafe({
132
+ bytes,
133
+ cid,
134
+ value: maybeValue,
135
+ codec: codec3
136
+ }) {
137
+ const value = await Promise.resolve(maybeValue !== void 0 ? maybeValue : codec3?.decode(bytes));
138
+ if (value === void 0) throw new Error('Missing required argument, must either provide "value" or "codec"');
139
+ return new Block({
140
+ cid,
141
+ bytes,
142
+ value
143
+ });
144
+ }
71
145
 
72
146
  // src/crdt-helpers.ts
73
- import { encode as encode3, decode as decode3, Block as Block2 } from "multiformats/block";
74
- import { parse as parse2 } from "multiformats/link";
75
- import { sha256 as hasher2 } from "multiformats/hashes/sha2";
76
- import * as codec2 from "@ipld/dag-cbor";
147
+ import { parse as parse3 } from "multiformats/link";
148
+ import { sha256 as hasher5 } from "multiformats/hashes/sha2";
149
+ import * as codec from "@ipld/dag-cbor";
77
150
  import { put, get, entries, root } from "@web3-storage/pail/crdt";
78
151
  import { EventFetcher, vis } from "@web3-storage/pail/clock";
79
152
  import * as Batch from "@web3-storage/pail/crdt/batch";
@@ -84,512 +157,146 @@ __export(blockstore_exports, {
84
157
  BaseBlockstore: () => BaseBlockstore,
85
158
  CarTransaction: () => CarTransaction,
86
159
  CompactionFetcher: () => CompactionFetcher,
87
- ConnectREST: () => ConnectREST,
88
160
  ConnectionBase: () => ConnectionBase,
89
- DataStore: () => DataStore,
90
161
  EncryptedBlockstore: () => EncryptedBlockstore,
91
- Loadable: () => Loadable,
162
+ FragmentGateway: () => FragmentGateway,
92
163
  Loader: () => Loader,
93
- MetaStore: () => MetaStore,
94
- NotFoundError: () => NotFoundError,
95
- RemoteWAL: () => RemoteWAL,
96
- isNotFoundError: () => isNotFoundError,
164
+ addCryptoKeyToGatewayMetaPayload: () => addCryptoKeyToGatewayMetaPayload,
165
+ ensureStart: () => ensureStart,
166
+ getGatewayFromURL: () => getGatewayFromURL,
97
167
  parseCarFile: () => parseCarFile,
98
168
  registerStoreProtocol: () => registerStoreProtocol,
169
+ setCryptoKeyFromGatewayMetaPayload: () => setCryptoKeyFromGatewayMetaPayload,
99
170
  testStoreFactory: () => testStoreFactory,
100
- toStoreRuntime: () => toStoreRuntime,
101
- toURL: () => toURL
171
+ toCIDBlock: () => toCIDBlock,
172
+ toStoreRuntime: () => toStoreRuntime
102
173
  });
103
174
 
104
- // src/blockstore/connection-base.ts
105
- import { EventBlock, decodeEventBlock } from "@web3-storage/pail/clock";
106
- import { MemoryBlockstore } from "@web3-storage/pail/block";
175
+ // src/blockstore/types.ts
176
+ function toCIDBlock(block) {
177
+ return block;
178
+ }
107
179
 
108
- // src/blockstore/task-manager.ts
109
- var TaskManager = class {
110
- constructor(loader) {
111
- this.eventsWeHandled = /* @__PURE__ */ new Set();
112
- this.queue = [];
113
- this.isProcessing = false;
114
- this.loader = loader;
115
- this.logger = ensureLogger(loader.logger, "TaskManager");
116
- }
117
- async handleEvent(eventBlock) {
118
- const cid = eventBlock.cid.toString();
119
- const parents = eventBlock.value.parents.map((cid2) => cid2.toString());
120
- for (const parent of parents) {
121
- this.eventsWeHandled.add(parent);
122
- }
123
- this.queue.push({ cid, eventBlock, retries: 0 });
124
- this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
125
- void this.processQueue();
126
- }
127
- async processQueue() {
128
- if (this.isProcessing) return;
129
- this.isProcessing = true;
130
- const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
131
- const first = filteredQueue[0];
132
- if (!first) {
133
- return;
134
- }
135
- try {
136
- this.loader?.remoteMetaStore?.handleByteHeads([first.eventBlock.value.data.dbMeta]);
137
- this.eventsWeHandled.add(first.cid);
138
- this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
139
- } catch (err) {
140
- if (first.retries++ > 3) {
141
- this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
142
- this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
143
- }
144
- await new Promise((resolve) => setTimeout(resolve, 50));
145
- throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
146
- } finally {
147
- this.isProcessing = false;
148
- if (this.queue.length > 0) {
149
- void this.processQueue();
150
- }
151
- }
152
- }
153
- };
180
+ // src/blockstore/store-factory.ts
181
+ import { KeyedResolvOnce as KeyedResolvOnce2, URI as URI5 } from "@adviser/cement";
154
182
 
155
- // src/blockstore/connection-base.ts
156
- var ConnectionBase = class {
157
- constructor(logger) {
158
- // readonly ready: Promise<unknown>;
159
- // todo move to LRU blockstore https://github.com/web3-storage/w3clock/blob/main/src/worker/block.js
160
- this.eventBlocks = new MemoryBlockstore();
161
- this.parents = [];
162
- this.loaded = Promise.resolve();
163
- this.logger = ensureLogger(logger, "ConnectionBase");
164
- }
165
- async refresh() {
166
- await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load("main");
167
- await (await throwFalsy(this.loader).remoteWAL())._process();
168
- }
169
- connect({ loader }) {
170
- if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
171
- this.connectMeta({ loader });
172
- this.connectStorage({ loader });
173
- }
174
- connectMeta({ loader }) {
175
- if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
176
- this.loader = loader;
177
- this.taskManager = new TaskManager(loader);
178
- this.onConnect();
179
- this.logger.Warn().Msg("connectMeta: connecting to remote meta store is disabled");
180
- }
181
- async onConnect() {
182
- return;
183
- }
184
- connectStorage({ loader }) {
185
- if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
186
- this.loader = loader;
187
- this.logger.Warn().Msg("connectStorage: connecting to remote meta store is disabled");
188
- }
189
- async createEventBlock(bytes) {
190
- const data = {
191
- dbMeta: bytes
192
- };
193
- const event = await EventBlock.create(
194
- data,
195
- this.parents
183
+ // src/runtime/files.ts
184
+ var files_exports = {};
185
+ __export(files_exports, {
186
+ decodeFile: () => decodeFile,
187
+ encodeFile: () => encodeFile
188
+ });
189
+ import * as UnixFS from "@ipld/unixfs";
190
+ import * as raw from "multiformats/codecs/raw";
191
+ import { withMaxChunkSize } from "@ipld/unixfs/file/chunker/fixed";
192
+ import { withWidth } from "@ipld/unixfs/file/layout/balanced";
193
+ import { exporter } from "ipfs-unixfs-exporter";
194
+ var queuingStrategy = UnixFS.withCapacity();
195
+ var settings = UnixFS.configure({
196
+ fileChunkEncoder: raw,
197
+ smallFileEncoder: raw,
198
+ chunker: withMaxChunkSize(1024 * 1024),
199
+ fileLayout: withWidth(1024)
200
+ });
201
+ async function collect(collectable) {
202
+ const chunks = [];
203
+ await collectable.pipeTo(
204
+ new WritableStream({
205
+ write(chunk) {
206
+ chunks.push(chunk);
207
+ }
208
+ })
209
+ );
210
+ return chunks;
211
+ }
212
+ async function encodeFile(blob) {
213
+ const readable = createFileEncoderStream(blob);
214
+ const blocks = await collect(readable);
215
+ return { cid: blocks.at(-1).cid, blocks };
216
+ }
217
+ async function decodeFile(blocks, cid, meta) {
218
+ const entry = await exporter(cid.toString(), blocks, { length: meta.size });
219
+ const chunks = [];
220
+ for await (const chunk of entry.content()) {
221
+ chunks.push(chunk);
222
+ }
223
+ return new File(chunks, entry.name, { type: meta.type, lastModified: 0 });
224
+ }
225
+ function createFileEncoderStream(blob) {
226
+ const { readable, writable } = new TransformStream({}, queuingStrategy);
227
+ const unixfsWriter = UnixFS.createWriter({ writable, settings });
228
+ const fileBuilder = new UnixFSFileBuilder("", blob);
229
+ void (async () => {
230
+ await fileBuilder.finalize(unixfsWriter);
231
+ await unixfsWriter.close();
232
+ })();
233
+ return readable;
234
+ }
235
+ var UnixFSFileBuilder = class {
236
+ #file;
237
+ constructor(name, file) {
238
+ this.name = name;
239
+ this.#file = file;
240
+ }
241
+ async finalize(writer) {
242
+ const unixfsFileWriter = UnixFS.createFileWriter(writer);
243
+ await this.#file.stream().pipeTo(
244
+ new WritableStream({
245
+ async write(chunk) {
246
+ await unixfsFileWriter.write(chunk);
247
+ }
248
+ })
196
249
  );
197
- await this.eventBlocks.put(event.cid, event.bytes);
198
- return event;
199
- }
200
- async decodeEventBlock(bytes) {
201
- const event = await decodeEventBlock(bytes);
202
- return event;
250
+ return await unixfsFileWriter.close();
203
251
  }
204
- // move this stuff to connect
205
- // async getDashboardURL(compact = true) {
206
- // const baseUrl = 'https://dashboard.fireproof.storage/'
207
- // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
208
- // // if (compact) {
209
- // // await this.compact()
210
- // // }
211
- // const currents = await this.loader?.metaStore?.load()
212
- // if (!currents) throw new Error("Can't sync empty database: save data first")
213
- // if (currents.length > 1)
214
- // throw new Error("Can't sync database with split heads: make an update first")
215
- // const current = currents[0]
216
- // const params = {
217
- // car: current.car.toString()
218
- // }
219
- // if (current.key) {
220
- // // @ts-ignore
221
- // params.key = current.key.toString()
222
- // }
223
- // // @ts-ignore
224
- // if (this.name) {
225
- // // @ts-ignore
226
- // params.name = this.name
227
- // }
228
- // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
229
- // console.log('Import to dashboard: ' + url.toString())
230
- // return url
231
- // }
232
- // openDashboard() {
233
- // void this.getDashboardURL().then(url => {
234
- // if (url) window.open(url.toString(), '_blank')
235
- // })
236
- // }
237
252
  };
238
253
 
239
- // src/blockstore/connect-rest.ts
240
- var ConnectREST = class extends ConnectionBase {
241
- constructor(base, logger) {
242
- super(ensureLogger(logger, "ConnectREST"));
243
- this.baseUrl = new URL(base);
244
- }
245
- async dataUpload(bytes, params) {
246
- const carCid = params.car.toString();
247
- const uploadURL = new URL(`/cars/${carCid}.car`, this.baseUrl);
248
- const done = await fetch(uploadURL, { method: "PUT", body: bytes });
249
- if (!done.ok) {
250
- throw this.logger.Error().Msg("failed to upload data " + done.statusText);
251
- }
252
- }
253
- async dataDownload(params) {
254
- const { car } = params;
255
- const fetchFromUrl = new URL(`/cars/${car.toString()}.car`, this.baseUrl);
256
- const response = await fetch(fetchFromUrl);
257
- if (!response.ok) {
258
- return void 0;
259
- }
260
- const bytes = new Uint8Array(await response.arrayBuffer());
261
- return bytes;
254
+ // src/blockstore/store.ts
255
+ import { format as format2, parse as parse2 } from "@ipld/dag-json";
256
+ import { exception2Result, ResolveOnce as ResolveOnce3, Result as Result5 } from "@adviser/cement";
257
+
258
+ // src/types.ts
259
+ function isFalsy(value) {
260
+ return value === false && value === null && value === void 0;
261
+ }
262
+ function throwFalsy(value) {
263
+ if (isFalsy(value)) {
264
+ throw new Error("value is Falsy");
262
265
  }
263
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
264
- async metaUpload(bytes, params) {
266
+ return value;
267
+ }
268
+ function falsyToUndef(value) {
269
+ if (isFalsy(value)) {
265
270
  return void 0;
266
271
  }
267
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
268
- async metaDownload(params) {
269
- return [];
270
- }
271
- };
272
-
273
- // src/blockstore/store-factory.ts
274
- import { KeyedResolvOnce } from "@adviser/cement";
275
-
276
- // src/blockstore/store.ts
277
- import pLimit2 from "p-limit";
278
- import { format, parse } from "@ipld/dag-json";
279
- import { ResolveOnce as ResolveOnce2, Result as Result2 } from "@adviser/cement";
272
+ return value;
273
+ }
280
274
 
281
275
  // src/blockstore/loader.ts
282
276
  import pLimit from "p-limit";
283
277
  import { CarReader } from "@ipld/car";
284
- import { ResolveOnce } from "@adviser/cement";
285
-
286
- // src/blockstore/types.ts
287
- function toCIDBlock(block) {
288
- return block;
289
- }
278
+ import { ResolveOnce as ResolveOnce2 } from "@adviser/cement";
290
279
 
291
280
  // src/blockstore/loader-helpers.ts
292
- import { encode, decode } from "multiformats/block";
293
281
  import { sha256 as hasher } from "multiformats/hashes/sha2";
294
- import * as raw from "multiformats/codecs/raw";
295
- import * as CBW from "@ipld/car/buffer-writer";
296
- import * as codec from "@ipld/dag-cbor";
297
- async function encodeCarFile(roots, t) {
298
- let size = 0;
299
- const headerSize = CBW.headerLength({ roots });
300
- size += headerSize;
301
- for (const { cid, bytes } of t.entries()) {
302
- size += CBW.blockLength({ cid, bytes });
303
- }
304
- const buffer = new Uint8Array(size);
305
- const writer = CBW.createWriter(buffer, { headerSize });
306
- for (const r of roots) {
307
- writer.addRoot(r);
308
- }
309
- for (const { cid, bytes } of t.entries()) {
310
- writer.write({ cid, bytes });
311
- }
312
- writer.close();
313
- return await encode({ value: writer.bytes, hasher, codec: raw });
314
- }
315
- async function encodeCarHeader(fp) {
316
- return await encode({
317
- value: { fp },
318
- hasher,
319
- codec
320
- });
321
- }
282
+ import * as dagCodec from "@ipld/dag-cbor";
322
283
  async function parseCarFile(reader, logger) {
323
284
  const roots = await reader.getRoots();
324
285
  const header = await reader.get(roots[0]);
325
286
  if (!header) throw logger.Error().Msg("missing header block").AsError();
326
- const { value } = await decode({ bytes: header.bytes, hasher, codec });
327
- const fpvalue = value;
287
+ const dec = await decode({ bytes: header.bytes, hasher, codec: dagCodec });
288
+ const fpvalue = dec.value;
328
289
  if (fpvalue && !fpvalue.fp) {
329
290
  throw logger.Error().Msg("missing fp").AsError();
330
291
  }
331
292
  return fpvalue.fp;
332
293
  }
333
294
 
334
- // src/blockstore/encrypt-helpers.ts
335
- import { sha256 } from "multiformats/hashes/sha2";
336
- import { CID as CID2 } from "multiformats";
337
- import { encode as encode2, decode as decode2, create as mfCreate } from "multiformats/block";
338
- import * as dagcbor from "@ipld/dag-cbor";
339
- import { MemoryBlockstore as MemoryBlockstore2 } from "@web3-storage/pail/block";
340
- import { bf } from "prolly-trees/utils";
341
- import { nocache as cache } from "prolly-trees/cache";
342
- import { create, load } from "prolly-trees/cid-set";
343
-
344
- // src/blockstore/encrypt-codec.ts
345
- import { CID } from "multiformats";
346
- function makeCodec(ilogger, crypto, randomBytes) {
347
- const logger = ensureLogger(ilogger, "makeCodec");
348
- const enc32 = (value) => {
349
- value = +value;
350
- const buff = new Uint8Array(4);
351
- buff[3] = value >>> 24;
352
- buff[2] = value >>> 16;
353
- buff[1] = value >>> 8;
354
- buff[0] = value & 255;
355
- return buff;
356
- };
357
- const readUInt32LE = (buffer) => {
358
- const offset = buffer.byteLength - 4;
359
- return (buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16) + buffer[offset + 3] * 16777216;
360
- };
361
- const concat = (buffers) => {
362
- const uint8Arrays = buffers.map((b) => b instanceof ArrayBuffer ? new Uint8Array(b) : b);
363
- const totalLength = uint8Arrays.reduce((sum, arr) => sum + arr.length, 0);
364
- const result = new Uint8Array(totalLength);
365
- let offset = 0;
366
- for (const arr of uint8Arrays) {
367
- result.set(arr, offset);
368
- offset += arr.length;
369
- }
370
- return result;
371
- };
372
- const encode4 = ({ iv, bytes }) => concat([iv, bytes]);
373
- const decode4 = (bytes) => {
374
- const iv = bytes.subarray(0, 12);
375
- bytes = bytes.slice(12);
376
- return { iv, bytes };
377
- };
378
- const code = 3145728 + 1337;
379
- async function subtleKey(key) {
380
- return await crypto.importKey(
381
- "raw",
382
- // raw or jwk
383
- key,
384
- // raw data
385
- "AES-GCM",
386
- false,
387
- // extractable
388
- ["encrypt", "decrypt"]
389
- );
390
- }
391
- const decrypt = async ({ key, value }) => {
392
- const { bytes: inBytes, iv } = value;
393
- const cryKey = await subtleKey(key);
394
- const deBytes = await crypto.decrypt(
395
- {
396
- name: "AES-GCM",
397
- iv,
398
- tagLength: 128
399
- },
400
- cryKey,
401
- inBytes
402
- );
403
- const bytes = new Uint8Array(deBytes);
404
- const len = readUInt32LE(bytes.subarray(0, 4));
405
- const cid = CID.decode(bytes.subarray(4, 4 + len));
406
- return { cid, bytes: bytes.subarray(4 + len) };
407
- };
408
- const encrypt = async ({ key, cid, bytes }) => {
409
- const len = enc32(cid.bytes.byteLength);
410
- const iv = randomBytes(12);
411
- const msg = concat([len, cid.bytes, bytes]);
412
- try {
413
- const cryKey = await subtleKey(key);
414
- const deBytes = await crypto.encrypt(
415
- {
416
- name: "AES-GCM",
417
- iv,
418
- tagLength: 128
419
- },
420
- cryKey,
421
- msg
422
- );
423
- bytes = new Uint8Array(deBytes);
424
- } catch (e) {
425
- throw logger.Error().Err(e).Msg("encrypt failed").AsError();
426
- }
427
- return { value: { bytes, iv } };
428
- };
429
- const cryptoFn = (key) => {
430
- return { encrypt: (opts) => encrypt({ ...opts, key }), decrypt: (opts) => decrypt({ ...opts, key }) };
431
- };
432
- const name = "jchris@encrypted-block:aes-gcm";
433
- return { encode: encode4, decode: decode4, code, name, encrypt, decrypt, crypto: cryptoFn };
434
- }
435
-
436
- // src/blockstore/encrypt-helpers.ts
437
- function carLogIncludesGroup(list, cidMatch) {
438
- return list.some((cid) => {
439
- return cid.toString() === cidMatch.toString();
440
- });
441
- }
442
- function makeEncDec(logger, crypto, randomBytes) {
443
- const codec4 = makeCodec(logger, crypto, randomBytes);
444
- const encrypt = async function* ({
445
- get: get2,
446
- cids,
447
- hasher: hasher4,
448
- key,
449
- cache: cache3,
450
- chunker: chunker2,
451
- root: root3
452
- }) {
453
- const set = /* @__PURE__ */ new Set();
454
- let eroot;
455
- if (!carLogIncludesGroup(cids, root3)) cids.push(root3);
456
- for (const cid of cids) {
457
- const unencrypted = await get2(cid);
458
- if (!unencrypted) throw logger.Error().Ref("cid", cid).Msg("missing cid block").AsError();
459
- const encrypted = await codec4.encrypt({ ...unencrypted, key });
460
- const block2 = await encode2({ ...encrypted, codec: codec4, hasher: hasher4 });
461
- yield block2;
462
- set.add(block2.cid.toString());
463
- if (unencrypted.cid.equals(root3)) eroot = block2.cid;
464
- }
465
- if (!eroot) throw logger.Error().Msg("cids does not include root").AsError();
466
- const list = [...set].map((s) => CID2.parse(s));
467
- let last;
468
- for await (const node of create({ list, get: get2, cache: cache3, chunker: chunker2, hasher: hasher4, codec: dagcbor })) {
469
- const block2 = await node.block;
470
- yield block2;
471
- last = block2;
472
- }
473
- if (!last) throw logger.Error().Msg("missing last block").AsError();
474
- const head = [eroot, last.cid];
475
- const block = await encode2({ value: head, codec: dagcbor, hasher: hasher4 });
476
- yield block;
477
- };
478
- const decrypt = async function* ({
479
- root: root3,
480
- get: get2,
481
- key,
482
- cache: cache3,
483
- chunker: chunker2,
484
- hasher: hasher4
485
- }) {
486
- const getWithDecode = async (cid) => get2(cid).then(async (block) => {
487
- if (!block) return;
488
- const decoded = await decode2({ ...block, codec: dagcbor, hasher: hasher4 });
489
- return decoded;
490
- });
491
- const getWithDecrypt = async (cid) => get2(cid).then(async (block) => {
492
- if (!block) return;
493
- const decoded = await decode2({ ...block, codec: codec4, hasher: hasher4 });
494
- return decoded;
495
- });
496
- const decodedRoot = await getWithDecode(root3);
497
- if (!decodedRoot) throw logger.Error().Msg("missing root").AsError();
498
- if (!decodedRoot.bytes) throw logger.Error().Msg("missing bytes").AsError();
499
- const {
500
- value: [eroot, tree]
501
- } = decodedRoot;
502
- const rootBlock = await get2(eroot);
503
- if (!rootBlock) throw logger.Error().Msg("missing root block").AsError();
504
- const cidset = await load({ cid: tree, get: getWithDecode, cache: cache3, chunker: chunker2, codec: codec4, hasher: hasher4 });
505
- const { result: nodes } = await cidset.getAllEntries();
506
- const unwrap = async (eblock) => {
507
- if (!eblock) throw logger.Error().Msg("missing block").AsError();
508
- if (!eblock.value) {
509
- eblock = await decode2({ ...eblock, codec: codec4, hasher: hasher4 });
510
- if (!eblock.value) throw logger.Error().Msg("missing value").AsError();
511
- }
512
- const { bytes, cid } = await codec4.decrypt({ ...eblock, key }).catch((e) => {
513
- throw e;
514
- });
515
- const block = await mfCreate({ cid, bytes, hasher: hasher4, codec: codec4 });
516
- return block;
517
- };
518
- const promises = [];
519
- for (const { cid } of nodes) {
520
- if (!rootBlock.cid.equals(cid)) promises.push(getWithDecrypt(cid).then(unwrap));
521
- }
522
- yield* promises;
523
- yield unwrap(rootBlock);
524
- };
525
- return { encrypt, decrypt };
526
- }
527
- var chunker = bf(30);
528
- function hexStringToUint8Array(hexString) {
529
- const length = hexString.length;
530
- const uint8Array = new Uint8Array(length / 2);
531
- for (let i = 0; i < length; i += 2) {
532
- uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
533
- }
534
- return uint8Array;
535
- }
536
- async function encryptedEncodeCarFile(logger, crypto, key, rootCid, t) {
537
- const encryptionKey = hexStringToUint8Array(key);
538
- const encryptedBlocks = new MemoryBlockstore2();
539
- const cidsToEncrypt = [];
540
- for (const { cid, bytes } of t.entries()) {
541
- cidsToEncrypt.push(cid);
542
- const g = await t.get(cid);
543
- if (!g) throw logger.Error().Ref("cid", cid).Int("bytes", bytes.length).Msg("missing cid block").AsError();
544
- }
545
- let last = null;
546
- const { encrypt } = makeEncDec(logger, crypto, crypto.randomBytes);
547
- for await (const block of encrypt({
548
- cids: cidsToEncrypt,
549
- get: t.get.bind(t),
550
- key: encryptionKey,
551
- hasher: sha256,
552
- chunker,
553
- cache,
554
- root: rootCid
555
- })) {
556
- await encryptedBlocks.put(block.cid, block.bytes);
557
- last = block;
558
- }
559
- if (!last) throw logger.Error().Msg("no blocks encrypted").AsError();
560
- const encryptedCar = await encodeCarFile([last.cid], encryptedBlocks);
561
- return encryptedCar;
562
- }
563
- async function decodeEncryptedCar(logger, crypto, key, reader) {
564
- const roots = await reader.getRoots();
565
- const root3 = roots[0];
566
- return await decodeCarBlocks(logger, crypto, root3, reader.get.bind(reader), key);
567
- }
568
- async function decodeCarBlocks(logger, crypto, root3, get2, keyMaterial) {
569
- const decryptionKeyUint8 = hexStringToUint8Array(keyMaterial);
570
- const decryptionKey = decryptionKeyUint8.buffer.slice(0, decryptionKeyUint8.byteLength);
571
- const decryptedBlocks = new MemoryBlockstore2();
572
- let last = null;
573
- const { decrypt } = makeEncDec(logger, crypto, crypto.randomBytes);
574
- for await (const block of decrypt({
575
- root: root3,
576
- get: get2,
577
- key: decryptionKey,
578
- hasher: sha256,
579
- chunker,
580
- cache
581
- })) {
582
- await decryptedBlocks.put(block.cid, block.bytes);
583
- last = block;
584
- }
585
- if (!last) throw logger.Error().Msg("no blocks decrypted").AsError();
586
- return { blocks: decryptedBlocks, root: last.cid };
587
- }
588
-
589
295
  // src/blockstore/transaction.ts
590
- import { MemoryBlockstore as MemoryBlockstore3 } from "@web3-storage/pail/block";
591
- var CarTransaction = class extends MemoryBlockstore3 {
592
- constructor(parent, opts = { add: true }) {
296
+ import { MemoryBlockstore } from "@web3-storage/pail/block";
297
+ import { toCryptoRuntime } from "@adviser/cement";
298
+ var CarTransaction = class extends MemoryBlockstore {
299
+ constructor(parent, opts = { add: true, noLoader: false }) {
593
300
  super();
594
301
  if (opts.add) {
595
302
  parent.transactions.add(this);
@@ -603,8 +310,8 @@ var CarTransaction = class extends MemoryBlockstore3 {
603
310
  return super.get(cid);
604
311
  }
605
312
  };
606
- function defaultedBlockstoreRuntime(opts, component, ctx) {
607
- const logger = ensureLogger(opts, component, ctx);
313
+ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
314
+ const logger = ensureLogger(sthis, component, ctx);
608
315
  const store = opts.store || {};
609
316
  return {
610
317
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -621,22 +328,24 @@ function defaultedBlockstoreRuntime(opts, component, ctx) {
621
328
  threshold: 1e3 * 1e3,
622
329
  ...opts,
623
330
  logger,
624
- crypto: toCryptoOpts(opts.crypto),
331
+ keyBag: opts.keyBag || {},
332
+ crypto: toCryptoRuntime(opts.crypto),
625
333
  store,
626
- storeRuntime: toStoreRuntime(store, logger)
334
+ storeRuntime: toStoreRuntime(store, sthis)
627
335
  };
628
336
  }
629
- var blockstoreFactory = function(opts) {
337
+ function blockstoreFactory(sthis, opts) {
630
338
  if (opts.name) {
631
- return new EncryptedBlockstore(opts);
339
+ return new EncryptedBlockstore(sthis, opts);
632
340
  } else {
633
341
  return new BaseBlockstore(opts);
634
342
  }
635
- };
343
+ }
636
344
  var BaseBlockstore = class {
637
345
  constructor(ebOpts = {}) {
638
346
  this.transactions = /* @__PURE__ */ new Set();
639
- this.ebOpts = defaultedBlockstoreRuntime(ebOpts, "BaseBlockstore");
347
+ this.sthis = ensureSuperThis(ebOpts);
348
+ this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
640
349
  this.logger = this.ebOpts.logger;
641
350
  }
642
351
  // ready: Promise<void>;
@@ -659,8 +368,8 @@ var BaseBlockstore = class {
659
368
  throw this.logger.Error().Msg("use a transaction to put").AsError();
660
369
  }
661
370
  // TransactionMeta
662
- async transaction(fn, _opts = {}) {
663
- const t = new CarTransaction(this);
371
+ async transaction(fn, _opts) {
372
+ const t = new CarTransaction(this, _opts);
664
373
  const done = await fn(t);
665
374
  this.lastTxMeta = done;
666
375
  return { t, meta: done };
@@ -677,16 +386,16 @@ var BaseBlockstore = class {
677
386
  }
678
387
  };
679
388
  var EncryptedBlockstore = class extends BaseBlockstore {
680
- constructor(ebOpts) {
389
+ constructor(sthis, ebOpts) {
681
390
  super(ebOpts);
682
391
  this.compacting = false;
683
- this.logger = ensureLogger(ebOpts, "EncryptedBlockstore");
392
+ this.logger = ensureLogger(this.sthis, "EncryptedBlockstore");
684
393
  const { name } = ebOpts;
685
394
  if (!name) {
686
395
  throw this.logger.Error().Msg("name required").AsError();
687
396
  }
688
397
  this.name = name;
689
- this.loader = new Loader(this.name, ebOpts);
398
+ this.loader = new Loader(this.name, ebOpts, sthis);
690
399
  }
691
400
  ready() {
692
401
  return this.loader.ready();
@@ -717,10 +426,13 @@ var EncryptedBlockstore = class extends BaseBlockstore {
717
426
  }
718
427
  throw this.logger.Error().Msg("failed to commit car files").AsError();
719
428
  }
720
- async getFile(car, cid, isPublic = false) {
429
+ async getFile(car, cid) {
721
430
  await this.ready();
722
431
  if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
723
- const reader = await this.loader.loadFileCar(car, isPublic);
432
+ const reader = await this.loader.loadFileCar(
433
+ car
434
+ /*, isPublic */
435
+ );
724
436
  const block = await reader.get(cid);
725
437
  if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
726
438
  return block.bytes;
@@ -776,10 +488,20 @@ var CompactionFetcher = class {
776
488
  };
777
489
 
778
490
  // src/blockstore/commit-queue.ts
491
+ import { Future } from "@adviser/cement";
779
492
  var CommitQueue = class {
780
493
  constructor() {
781
494
  this.queue = [];
782
495
  this.processing = false;
496
+ this._waitIdleItems = /* @__PURE__ */ new Set();
497
+ }
498
+ waitIdle() {
499
+ if (this.queue.length === 0 && !this.processing) {
500
+ return Promise.resolve();
501
+ }
502
+ const fn = new Future();
503
+ this._waitIdleItems.add(fn);
504
+ return fn.asPromise();
783
505
  }
784
506
  async enqueue(fn) {
785
507
  return new Promise((resolve, reject) => {
@@ -804,38 +526,384 @@ var CommitQueue = class {
804
526
  this.processing = true;
805
527
  const queueFn = this.queue.shift();
806
528
  if (queueFn) {
807
- queueFn();
529
+ queueFn().finally(() => {
530
+ });
808
531
  }
809
532
  }
533
+ if (this.queue.length === 0 && !this.processing) {
534
+ const toResolve = Array.from(this._waitIdleItems);
535
+ this._waitIdleItems.clear();
536
+ toResolve.map((fn) => fn.resolve());
537
+ }
810
538
  }
811
539
  };
812
540
 
813
- // src/blockstore/loader.ts
814
- import * as CBW2 from "@ipld/car/buffer-writer";
815
- function carLogIncludesGroup2(list, cids) {
816
- return list.some((arr) => {
817
- return arr.toString() === cids.toString();
818
- });
819
- }
820
- function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
821
- const byString = /* @__PURE__ */ new Map();
822
- for (const cid of list) {
823
- if (remove.has(cid.toString())) continue;
824
- byString.set(cid.toString(), cid);
541
+ // src/runtime/key-bag.ts
542
+ var key_bag_exports = {};
543
+ __export(key_bag_exports, {
544
+ KeyBag: () => KeyBag,
545
+ getKeyBag: () => getKeyBag,
546
+ registerKeyBagProviderFactory: () => registerKeyBagProviderFactory
547
+ });
548
+ import {
549
+ KeyedResolvOnce,
550
+ ResolveOnce,
551
+ ResolveSeq,
552
+ Result as Result2,
553
+ runtimeFn,
554
+ toCryptoRuntime as toCryptoRuntime2,
555
+ URI
556
+ } from "@adviser/cement";
557
+ import { base58btc } from "multiformats/bases/base58";
558
+ var KeyBag = class {
559
+ constructor(rt) {
560
+ this.rt = rt;
561
+ this._warnOnce = new ResolveOnce();
562
+ this._seq = new ResolveSeq();
563
+ this.logger = ensureLogger(rt.sthis, "KeyBag");
564
+ this.logger.Debug().Msg("KeyBag created");
565
+ }
566
+ async subtleKey(key) {
567
+ const extractable = this.rt.url.getParam("extractKey") === "_deprecated_internal_api";
568
+ if (extractable) {
569
+ this._warnOnce.once(
570
+ () => this.logger.Warn().Msg("extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!")
571
+ );
572
+ }
573
+ return await this.rt.crypto.importKey(
574
+ "raw",
575
+ // raw or jwk
576
+ base58btc.decode(key),
577
+ // hexStringToUint8Array(key), // raw data
578
+ "AES-GCM",
579
+ extractable,
580
+ ["encrypt", "decrypt"]
581
+ );
825
582
  }
826
- return [...byString.values()];
827
- }
828
- function toHexString(byteArray) {
829
- return Array.from(byteArray).map((byte) => byte.toString(16).padStart(2, "0")).join("");
830
- }
831
- var Loadable = class {
832
- constructor() {
833
- this.name = "";
834
- this.carLog = new Array();
583
+ async ensureKeyFromUrl(url, keyFactory) {
584
+ const storeKey = url.getParam("storekey");
585
+ if (storeKey === "insecure") {
586
+ return Result2.Ok(url);
587
+ }
588
+ if (!storeKey) {
589
+ const keyName = `@${keyFactory()}@`;
590
+ const ret = await this.getNamedKey(keyName);
591
+ if (ret.isErr()) {
592
+ return ret;
593
+ }
594
+ const urb = url.build().setParam("storekey", keyName);
595
+ return Result2.Ok(urb.URI());
596
+ }
597
+ if (storeKey.startsWith("@") && storeKey.endsWith("@")) {
598
+ const ret = await this.getNamedKey(storeKey);
599
+ if (ret.isErr()) {
600
+ return ret;
601
+ }
602
+ }
603
+ return Result2.Ok(url);
604
+ }
605
+ async toKeyWithFingerPrint(keyStr) {
606
+ const material = base58btc.decode(keyStr);
607
+ const key = await this.subtleKey(keyStr);
608
+ const fpr = await this.rt.crypto.digestSHA256(material);
609
+ return Result2.Ok({
610
+ key,
611
+ fingerPrint: base58btc.encode(new Uint8Array(fpr))
612
+ });
613
+ }
614
+ async setNamedKey(name, key) {
615
+ return this._seq.add(() => this._setNamedKey(name, key));
616
+ }
617
+ // avoid deadlock
618
+ async _setNamedKey(name, key) {
619
+ const item = {
620
+ name,
621
+ key
622
+ };
623
+ const bag = await this.rt.getBag();
624
+ this.logger.Debug().Str("name", name).Msg("setNamedKey");
625
+ await bag.set(name, item);
626
+ return await this.toKeyWithFingerPrint(item.key);
627
+ }
628
+ async getNamedExtractableKey(name, failIfNotFound = false) {
629
+ const ret = await this.getNamedKey(name, failIfNotFound);
630
+ if (ret.isErr()) {
631
+ return ret;
632
+ }
633
+ const named = ret.Ok();
634
+ return Result2.Ok({
635
+ ...named,
636
+ extract: async () => {
637
+ const ext = new Uint8Array(await this.rt.crypto.exportKey("raw", named.key));
638
+ return {
639
+ key: ext,
640
+ keyStr: base58btc.encode(ext)
641
+ };
642
+ }
643
+ });
644
+ }
645
+ async getNamedKey(name, failIfNotFound = false) {
646
+ const id = this.rt.sthis.nextId(4).str;
647
+ return this._seq.add(async () => {
648
+ const bag = await this.rt.getBag();
649
+ const named = await bag.get(name);
650
+ if (named) {
651
+ const fpr = await this.toKeyWithFingerPrint(named.key);
652
+ this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", fpr).Msg("fingerPrint getNamedKey");
653
+ return fpr;
654
+ }
655
+ if (failIfNotFound) {
656
+ this.logger.Debug().Str("id", id).Str("name", name).Msg("failIfNotFound getNamedKey");
657
+ return Result2.Err(new Error(`Key not found: ${name}`));
658
+ }
659
+ const ret = await this._setNamedKey(name, base58btc.encode(this.rt.crypto.randomBytes(this.rt.keyLength)));
660
+ this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", ret).Msg("createKey getNamedKey-post");
661
+ return ret;
662
+ });
835
663
  }
836
664
  };
665
+ var keyBagProviderFactories = new Map(
666
+ [
667
+ {
668
+ protocol: "file:",
669
+ factory: async (url, sthis) => {
670
+ const { KeyBagProviderFile } = await import("./key-bag-file-WADZBHYG.js");
671
+ return new KeyBagProviderFile(url, sthis);
672
+ }
673
+ },
674
+ {
675
+ protocol: "indexdb:",
676
+ factory: async (url, sthis) => {
677
+ const { KeyBagProviderIndexDB } = await import("./key-bag-indexdb-PGVAI3FJ.js");
678
+ return new KeyBagProviderIndexDB(url, sthis);
679
+ }
680
+ }
681
+ ].map((i) => [i.protocol, i])
682
+ );
683
+ function registerKeyBagProviderFactory(item) {
684
+ const protocol = item.protocol.endsWith(":") ? item.protocol : item.protocol + ":";
685
+ keyBagProviderFactories.set(protocol, {
686
+ ...item,
687
+ protocol
688
+ });
689
+ }
690
+ function defaultKeyBagOpts(sthis, kbo) {
691
+ if (kbo.keyRuntime) {
692
+ return kbo.keyRuntime;
693
+ }
694
+ const logger = ensureLogger(sthis, "KeyBag");
695
+ let url;
696
+ if (kbo.url) {
697
+ url = URI.from(kbo.url);
698
+ logger.Debug().Url(url).Msg("from opts");
699
+ } else {
700
+ let bagFnameOrUrl = sthis.env.get("FP_KEYBAG_URL");
701
+ if (runtimeFn().isBrowser) {
702
+ url = URI.from(bagFnameOrUrl || "indexdb://fp-keybag");
703
+ } else {
704
+ if (!bagFnameOrUrl) {
705
+ const home = sthis.env.get("HOME");
706
+ bagFnameOrUrl = `${home}/.fireproof/keybag`;
707
+ url = URI.from(`file://${bagFnameOrUrl}`);
708
+ } else {
709
+ url = URI.from(bagFnameOrUrl);
710
+ }
711
+ }
712
+ logger.Debug().Url(url).Msg("from env");
713
+ }
714
+ const kitem = keyBagProviderFactories.get(url.protocol);
715
+ if (!kitem) {
716
+ throw logger.Error().Url(url).Msg("unsupported protocol").AsError();
717
+ }
718
+ const getBag = async () => kitem.factory(url, sthis);
719
+ if (url.hasParam("masterkey")) {
720
+ throw logger.Error().Url(url).Msg("masterkey is not supported").AsError();
721
+ }
722
+ return {
723
+ url,
724
+ crypto: kbo.crypto || toCryptoRuntime2({}),
725
+ sthis,
726
+ logger,
727
+ keyLength: kbo.keyLength || 16,
728
+ getBag,
729
+ id: () => {
730
+ return url.toString();
731
+ }
732
+ };
733
+ }
734
+ var _keyBags = new KeyedResolvOnce();
735
+ async function getKeyBag(sthis, kbo = {}) {
736
+ await sthis.start();
737
+ const rt = defaultKeyBagOpts(sthis, kbo);
738
+ return _keyBags.get(rt.id()).once(async () => new KeyBag(rt));
739
+ }
740
+
741
+ // src/blockstore/commitor.ts
742
+ import * as CBW from "@ipld/car/buffer-writer";
743
+ import { sha256 as hasher2 } from "multiformats/hashes/sha2";
744
+ import * as dagCodec2 from "@ipld/dag-cbor";
745
+ async function encodeCarFile(roots, t, codec3) {
746
+ let size = 0;
747
+ const headerSize = CBW.headerLength({ roots });
748
+ size += headerSize;
749
+ for (const { cid, bytes } of t.entries()) {
750
+ size += CBW.blockLength({ cid, bytes });
751
+ }
752
+ const buffer = new Uint8Array(size);
753
+ const writer = CBW.createWriter(buffer, { headerSize });
754
+ for (const r of roots) {
755
+ writer.addRoot(r);
756
+ }
757
+ for (const { cid, bytes } of t.entries()) {
758
+ writer.write({ cid, bytes });
759
+ }
760
+ writer.close();
761
+ return await encode({ value: writer.bytes, hasher: hasher2, codec: codec3 });
762
+ }
763
+ async function createCarFile(encoder, cid, t) {
764
+ return encodeCarFile([cid], t, encoder);
765
+ }
766
+ async function commitFiles(fileStore, walStore, t, done) {
767
+ const { files: roots } = makeFileCarHeader(done);
768
+ const cids = [];
769
+ const codec3 = (await fileStore.keyedCrypto()).codec();
770
+ const cars = await prepareCarFilesFiles(codec3, roots, t);
771
+ for (const car of cars) {
772
+ const { cid, bytes } = car;
773
+ await fileStore.save({ cid, bytes });
774
+ await walStore.enqueueFile(
775
+ cid
776
+ /*, !!opts.public*/
777
+ );
778
+ cids.push(cid);
779
+ }
780
+ return cids;
781
+ }
782
+ function makeFileCarHeader(result) {
783
+ const files = [];
784
+ for (const [, meta] of Object.entries(result.files || {})) {
785
+ if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
786
+ files.push(meta.cid);
787
+ }
788
+ }
789
+ return { ...result, files };
790
+ }
791
+ async function prepareCarFilesFiles(encoder, roots, t) {
792
+ return [await encodeCarFile(roots, t, encoder)];
793
+ }
794
+ function makeCarHeader(meta, cars, compact = false) {
795
+ const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
796
+ return { ...coreHeader, meta };
797
+ }
798
+ async function encodeCarHeader(fp) {
799
+ return await encode({
800
+ value: { fp },
801
+ hasher: hasher2,
802
+ codec: dagCodec2
803
+ });
804
+ }
805
+ async function commit(params, t, done, opts = { noLoader: false, compact: false }) {
806
+ const fp = makeCarHeader(done, params.carLog, !!opts.compact);
807
+ const rootBlock = await encodeCarHeader(fp);
808
+ const cars = await prepareCarFiles(params.encoder, params.threshold, rootBlock, t);
809
+ const cids = [];
810
+ for (const car of cars) {
811
+ const { cid, bytes } = car;
812
+ await params.carStore.save({ cid, bytes });
813
+ cids.push(cid);
814
+ }
815
+ const newDbMeta = { cars: cids };
816
+ await params.WALStore.enqueue(newDbMeta, opts);
817
+ await params.metaStore.save(newDbMeta);
818
+ return { cgrp: cids, header: fp };
819
+ }
820
+ async function prepareCarFiles(encoder, threshold, rootBlock, t) {
821
+ const carFiles = [];
822
+ threshold = threshold || 128e3 * 8;
823
+ let clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
824
+ clonedt.putSync(rootBlock.cid, rootBlock.bytes);
825
+ let newsize = CBW.blockLength(toCIDBlock(rootBlock));
826
+ let cidRootBlock = rootBlock;
827
+ for (const { cid, bytes } of t.entries()) {
828
+ newsize += CBW.blockLength(toCIDBlock({ cid, bytes }));
829
+ if (newsize >= threshold) {
830
+ carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
831
+ clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
832
+ clonedt.putSync(cid, bytes);
833
+ cidRootBlock = { cid, bytes };
834
+ newsize = CBW.blockLength(toCIDBlock({ cid, bytes }));
835
+ } else {
836
+ clonedt.putSync(cid, bytes);
837
+ }
838
+ }
839
+ carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
840
+ return carFiles;
841
+ }
842
+
843
+ // src/blockstore/loader.ts
844
+ import { sha256 as hasher3 } from "multiformats/hashes/sha2";
845
+
846
+ // src/blockstore/task-manager.ts
847
+ var TaskManager = class {
848
+ constructor(sthis, callback) {
849
+ this.eventsWeHandled = /* @__PURE__ */ new Set();
850
+ this.queue = [];
851
+ this.isProcessing = false;
852
+ this.logger = ensureLogger(sthis, "TaskManager");
853
+ this.callback = callback;
854
+ }
855
+ async handleEvent(cid, parents, dbMeta) {
856
+ for (const parent of parents) {
857
+ this.eventsWeHandled.add(parent.toString());
858
+ }
859
+ this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
860
+ this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
861
+ void this.processQueue();
862
+ }
863
+ async processQueue() {
864
+ if (this.isProcessing) return;
865
+ this.isProcessing = true;
866
+ const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
867
+ const first = filteredQueue[0];
868
+ if (!first) {
869
+ return;
870
+ }
871
+ try {
872
+ await this.callback(first.dbMeta);
873
+ this.eventsWeHandled.add(first.cid);
874
+ this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
875
+ } catch (err) {
876
+ if (first.retries++ > 3) {
877
+ this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
878
+ this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
879
+ }
880
+ await new Promise((resolve) => setTimeout(resolve, 50));
881
+ throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
882
+ } finally {
883
+ this.isProcessing = false;
884
+ if (this.queue.length > 0) {
885
+ void this.processQueue();
886
+ }
887
+ }
888
+ }
889
+ };
890
+
891
+ // src/blockstore/loader.ts
892
+ function carLogIncludesGroup(list, cids) {
893
+ return list.some((arr) => {
894
+ return arr.toString() === cids.toString();
895
+ });
896
+ }
897
+ function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
898
+ const byString = /* @__PURE__ */ new Map();
899
+ for (const cid of list) {
900
+ if (remove.has(cid.toString())) continue;
901
+ byString.set(cid.toString(), cid);
902
+ }
903
+ return [...byString.values()];
904
+ }
837
905
  var Loader = class {
838
- constructor(name, ebOpts) {
906
+ constructor(name, ebOpts, sthis) {
839
907
  this.commitQueue = new CommitQueue();
840
908
  this.isCompacting = false;
841
909
  this.carReaders = /* @__PURE__ */ new Map();
@@ -845,9 +913,11 @@ var Loader = class {
845
913
  this.getBlockCache = /* @__PURE__ */ new Map();
846
914
  this.seenMeta = /* @__PURE__ */ new Set();
847
915
  this.writeLimit = pLimit(1);
848
- this.onceReady = new ResolveOnce();
916
+ this.onceReady = new ResolveOnce2();
849
917
  this.name = name;
918
+ this.sthis = sthis;
850
919
  this.ebOpts = defaultedBlockstoreRuntime(
920
+ sthis,
851
921
  {
852
922
  ...ebOpts,
853
923
  name
@@ -855,34 +925,42 @@ var Loader = class {
855
925
  "Loader"
856
926
  );
857
927
  this.logger = this.ebOpts.logger;
928
+ this.taskManager = new TaskManager(sthis, async (dbMeta) => {
929
+ await this.handleDbMetasFromStore([dbMeta]);
930
+ });
858
931
  }
859
932
  // readonly id = uuidv4();
933
+ async keyBag() {
934
+ return getKeyBag(this.sthis, this.ebOpts.keyBag);
935
+ }
860
936
  async carStore() {
861
937
  return this.ebOpts.storeRuntime.makeDataStore(this);
862
938
  }
863
939
  async fileStore() {
864
940
  return this.ebOpts.storeRuntime.makeDataStore(this);
865
941
  }
866
- async remoteWAL() {
867
- return this.ebOpts.storeRuntime.makeRemoteWAL(this);
942
+ async WALStore() {
943
+ return this.ebOpts.storeRuntime.makeWALStore(this);
868
944
  }
869
945
  async metaStore() {
870
946
  return this.ebOpts.storeRuntime.makeMetaStore(this);
871
947
  }
872
948
  async ready() {
873
949
  return this.onceReady.once(async () => {
874
- const metas = this.ebOpts.meta ? [this.ebOpts.meta] : await (await this.metaStore()).load("main");
875
- if (metas) {
950
+ const metas = await (await this.metaStore()).load();
951
+ if (this.ebOpts.meta) {
952
+ await this.handleDbMetasFromStore([this.ebOpts.meta]);
953
+ } else if (metas) {
876
954
  await this.handleDbMetasFromStore(metas);
877
955
  }
878
956
  });
879
957
  }
880
958
  async close() {
881
- const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.remoteWAL()]);
959
+ const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
882
960
  await Promise.all(toClose.map((store) => store.close()));
883
961
  }
884
962
  async destroy() {
885
- const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.remoteWAL()]);
963
+ const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
886
964
  await Promise.all(toDestroy.map((store) => store.destroy()));
887
965
  }
888
966
  // async snapToCar(carCid: AnyLink | string) {
@@ -896,6 +974,7 @@ var Loader = class {
896
974
  // await this._applyCarHeader(carHeader, true)
897
975
  // }
898
976
  async handleDbMetasFromStore(metas) {
977
+ this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
899
978
  for (const meta of metas) {
900
979
  await this.writeLimit(async () => {
901
980
  await this.mergeDbMetaIntoClock(meta);
@@ -908,10 +987,7 @@ var Loader = class {
908
987
  }
909
988
  if (this.seenMeta.has(meta.cars.toString())) return;
910
989
  this.seenMeta.add(meta.cars.toString());
911
- if (meta.key) {
912
- await this.setKey(meta.key);
913
- }
914
- if (carLogIncludesGroup2(this.carLog, meta.cars)) {
990
+ if (carLogIncludesGroup(this.carLog, meta.cars)) {
915
991
  return;
916
992
  }
917
993
  const carHeader = await this.loadCarHeaderFromMeta(meta);
@@ -920,45 +996,60 @@ var Loader = class {
920
996
  this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
921
997
  await this.ebOpts.applyMeta?.(carHeader.meta);
922
998
  }
923
- async ingestKeyFromMeta(meta) {
924
- const { key } = meta;
925
- if (key) {
926
- await this.setKey(key);
927
- }
928
- }
999
+ // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
1000
+ // const { key } = meta;
1001
+ // if (key) {
1002
+ // await this.setKey(key);
1003
+ // }
1004
+ // }
929
1005
  async loadCarHeaderFromMeta({ cars: cids }) {
930
1006
  const reader = await this.loadCar(cids[0]);
931
1007
  return await parseCarFile(reader, this.logger);
932
1008
  }
933
- async _getKey() {
934
- if (this.key) return this.key;
935
- if (!this.ebOpts.public) {
936
- await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
937
- }
938
- return this.key || void 0;
939
- }
940
- async commitFiles(t, done, opts = { noLoader: false, compact: false }) {
941
- return this.commitQueue.enqueue(() => this._commitInternalFiles(t, done, opts));
942
- }
943
- // can these skip the queue? or have a file queue?
944
- async _commitInternalFiles(t, done, opts = { noLoader: false, compact: false }) {
1009
+ // async _getKey(): Promise<string | undefined> {
1010
+ // if (this.key) return this.key;
1011
+ // // generate a random key
1012
+ // if (!this.ebOpts.public) {
1013
+ // await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
1014
+ // }
1015
+ // return this.key || undefined;
1016
+ // }
1017
+ async commitFiles(t, done) {
945
1018
  await this.ready();
946
- const { files: roots } = this.makeFileCarHeader(done);
947
- const cids = [];
948
- const cars = await this.prepareCarFilesFiles(roots, t, !!opts.public);
949
- for (const car of cars) {
950
- const { cid, bytes } = car;
951
- await (await this.fileStore()).save({ cid, bytes });
952
- await (await this.remoteWAL()).enqueueFile(cid, !!opts.public);
953
- cids.push(cid);
954
- }
955
- return cids;
1019
+ const fstore = await this.fileStore();
1020
+ const wstore = await this.WALStore();
1021
+ return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
956
1022
  }
957
- async loadFileCar(cid, isPublic = false) {
958
- return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore, isPublic);
1023
+ async loadFileCar(cid) {
1024
+ return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
959
1025
  }
960
1026
  async commit(t, done, opts = { noLoader: false, compact: false }) {
961
- return this.commitQueue.enqueue(() => this._commitInternal(t, done, opts));
1027
+ await this.ready();
1028
+ const fstore = await this.fileStore();
1029
+ const params = {
1030
+ encoder: (await fstore.keyedCrypto()).codec(),
1031
+ carLog: this.carLog,
1032
+ carStore: fstore,
1033
+ WALStore: await this.WALStore(),
1034
+ metaStore: await this.metaStore(),
1035
+ threshold: this.ebOpts.threshold
1036
+ };
1037
+ return this.commitQueue.enqueue(async () => {
1038
+ await this.cacheTransaction(t);
1039
+ const ret = await commit(params, t, done, opts);
1040
+ await this.updateCarLog(ret.cgrp, ret.header, !!opts.compact);
1041
+ return ret.cgrp;
1042
+ });
1043
+ }
1044
+ async updateCarLog(cids, fp, compact) {
1045
+ if (compact) {
1046
+ const previousCompactCid = fp.compact[fp.compact.length - 1];
1047
+ fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1048
+ this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
1049
+ await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
1050
+ } else {
1051
+ this.carLog.unshift(cids);
1052
+ }
962
1053
  }
963
1054
  async cacheTransaction(t) {
964
1055
  for await (const block of t.entries()) {
@@ -978,79 +1069,6 @@ var Loader = class {
978
1069
  }
979
1070
  }
980
1071
  }
981
- async _commitInternal(t, done, opts = { noLoader: false, compact: false }) {
982
- await this.ready();
983
- const fp = this.makeCarHeader(done, this.carLog, !!opts.compact);
984
- const rootBlock = await encodeCarHeader(fp);
985
- const cars = await this.prepareCarFiles(rootBlock, t, !!opts.public);
986
- const cids = [];
987
- for (const car of cars) {
988
- const { cid, bytes } = car;
989
- await (await this.carStore()).save({ cid, bytes });
990
- cids.push(cid);
991
- }
992
- await this.cacheTransaction(t);
993
- const newDbMeta = { cars: cids, key: this.key || null };
994
- await (await this.remoteWAL()).enqueue(newDbMeta, opts);
995
- await (await this.metaStore()).save(newDbMeta);
996
- await this.updateCarLog(cids, fp, !!opts.compact);
997
- return cids;
998
- }
999
- async prepareCarFilesFiles(roots, t, isPublic) {
1000
- const theKey = isPublic ? null : await this._getKey();
1001
- const car = theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, roots[0], t) : await encodeCarFile(roots, t);
1002
- return [car];
1003
- }
1004
- async prepareCarFiles(rootBlock, t, isPublic) {
1005
- const theKey = isPublic ? void 0 : await this._getKey();
1006
- const carFiles = [];
1007
- const threshold = this.ebOpts.threshold || 1e3 * 1e3;
1008
- let clonedt = new CarTransaction(t.parent, { add: false });
1009
- clonedt.putSync(rootBlock.cid, rootBlock.bytes);
1010
- let newsize = CBW2.blockLength(toCIDBlock(rootBlock));
1011
- let cidRootBlock = rootBlock;
1012
- for (const { cid, bytes } of t.entries()) {
1013
- newsize += CBW2.blockLength(toCIDBlock({ cid, bytes }));
1014
- if (newsize >= threshold) {
1015
- carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
1016
- clonedt = new CarTransaction(t.parent, { add: false });
1017
- clonedt.putSync(cid, bytes);
1018
- cidRootBlock = { cid, bytes };
1019
- newsize = CBW2.blockLength(toCIDBlock({ cid, bytes }));
1020
- } else {
1021
- clonedt.putSync(cid, bytes);
1022
- }
1023
- }
1024
- carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
1025
- return carFiles;
1026
- }
1027
- async createCarFile(theKey, cid, t) {
1028
- try {
1029
- return theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, cid, t) : await encodeCarFile([cid], t);
1030
- } catch (e) {
1031
- console.error("error creating car file", e);
1032
- throw e;
1033
- }
1034
- }
1035
- makeFileCarHeader(result) {
1036
- const files = [];
1037
- for (const [, meta] of Object.entries(result.files || {})) {
1038
- if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
1039
- files.push(meta.cid);
1040
- }
1041
- }
1042
- return { ...result, files };
1043
- }
1044
- async updateCarLog(cids, fp, compact) {
1045
- if (compact) {
1046
- const previousCompactCid = fp.compact[fp.compact.length - 1];
1047
- fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1048
- this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
1049
- await this.removeCidsForCompact(previousCompactCid[0]);
1050
- } else {
1051
- this.carLog.unshift(cids);
1052
- }
1053
- }
1054
1072
  async removeCidsForCompact(cid) {
1055
1073
  const carHeader = await this.loadCarHeaderFromMeta({
1056
1074
  cars: [cid]
@@ -1069,9 +1087,9 @@ var Loader = class {
1069
1087
  // await this.remoteWAL!.enqueue(dbMeta, { public: false })
1070
1088
  // }
1071
1089
  // }
1072
- async *entries(cache3 = true) {
1090
+ async *entries(cache2 = true) {
1073
1091
  await this.ready();
1074
- if (cache3) {
1092
+ if (cache2) {
1075
1093
  for (const [, block] of this.getBlockCache) {
1076
1094
  yield block;
1077
1095
  }
@@ -1153,10 +1171,6 @@ var Loader = class {
1153
1171
  }
1154
1172
  return got;
1155
1173
  }
1156
- makeCarHeader(meta, cars, compact = false) {
1157
- const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
1158
- return { ...coreHeader, meta };
1159
- }
1160
1174
  async loadCar(cid) {
1161
1175
  if (!this.carStore) {
1162
1176
  throw this.logger.Error().Msg("car store not initialized").AsError();
@@ -1164,94 +1178,516 @@ var Loader = class {
1164
1178
  const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
1165
1179
  return loaded;
1166
1180
  }
1181
+ async makeDecoderAndCarReader(cid, local, remote) {
1182
+ const cidsString = cid.toString();
1183
+ let loadedCar = void 0;
1184
+ let activeStore = local;
1185
+ try {
1186
+ this.logger.Debug().Str("cid", cidsString).Msg("loading car");
1187
+ loadedCar = await local.load(cid);
1188
+ this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
1189
+ } catch (e) {
1190
+ if (remote) {
1191
+ const remoteCar = await remote.load(cid);
1192
+ if (remoteCar) {
1193
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
1194
+ await local.save(remoteCar);
1195
+ loadedCar = remoteCar;
1196
+ activeStore = remote;
1197
+ }
1198
+ } else {
1199
+ this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
1200
+ }
1201
+ }
1202
+ if (!loadedCar) {
1203
+ throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
1204
+ }
1205
+ const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher3, codec: (await activeStore.keyedCrypto()).codec() });
1206
+ const rawReader = await CarReader.fromBytes(bytes.value);
1207
+ const readerP = Promise.resolve(rawReader);
1208
+ const cachedReaderP = readerP.then(async (reader) => {
1209
+ await this.cacheCarReader(cidsString, reader).catch((e) => {
1210
+ this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
1211
+ return;
1212
+ });
1213
+ return reader;
1214
+ });
1215
+ this.carReaders.set(cidsString, cachedReaderP);
1216
+ return readerP;
1217
+ }
1167
1218
  //What if instead it returns an Array of CarHeader
1168
- async storesLoadCar(cid, local, remote, publicFiles) {
1219
+ async storesLoadCar(cid, local, remote) {
1169
1220
  const cidsString = cid.toString();
1170
- if (!this.carReaders.has(cidsString)) {
1171
- this.carReaders.set(
1172
- cidsString,
1173
- (async () => {
1174
- let loadedCar = void 0;
1175
- try {
1176
- this.logger.Debug().Str("cid", cidsString).Msg("loading car");
1177
- loadedCar = await local.load(cid);
1178
- this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
1179
- } catch (e) {
1180
- if (remote) {
1181
- const remoteCar = await remote.load(cid);
1182
- if (remoteCar) {
1183
- this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
1184
- await local.save(remoteCar);
1185
- loadedCar = remoteCar;
1186
- }
1187
- } else {
1188
- this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
1189
- }
1190
- }
1191
- if (!loadedCar) {
1192
- throw this.logger.Error().Url(local.url).Str("cid", cidsString).Msg("missing car files").AsError();
1193
- }
1194
- const rawReader = await CarReader.fromBytes(loadedCar.bytes);
1195
- const readerP = publicFiles ? Promise.resolve(rawReader) : this.ensureDecryptedReader(rawReader);
1196
- const cachedReaderP = readerP.then(async (reader) => {
1197
- await this.cacheCarReader(cidsString, reader).catch(() => {
1198
- return;
1199
- });
1200
- return reader;
1201
- });
1202
- this.carReaders.set(cidsString, cachedReaderP);
1203
- return readerP;
1204
- })().catch((e) => {
1205
- this.carReaders.delete(cidsString);
1206
- throw e;
1207
- })
1208
- );
1221
+ let dacr = this.carReaders.get(cidsString);
1222
+ if (!dacr) {
1223
+ dacr = this.makeDecoderAndCarReader(cid, local, remote);
1224
+ this.carReaders.set(cidsString, dacr);
1225
+ }
1226
+ return dacr;
1227
+ }
1228
+ async getMoreReaders(cids) {
1229
+ const limit = pLimit(5);
1230
+ const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
1231
+ await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
1232
+ }
1233
+ };
1234
+
1235
+ // src/runtime/keyed-crypto.ts
1236
+ var keyed_crypto_exports = {};
1237
+ __export(keyed_crypto_exports, {
1238
+ BlockIvKeyIdCodec: () => BlockIvKeyIdCodec,
1239
+ keyedCryptoFactory: () => keyedCryptoFactory
1240
+ });
1241
+ import { base58btc as base58btc2 } from "multiformats/bases/base58";
1242
+ import { sha256 as hasher4 } from "multiformats/hashes/sha2";
1243
+ import * as CBOR from "cborg";
1244
+ var generateIV = {
1245
+ random: {
1246
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1247
+ calc: async (ko, crypto, data) => {
1248
+ return crypto.randomBytes(ko.ivLength);
1249
+ },
1250
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1251
+ verify: async (ko, crypto, iv, data) => {
1252
+ return true;
1253
+ }
1254
+ },
1255
+ hash: {
1256
+ calc: async (ko, crypto, data) => {
1257
+ const hash = await hasher4.digest(data);
1258
+ const hashBytes = new Uint8Array(hash.bytes);
1259
+ const hashArray = new Uint8Array(ko.ivLength);
1260
+ for (let i = 0; i < hashBytes.length; i++) {
1261
+ hashArray[i % ko.ivLength] ^= hashBytes[i];
1262
+ }
1263
+ return hashArray;
1264
+ },
1265
+ verify: async function(ko, crypto, iv, data) {
1266
+ return ko.url.getParam("ivverify") !== "disable" && UInt8ArrayEqual(iv, await this.calc(ko, crypto, data));
1267
+ }
1268
+ }
1269
+ };
1270
+ function getGenerateIVFn(url, opts) {
1271
+ const ivhash = opts.ivCalc || url.getParam("ivhash") || "hash";
1272
+ return generateIV[ivhash] || generateIV["hash"];
1273
+ }
1274
+ var BlockIvKeyIdCodec = class {
1275
+ constructor(ko, iv, opts) {
1276
+ this.code = 3147065;
1277
+ this.name = "Fireproof@encrypted-block:aes-gcm";
1278
+ this.ko = ko;
1279
+ this.iv = iv;
1280
+ this.opts = opts || {};
1281
+ }
1282
+ async encode(data) {
1283
+ const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
1284
+ const { iv } = this.ko.algo(calcIv);
1285
+ const fprt = await this.ko.fingerPrint();
1286
+ const keyId = base58btc2.decode(fprt);
1287
+ this.ko.logger.Debug().Str("fp", fprt).Msg("encode");
1288
+ return CBOR.encode({
1289
+ iv,
1290
+ keyId,
1291
+ data: await this.ko._encrypt({ iv, bytes: data })
1292
+ });
1293
+ }
1294
+ async decode(abytes) {
1295
+ let bytes;
1296
+ if (abytes instanceof Uint8Array) {
1297
+ bytes = abytes;
1298
+ } else {
1299
+ bytes = new Uint8Array(abytes);
1300
+ }
1301
+ const { iv, keyId, data } = CBOR.decode(bytes);
1302
+ const fprt = await this.ko.fingerPrint();
1303
+ this.ko.logger.Debug().Str("fp", base58btc2.encode(keyId)).Msg("decode");
1304
+ if (base58btc2.encode(keyId) !== fprt) {
1305
+ throw this.ko.logger.Error().Str("fp", fprt).Str("keyId", base58btc2.encode(keyId)).Msg("keyId mismatch").AsError();
1306
+ }
1307
+ const result = await this.ko._decrypt({ iv, bytes: data });
1308
+ if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
1309
+ throw this.ko.logger.Error().Msg("iv missmatch").AsError();
1310
+ }
1311
+ return result;
1312
+ }
1313
+ };
1314
+ var keyedCrypto = class {
1315
+ constructor(url, key, cyopt, sthis) {
1316
+ this.ivLength = 12;
1317
+ this.isEncrypting = true;
1318
+ this.logger = ensureLogger(sthis, "keyedCrypto");
1319
+ this.crypto = cyopt;
1320
+ this.key = key;
1321
+ this.url = url;
1322
+ }
1323
+ fingerPrint() {
1324
+ return Promise.resolve(this.key.fingerPrint);
1325
+ }
1326
+ codec(iv, opts) {
1327
+ return new BlockIvKeyIdCodec(this, iv, opts);
1328
+ }
1329
+ algo(iv) {
1330
+ return {
1331
+ name: "AES-GCM",
1332
+ iv: iv || this.crypto.randomBytes(this.ivLength),
1333
+ tagLength: 128
1334
+ };
1335
+ }
1336
+ async _decrypt(data) {
1337
+ this.logger.Debug().Len(data.bytes, "bytes").Len(data.iv, "iv").Str("fp", this.key.fingerPrint).Msg("decrypting");
1338
+ return new Uint8Array(await this.crypto.decrypt(this.algo(data.iv), this.key.key, data.bytes));
1339
+ }
1340
+ async _encrypt(data) {
1341
+ this.logger.Debug().Len(data.bytes).Str("fp", this.key.fingerPrint).Msg("encrypting");
1342
+ const a = this.algo(data.iv);
1343
+ return new Uint8Array(await this.crypto.encrypt(a, this.key.key, data.bytes));
1344
+ }
1345
+ };
1346
+ var nullCodec = class {
1347
+ constructor() {
1348
+ this.code = 0;
1349
+ this.name = "Fireproof@unencrypted-block";
1350
+ }
1351
+ encode(data) {
1352
+ return data;
1353
+ }
1354
+ decode(data) {
1355
+ return data;
1356
+ }
1357
+ };
1358
+ var noCrypto = class {
1359
+ constructor(url, cyrt, sthis) {
1360
+ this.ivLength = 0;
1361
+ this.code = 0;
1362
+ this.name = "Fireproof@unencrypted-block";
1363
+ this.isEncrypting = false;
1364
+ this._fingerPrint = "noCrypto:" + Math.random();
1365
+ this.logger = ensureLogger(sthis, "noCrypto");
1366
+ this.crypto = cyrt;
1367
+ this.url = url;
1368
+ }
1369
+ fingerPrint() {
1370
+ return Promise.resolve(this._fingerPrint);
1371
+ }
1372
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1373
+ codec(iv) {
1374
+ return new nullCodec();
1375
+ }
1376
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1377
+ algo(iv) {
1378
+ return {
1379
+ name: "noCrypto",
1380
+ iv: new Uint8Array(),
1381
+ tagLength: 0
1382
+ };
1383
+ }
1384
+ _decrypt() {
1385
+ throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
1386
+ }
1387
+ _encrypt() {
1388
+ throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
1389
+ }
1390
+ };
1391
+ async function keyedCryptoFactory(url, kb, sthis) {
1392
+ const storekey = url.getParam("storekey");
1393
+ if (storekey && storekey !== "insecure") {
1394
+ let rkey = await kb.getNamedKey(storekey, true);
1395
+ if (rkey.isErr()) {
1396
+ try {
1397
+ rkey = await kb.toKeyWithFingerPrint(storekey);
1398
+ } catch (e) {
1399
+ throw sthis.logger.Error().Err(e).Str("keybag", kb.rt.id()).Str("name", storekey).Msg("getNamedKey failed").AsError();
1400
+ }
1401
+ }
1402
+ return new keyedCrypto(url, rkey.Ok(), kb.rt.crypto, sthis);
1403
+ }
1404
+ return new noCrypto(url, kb.rt.crypto, sthis);
1405
+ }
1406
+
1407
+ // src/blockstore/fragment-gateway.ts
1408
+ import { Result as Result3 } from "@adviser/cement";
1409
+ import { base58btc as base58btc3 } from "multiformats/bases/base58";
1410
+ import { encode as encode3, decode as decode3 } from "cborg";
1411
+ function getFragSize(url) {
1412
+ const fragSize = url.getParam("fragSize");
1413
+ let ret = 0;
1414
+ if (fragSize) {
1415
+ ret = parseInt(fragSize);
1416
+ }
1417
+ if (isNaN(ret) || ret <= 0) {
1418
+ ret = 0;
1419
+ }
1420
+ return ret;
1421
+ }
1422
+ async function getFrags(url, innerGW, headerSize, logger) {
1423
+ const fragSize = getFragSize(url);
1424
+ if (!fragSize) {
1425
+ const res = await innerGW.get(url);
1426
+ if (res.isErr()) {
1427
+ return [res];
1428
+ }
1429
+ const data = res.unwrap();
1430
+ return [
1431
+ Result3.Ok({
1432
+ fid: new Uint8Array(0),
1433
+ ofs: 0,
1434
+ len: data.length,
1435
+ data
1436
+ })
1437
+ ];
1438
+ }
1439
+ const firstRaw = await innerGW.get(url.build().setParam("ofs", "0").URI());
1440
+ if (firstRaw.isErr()) {
1441
+ return [firstRaw];
1442
+ }
1443
+ const firstFragment = decode3(firstRaw.unwrap());
1444
+ const blockSize = firstFragment.data.length;
1445
+ const ops = [Promise.resolve(Result3.Ok(firstFragment))];
1446
+ const fidStr = base58btc3.encode(firstFragment.fid);
1447
+ const fragUrl = url.build().setParam("fid", fidStr).setParam("len", firstFragment.len.toString()).setParam("headerSize", headerSize.toString());
1448
+ for (let ofs = blockSize; ofs < firstFragment.len; ofs += blockSize) {
1449
+ ops.push(
1450
+ (async (furl, ofs2) => {
1451
+ const raw2 = await innerGW.get(furl);
1452
+ if (raw2.isErr()) {
1453
+ return raw2;
1454
+ }
1455
+ const fragment = decode3(raw2.unwrap());
1456
+ if (base58btc3.encode(fragment.fid) !== fidStr) {
1457
+ return Result3.Err(logger.Error().Msg("Fragment fid mismatch").AsError());
1458
+ }
1459
+ if (fragment.ofs !== ofs2) {
1460
+ return Result3.Err(logger.Error().Uint64("ofs", ofs2).Msg("Fragment ofs mismatch").AsError());
1461
+ }
1462
+ return Result3.Ok(fragment);
1463
+ })(fragUrl.setParam("ofs", ofs.toString()).URI(), ofs)
1464
+ );
1465
+ }
1466
+ return Promise.all(ops);
1467
+ }
1468
+ var FragmentGateway = class {
1469
+ constructor(sthis, innerGW) {
1470
+ this.fidLength = 4;
1471
+ this.headerSize = 32;
1472
+ this.sthis = ensureSuperLog(sthis, "FragmentGateway");
1473
+ this.logger = this.sthis.logger;
1474
+ this.innerGW = innerGW;
1475
+ }
1476
+ slicer(url, body) {
1477
+ const fragSize = getFragSize(url);
1478
+ if (!fragSize) {
1479
+ return [this.innerGW.put(url, body)];
1480
+ }
1481
+ const blocksize = fragSize - this.headerSize;
1482
+ if (blocksize <= 0) {
1483
+ throw this.logger.Error().Uint64("fragSize", fragSize).Uint64("headerSize", this.headerSize).Msg("Fragment size is too small").AsError();
1484
+ }
1485
+ const ops = [];
1486
+ const fid = this.sthis.nextId(this.fidLength);
1487
+ const fragUrl = url.build().setParam("fid", fid.str).setParam("len", body.length.toString()).setParam("headerSize", this.headerSize.toString());
1488
+ for (let ofs = 0; ofs < body.length; ofs += blocksize) {
1489
+ const block = encode3({
1490
+ fid: fid.bin,
1491
+ ofs,
1492
+ len: body.length,
1493
+ data: body.slice(ofs, ofs + blocksize)
1494
+ });
1495
+ if (block.length > fragSize) {
1496
+ throw this.logger.Error().Uint64("block", block.length).Uint64("fragSize", fragSize).Msg("Block size to big").AsError();
1497
+ }
1498
+ ops.push(this.innerGW.put(fragUrl.setParam("ofs", ofs.toString()).URI(), block));
1499
+ }
1500
+ return ops;
1501
+ }
1502
+ buildUrl(baseUrl, key) {
1503
+ return this.innerGW.buildUrl(baseUrl, key);
1504
+ }
1505
+ async destroy(iurl) {
1506
+ return this.innerGW.destroy(iurl);
1507
+ }
1508
+ async start(url) {
1509
+ this.headerSize = encode3({
1510
+ fid: this.sthis.nextId(this.fidLength).bin,
1511
+ ofs: 1024 * 1024,
1512
+ // 32bit
1513
+ len: 16 * 1024 * 1024,
1514
+ // 32bit
1515
+ data: new Uint8Array(1024)
1516
+ }).length - 1024;
1517
+ return this.innerGW.start(url);
1518
+ }
1519
+ async close(url) {
1520
+ return this.innerGW.close(url);
1521
+ }
1522
+ async put(url, body) {
1523
+ await Promise.all(this.slicer(url, body));
1524
+ return Result3.Ok(void 0);
1525
+ }
1526
+ async get(url) {
1527
+ const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
1528
+ let buffer = void 0;
1529
+ for (const rfrag of rfrags) {
1530
+ if (rfrag.isErr()) {
1531
+ return Result3.Err(rfrag.Err());
1532
+ }
1533
+ const frag = rfrag.Ok();
1534
+ buffer = buffer || new Uint8Array(frag.len);
1535
+ buffer.set(frag.data, frag.ofs);
1536
+ }
1537
+ return Result3.Ok(buffer || new Uint8Array(0));
1538
+ }
1539
+ async subscribe(url, callback) {
1540
+ if (this.innerGW.subscribe) {
1541
+ return this.innerGW.subscribe(url, callback);
1542
+ } else {
1543
+ return Result3.Err(this.logger.Error().Url(url).Msg("subscribe not supported").AsError());
1544
+ }
1545
+ }
1546
+ async delete(url) {
1547
+ const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
1548
+ for (const rfrag of rfrags) {
1549
+ if (rfrag.isErr()) {
1550
+ return Result3.Err(rfrag.Err());
1551
+ }
1552
+ const frag = rfrag.Ok();
1553
+ const fidStr = base58btc3.encode(frag.fid);
1554
+ const fragUrl = url.build().setParam("fid", fidStr).setParam("len", frag.len.toString()).setParam("headerSize", this.headerSize.toString()).URI();
1555
+ await this.innerGW.delete(fragUrl);
1556
+ }
1557
+ return Result3.Ok(void 0);
1558
+ }
1559
+ };
1560
+
1561
+ // src/blockstore/meta-key-helper.ts
1562
+ import { format, parse } from "@ipld/dag-json";
1563
+ import { EventBlock, decodeEventBlock } from "@web3-storage/pail/clock";
1564
+ import { CID as CID2 } from "multiformats";
1565
+ import { base64pad } from "multiformats/bases/base64";
1566
+ import { Result as Result4 } from "@adviser/cement";
1567
+ async function decodeGatewayMetaBytesToDbMeta(sthis, byteHeads) {
1568
+ const crdtEntries = JSON.parse(sthis.txt.decode(byteHeads));
1569
+ if (!crdtEntries.length) {
1570
+ sthis.logger.Debug().Str("byteHeads", new TextDecoder().decode(byteHeads)).Msg("No CRDT entries found");
1571
+ return [];
1572
+ }
1573
+ if (!crdtEntries.map) {
1574
+ sthis.logger.Debug().Str("crdtEntries", JSON.stringify(crdtEntries)).Msg("No data in CRDT entries");
1575
+ return [];
1576
+ }
1577
+ return Promise.all(
1578
+ crdtEntries.map(async (crdtEntry) => {
1579
+ const eventBlock = await decodeEventBlock(base64pad.decode(crdtEntry.data));
1580
+ const dbMeta = parse(sthis.txt.decode(eventBlock.value.data.dbMeta));
1581
+ return {
1582
+ eventCid: eventBlock.cid,
1583
+ parents: crdtEntry.parents,
1584
+ dbMeta
1585
+ };
1586
+ })
1587
+ );
1588
+ }
1589
+ async function setCryptoKeyFromGatewayMetaPayload(uri, sthis, data) {
1590
+ try {
1591
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Setting crypto key from gateway meta payload");
1592
+ const keyInfo = await decodeGatewayMetaBytesToDbMeta(sthis, data);
1593
+ if (keyInfo.length) {
1594
+ const dbMeta = keyInfo[0].dbMeta;
1595
+ if (dbMeta.key) {
1596
+ const kb = await getKeyBag(sthis);
1597
+ const keyName = getStoreKeyName(uri);
1598
+ const res = await kb.setNamedKey(keyName, dbMeta.key);
1599
+ if (res.isErr()) {
1600
+ sthis.logger.Debug().Str("keyName", keyName).Str("dbMeta.key", dbMeta.key).Msg("Failed to set named key");
1601
+ throw res.Err();
1602
+ }
1603
+ }
1604
+ sthis.logger.Debug().Str("dbMeta.key", dbMeta.key).Str("uri", uri.toString()).Msg("Set crypto key from gateway meta payload");
1605
+ return Result4.Ok(dbMeta);
1209
1606
  }
1210
- return this.carReaders.get(cidsString);
1607
+ sthis.logger.Debug().Str("data", new TextDecoder().decode(data)).Msg("No crypto in gateway meta payload");
1608
+ return Result4.Ok(void 0);
1609
+ } catch (error) {
1610
+ sthis.logger.Debug().Err(error).Msg("Failed to set crypto key from gateway meta payload");
1611
+ return Result4.Err(error);
1211
1612
  }
1212
- async ensureDecryptedReader(reader) {
1213
- const theKey = await this._getKey();
1214
- if (this.ebOpts.public || !(theKey && this.ebOpts.crypto)) {
1215
- return reader;
1613
+ }
1614
+ async function addCryptoKeyToGatewayMetaPayload(uri, sthis, body) {
1615
+ try {
1616
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Adding crypto key to gateway meta payload");
1617
+ const keyName = getStoreKeyName(uri);
1618
+ const kb = await getKeyBag(sthis);
1619
+ const res = await kb.getNamedExtractableKey(keyName, true);
1620
+ if (res.isErr()) {
1621
+ sthis.logger.Error().Str("keyName", keyName).Msg("Failed to get named extractable key");
1622
+ throw res.Err();
1216
1623
  }
1217
- const { blocks, root: root3 } = await decodeEncryptedCar(this.logger, this.ebOpts.crypto, theKey, reader);
1624
+ const keyData = await res.Ok().extract();
1625
+ const dbMetas = await decodeGatewayMetaBytesToDbMeta(sthis, body);
1626
+ const { dbMeta, parents } = dbMetas[0];
1627
+ const parentLinks = parents.map((p) => CID2.parse(p));
1628
+ dbMeta.key = keyData.keyStr;
1629
+ const events = await Promise.all([dbMeta].map((dbMeta2) => createDbMetaEventBlock(sthis, dbMeta2, parentLinks)));
1630
+ const encoded = await encodeEventsWithParents(sthis, events, parentLinks);
1631
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Added crypto key to gateway meta payload");
1632
+ return Result4.Ok(encoded);
1633
+ } catch (error) {
1634
+ sthis.logger.Error().Err(error).Msg("Failed to add crypto key to gateway meta payload");
1635
+ return Result4.Err(error);
1636
+ }
1637
+ }
1638
+ function getStoreKeyName(url) {
1639
+ const storeKeyName = [url.getParam("localName") || url.getParam("name")];
1640
+ const idx = url.getParam("index");
1641
+ if (idx) {
1642
+ storeKeyName.push(idx);
1643
+ }
1644
+ storeKeyName.push("data");
1645
+ return `@${storeKeyName.join(":")}@`;
1646
+ }
1647
+ async function createDbMetaEventBlock(sthis, dbMeta, parents) {
1648
+ const event = await EventBlock.create(
1649
+ {
1650
+ dbMeta: sthis.txt.encode(format(dbMeta))
1651
+ },
1652
+ parents
1653
+ );
1654
+ return event;
1655
+ }
1656
+ async function encodeEventsWithParents(sthis, events, parents) {
1657
+ const crdtEntries = events.map((event) => {
1658
+ const base64String = base64pad.encode(event.bytes);
1218
1659
  return {
1219
- getRoots: () => [root3],
1220
- get: blocks.get.bind(blocks),
1221
- blocks: blocks.entries.bind(blocks)
1660
+ cid: event.cid.toString(),
1661
+ data: base64String,
1662
+ parents: parents.map((p) => p.toString())
1222
1663
  };
1223
- }
1224
- async setKey(key) {
1225
- if (this.key && this.key !== key)
1226
- throw this.logger.Error().Str("this.key", this.key).Str("key", key).Msg("setting key").AsError();
1227
- this.key = key;
1228
- const encoder = new TextEncoder();
1229
- const data = encoder.encode(key);
1230
- const hashBuffer = await this.ebOpts.crypto.digestSHA256(data);
1231
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1232
- this.keyId = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1233
- }
1234
- async getMoreReaders(cids) {
1235
- const limit = pLimit(5);
1236
- const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
1237
- await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
1238
- }
1239
- };
1664
+ });
1665
+ return sthis.txt.encode(JSON.stringify(crdtEntries));
1666
+ }
1240
1667
 
1241
1668
  // src/blockstore/store.ts
1669
+ import pRetry from "p-retry";
1670
+ import pMap from "p-map";
1242
1671
  function guardVersion(url) {
1243
- if (!url.searchParams.has("version")) {
1244
- return Result2.Err(`missing version: ${url.toString()}`);
1672
+ if (!url.hasParam("version")) {
1673
+ return Result5.Err(`missing version: ${url.toString()}`);
1245
1674
  }
1246
- return Result2.Ok(url);
1675
+ return Result5.Ok(url);
1247
1676
  }
1248
- var VersionedStore = class {
1249
- constructor(name, url, logger) {
1677
+ var BaseStoreImpl = class {
1678
+ constructor(name, url, opts, sthis, logger) {
1250
1679
  this._onStarted = [];
1251
1680
  this._onClosed = [];
1252
1681
  this.name = name;
1253
- this.url = url;
1254
- this.logger = logger;
1682
+ this._url = url;
1683
+ this.keybag = opts.keybag;
1684
+ this.sthis = sthis;
1685
+ this.logger = logger.With().Ref("url", () => this._url.toString()).Str("name", name).Logger();
1686
+ this.gateway = new FragmentGateway(this.sthis, opts.gateway);
1687
+ this.loader = opts.loader;
1688
+ }
1689
+ url() {
1690
+ return this._url;
1255
1691
  }
1256
1692
  onStarted(fn) {
1257
1693
  this._onStarted.push(fn);
@@ -1259,38 +1695,93 @@ var VersionedStore = class {
1259
1695
  onClosed(fn) {
1260
1696
  this._onClosed.push(fn);
1261
1697
  }
1262
- };
1263
- var textEncoder = new TextEncoder();
1264
- var textDecoder = new TextDecoder();
1265
- var MetaStore = class extends VersionedStore {
1266
- constructor(name, url, logger, gateway) {
1267
- super(name, url, ensureLogger(logger, "MetaStore", {}));
1268
- this.tag = "header-base";
1269
- this.gateway = gateway;
1270
- }
1271
- makeHeader({ cars, key }) {
1272
- const toEncode = { cars };
1273
- if (key) toEncode.key = key;
1274
- return format(toEncode);
1275
- }
1276
- parseHeader(headerData) {
1277
- const got = parse(headerData);
1278
- return got;
1698
+ async ready() {
1699
+ return;
1700
+ }
1701
+ async keyedCrypto() {
1702
+ return keyedCryptoFactory(this._url, await this.keybag(), this.sthis);
1279
1703
  }
1280
1704
  async start() {
1281
- this.logger.Debug().Msg("starting");
1282
- const res = await this.gateway.start(this.url);
1705
+ this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
1706
+ this._url = this._url.build().setParam("store", this.storeType).URI();
1707
+ const res = await this.gateway.start(this._url);
1283
1708
  if (res.isErr()) {
1709
+ this.logger.Error().Result("gw-start", res).Msg("started-gateway");
1284
1710
  return res;
1285
1711
  }
1712
+ this._url = res.Ok();
1713
+ const kb = await this.keybag();
1714
+ const skRes = await kb.ensureKeyFromUrl(this._url, () => {
1715
+ const idx = this._url.getParam("index");
1716
+ const storeKeyName = [this.name];
1717
+ if (idx) {
1718
+ storeKeyName.push(idx);
1719
+ }
1720
+ storeKeyName.push(this.storeType);
1721
+ return storeKeyName.join(":");
1722
+ });
1723
+ if (skRes.isErr()) {
1724
+ return skRes;
1725
+ }
1726
+ this._url = skRes.Ok();
1727
+ const version = guardVersion(this._url);
1728
+ if (version.isErr()) {
1729
+ this.logger.Error().Result("version", version).Msg("guardVersion");
1730
+ await this.close();
1731
+ return version;
1732
+ }
1733
+ if (this.ready) {
1734
+ const fn = this.ready.bind(this);
1735
+ const ready = await exception2Result(fn);
1736
+ if (ready.isErr()) {
1737
+ await this.close();
1738
+ return ready;
1739
+ }
1740
+ }
1286
1741
  this._onStarted.forEach((fn) => fn());
1287
- return guardVersion(this.url);
1742
+ this.logger.Debug().Msg("started");
1743
+ return version;
1744
+ }
1745
+ };
1746
+ var MetaStoreImpl = class extends BaseStoreImpl {
1747
+ // remote: boolean;
1748
+ constructor(sthis, name, url, opts) {
1749
+ super(name, url, { ...opts }, sthis, ensureLogger(sthis, "MetaStoreImpl"));
1750
+ this.storeType = "meta";
1751
+ this.subscribers = /* @__PURE__ */ new Map();
1752
+ this.parents = [];
1753
+ if (
1754
+ /*this.remote && */
1755
+ opts.gateway.subscribe
1756
+ ) {
1757
+ this.onStarted(async () => {
1758
+ this.logger.Debug().Str("url", this.url().toString()).Msg("Subscribing to the gateway");
1759
+ opts.gateway.subscribe?.(this.url(), async (message) => {
1760
+ this.logger.Debug().Msg("Received message from gateway");
1761
+ const dbMetas = await decodeGatewayMetaBytesToDbMeta(this.sthis, message);
1762
+ await Promise.all(
1763
+ dbMetas.map((dbMeta) => this.loader?.taskManager?.handleEvent(dbMeta.eventCid, dbMeta.parents, dbMeta.dbMeta))
1764
+ );
1765
+ this.updateParentsFromDbMetas(dbMetas);
1766
+ });
1767
+ });
1768
+ }
1769
+ }
1770
+ updateParentsFromDbMetas(dbMetas) {
1771
+ const cids = dbMetas.map((m) => m.eventCid);
1772
+ const dbMetaParents = dbMetas.flatMap((m) => m.parents);
1773
+ const uniqueParentsMap = new Map([...this.parents, ...cids].map((p) => [p.toString(), p]));
1774
+ const dbMetaParentsSet = new Set(dbMetaParents.map((p) => p.toString()));
1775
+ this.parents = Array.from(uniqueParentsMap.values()).filter((p) => !dbMetaParentsSet.has(p.toString()));
1288
1776
  }
1289
- async load(branch) {
1290
- this.logger.Debug().Str("branch", branch || "").Msg("loading");
1291
- const url = await this.gateway.buildUrl(this.url, branch || "main");
1777
+ async handleByteHeads(byteHeads) {
1778
+ return await decodeGatewayMetaBytesToDbMeta(this.sthis, byteHeads);
1779
+ }
1780
+ async load() {
1781
+ const branch = "main";
1782
+ const url = await this.gateway.buildUrl(this.url(), branch);
1292
1783
  if (url.isErr()) {
1293
- throw this.logger.Error().Result("buidUrl", url).Str("branch", branch || "").Url(this.url).Msg("got error from gateway.buildUrl").AsError();
1784
+ throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
1294
1785
  }
1295
1786
  const bytes = await this.gateway.get(url.Ok());
1296
1787
  if (bytes.isErr()) {
@@ -1299,66 +1790,45 @@ var MetaStore = class extends VersionedStore {
1299
1790
  }
1300
1791
  throw this.logger.Error().Url(url.Ok()).Result("bytes:", bytes).Msg("gateway get").AsError();
1301
1792
  }
1302
- try {
1303
- return [this.parseHeader(textDecoder.decode(bytes.Ok()))];
1304
- } catch (e) {
1305
- throw this.logger.Error().Err(e).Msg("parseHeader").AsError();
1306
- }
1793
+ const dbMetas = await this.handleByteHeads(bytes.Ok());
1794
+ await this.loader?.handleDbMetasFromStore(dbMetas.map((m) => m.dbMeta));
1795
+ this.updateParentsFromDbMetas(dbMetas);
1796
+ return dbMetas.map((m) => m.dbMeta);
1307
1797
  }
1308
- async save(meta, branch = "main") {
1798
+ async save(meta, branch) {
1799
+ branch = branch || "main";
1309
1800
  this.logger.Debug().Str("branch", branch).Any("meta", meta).Msg("saving meta");
1310
- const bytes = this.makeHeader(meta);
1311
- const url = await this.gateway.buildUrl(this.url, branch);
1801
+ const event = await createDbMetaEventBlock(this.sthis, meta, this.parents);
1802
+ const bytes = await encodeEventsWithParents(this.sthis, [event], this.parents);
1803
+ const url = await this.gateway.buildUrl(this.url(), branch);
1312
1804
  if (url.isErr()) {
1313
- throw this.logger.Error().Err(url.Err()).Str("branch", branch).Url(this.url).Msg("got error from gateway.buildUrl").AsError();
1805
+ throw this.logger.Error().Err(url.Err()).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
1314
1806
  }
1315
- const res = await this.gateway.put(url.Ok(), textEncoder.encode(bytes));
1807
+ this.parents = [event.cid];
1808
+ const res = await this.gateway.put(url.Ok(), bytes);
1316
1809
  if (res.isErr()) {
1317
1810
  throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
1318
1811
  }
1319
- return res.Ok();
1812
+ return res;
1320
1813
  }
1321
1814
  async close() {
1322
- await this.gateway.close(this.url);
1815
+ await this.gateway.close(this.url());
1323
1816
  this._onClosed.forEach((fn) => fn());
1324
- return Result2.Ok(void 0);
1817
+ return Result5.Ok(void 0);
1325
1818
  }
1326
1819
  async destroy() {
1327
- return this.gateway.destroy(this.url);
1820
+ return this.gateway.destroy(this.url());
1328
1821
  }
1329
1822
  };
1330
- var DataStore = class extends VersionedStore {
1331
- constructor(name, url, logger, gateway) {
1332
- super(
1333
- name,
1334
- url,
1335
- ensureLogger(logger, "DataStore", {
1336
- url: () => url.toString()
1337
- })
1338
- );
1339
- this.tag = "car-base";
1340
- this.gateway = gateway;
1341
- }
1342
- async start() {
1343
- this.logger.Debug().Msg("starting-gateway");
1344
- const res = await this.gateway.start(this.url);
1345
- if (res.isErr()) {
1346
- this.logger.Error().Result("gw-start", res).Msg("started-gateway");
1347
- return res;
1348
- }
1349
- this._onStarted.forEach((fn) => fn());
1350
- const version = guardVersion(this.url);
1351
- if (version.isErr()) {
1352
- this.logger.Error().Result("version", version).Msg("guardVersion");
1353
- await this.close();
1354
- return version;
1355
- }
1356
- this.logger.Debug().Msg("started");
1357
- return version;
1823
+ var DataStoreImpl = class extends BaseStoreImpl {
1824
+ // readonly tag: string = "car-base";
1825
+ constructor(sthis, name, url, opts) {
1826
+ super(name, url, { ...opts }, sthis, ensureLogger(sthis, "DataStoreImpl"));
1827
+ this.storeType = "data";
1358
1828
  }
1359
1829
  async load(cid) {
1360
1830
  this.logger.Debug().Any("cid", cid).Msg("loading");
1361
- const url = await this.gateway.buildUrl(this.url, cid.toString());
1831
+ const url = await this.gateway.buildUrl(this.url(), cid.toString());
1362
1832
  if (url.isErr()) {
1363
1833
  throw this.logger.Error().Err(url.Err()).Str("cid", cid.toString()).Msg("got error from gateway.buildUrl").AsError();
1364
1834
  }
@@ -1371,7 +1841,7 @@ var DataStore = class extends VersionedStore {
1371
1841
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1372
1842
  async save(car, opts) {
1373
1843
  this.logger.Debug().Any("cid", car.cid.toString()).Msg("saving");
1374
- const url = await this.gateway.buildUrl(this.url, car.cid.toString());
1844
+ const url = await this.gateway.buildUrl(this.url(), car.cid.toString());
1375
1845
  if (url.isErr()) {
1376
1846
  throw this.logger.Error().Err(url.Err()).Ref("cid", car.cid).Msg("got error from gateway.buildUrl").AsError();
1377
1847
  }
@@ -1382,31 +1852,30 @@ var DataStore = class extends VersionedStore {
1382
1852
  return res.Ok();
1383
1853
  }
1384
1854
  async remove(cid) {
1385
- const url = await this.gateway.buildUrl(this.url, cid.toString());
1855
+ const url = await this.gateway.buildUrl(this.url(), cid.toString());
1386
1856
  if (url.isErr()) {
1387
1857
  return url;
1388
1858
  }
1389
1859
  return this.gateway.delete(url.Ok());
1390
1860
  }
1391
1861
  async close() {
1392
- await this.gateway.close(this.url);
1862
+ await this.gateway.close(this.url());
1393
1863
  this._onClosed.forEach((fn) => fn());
1394
- return Result2.Ok(void 0);
1864
+ return Result5.Ok(void 0);
1395
1865
  }
1396
1866
  destroy() {
1397
- return this.gateway.destroy(this.url);
1867
+ return this.gateway.destroy(this.url());
1398
1868
  }
1399
1869
  };
1400
- var RemoteWAL = class extends VersionedStore {
1401
- constructor(loader, url, logger, gateway) {
1402
- super(loader.name, url, ensureLogger(logger, "RemoteWAL"));
1403
- this.tag = "rwal-base";
1404
- this._ready = new ResolveOnce2();
1870
+ var WALStoreImpl = class extends BaseStoreImpl {
1871
+ constructor(loader, url, opts) {
1872
+ super(loader.name, url, { ...opts }, loader.sthis, ensureLogger(loader.sthis, "WALStoreImpl"));
1873
+ this.storeType = "wal";
1874
+ this._ready = new ResolveOnce3();
1405
1875
  this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
1406
1876
  this.processing = void 0;
1407
1877
  this.processQueue = new CommitQueue();
1408
1878
  this.loader = loader;
1409
- this.gateway = gateway;
1410
1879
  }
1411
1880
  async ready() {
1412
1881
  return this._ready.once(async () => {
@@ -1425,124 +1894,121 @@ var RemoteWAL = class extends VersionedStore {
1425
1894
  }
1426
1895
  async enqueue(dbMeta, opts) {
1427
1896
  await this.ready();
1428
- if (opts.noLoader) {
1897
+ if (opts.compact) {
1898
+ this.walState.operations = [];
1899
+ this.walState.noLoaderOps = [dbMeta];
1900
+ } else if (opts.noLoader) {
1429
1901
  this.walState.noLoaderOps.push(dbMeta);
1430
1902
  } else {
1431
1903
  this.walState.operations.push(dbMeta);
1432
1904
  }
1433
1905
  await this.save(this.walState);
1434
- void this._process();
1906
+ if (!opts.noLoader) {
1907
+ void this.process();
1908
+ }
1435
1909
  }
1436
1910
  async enqueueFile(fileCid, publicFile = false) {
1437
1911
  await this.ready();
1438
1912
  this.walState.fileOperations.push({ cid: fileCid, public: publicFile });
1439
1913
  }
1440
- async _process() {
1914
+ async process() {
1441
1915
  await this.ready();
1442
1916
  if (!this.loader.remoteCarStore) return;
1443
1917
  await this.processQueue.enqueue(async () => {
1444
- await this._doProcess();
1918
+ try {
1919
+ await this._doProcess();
1920
+ } catch (e) {
1921
+ this.logger.Error().Any("error", e).Msg("error processing wal");
1922
+ }
1445
1923
  if (this.walState.operations.length || this.walState.fileOperations.length || this.walState.noLoaderOps.length) {
1446
- setTimeout(() => void this._process(), 0);
1924
+ setTimeout(() => void this.process(), 0);
1447
1925
  }
1448
1926
  });
1449
1927
  }
1450
1928
  async _doProcess() {
1451
1929
  if (!this.loader.remoteCarStore) return;
1452
- const rmlp = (async () => {
1453
- const operations = [...this.walState.operations];
1454
- const fileOperations = [...this.walState.fileOperations];
1455
- const uploads = [];
1456
- const noLoaderOps = [...this.walState.noLoaderOps];
1457
- const limit = pLimit2(5);
1458
- if (operations.length + fileOperations.length + noLoaderOps.length === 0) return;
1459
- for (const dbMeta of noLoaderOps) {
1460
- const uploadP = limit(async () => {
1461
- for (const cid of dbMeta.cars) {
1462
- const car = await (await this.loader.carStore()).load(cid);
1463
- if (!car) {
1464
- if (carLogIncludesGroup2(this.loader.carLog, dbMeta.cars))
1465
- throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
1466
- } else {
1467
- await throwFalsy(this.loader.remoteCarStore).save(car);
1930
+ const operations = [...this.walState.operations];
1931
+ const noLoaderOps = [...this.walState.noLoaderOps];
1932
+ const fileOperations = [...this.walState.fileOperations];
1933
+ if (operations.length + noLoaderOps.length + fileOperations.length === 0) return;
1934
+ const concurrencyLimit = 3;
1935
+ const retryableUpload = (fn, description) => pRetry(fn, {
1936
+ retries: 5,
1937
+ onFailedAttempt: (error) => {
1938
+ this.logger.Warn().Msg(`Attempt ${error.attemptNumber} failed for ${description}. There are ${error.retriesLeft} retries left.`);
1939
+ }
1940
+ });
1941
+ try {
1942
+ await pMap(
1943
+ noLoaderOps,
1944
+ async (dbMeta) => {
1945
+ await retryableUpload(async () => {
1946
+ for (const cid of dbMeta.cars) {
1947
+ const car = await (await this.loader.carStore()).load(cid);
1948
+ if (!car) {
1949
+ if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
1950
+ throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
1951
+ }
1952
+ } else {
1953
+ await throwFalsy(this.loader.remoteCarStore).save(car);
1954
+ }
1468
1955
  }
1469
1956
  this.walState.noLoaderOps = this.walState.noLoaderOps.filter((op) => op !== dbMeta);
1470
- }
1471
- });
1472
- uploads.push(uploadP);
1473
- }
1474
- for (const dbMeta of operations) {
1475
- const uploadP = limit(async () => {
1476
- for (const cid of dbMeta.cars) {
1477
- const car = await (await this.loader.carStore()).load(cid).catch(() => null);
1478
- if (!car) {
1479
- if (carLogIncludesGroup2(this.loader.carLog, dbMeta.cars))
1480
- throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
1481
- } else {
1482
- await throwFalsy(this.loader.remoteCarStore).save(car);
1957
+ }, `noLoaderOp with dbMeta.cars=${dbMeta.cars.toString()}`);
1958
+ },
1959
+ { concurrency: concurrencyLimit }
1960
+ );
1961
+ await pMap(
1962
+ operations,
1963
+ async (dbMeta) => {
1964
+ await retryableUpload(async () => {
1965
+ for (const cid of dbMeta.cars) {
1966
+ const car = await (await this.loader.carStore()).load(cid);
1967
+ if (!car) {
1968
+ if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
1969
+ throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
1970
+ }
1971
+ } else {
1972
+ await throwFalsy(this.loader.remoteCarStore).save(car);
1973
+ }
1483
1974
  }
1484
- }
1485
- this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
1486
- });
1487
- uploads.push(uploadP);
1488
- }
1489
- if (fileOperations.length) {
1490
- const dbLoader = this.loader;
1491
- for (const { cid: fileCid, public: publicFile } of fileOperations) {
1492
- const uploadP = limit(async () => {
1493
- const fileBlock = await (await dbLoader.fileStore()).load(fileCid);
1494
- await dbLoader.remoteFileStore?.save(fileBlock, { public: publicFile });
1975
+ this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
1976
+ }, `operation with dbMeta.cars=${dbMeta.cars.toString()}`);
1977
+ },
1978
+ { concurrency: concurrencyLimit }
1979
+ );
1980
+ await pMap(
1981
+ fileOperations,
1982
+ async ({ cid: fileCid, public: publicFile }) => {
1983
+ await retryableUpload(async () => {
1984
+ const fileBlock = await (await this.loader.fileStore()).load(fileCid);
1985
+ if (!fileBlock) {
1986
+ throw this.logger.Error().Ref("cid", fileCid).Msg("missing file block").AsError();
1987
+ }
1988
+ await this.loader.remoteFileStore?.save(fileBlock, { public: publicFile });
1495
1989
  this.walState.fileOperations = this.walState.fileOperations.filter((op) => op.cid !== fileCid);
1496
- });
1497
- uploads.push(uploadP);
1498
- }
1499
- }
1500
- try {
1501
- const res = await Promise.allSettled(uploads);
1502
- const errors = res.filter((r) => r.status === "rejected");
1503
- if (errors.length) {
1504
- throw this.logger.Error().Any(
1505
- "errors",
1506
- errors.map((e) => e.reason)
1507
- ).Msg("error uploading").AsError();
1508
- errors[0].reason;
1509
- }
1510
- if (operations.length) {
1511
- const lastOp = operations[operations.length - 1];
1512
- await this.loader.remoteMetaStore?.save(lastOp).catch((e) => {
1513
- this.walState.operations.push(lastOp);
1514
- throw this.logger.Error().Any("error", e).Msg("error saving remote meta").AsError();
1515
- });
1516
- }
1517
- } finally {
1518
- await this.save(this.walState);
1990
+ }, `fileOperation with cid=${fileCid.toString()}`);
1991
+ },
1992
+ { concurrency: concurrencyLimit }
1993
+ );
1994
+ if (operations.length) {
1995
+ const lastOp = operations[operations.length - 1];
1996
+ await retryableUpload(async () => {
1997
+ await this.loader.remoteMetaStore?.save(lastOp);
1998
+ }, `remoteMetaStore save with dbMeta.cars=${lastOp.cars.toString()}`);
1519
1999
  }
1520
- })();
1521
- await rmlp;
1522
- }
1523
- async start() {
1524
- const res = await this.gateway.start(this.url);
1525
- if (res.isErr()) {
1526
- return res;
1527
- }
1528
- const ver = guardVersion(this.url);
1529
- if (ver.isErr()) {
1530
- await this.close();
1531
- return ver;
1532
- }
1533
- const ready = await exception2Result(() => this.ready());
1534
- this._onStarted.forEach((fn) => fn());
1535
- if (ready.isErr()) {
1536
- await this.close();
1537
- return ready;
2000
+ } catch (error) {
2001
+ this.logger.Error().Any("error", error).Msg("Processing failed");
2002
+ return;
2003
+ } finally {
2004
+ await this.save(this.walState);
1538
2005
  }
1539
- return ready;
1540
2006
  }
1541
2007
  async load() {
1542
2008
  this.logger.Debug().Msg("loading");
1543
- const filepath = await this.gateway.buildUrl(this.url, "main");
2009
+ const filepath = await this.gateway.buildUrl(this.url(), "main");
1544
2010
  if (filepath.isErr()) {
1545
- throw this.logger.Error().Err(filepath.Err()).Str("url", this.url.toString()).Msg("error building url").AsError();
2011
+ throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
1546
2012
  }
1547
2013
  const bytes = await this.gateway.get(filepath.Ok());
1548
2014
  if (bytes.isErr()) {
@@ -1552,71 +2018,83 @@ var RemoteWAL = class extends VersionedStore {
1552
2018
  throw this.logger.Error().Err(bytes.Err()).Msg("error get").AsError();
1553
2019
  }
1554
2020
  try {
1555
- return bytes && parse(textDecoder.decode(bytes.Ok()));
2021
+ return bytes && parse2(this.sthis.txt.decode(bytes.Ok()));
1556
2022
  } catch (e) {
1557
2023
  throw this.logger.Error().Err(e).Msg("error parse").AsError();
1558
2024
  }
1559
2025
  }
1560
2026
  async save(state) {
1561
- const filepath = await this.gateway.buildUrl(this.url, "main");
2027
+ const filepath = await this.gateway.buildUrl(this.url(), "main");
1562
2028
  if (filepath.isErr()) {
1563
- throw this.logger.Error().Err(filepath.Err()).Str("url", this.url.toString()).Msg("error building url").AsError();
2029
+ throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
1564
2030
  }
1565
2031
  let encoded;
1566
2032
  try {
1567
- encoded = format(state);
2033
+ encoded = format2(state);
1568
2034
  } catch (e) {
1569
2035
  throw this.logger.Error().Err(e).Any("state", state).Msg("error format").AsError();
1570
2036
  }
1571
- const res = await this.gateway.put(filepath.Ok(), textEncoder.encode(encoded));
2037
+ const res = await this.gateway.put(filepath.Ok(), this.sthis.txt.encode(encoded));
1572
2038
  if (res.isErr()) {
1573
2039
  throw this.logger.Error().Err(res.Err()).Str("filePath", filepath.Ok().toString()).Msg("error saving").AsError();
1574
2040
  }
1575
2041
  }
1576
2042
  async close() {
1577
- await this.gateway.close(this.url);
2043
+ await this.gateway.close(this.url());
1578
2044
  this._onClosed.forEach((fn) => fn());
1579
- return Result2.Ok(void 0);
2045
+ return Result5.Ok(void 0);
1580
2046
  }
1581
2047
  destroy() {
1582
- return this.gateway.destroy(this.url);
2048
+ return this.gateway.destroy(this.url());
1583
2049
  }
1584
2050
  };
1585
2051
 
1586
2052
  // src/blockstore/store-factory.ts
1587
2053
  function ensureIsIndex(url, isIndex) {
1588
2054
  if (isIndex) {
1589
- url.searchParams.set("index", isIndex);
1590
- return url;
1591
- } else {
1592
- url.searchParams.delete("index");
1593
- return url;
1594
- }
1595
- }
1596
- function toURL(pathOrUrl, isIndex) {
1597
- if (pathOrUrl instanceof URL) return ensureIsIndex(pathOrUrl, isIndex);
1598
- try {
1599
- const url = new URL(pathOrUrl);
1600
- return ensureIsIndex(url, isIndex);
1601
- } catch (e) {
1602
- const url = new URL(`file://${pathOrUrl}`);
1603
- return ensureIsIndex(url, isIndex);
2055
+ return url.build().setParam("index", isIndex).URI();
1604
2056
  }
2057
+ return url.build().delParam("index").URI();
1605
2058
  }
1606
- var storeFactory = /* @__PURE__ */ new Map();
1607
2059
  function ensureName(name, url) {
1608
- if (!url.searchParams.has("name")) {
1609
- url.searchParams.set("name", name);
2060
+ if (!url.hasParam("name")) {
2061
+ return url.build().setParam("name", name).URI();
1610
2062
  }
2063
+ return url;
1611
2064
  }
2065
+ var storeFactory = /* @__PURE__ */ new Map();
1612
2066
  function buildURL(optURL, loader) {
1613
2067
  const storeOpts = loader.ebOpts.store;
1614
2068
  const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
1615
2069
  let obuUrl;
1616
2070
  if (obuItem && obuItem.overrideBaseURL) {
1617
- obuUrl = new URL(obuItem.overrideBaseURL);
2071
+ obuUrl = URI5.from(obuItem.overrideBaseURL);
1618
2072
  }
1619
- return toURL(optURL || obuUrl || dataDir(loader.name, storeOpts.stores?.base), storeOpts.isIndex);
2073
+ const ret = ensureIsIndex(
2074
+ URI5.from(optURL || obuUrl || dataDir(loader.sthis, loader.name, storeOpts.stores?.base)),
2075
+ storeOpts.isIndex
2076
+ );
2077
+ return ret;
2078
+ }
2079
+ var onceGateway = new KeyedResolvOnce2();
2080
+ async function getGatewayFromURL(url, sthis) {
2081
+ return onceGateway.get(url.toString()).once(async () => {
2082
+ const item = storeFactory.get(url.protocol);
2083
+ if (item) {
2084
+ const ret = {
2085
+ gateway: await item.gateway(sthis),
2086
+ test: await item.test(sthis)
2087
+ };
2088
+ const res = await ret.gateway.start(url);
2089
+ if (res.isErr()) {
2090
+ sthis.logger.Error().Result("start", res).Msg("start failed");
2091
+ return void 0;
2092
+ }
2093
+ return ret;
2094
+ }
2095
+ sthis.logger.Warn().Url(url).Msg("unsupported protocol");
2096
+ return void 0;
2097
+ });
1620
2098
  }
1621
2099
  function registerStoreProtocol(item) {
1622
2100
  let protocol = item.protocol;
@@ -1625,8 +2103,7 @@ function registerStoreProtocol(item) {
1625
2103
  }
1626
2104
  if (storeFactory.has(protocol)) {
1627
2105
  if (!item.overrideBaseURL && storeFactory.get(protocol) !== item) {
1628
- const logger = ensureLogger({}, "registerStoreProtocol", { protocol });
1629
- logger.Warn().Msg(`protocol ${protocol} already registered`);
2106
+ throw new Error(`we need a logger here`);
1630
2107
  return () => {
1631
2108
  };
1632
2109
  }
@@ -1641,106 +2118,92 @@ function registerStoreProtocol(item) {
1641
2118
  storeFactory.delete(protocol);
1642
2119
  };
1643
2120
  }
1644
- function runStoreFactory(url, logger, run) {
1645
- const item = storeFactory.get(url.protocol);
1646
- if (!item) {
1647
- throw logger.Error().Url(url).Str("protocol", url.protocol).Any("keys", Array(storeFactory.keys())).Msg(`unsupported protocol`).AsError();
1648
- }
1649
- logger.Debug().Str("protocol", url.protocol).Msg("run");
1650
- return run(item);
1651
- }
1652
- var onceLoadDataGateway = new KeyedResolvOnce();
1653
- function loadDataGateway(url, logger) {
1654
- return onceLoadDataGateway.get(url.protocol).once(async () => {
1655
- return runStoreFactory(url, logger, async (item) => item.data(logger));
1656
- });
1657
- }
1658
- var onceDataStoreFactory = new KeyedResolvOnce();
2121
+ var onceDataStoreFactory = new KeyedResolvOnce2();
1659
2122
  async function dataStoreFactory(loader) {
1660
- const url = buildURL(loader.ebOpts.store.stores?.data, loader);
1661
- ensureName(loader.name, url);
1662
- const logger = ensureLogger(loader.logger, "dataStoreFactory", { url: url.toString() });
1663
- url.searchParams.set("store", "data");
2123
+ const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.data, loader)).build().setParam("store", "data").URI();
2124
+ const sthis = ensureSuperLog(loader.sthis, "dataStoreFactory", { url: url.toString() });
1664
2125
  return onceDataStoreFactory.get(url.toString()).once(async () => {
1665
- const gateway = await loadDataGateway(url, logger);
1666
- const store = new DataStore(loader.name, url, loader.logger, gateway);
1667
- const ret = await store.start();
1668
- if (ret.isErr()) {
1669
- throw logger.Error().Result("start", ret).Msg("start failed").AsError();
1670
- }
1671
- logger.Debug().Str("prepared", store.url.toString()).Msg("produced");
2126
+ const gateway = await getGatewayFromURL(url, sthis);
2127
+ if (!gateway) {
2128
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2129
+ }
2130
+ const store = new DataStoreImpl(sthis, loader.name, url, {
2131
+ gateway: gateway.gateway,
2132
+ keybag: () => getKeyBag(loader.sthis, {
2133
+ ...loader.ebOpts.keyBag
2134
+ })
2135
+ });
1672
2136
  return store;
1673
2137
  });
1674
2138
  }
1675
- var onceLoadMetaGateway = new KeyedResolvOnce();
1676
- function loadMetaGateway(url, logger) {
1677
- return onceLoadMetaGateway.get(url.protocol).once(async () => {
1678
- return runStoreFactory(url, logger, async (item) => item.meta(logger));
1679
- });
1680
- }
1681
- var onceMetaStoreFactory = new KeyedResolvOnce();
2139
+ var onceMetaStoreFactory = new KeyedResolvOnce2();
1682
2140
  async function metaStoreFactory(loader) {
1683
- const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
1684
- ensureName(loader.name, url);
1685
- const logger = ensureLogger(loader.logger, "metaStoreFactory", { url: () => url.toString() });
1686
- url.searchParams.set("store", "meta");
2141
+ const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.meta, loader)).build().setParam("store", "meta").URI();
2142
+ const sthis = ensureSuperLog(loader.sthis, "metaStoreFactory", { url: () => url.toString() });
1687
2143
  return onceMetaStoreFactory.get(url.toString()).once(async () => {
1688
- logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
1689
- const gateway = await loadMetaGateway(url, logger);
1690
- const store = new MetaStore(loader.name, url, loader.logger, gateway);
1691
- const ret = await store.start();
1692
- if (ret.isErr()) {
1693
- throw logger.Error().Result("start", ret).Msg("start failed").AsError();
1694
- }
2144
+ sthis.logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
2145
+ const gateway = await getGatewayFromURL(url, sthis);
2146
+ if (!gateway) {
2147
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2148
+ }
2149
+ const store = new MetaStoreImpl(loader.sthis, loader.name, url, {
2150
+ gateway: gateway.gateway,
2151
+ keybag: () => getKeyBag(loader.sthis, {
2152
+ ...loader.ebOpts.keyBag
2153
+ })
2154
+ });
1695
2155
  return store;
1696
2156
  });
1697
2157
  }
1698
- var onceWalGateway = new KeyedResolvOnce();
1699
- function loadWalGateway(url, logger) {
1700
- return onceWalGateway.get(url.protocol).once(async () => {
1701
- return runStoreFactory(url, logger, async (item) => item.wal(logger));
1702
- });
1703
- }
1704
- var onceRemoteWalFactory = new KeyedResolvOnce();
2158
+ var onceRemoteWalFactory = new KeyedResolvOnce2();
1705
2159
  async function remoteWalFactory(loader) {
1706
- const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
1707
- ensureName(loader.name, url);
1708
- const logger = ensureLogger(loader.logger, "remoteWalFactory", { url: url.toString() });
1709
- url.searchParams.set("store", "wal");
2160
+ const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.wal, loader)).build().setParam("store", "wal").URI();
2161
+ const sthis = ensureSuperLog(loader.sthis, "remoteWalFactory", { url: url.toString() });
1710
2162
  return onceRemoteWalFactory.get(url.toString()).once(async () => {
1711
- const gateway = await loadWalGateway(url, logger);
1712
- logger.Debug().Str("prepared", url.toString()).Msg("produced");
1713
- const store = new RemoteWAL(loader, url, loader.logger, gateway);
1714
- const ret = await store.start();
1715
- if (ret.isErr()) {
1716
- throw logger.Error().Result("start", ret).Msg("start failed").AsError();
1717
- }
2163
+ const gateway = await getGatewayFromURL(url, sthis);
2164
+ if (!gateway) {
2165
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2166
+ }
2167
+ sthis.logger.Debug().Str("prepared", url.toString()).Msg("produced");
2168
+ const store = new WALStoreImpl(loader, url, {
2169
+ gateway: gateway.gateway,
2170
+ keybag: () => getKeyBag(loader.sthis, {
2171
+ ...loader.ebOpts.keyBag
2172
+ })
2173
+ });
1718
2174
  return store;
1719
2175
  });
1720
2176
  }
1721
- async function testStoreFactory(url, ilogger) {
1722
- const logger = ensureLogger(
1723
- {
1724
- logger: ilogger
1725
- },
1726
- "testStoreFactory"
1727
- );
1728
- return runStoreFactory(url, logger, async (item) => item.test(logger));
2177
+ async function testStoreFactory(url, sthis) {
2178
+ sthis = ensureSuperLog(sthis, "testStoreFactory");
2179
+ const gateway = await getGatewayFromURL(url, sthis);
2180
+ if (!gateway) {
2181
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2182
+ }
2183
+ return gateway.test;
2184
+ }
2185
+ async function ensureStart(store, logger) {
2186
+ const ret = await store.start();
2187
+ if (ret.isErr()) {
2188
+ throw logger.Error().Result("start", ret).Msg("start failed").AsError();
2189
+ }
2190
+ logger.Debug().Url(ret.Ok(), "prepared").Msg("produced");
2191
+ return store;
1729
2192
  }
1730
- function toStoreRuntime(opts, ilogger) {
1731
- const logger = ensureLogger(ilogger, "toStoreRuntime", {});
2193
+ function toStoreRuntime(opts, sthis) {
2194
+ const logger = ensureLogger(sthis, "toStoreRuntime", {});
1732
2195
  return {
1733
- makeMetaStore: (loader) => {
2196
+ makeMetaStore: async (loader) => {
1734
2197
  logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
1735
- return (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader);
2198
+ return ensureStart(await (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader), logger);
1736
2199
  },
1737
- makeDataStore: (loader) => {
2200
+ makeDataStore: async (loader) => {
1738
2201
  logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
1739
- return (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader);
2202
+ return ensureStart(await (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader), logger);
1740
2203
  },
1741
- makeRemoteWAL: (loader) => {
1742
- logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeRemoteWAL).Msg("makeRemoteWAL");
1743
- return (loader.ebOpts.store.makeRemoteWAL || remoteWalFactory)(loader);
2204
+ makeWALStore: async (loader) => {
2205
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeWALStore).Msg("makeRemoteWAL");
2206
+ return ensureStart(await (loader.ebOpts.store.makeWALStore || remoteWalFactory)(loader), logger);
1744
2207
  },
1745
2208
  encodeFile: opts.encodeFile || encodeFile,
1746
2209
  decodeFile: opts.decodeFile || decodeFile
@@ -1748,43 +2211,131 @@ function toStoreRuntime(opts, ilogger) {
1748
2211
  }
1749
2212
  registerStoreProtocol({
1750
2213
  protocol: "file:",
1751
- data: async (logger) => {
1752
- const { FileDataGateway } = await import("./store-file-VJ6BI4II.js");
1753
- return new FileDataGateway(logger);
2214
+ gateway: async (sthis) => {
2215
+ const { FileGateway } = await import("./gateway-5FCWPX5W.js");
2216
+ return new FileGateway(sthis);
1754
2217
  },
1755
- meta: async (logger) => {
1756
- const { FileMetaGateway } = await import("./store-file-VJ6BI4II.js");
1757
- return new FileMetaGateway(logger);
1758
- },
1759
- wal: async (logger) => {
1760
- const { FileWALGateway } = await import("./store-file-VJ6BI4II.js");
1761
- return new FileWALGateway(logger);
1762
- },
1763
- test: async (logger) => {
1764
- const { FileTestStore } = await import("./store-file-VJ6BI4II.js");
1765
- return new FileTestStore(logger);
2218
+ test: async (sthis) => {
2219
+ const { FileTestStore } = await import("./gateway-5FCWPX5W.js");
2220
+ return new FileTestStore(sthis);
1766
2221
  }
1767
2222
  });
1768
2223
  registerStoreProtocol({
1769
2224
  protocol: "indexdb:",
1770
- data: async (logger) => {
1771
- const { IndexDBDataGateway } = await import("./store-indexdb-WLRSICCB.js");
1772
- return new IndexDBDataGateway(logger);
1773
- },
1774
- meta: async (logger) => {
1775
- const { IndexDBMetaGateway } = await import("./store-indexdb-WLRSICCB.js");
1776
- return new IndexDBMetaGateway(logger);
1777
- },
1778
- wal: async (logger) => {
1779
- const { IndexDBMetaGateway } = await import("./store-indexdb-WLRSICCB.js");
1780
- return new IndexDBMetaGateway(logger);
2225
+ gateway: async (sthis) => {
2226
+ const { IndexDBGateway } = await import("./gateway-H7UD6TNB.js");
2227
+ return new IndexDBGateway(sthis);
1781
2228
  },
1782
- test: async (logger) => {
1783
- const { IndexDBTestStore } = await import("./store-indexdb-WLRSICCB.js");
1784
- return new IndexDBTestStore(logger);
2229
+ test: async (sthis) => {
2230
+ const { IndexDBTestStore } = await import("./gateway-H7UD6TNB.js");
2231
+ return new IndexDBTestStore(sthis);
1785
2232
  }
1786
2233
  });
1787
2234
 
2235
+ // src/blockstore/store-remote.ts
2236
+ async function RemoteDataStore(sthis, name, url, opts) {
2237
+ const ds = new DataStoreImpl(sthis, name, url, opts);
2238
+ await ds.start();
2239
+ return ds;
2240
+ }
2241
+ async function RemoteMetaStore(sthis, name, url, opts) {
2242
+ const ms = new MetaStoreImpl(
2243
+ sthis,
2244
+ name,
2245
+ url,
2246
+ opts
2247
+ /* , true*/
2248
+ );
2249
+ await ms.start();
2250
+ return ms;
2251
+ }
2252
+
2253
+ // src/blockstore/connection-base.ts
2254
+ var ConnectionBase = class {
2255
+ constructor(url, logger) {
2256
+ this.loaded = Promise.resolve();
2257
+ this.logger = logger;
2258
+ this.url = url;
2259
+ }
2260
+ async refresh() {
2261
+ await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load();
2262
+ await (await throwFalsy(this.loader).WALStore()).process();
2263
+ }
2264
+ async connect_X({ loader }) {
2265
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
2266
+ await this.connectMeta_X({ loader });
2267
+ await this.connectStorage_X({ loader });
2268
+ }
2269
+ async connectMeta_X({ loader }) {
2270
+ if (!loader) throw this.logger.Error().Msg("connectMeta_X: loader is required").AsError();
2271
+ this.loader = loader;
2272
+ await this.onConnect();
2273
+ const metaUrl = this.url.build().defParam("store", "meta").URI();
2274
+ const gateway = await getGatewayFromURL(metaUrl, this.loader.sthis);
2275
+ if (!gateway) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: gateway is required").AsError();
2276
+ const dbName = metaUrl.getParam("name");
2277
+ if (!dbName) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: name is required").AsError();
2278
+ const remote = await RemoteMetaStore(loader.sthis, dbName, metaUrl, {
2279
+ gateway: gateway.gateway,
2280
+ keybag: () => getKeyBag(loader.sthis, loader.ebOpts.keyBag),
2281
+ loader
2282
+ });
2283
+ this.loader.remoteMetaStore = remote;
2284
+ this.loaded = this.loader.ready().then(async () => {
2285
+ remote.load().then(async () => {
2286
+ (await throwFalsy(this.loader).WALStore()).process();
2287
+ });
2288
+ });
2289
+ }
2290
+ async connectStorage_X({ loader }) {
2291
+ if (!loader) throw this.logger.Error().Msg("connectStorage_X: loader is required").AsError();
2292
+ this.loader = loader;
2293
+ const dataUrl = this.url.build().defParam("store", "data").URI();
2294
+ const gateway = await getGatewayFromURL(dataUrl, this.loader.sthis);
2295
+ if (!gateway) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: gateway is required").AsError();
2296
+ const name = dataUrl.getParam("name");
2297
+ if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: name is required").AsError;
2298
+ loader.remoteCarStore = await RemoteDataStore(loader.sthis, name, this.url, {
2299
+ gateway: gateway.gateway,
2300
+ keybag: () => getKeyBag(loader.sthis, this.loader?.ebOpts.keyBag)
2301
+ });
2302
+ loader.remoteFileStore = loader.remoteCarStore;
2303
+ }
2304
+ // move this stuff to connect
2305
+ // async getDashboardURL(compact = true) {
2306
+ // const baseUrl = 'https://dashboard.fireproof.storage/'
2307
+ // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
2308
+ // // if (compact) {
2309
+ // // await this.compact()
2310
+ // // }
2311
+ // const currents = await this.loader?.metaStore?.load()
2312
+ // if (!currents) throw new Error("Can't sync empty database: save data first")
2313
+ // if (currents.length > 1)
2314
+ // throw new Error("Can't sync database with split heads: make an update first")
2315
+ // const current = currents[0]
2316
+ // const params = {
2317
+ // car: current.car.toString()
2318
+ // }
2319
+ // if (current.key) {
2320
+ // // @ts-ignore
2321
+ // params.key = current.key.toString()
2322
+ // }
2323
+ // // @ts-ignore
2324
+ // if (this.name) {
2325
+ // // @ts-ignore
2326
+ // params.name = this.name
2327
+ // }
2328
+ // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
2329
+ // console.log('Import to dashboard: ' + url.toString())
2330
+ // return url
2331
+ // }
2332
+ // openDashboard() {
2333
+ // void this.getDashboardURL().then(url => {
2334
+ // if (url) window.open(url.toString(), '_blank')
2335
+ // })
2336
+ // }
2337
+ };
2338
+
1788
2339
  // src/crdt-helpers.ts
1789
2340
  function time(tag) {
1790
2341
  }
@@ -1833,7 +2384,7 @@ async function writeDocContent(store, blocks, update, logger) {
1833
2384
  await processFiles(store, blocks, update.value, logger);
1834
2385
  value = { doc: update.value };
1835
2386
  }
1836
- const block = await encode3({ value, hasher: hasher2, codec: codec2 });
2387
+ const block = await encode({ value, hasher: hasher5, codec });
1837
2388
  blocks.putSync(block.cid, block.bytes);
1838
2389
  return block.cid;
1839
2390
  }
@@ -1842,10 +2393,16 @@ async function processFiles(store, blocks, doc, logger) {
1842
2393
  await processFileset(logger, store, blocks, doc._files);
1843
2394
  }
1844
2395
  if (doc._publicFiles) {
1845
- await processFileset(logger, store, blocks, doc._publicFiles, true);
2396
+ await processFileset(
2397
+ logger,
2398
+ store,
2399
+ blocks,
2400
+ doc._publicFiles
2401
+ /*, true*/
2402
+ );
1846
2403
  }
1847
2404
  }
1848
- async function processFileset(logger, store, blocks, files, publicFiles = false) {
2405
+ async function processFileset(logger, store, blocks, files) {
1849
2406
  const dbBlockstore = blocks.parent;
1850
2407
  if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, database name is required").AsError();
1851
2408
  const t = new CarTransaction(dbBlockstore);
@@ -1867,9 +2424,10 @@ async function processFileset(logger, store, blocks, files, publicFiles = false)
1867
2424
  }
1868
2425
  }
1869
2426
  if (didPut.length) {
1870
- const car = await dbBlockstore.loader.commitFiles(t, { files }, {
1871
- public: publicFiles
1872
- });
2427
+ const car = await dbBlockstore.loader.commitFiles(
2428
+ t,
2429
+ { files }
2430
+ );
1873
2431
  if (car) {
1874
2432
  for (const name of didPut) {
1875
2433
  files[name] = { car, ...files[name] };
@@ -1903,7 +2461,7 @@ function readFileset(blocks, files, isPublic = false) {
1903
2461
  fileMeta.file = async () => await blocks.ebOpts.storeRuntime.decodeFile(
1904
2462
  {
1905
2463
  get: async (cid) => {
1906
- return await blocks.getFile(throwFalsy(fileMeta.car), cid, isPublic);
2464
+ return await blocks.getFile(throwFalsy(fileMeta.car), cid);
1907
2465
  }
1908
2466
  },
1909
2467
  fileMeta.cid,
@@ -1917,7 +2475,7 @@ function readFileset(blocks, files, isPublic = false) {
1917
2475
  async function getValueFromLink(blocks, link, logger) {
1918
2476
  const block = await blocks.get(link);
1919
2477
  if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
1920
- const { value } = await decode3({ bytes: block.bytes, hasher: hasher2, codec: codec2 });
2478
+ const { value } = await decode({ bytes: block.bytes, hasher: hasher5, codec });
1921
2479
  const cvalue = {
1922
2480
  ...value,
1923
2481
  cid: link
@@ -1926,17 +2484,21 @@ async function getValueFromLink(blocks, link, logger) {
1926
2484
  return cvalue;
1927
2485
  }
1928
2486
  var DirtyEventFetcher = class extends EventFetcher {
2487
+ constructor(logger, blocks) {
2488
+ super(blocks);
2489
+ this.logger = logger;
2490
+ }
1929
2491
  async get(link) {
1930
2492
  try {
1931
2493
  return super.get(link);
1932
2494
  } catch (e) {
1933
- console.error("missing event", link.toString(), e);
2495
+ this.logger.Error().Ref("link", link.toString()).Err(e).Msg("Missing event");
1934
2496
  return { value: void 0 };
1935
2497
  }
1936
2498
  }
1937
2499
  };
1938
2500
  async function clockChangesSince(blocks, head, since, opts, logger) {
1939
- const eventsFetcher = opts.dirty ? new DirtyEventFetcher(blocks) : new EventFetcher(blocks);
2501
+ const eventsFetcher = opts.dirty ? new DirtyEventFetcher(logger, blocks) : new EventFetcher(blocks);
1940
2502
  const keys = /* @__PURE__ */ new Set();
1941
2503
  const updates = await gatherUpdates(
1942
2504
  blocks,
@@ -2032,20 +2594,19 @@ async function doCompact(blockLog, head, logger) {
2032
2594
  isCompacting = false;
2033
2595
  }
2034
2596
  async function getBlock(blocks, cidString) {
2035
- const block = await blocks.get(parse2(cidString));
2597
+ const block = await blocks.get(parse3(cidString));
2036
2598
  if (!block) throw new Error(`Missing block ${cidString}`);
2037
- const { cid, value } = await decode3({ bytes: block.bytes, codec: codec2, hasher: hasher2 });
2038
- return new Block2({ cid, value, bytes: block.bytes });
2599
+ const { cid, value } = await decode({ bytes: block.bytes, codec, hasher: hasher5 });
2600
+ return new Block({ cid, value, bytes: block.bytes });
2039
2601
  }
2040
2602
 
2041
2603
  // src/indexer-helpers.ts
2042
- import { create as create3 } from "multiformats/block";
2043
- import { sha256 as hasher3 } from "multiformats/hashes/sha2";
2044
- import * as codec3 from "@ipld/dag-cbor";
2604
+ import { sha256 as hasher6 } from "multiformats/hashes/sha2";
2605
+ import * as codec2 from "@ipld/dag-cbor";
2045
2606
  import charwise from "charwise";
2046
2607
  import * as DbIndex from "prolly-trees/db-index";
2047
- import { bf as bf2, simpleCompare } from "prolly-trees/utils";
2048
- import { nocache as cache2 } from "prolly-trees/cache";
2608
+ import { bf, simpleCompare } from "prolly-trees/utils";
2609
+ import { nocache as cache } from "prolly-trees/cache";
2049
2610
  var IndexTree = class {
2050
2611
  };
2051
2612
  function refCompare(aRef, bRef) {
@@ -2061,8 +2622,8 @@ function compare(a, b) {
2061
2622
  if (comp !== 0) return comp;
2062
2623
  return refCompare(aRef, bRef);
2063
2624
  }
2064
- var byKeyOpts = { cache: cache2, chunker: bf2(30), codec: codec3, hasher: hasher3, compare };
2065
- var byIdOpts = { cache: cache2, chunker: bf2(30), codec: codec3, hasher: hasher3, compare: simpleCompare };
2625
+ var byKeyOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare };
2626
+ var byIdOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare: simpleCompare };
2066
2627
  function indexEntriesForChanges(changes, mapFn) {
2067
2628
  const indexEntries = [];
2068
2629
  changes.forEach(({ id: key, value, del }) => {
@@ -2090,7 +2651,7 @@ function makeProllyGetBlock(blocks) {
2090
2651
  const block = await blocks.get(address);
2091
2652
  if (!block) throw new Error(`Missing block ${address.toString()}`);
2092
2653
  const { cid, bytes } = block;
2093
- return create3({ cid, bytes, hasher: hasher3, codec: codec3 });
2654
+ return create({ cid, bytes, hasher: hasher6, codec: codec2 });
2094
2655
  };
2095
2656
  }
2096
2657
  async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
@@ -2161,25 +2722,25 @@ function encodeKey(key) {
2161
2722
  }
2162
2723
 
2163
2724
  // src/indexer.ts
2164
- function index({ _crdt }, name, mapFn, meta) {
2725
+ function index(sthis, { _crdt }, name, mapFn, meta) {
2165
2726
  if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
2166
2727
  if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
2167
2728
  if (_crdt.indexers.has(name)) {
2168
2729
  const idx = _crdt.indexers.get(name);
2169
2730
  idx.applyMapFn(name, mapFn, meta);
2170
2731
  } else {
2171
- const idx = new Index(_crdt, name, mapFn, meta);
2732
+ const idx = new Index(sthis, _crdt, name, mapFn, meta);
2172
2733
  _crdt.indexers.set(name, idx);
2173
2734
  }
2174
2735
  return _crdt.indexers.get(name);
2175
2736
  }
2176
2737
  var Index = class {
2177
- constructor(crdt, name, mapFn, meta) {
2738
+ constructor(sthis, crdt, name, mapFn, meta) {
2178
2739
  this.mapFnString = "";
2179
2740
  this.byKey = new IndexTree();
2180
2741
  this.byId = new IndexTree();
2181
2742
  this.includeDocsDefault = false;
2182
- this.logger = ensureLogger(crdt.logger, "Index");
2743
+ this.logger = ensureLogger(sthis, "Index");
2183
2744
  this.blockstore = crdt.indexBlockstore;
2184
2745
  this.crdt = crdt;
2185
2746
  this.applyMapFn(name, mapFn, meta);
@@ -2234,7 +2795,7 @@ var Index = class {
2234
2795
  }
2235
2796
  if (this.mapFnString) {
2236
2797
  if (this.mapFnString !== mapFn.toString()) {
2237
- throw this.logger.Error().Msg("cannot apply different mapFn app").AsError();
2798
+ throw this.logger.Error().Str("mapFnString", this.mapFnString).Str("mapFn", mapFn.toString()).Msg("cannot apply different mapFn app").AsError();
2238
2799
  }
2239
2800
  } else {
2240
2801
  this.mapFnString = mapFn.toString();
@@ -2369,7 +2930,7 @@ var Index = class {
2369
2930
  // src/crdt-clock.ts
2370
2931
  import { advance } from "@web3-storage/pail/clock";
2371
2932
  import { root as root2 } from "@web3-storage/pail/crdt";
2372
- import { ResolveOnce as ResolveOnce3 } from "@adviser/cement";
2933
+ import { ResolveOnce as ResolveOnce4 } from "@adviser/cement";
2373
2934
 
2374
2935
  // src/apply-head-queue.ts
2375
2936
  function applyHeadQueue(worker, logger) {
@@ -2384,7 +2945,7 @@ function applyHeadQueue(worker, logger) {
2384
2945
  queue.sort((a, b) => b.updates ? 1 : -1);
2385
2946
  const task = queue.shift();
2386
2947
  if (!task) continue;
2387
- await worker(task.newHead, task.prevHead, task.updates !== null).catch((e) => {
2948
+ await worker(task.newHead, task.prevHead, task.updates !== void 0).catch((e) => {
2388
2949
  throw logger.Error().Err(e).Msg("int_applyHead worker error").AsError();
2389
2950
  });
2390
2951
  if (task.updates) {
@@ -2426,9 +2987,9 @@ var CRDTClock = class {
2426
2987
  this.zoomers = /* @__PURE__ */ new Set();
2427
2988
  this.watchers = /* @__PURE__ */ new Set();
2428
2989
  this.emptyWatchers = /* @__PURE__ */ new Set();
2429
- this._ready = new ResolveOnce3();
2990
+ this._ready = new ResolveOnce4();
2430
2991
  this.blockstore = blockstore;
2431
- this.logger = ensureLogger(blockstore.logger, "CRDTClock");
2992
+ this.logger = ensureLogger(blockstore.sthis, "CRDTClock");
2432
2993
  this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
2433
2994
  }
2434
2995
  async ready() {
@@ -2474,6 +3035,7 @@ var CRDTClock = class {
2474
3035
  this.zoomers.add(fn);
2475
3036
  }
2476
3037
  async int_applyHead(newHead, prevHead, localUpdates) {
3038
+ const noLoader = !localUpdates;
2477
3039
  const ogHead = sortClockHead(this.head);
2478
3040
  newHead = sortClockHead(newHead);
2479
3041
  if (compareClockHeads(ogHead, newHead)) {
@@ -2484,7 +3046,6 @@ var CRDTClock = class {
2484
3046
  this.setHead(newHead);
2485
3047
  return;
2486
3048
  }
2487
- const noLoader = !localUpdates;
2488
3049
  if (!this.blockstore) {
2489
3050
  throw this.logger.Error().Msg("missing blockstore").AsError();
2490
3051
  }
@@ -2501,7 +3062,7 @@ var CRDTClock = class {
2501
3062
  }
2502
3063
  return { head: advancedHead };
2503
3064
  },
2504
- { noLoader }
3065
+ { noLoader, add: false }
2505
3066
  );
2506
3067
  this.setHead(meta.head);
2507
3068
  }
@@ -2535,13 +3096,14 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
2535
3096
 
2536
3097
  // src/crdt.ts
2537
3098
  var CRDT = class {
2538
- constructor(name, opts = {}) {
2539
- this.onceReady = new ResolveOnce4();
3099
+ constructor(sthis, name, opts = {}) {
2540
3100
  this.indexers = /* @__PURE__ */ new Map();
3101
+ this.onceReady = new ResolveOnce5();
3102
+ this.sthis = sthis;
2541
3103
  this.name = name;
2542
- this.logger = ensureLogger(opts, "CRDT");
3104
+ this.logger = ensureLogger(sthis, "CRDT");
2543
3105
  this.opts = opts;
2544
- this.blockstore = blockstoreFactory({
3106
+ this.blockstore = blockstoreFactory(sthis, {
2545
3107
  name,
2546
3108
  applyMeta: async (meta) => {
2547
3109
  const crdtMeta = meta;
@@ -2553,22 +3115,20 @@ var CRDT = class {
2553
3115
  return { head: this.clock.head };
2554
3116
  },
2555
3117
  autoCompact: this.opts.autoCompact || 100,
2556
- crypto: this.opts.crypto,
2557
3118
  store: { ...this.opts.store, isIndex: void 0 },
2558
3119
  public: this.opts.public,
2559
3120
  meta: this.opts.meta,
2560
3121
  threshold: this.opts.threshold
2561
3122
  });
2562
- this.indexBlockstore = blockstoreFactory({
3123
+ this.indexBlockstore = blockstoreFactory(sthis, {
2563
3124
  name,
2564
3125
  applyMeta: async (meta) => {
2565
3126
  const idxCarMeta = meta;
2566
3127
  if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
2567
3128
  for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
2568
- index({ _crdt: this }, name2, void 0, idx);
3129
+ index(this.sthis, { _crdt: this }, name2, void 0, idx);
2569
3130
  }
2570
3131
  },
2571
- crypto: this.opts.crypto,
2572
3132
  store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
2573
3133
  public: this.opts.public
2574
3134
  });
@@ -2579,17 +3139,6 @@ var CRDT = class {
2579
3139
  }
2580
3140
  });
2581
3141
  }
2582
- async ready() {
2583
- return this.onceReady.once(async () => {
2584
- await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
2585
- });
2586
- }
2587
- async close() {
2588
- await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
2589
- }
2590
- async destroy() {
2591
- await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
2592
- }
2593
3142
  async bulk(updates) {
2594
3143
  await this.ready();
2595
3144
  const prevHead = [...this.clock.head];
@@ -2610,6 +3159,21 @@ var CRDT = class {
2610
3159
  await this.clock.applyHead(done.meta.head, prevHead, updates);
2611
3160
  return done.meta;
2612
3161
  }
3162
+ async ready() {
3163
+ return this.onceReady.once(async () => {
3164
+ try {
3165
+ await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
3166
+ } catch (e) {
3167
+ throw this.logger.Error().Err(e).Msg(`CRDT is not ready`).AsError();
3168
+ }
3169
+ });
3170
+ }
3171
+ async close() {
3172
+ await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
3173
+ }
3174
+ async destroy() {
3175
+ await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
3176
+ }
2613
3177
  // if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
2614
3178
  async allDocs() {
2615
3179
  await this.ready();
@@ -2654,11 +3218,12 @@ var Database = class {
2654
3218
  this._listening = false;
2655
3219
  this._listeners = /* @__PURE__ */ new Set();
2656
3220
  this._noupdate_listeners = /* @__PURE__ */ new Set();
2657
- this._ready = new ResolveOnce5();
3221
+ this._ready = new ResolveOnce6();
2658
3222
  this.name = name;
2659
3223
  this.opts = opts || this.opts;
2660
- this.logger = ensureLogger(this.opts, "Database");
2661
- this._crdt = new CRDT(name, this.opts);
3224
+ this.sthis = ensureSuperThis(this.opts);
3225
+ this.logger = ensureLogger(this.sthis, "Database");
3226
+ this._crdt = new CRDT(this.sthis, name, this.opts);
2662
3227
  this.blockstore = this._crdt.blockstore;
2663
3228
  this._writeQueue = writeQueue(async (updates) => {
2664
3229
  return await this._crdt.bulk(updates);
@@ -2682,7 +3247,7 @@ var Database = class {
2682
3247
  }
2683
3248
  async ready() {
2684
3249
  return this._ready.once(async () => {
2685
- await SysContainer.start();
3250
+ await this.sthis.start();
2686
3251
  await this._crdt.ready();
2687
3252
  await this.blockstore.ready();
2688
3253
  });
@@ -2702,7 +3267,7 @@ var Database = class {
2702
3267
  await this.ready();
2703
3268
  this.logger.Debug().Str("id", doc._id).Msg("put");
2704
3269
  const { _id, ...value } = doc;
2705
- const docId = _id || uuidv7();
3270
+ const docId = _id || this.sthis.timeOrderedNextId().str;
2706
3271
  const result = await this._writeQueue.push({
2707
3272
  id: docId,
2708
3273
  value: {
@@ -2767,7 +3332,7 @@ var Database = class {
2767
3332
  await this.ready();
2768
3333
  this.logger.Debug().Any("field", field).Any("opts", opts).Msg("query");
2769
3334
  const _crdt = this._crdt;
2770
- const idx = typeof field === "string" ? index({ _crdt }, field) : index({ _crdt }, makeName(field.toString()), field);
3335
+ const idx = typeof field === "string" ? index(this.sthis, { _crdt }, field) : index(this.sthis, { _crdt }, makeName(field.toString()), field);
2771
3336
  return await idx.query(opts);
2772
3337
  }
2773
3338
  async compact() {
@@ -2804,12 +3369,7 @@ function fireproof(name, opts) {
2804
3369
  const key = JSON.stringify(
2805
3370
  toSortedArray({
2806
3371
  name,
2807
- stores: toSortedArray(opts?.store?.stores),
2808
- makeMetaStore: !!opts?.store?.makeMetaStore,
2809
- makeDataStore: !!opts?.store?.makeDataStore,
2810
- makeRemoteWAL: !!opts?.store?.makeRemoteWAL,
2811
- encodeFile: !!opts?.store?.encodeFile,
2812
- decodeFile: !!opts?.store?.decodeFile
3372
+ stores: toSortedArray(opts?.store?.stores)
2813
3373
  })
2814
3374
  );
2815
3375
  let db = Database.databases.get(key);
@@ -2824,7 +3384,10 @@ function makeName(fnString) {
2824
3384
  let found = null;
2825
3385
  const matches = Array.from(fnString.matchAll(regex), (match) => match[1].trim());
2826
3386
  if (matches.length === 0) {
2827
- found = /=>\s*(.*)/.exec(fnString);
3387
+ found = /=>\s*{?\s*([^{}]+)\s*}?/.exec(fnString);
3388
+ if (found && found[1].includes("return")) {
3389
+ found = null;
3390
+ }
2828
3391
  }
2829
3392
  if (!found) {
2830
3393
  return fnString;
@@ -2833,20 +3396,53 @@ function makeName(fnString) {
2833
3396
  }
2834
3397
  }
2835
3398
 
3399
+ // src/runtime/index.ts
3400
+ var runtime_exports = {};
3401
+ __export(runtime_exports, {
3402
+ FILESTORE_VERSION: () => FILESTORE_VERSION,
3403
+ INDEXDB_VERSION: () => INDEXDB_VERSION,
3404
+ files: () => files_exports,
3405
+ getFileName: () => getFileName,
3406
+ getFileSystem: () => getFileSystem,
3407
+ getPath: () => getPath,
3408
+ kb: () => key_bag_exports,
3409
+ kc: () => keyed_crypto_exports,
3410
+ mf: () => wait_pr_multiformats_exports,
3411
+ runtimeFn: () => runtimeFn2,
3412
+ toArrayBuffer: () => toArrayBuffer
3413
+ });
3414
+
3415
+ // src/runtime/wait-pr-multiformats/index.ts
3416
+ var wait_pr_multiformats_exports = {};
3417
+ __export(wait_pr_multiformats_exports, {
3418
+ block: () => block_exports,
3419
+ codec: () => codec_interface_exports
3420
+ });
3421
+
3422
+ // src/runtime/wait-pr-multiformats/codec-interface.ts
3423
+ var codec_interface_exports = {};
3424
+
3425
+ // src/runtime/index.ts
3426
+ import { runtimeFn as runtimeFn2 } from "@adviser/cement";
3427
+
2836
3428
  // src/version.ts
2837
3429
  var PACKAGE_VERSION = Object.keys({
2838
- "0.19.8-dev-global": "xxxx"
3430
+ "0.19.8-dev-series-2": "xxxx"
2839
3431
  })[0];
2840
3432
  export {
2841
3433
  CRDT,
2842
3434
  Database,
2843
3435
  Index,
3436
+ NotFoundError,
2844
3437
  PACKAGE_VERSION,
2845
3438
  Result,
3439
+ UInt8ArrayEqual,
2846
3440
  blockstore_exports as blockstore,
2847
3441
  blockstore_exports as bs,
3442
+ dataDir,
2848
3443
  ensureLogger,
2849
- exception2Result,
3444
+ ensureSuperLog,
3445
+ ensureSuperThis,
2850
3446
  exceptionWrapper,
2851
3447
  falsyToUndef,
2852
3448
  fireproof,
@@ -2855,6 +3451,7 @@ export {
2855
3451
  getStore,
2856
3452
  index,
2857
3453
  isFalsy,
3454
+ isNotFoundError,
2858
3455
  runtime_exports as rt,
2859
3456
  runtime_exports as runtime,
2860
3457
  throwFalsy