@fireproof/core 0.19.111 → 0.19.112-dev-web

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 (62) hide show
  1. package/{chunk-OFGPKRCM.js → chunk-GZANCVTS.js} +3 -1
  2. package/chunk-GZANCVTS.js.map +1 -0
  3. package/{chunk-WS3YRPIA.js → chunk-LNFBDD6E.js} +4 -4
  4. package/chunk-LNFBDD6E.js.map +1 -0
  5. package/deno.json +1 -2
  6. package/{gateway-H7UD6TNB.js → gateway@skip-esm-O655UEIP.js} +3 -3
  7. package/gateway@skip-esm-O655UEIP.js.map +1 -0
  8. package/{gateway-5FCWPX5W.js → gateway@skip-iife-OZ2V32XH.js} +5 -5
  9. package/gateway@skip-iife-OZ2V32XH.js.map +1 -0
  10. package/index.cjs +32 -30
  11. package/index.cjs.map +1 -1
  12. package/index.global.js +417 -178
  13. package/index.global.js.map +1 -1
  14. package/index.js +11 -11
  15. package/index.js.map +1 -1
  16. package/{key-bag-file-WADZBHYG.js → key-bag-file-4TYN2H7F.js} +3 -3
  17. package/{key-bag-indexdb-PGVAI3FJ.js → key-bag-indexdb-JEOAS4WM.js} +3 -3
  18. package/{mem-filesystem-YPPJV7Q2.js → mem-filesystem@skip-iife-CJI7IIKV.js} +4 -4
  19. package/mem-filesystem@skip-iife-CJI7IIKV.js.map +1 -0
  20. package/metafile-cjs.json +1 -1
  21. package/metafile-esm.json +1 -1
  22. package/metafile-iife.json +1 -1
  23. package/{node-filesystem-INX4ZTHE.js → node-filesystem@skip-iife-O74VAABQ.js} +4 -4
  24. package/node-filesystem@skip-iife-O74VAABQ.js.map +1 -0
  25. package/package.json +15 -6
  26. package/tests/blockstore/keyed-crypto.test.ts +2 -2
  27. package/tests/blockstore/store.test.ts +3 -5
  28. package/tests/fireproof/all-gateway.test.ts +11 -9
  29. package/{utils-QO2HIWGI.js → utils-L7MUZUJX.js} +3 -3
  30. package/web/bundle-not-impl-UH74NK5L.js +5 -0
  31. package/web/bundle-not-impl-UH74NK5L.js.map +1 -0
  32. package/web/chunk-2DC5ZIR4.js +7 -0
  33. package/web/chunk-2DC5ZIR4.js.map +1 -0
  34. package/web/chunk-Q5W7UNMP.js +292 -0
  35. package/web/chunk-Q5W7UNMP.js.map +1 -0
  36. package/web/chunk-S4HRSKEO.js +75 -0
  37. package/web/chunk-S4HRSKEO.js.map +1 -0
  38. package/web/gateway@skip-esm-GI5PRACF.js +165 -0
  39. package/web/gateway@skip-esm-GI5PRACF.js.map +1 -0
  40. package/web/index.cjs +4138 -0
  41. package/web/index.cjs.map +1 -0
  42. package/web/index.d.cts +1139 -0
  43. package/web/index.d.ts +1139 -0
  44. package/web/index.js +3478 -0
  45. package/web/index.js.map +1 -0
  46. package/web/key-bag-file-4SJQGORQ.js +54 -0
  47. package/web/key-bag-file-4SJQGORQ.js.map +1 -0
  48. package/web/key-bag-indexdb-GSQOUUVQ.js +50 -0
  49. package/web/key-bag-indexdb-GSQOUUVQ.js.map +1 -0
  50. package/web/metafile-cjs.json +1 -0
  51. package/web/metafile-esm.json +1 -0
  52. package/web/utils-EFZJNXH5.js +14 -0
  53. package/web/utils-EFZJNXH5.js.map +1 -0
  54. package/chunk-OFGPKRCM.js.map +0 -1
  55. package/chunk-WS3YRPIA.js.map +0 -1
  56. package/gateway-5FCWPX5W.js.map +0 -1
  57. package/gateway-H7UD6TNB.js.map +0 -1
  58. package/mem-filesystem-YPPJV7Q2.js.map +0 -1
  59. package/node-filesystem-INX4ZTHE.js.map +0 -1
  60. /package/{key-bag-file-WADZBHYG.js.map → key-bag-file-4TYN2H7F.js.map} +0 -0
  61. /package/{key-bag-indexdb-PGVAI3FJ.js.map → key-bag-indexdb-JEOAS4WM.js.map} +0 -0
  62. /package/{utils-QO2HIWGI.js.map → utils-L7MUZUJX.js.map} +0 -0
package/web/index.js ADDED
@@ -0,0 +1,3478 @@
1
+ import {
2
+ getFileName,
3
+ getFileSystem,
4
+ getPath,
5
+ toArrayBuffer
6
+ } from "./chunk-S4HRSKEO.js";
7
+ import {
8
+ INDEXDB_VERSION
9
+ } from "./chunk-2DC5ZIR4.js";
10
+ import {
11
+ NotFoundError,
12
+ Result,
13
+ UInt8ArrayEqual,
14
+ __export,
15
+ dataDir,
16
+ ensureLogger,
17
+ ensureSuperLog,
18
+ ensureSuperThis,
19
+ exceptionWrapper,
20
+ getKey,
21
+ getName,
22
+ getStore,
23
+ isNotFoundError
24
+ } from "./chunk-Q5W7UNMP.js";
25
+
26
+ // src/database.ts
27
+ import { ResolveOnce as ResolveOnce6 } from "@adviser/cement";
28
+
29
+ // src/write-queue.ts
30
+ function writeQueue(worker, payload = Infinity, unbounded = false) {
31
+ const queue = [];
32
+ let isProcessing = false;
33
+ async function process() {
34
+ if (isProcessing || queue.length === 0) return;
35
+ isProcessing = true;
36
+ const tasksToProcess = queue.splice(0, payload);
37
+ const updates = tasksToProcess.map((item) => item.task);
38
+ if (unbounded) {
39
+ const promises = updates.map(async (update, index2) => {
40
+ try {
41
+ const result = await worker([update]);
42
+ tasksToProcess[index2].resolve(result);
43
+ } catch (error) {
44
+ tasksToProcess[index2].reject(error);
45
+ }
46
+ });
47
+ await Promise.all(promises);
48
+ } else {
49
+ try {
50
+ const result = await worker(updates);
51
+ tasksToProcess.forEach((task) => task.resolve(result));
52
+ } catch (error) {
53
+ tasksToProcess.forEach((task) => task.reject(error));
54
+ }
55
+ }
56
+ isProcessing = false;
57
+ void process();
58
+ }
59
+ return {
60
+ push(task) {
61
+ return new Promise((resolve, reject) => {
62
+ queue.push({ task, resolve, reject });
63
+ void process();
64
+ });
65
+ }
66
+ };
67
+ }
68
+
69
+ // src/crdt.ts
70
+ import { ResolveOnce as ResolveOnce5 } from "@adviser/cement";
71
+
72
+ // src/runtime/wait-pr-multiformats/block.ts
73
+ var block_exports = {};
74
+ __export(block_exports, {
75
+ Block: () => Block,
76
+ create: () => create,
77
+ createUnsafe: () => createUnsafe,
78
+ decode: () => decode,
79
+ encode: () => encode
80
+ });
81
+ import { bytes as binary, CID } from "multiformats";
82
+ import { Block as mfBlock } from "multiformats/block";
83
+ var Block = mfBlock;
84
+ async function decode({
85
+ bytes,
86
+ codec: codec3,
87
+ hasher: hasher7
88
+ }) {
89
+ if (bytes == null) throw new Error('Missing required argument "bytes"');
90
+ if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
91
+ const value = await Promise.resolve(codec3.decode(bytes));
92
+ const hash = await hasher7.digest(bytes);
93
+ const cid = CID.create(1, codec3.code, hash);
94
+ return new mfBlock({ value, bytes, cid });
95
+ }
96
+ async function encode({
97
+ value,
98
+ codec: codec3,
99
+ hasher: hasher7
100
+ }) {
101
+ if (typeof value === "undefined") throw new Error('Missing required argument "value"');
102
+ if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
103
+ const bytes = await Promise.resolve(codec3.encode(value));
104
+ const hash = await hasher7.digest(bytes);
105
+ const cid = CID.create(1, codec3.code, hash);
106
+ return new mfBlock({ value, bytes, cid });
107
+ }
108
+ async function create({
109
+ bytes,
110
+ cid,
111
+ hasher: hasher7,
112
+ codec: codec3
113
+ }) {
114
+ if (bytes == null) throw new Error('Missing required argument "bytes"');
115
+ if (hasher7 == null) throw new Error('Missing required argument "hasher"');
116
+ const value = await Promise.resolve(codec3.decode(bytes));
117
+ const hash = await hasher7.digest(bytes);
118
+ if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
119
+ throw new Error("CID hash does not match bytes");
120
+ }
121
+ return createUnsafe({
122
+ bytes,
123
+ cid,
124
+ value,
125
+ codec: codec3
126
+ });
127
+ }
128
+ async function createUnsafe({
129
+ bytes,
130
+ cid,
131
+ value: maybeValue,
132
+ codec: codec3
133
+ }) {
134
+ const value = await Promise.resolve(maybeValue !== void 0 ? maybeValue : codec3?.decode(bytes));
135
+ if (value === void 0) throw new Error('Missing required argument, must either provide "value" or "codec"');
136
+ return new Block({
137
+ cid,
138
+ bytes,
139
+ value
140
+ });
141
+ }
142
+
143
+ // src/crdt-helpers.ts
144
+ import { parse as parse3 } from "multiformats/link";
145
+ import { sha256 as hasher5 } from "multiformats/hashes/sha2";
146
+ import * as codec from "@ipld/dag-cbor";
147
+ import { put, get, entries, root } from "@web3-storage/pail/crdt";
148
+ import { EventFetcher, vis } from "@web3-storage/pail/clock";
149
+ import * as Batch from "@web3-storage/pail/crdt/batch";
150
+
151
+ // src/blockstore/index.ts
152
+ var blockstore_exports = {};
153
+ __export(blockstore_exports, {
154
+ BaseBlockstore: () => BaseBlockstore,
155
+ CarTransaction: () => CarTransaction,
156
+ CompactionFetcher: () => CompactionFetcher,
157
+ ConnectionBase: () => ConnectionBase,
158
+ EncryptedBlockstore: () => EncryptedBlockstore,
159
+ FragmentGateway: () => FragmentGateway,
160
+ Loader: () => Loader,
161
+ addCryptoKeyToGatewayMetaPayload: () => addCryptoKeyToGatewayMetaPayload,
162
+ ensureStart: () => ensureStart,
163
+ getGatewayFromURL: () => getGatewayFromURL,
164
+ parseCarFile: () => parseCarFile,
165
+ registerStoreProtocol: () => registerStoreProtocol,
166
+ setCryptoKeyFromGatewayMetaPayload: () => setCryptoKeyFromGatewayMetaPayload,
167
+ testStoreFactory: () => testStoreFactory,
168
+ toCIDBlock: () => toCIDBlock,
169
+ toStoreRuntime: () => toStoreRuntime
170
+ });
171
+
172
+ // src/blockstore/types.ts
173
+ function toCIDBlock(block) {
174
+ return block;
175
+ }
176
+
177
+ // src/blockstore/store-factory.ts
178
+ import { KeyedResolvOnce as KeyedResolvOnce2, URI as URI5 } from "@adviser/cement";
179
+
180
+ // src/runtime/files.ts
181
+ var files_exports = {};
182
+ __export(files_exports, {
183
+ decodeFile: () => decodeFile,
184
+ encodeFile: () => encodeFile
185
+ });
186
+ import * as UnixFS from "@ipld/unixfs";
187
+ import * as raw from "multiformats/codecs/raw";
188
+ import { withMaxChunkSize } from "@ipld/unixfs/file/chunker/fixed";
189
+ import { withWidth } from "@ipld/unixfs/file/layout/balanced";
190
+ import { exporter } from "ipfs-unixfs-exporter";
191
+ var queuingStrategy = UnixFS.withCapacity();
192
+ var settings = UnixFS.configure({
193
+ fileChunkEncoder: raw,
194
+ smallFileEncoder: raw,
195
+ chunker: withMaxChunkSize(1024 * 1024),
196
+ fileLayout: withWidth(1024)
197
+ });
198
+ async function collect(collectable) {
199
+ const chunks = [];
200
+ await collectable.pipeTo(
201
+ new WritableStream({
202
+ write(chunk) {
203
+ chunks.push(chunk);
204
+ }
205
+ })
206
+ );
207
+ return chunks;
208
+ }
209
+ async function encodeFile(blob) {
210
+ const readable = createFileEncoderStream(blob);
211
+ const blocks = await collect(readable);
212
+ return { cid: blocks.at(-1).cid, blocks };
213
+ }
214
+ async function decodeFile(blocks, cid, meta) {
215
+ const entry = await exporter(cid.toString(), blocks, { length: meta.size });
216
+ const chunks = [];
217
+ for await (const chunk of entry.content()) {
218
+ chunks.push(chunk);
219
+ }
220
+ return new File(chunks, entry.name, { type: meta.type, lastModified: 0 });
221
+ }
222
+ function createFileEncoderStream(blob) {
223
+ const { readable, writable } = new TransformStream({}, queuingStrategy);
224
+ const unixfsWriter = UnixFS.createWriter({ writable, settings });
225
+ const fileBuilder = new UnixFSFileBuilder("", blob);
226
+ void (async () => {
227
+ await fileBuilder.finalize(unixfsWriter);
228
+ await unixfsWriter.close();
229
+ })();
230
+ return readable;
231
+ }
232
+ var UnixFSFileBuilder = class {
233
+ #file;
234
+ constructor(name, file) {
235
+ this.name = name;
236
+ this.#file = file;
237
+ }
238
+ async finalize(writer) {
239
+ const unixfsFileWriter = UnixFS.createFileWriter(writer);
240
+ await this.#file.stream().pipeTo(
241
+ new WritableStream({
242
+ async write(chunk) {
243
+ await unixfsFileWriter.write(chunk);
244
+ }
245
+ })
246
+ );
247
+ return await unixfsFileWriter.close();
248
+ }
249
+ };
250
+
251
+ // src/blockstore/store.ts
252
+ import { format as format2, parse as parse2 } from "@ipld/dag-json";
253
+ import { exception2Result, ResolveOnce as ResolveOnce3, Result as Result5 } from "@adviser/cement";
254
+
255
+ // src/types.ts
256
+ function isFalsy(value) {
257
+ return value === false && value === null && value === void 0;
258
+ }
259
+ function throwFalsy(value) {
260
+ if (isFalsy(value)) {
261
+ throw new Error("value is Falsy");
262
+ }
263
+ return value;
264
+ }
265
+ function falsyToUndef(value) {
266
+ if (isFalsy(value)) {
267
+ return void 0;
268
+ }
269
+ return value;
270
+ }
271
+
272
+ // src/blockstore/loader.ts
273
+ import pLimit from "p-limit";
274
+ import { CarReader } from "@ipld/car";
275
+ import { ResolveOnce as ResolveOnce2 } from "@adviser/cement";
276
+
277
+ // src/blockstore/loader-helpers.ts
278
+ import { sha256 as hasher } from "multiformats/hashes/sha2";
279
+ import * as dagCodec from "@ipld/dag-cbor";
280
+ async function parseCarFile(reader, logger) {
281
+ const roots = await reader.getRoots();
282
+ const header = await reader.get(roots[0]);
283
+ if (!header) throw logger.Error().Msg("missing header block").AsError();
284
+ const dec = await decode({ bytes: header.bytes, hasher, codec: dagCodec });
285
+ const fpvalue = dec.value;
286
+ if (fpvalue && !fpvalue.fp) {
287
+ throw logger.Error().Msg("missing fp").AsError();
288
+ }
289
+ return fpvalue.fp;
290
+ }
291
+
292
+ // src/blockstore/transaction.ts
293
+ import { MemoryBlockstore } from "@web3-storage/pail/block";
294
+ import { toCryptoRuntime } from "@adviser/cement";
295
+ var CarTransaction = class extends MemoryBlockstore {
296
+ constructor(parent, opts = { add: true, noLoader: false }) {
297
+ super();
298
+ if (opts.add) {
299
+ parent.transactions.add(this);
300
+ }
301
+ this.parent = parent;
302
+ }
303
+ async get(cid) {
304
+ return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
305
+ }
306
+ async superGet(cid) {
307
+ return super.get(cid);
308
+ }
309
+ };
310
+ function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
311
+ const logger = ensureLogger(sthis, component, ctx);
312
+ const store = opts.store || {};
313
+ return {
314
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
315
+ applyMeta: (meta, snap) => {
316
+ return Promise.resolve();
317
+ },
318
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
319
+ compact: async (blocks) => {
320
+ return {};
321
+ },
322
+ autoCompact: 100,
323
+ public: false,
324
+ name: void 0,
325
+ threshold: 1e3 * 1e3,
326
+ ...opts,
327
+ logger,
328
+ keyBag: opts.keyBag || {},
329
+ crypto: toCryptoRuntime(opts.crypto),
330
+ store,
331
+ storeRuntime: toStoreRuntime(store, sthis)
332
+ };
333
+ }
334
+ function blockstoreFactory(sthis, opts) {
335
+ if (opts.name) {
336
+ return new EncryptedBlockstore(sthis, opts);
337
+ } else {
338
+ return new BaseBlockstore(opts);
339
+ }
340
+ }
341
+ var BaseBlockstore = class {
342
+ constructor(ebOpts = {}) {
343
+ this.transactions = /* @__PURE__ */ new Set();
344
+ this.sthis = ensureSuperThis(ebOpts);
345
+ this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
346
+ this.logger = this.ebOpts.logger;
347
+ }
348
+ // ready: Promise<void>;
349
+ ready() {
350
+ return Promise.resolve();
351
+ }
352
+ async close() {
353
+ }
354
+ async destroy() {
355
+ }
356
+ async compact() {
357
+ }
358
+ async get(cid) {
359
+ if (!cid) throw this.logger.Error().Msg("required cid").AsError();
360
+ for (const f of this.transactions) {
361
+ const v = await f.superGet(cid);
362
+ if (v) return v;
363
+ }
364
+ }
365
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
366
+ async put(cid, block) {
367
+ throw this.logger.Error().Msg("use a transaction to put").AsError();
368
+ }
369
+ // TransactionMeta
370
+ async transaction(fn, _opts) {
371
+ const t = new CarTransaction(this, _opts);
372
+ const done = await fn(t);
373
+ this.lastTxMeta = done;
374
+ return { t, meta: done };
375
+ }
376
+ openTransaction(opts = { add: true, noLoader: false }) {
377
+ return new CarTransaction(this, opts);
378
+ }
379
+ async commitTransaction(t, done, opts) {
380
+ if (!this.loader) throw this.logger.Error().Msg("loader required to commit").AsError();
381
+ const cars = await this.loader?.commit(t, done, opts);
382
+ if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
383
+ setTimeout(() => void this.compact(), 10);
384
+ }
385
+ if (cars) {
386
+ this.transactions.delete(t);
387
+ return { meta: done, cars, t };
388
+ }
389
+ throw this.logger.Error().Msg("failed to commit car files").AsError();
390
+ }
391
+ async *entries() {
392
+ const seen = /* @__PURE__ */ new Set();
393
+ for (const t of this.transactions) {
394
+ for await (const blk of t.entries()) {
395
+ if (seen.has(blk.cid.toString())) continue;
396
+ seen.add(blk.cid.toString());
397
+ yield blk;
398
+ }
399
+ }
400
+ }
401
+ };
402
+ var EncryptedBlockstore = class extends BaseBlockstore {
403
+ constructor(sthis, ebOpts) {
404
+ super(ebOpts);
405
+ this.compacting = false;
406
+ this.logger = ensureLogger(this.sthis, "EncryptedBlockstore");
407
+ const { name } = ebOpts;
408
+ if (!name) {
409
+ throw this.logger.Error().Msg("name required").AsError();
410
+ }
411
+ this.name = name;
412
+ this.loader = new Loader(this.name, ebOpts, sthis);
413
+ }
414
+ ready() {
415
+ return this.loader.ready();
416
+ }
417
+ close() {
418
+ return this.loader.close();
419
+ }
420
+ destroy() {
421
+ return this.loader.destroy();
422
+ }
423
+ async get(cid) {
424
+ const got = await super.get(cid);
425
+ if (got) return got;
426
+ if (!this.loader) {
427
+ return;
428
+ }
429
+ return falsyToUndef(await this.loader.getBlock(cid));
430
+ }
431
+ async transaction(fn, opts = { noLoader: false }) {
432
+ const { t, meta: done } = await super.transaction(fn);
433
+ const cars = await this.loader.commit(t, done, opts);
434
+ if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
435
+ setTimeout(() => void this.compact(), 10);
436
+ }
437
+ if (cars) {
438
+ this.transactions.delete(t);
439
+ return { meta: done, cars, t };
440
+ }
441
+ throw this.logger.Error().Msg("failed to commit car files").AsError();
442
+ }
443
+ async getFile(car, cid) {
444
+ await this.ready();
445
+ if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
446
+ const reader = await this.loader.loadFileCar(
447
+ car
448
+ /*, isPublic */
449
+ );
450
+ const block = await reader.get(cid);
451
+ if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
452
+ return block.bytes;
453
+ }
454
+ async compact() {
455
+ await this.ready();
456
+ if (!this.loader) throw this.logger.Error().Msg("loader required to compact").AsError();
457
+ if (this.loader.carLog.length < 2) return;
458
+ const compactFn = this.ebOpts.compact || ((blocks) => this.defaultCompact(blocks, this.logger));
459
+ if (!compactFn || this.compacting) return;
460
+ const blockLog = new CompactionFetcher(this);
461
+ this.compacting = true;
462
+ const meta = await compactFn(blockLog);
463
+ await this.loader?.commit(blockLog.loggedBlocks, meta, {
464
+ compact: true,
465
+ noLoader: true
466
+ });
467
+ this.compacting = false;
468
+ }
469
+ async defaultCompact(blocks, logger) {
470
+ if (!this.loader) {
471
+ throw logger.Error().Msg("no loader").AsError();
472
+ }
473
+ if (!this.lastTxMeta) {
474
+ throw logger.Error().Msg("no lastTxMeta").AsError();
475
+ }
476
+ for await (const blk of this.loader.entries(false)) {
477
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
478
+ }
479
+ for (const t of this.transactions) {
480
+ for await (const blk of t.entries()) {
481
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
482
+ }
483
+ }
484
+ return this.lastTxMeta;
485
+ }
486
+ async *entries() {
487
+ for await (const blk of this.loader.entries()) {
488
+ yield blk;
489
+ }
490
+ }
491
+ };
492
+ var CompactionFetcher = class {
493
+ constructor(blocks) {
494
+ this.blockstore = blocks;
495
+ this.loggedBlocks = new CarTransaction(blocks);
496
+ }
497
+ async get(cid) {
498
+ const block = await this.blockstore.get(cid);
499
+ if (block) this.loggedBlocks.putSync(cid, block.bytes);
500
+ return falsyToUndef(block);
501
+ }
502
+ };
503
+
504
+ // src/blockstore/commit-queue.ts
505
+ import { Future } from "@adviser/cement";
506
+ var CommitQueue = class {
507
+ constructor() {
508
+ this.queue = [];
509
+ this.processing = false;
510
+ this._waitIdleItems = /* @__PURE__ */ new Set();
511
+ }
512
+ waitIdle() {
513
+ if (this.queue.length === 0 && !this.processing) {
514
+ return Promise.resolve();
515
+ }
516
+ const fn = new Future();
517
+ this._waitIdleItems.add(fn);
518
+ return fn.asPromise();
519
+ }
520
+ async enqueue(fn) {
521
+ return new Promise((resolve, reject) => {
522
+ const queueFn = async () => {
523
+ try {
524
+ resolve(await fn());
525
+ } catch (e) {
526
+ reject(e);
527
+ } finally {
528
+ this.processing = false;
529
+ this.processNext();
530
+ }
531
+ };
532
+ this.queue.push(queueFn);
533
+ if (!this.processing) {
534
+ this.processNext();
535
+ }
536
+ });
537
+ }
538
+ processNext() {
539
+ if (this.queue.length > 0 && !this.processing) {
540
+ this.processing = true;
541
+ const queueFn = this.queue.shift();
542
+ if (queueFn) {
543
+ queueFn().finally(() => {
544
+ });
545
+ }
546
+ }
547
+ if (this.queue.length === 0 && !this.processing) {
548
+ const toResolve = Array.from(this._waitIdleItems);
549
+ this._waitIdleItems.clear();
550
+ toResolve.map((fn) => fn.resolve());
551
+ }
552
+ }
553
+ };
554
+
555
+ // src/runtime/key-bag.ts
556
+ var key_bag_exports = {};
557
+ __export(key_bag_exports, {
558
+ KeyBag: () => KeyBag,
559
+ getKeyBag: () => getKeyBag,
560
+ registerKeyBagProviderFactory: () => registerKeyBagProviderFactory
561
+ });
562
+ import {
563
+ KeyedResolvOnce,
564
+ ResolveOnce,
565
+ ResolveSeq,
566
+ Result as Result2,
567
+ runtimeFn,
568
+ toCryptoRuntime as toCryptoRuntime2,
569
+ URI
570
+ } from "@adviser/cement";
571
+ import { base58btc } from "multiformats/bases/base58";
572
+ var KeyBag = class {
573
+ constructor(rt) {
574
+ this.rt = rt;
575
+ this._warnOnce = new ResolveOnce();
576
+ this._seq = new ResolveSeq();
577
+ this.logger = ensureLogger(rt.sthis, "KeyBag");
578
+ this.logger.Debug().Msg("KeyBag created");
579
+ }
580
+ async subtleKey(key) {
581
+ const extractable = this.rt.url.getParam("extractKey") === "_deprecated_internal_api";
582
+ if (extractable) {
583
+ this._warnOnce.once(
584
+ () => this.logger.Warn().Msg("extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!")
585
+ );
586
+ }
587
+ return await this.rt.crypto.importKey(
588
+ "raw",
589
+ // raw or jwk
590
+ base58btc.decode(key),
591
+ // hexStringToUint8Array(key), // raw data
592
+ "AES-GCM",
593
+ extractable,
594
+ ["encrypt", "decrypt"]
595
+ );
596
+ }
597
+ async ensureKeyFromUrl(url, keyFactory) {
598
+ const storeKey = url.getParam("storekey");
599
+ if (storeKey === "insecure") {
600
+ return Result2.Ok(url);
601
+ }
602
+ if (!storeKey) {
603
+ const keyName = `@${keyFactory()}@`;
604
+ const ret = await this.getNamedKey(keyName);
605
+ if (ret.isErr()) {
606
+ return ret;
607
+ }
608
+ const urb = url.build().setParam("storekey", keyName);
609
+ return Result2.Ok(urb.URI());
610
+ }
611
+ if (storeKey.startsWith("@") && storeKey.endsWith("@")) {
612
+ const ret = await this.getNamedKey(storeKey);
613
+ if (ret.isErr()) {
614
+ return ret;
615
+ }
616
+ }
617
+ return Result2.Ok(url);
618
+ }
619
+ async toKeyWithFingerPrint(keyStr) {
620
+ const material = base58btc.decode(keyStr);
621
+ const key = await this.subtleKey(keyStr);
622
+ const fpr = await this.rt.crypto.digestSHA256(material);
623
+ return Result2.Ok({
624
+ key,
625
+ fingerPrint: base58btc.encode(new Uint8Array(fpr))
626
+ });
627
+ }
628
+ async setNamedKey(name, key) {
629
+ return this._seq.add(() => this._setNamedKey(name, key));
630
+ }
631
+ // avoid deadlock
632
+ async _setNamedKey(name, key) {
633
+ const item = {
634
+ name,
635
+ key
636
+ };
637
+ const bag = await this.rt.getBag();
638
+ this.logger.Debug().Str("name", name).Msg("setNamedKey");
639
+ await bag.set(name, item);
640
+ return await this.toKeyWithFingerPrint(item.key);
641
+ }
642
+ async getNamedExtractableKey(name, failIfNotFound = false) {
643
+ const ret = await this.getNamedKey(name, failIfNotFound);
644
+ if (ret.isErr()) {
645
+ return ret;
646
+ }
647
+ const named = ret.Ok();
648
+ return Result2.Ok({
649
+ ...named,
650
+ extract: async () => {
651
+ const ext = new Uint8Array(await this.rt.crypto.exportKey("raw", named.key));
652
+ return {
653
+ key: ext,
654
+ keyStr: base58btc.encode(ext)
655
+ };
656
+ }
657
+ });
658
+ }
659
+ async getNamedKey(name, failIfNotFound = false) {
660
+ const id = this.rt.sthis.nextId(4).str;
661
+ return this._seq.add(async () => {
662
+ const bag = await this.rt.getBag();
663
+ const named = await bag.get(name);
664
+ if (named) {
665
+ const fpr = await this.toKeyWithFingerPrint(named.key);
666
+ this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", fpr).Msg("fingerPrint getNamedKey");
667
+ return fpr;
668
+ }
669
+ if (failIfNotFound) {
670
+ this.logger.Debug().Str("id", id).Str("name", name).Msg("failIfNotFound getNamedKey");
671
+ return Result2.Err(new Error(`Key not found: ${name}`));
672
+ }
673
+ const ret = await this._setNamedKey(name, base58btc.encode(this.rt.crypto.randomBytes(this.rt.keyLength)));
674
+ this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", ret).Msg("createKey getNamedKey-post");
675
+ return ret;
676
+ });
677
+ }
678
+ };
679
+ var keyBagProviderFactories = new Map(
680
+ [
681
+ {
682
+ protocol: "file:",
683
+ factory: async (url, sthis) => {
684
+ const { KeyBagProviderFile } = await import("./key-bag-file-4SJQGORQ.js");
685
+ return new KeyBagProviderFile(url, sthis);
686
+ }
687
+ },
688
+ {
689
+ protocol: "indexdb:",
690
+ factory: async (url, sthis) => {
691
+ const { KeyBagProviderIndexDB } = await import("./key-bag-indexdb-GSQOUUVQ.js");
692
+ return new KeyBagProviderIndexDB(url, sthis);
693
+ }
694
+ }
695
+ ].map((i) => [i.protocol, i])
696
+ );
697
+ function registerKeyBagProviderFactory(item) {
698
+ const protocol = item.protocol.endsWith(":") ? item.protocol : item.protocol + ":";
699
+ keyBagProviderFactories.set(protocol, {
700
+ ...item,
701
+ protocol
702
+ });
703
+ }
704
+ function defaultKeyBagOpts(sthis, kbo) {
705
+ if (kbo.keyRuntime) {
706
+ return kbo.keyRuntime;
707
+ }
708
+ const logger = ensureLogger(sthis, "KeyBag");
709
+ let url;
710
+ if (kbo.url) {
711
+ url = URI.from(kbo.url);
712
+ logger.Debug().Url(url).Msg("from opts");
713
+ } else {
714
+ let bagFnameOrUrl = sthis.env.get("FP_KEYBAG_URL");
715
+ if (runtimeFn().isBrowser) {
716
+ url = URI.from(bagFnameOrUrl || "indexdb://fp-keybag");
717
+ } else {
718
+ if (!bagFnameOrUrl) {
719
+ const home = sthis.env.get("HOME");
720
+ bagFnameOrUrl = `${home}/.fireproof/keybag`;
721
+ url = URI.from(`file://${bagFnameOrUrl}`);
722
+ } else {
723
+ url = URI.from(bagFnameOrUrl);
724
+ }
725
+ }
726
+ logger.Debug().Url(url).Msg("from env");
727
+ }
728
+ const kitem = keyBagProviderFactories.get(url.protocol);
729
+ if (!kitem) {
730
+ throw logger.Error().Url(url).Msg("unsupported protocol").AsError();
731
+ }
732
+ const getBag = async () => kitem.factory(url, sthis);
733
+ if (url.hasParam("masterkey")) {
734
+ throw logger.Error().Url(url).Msg("masterkey is not supported").AsError();
735
+ }
736
+ return {
737
+ url,
738
+ crypto: kbo.crypto || toCryptoRuntime2({}),
739
+ sthis,
740
+ logger,
741
+ keyLength: kbo.keyLength || 16,
742
+ getBag,
743
+ id: () => {
744
+ return url.toString();
745
+ }
746
+ };
747
+ }
748
+ var _keyBags = new KeyedResolvOnce();
749
+ async function getKeyBag(sthis, kbo = {}) {
750
+ await sthis.start();
751
+ const rt = defaultKeyBagOpts(sthis, kbo);
752
+ return _keyBags.get(rt.id()).once(async () => new KeyBag(rt));
753
+ }
754
+
755
+ // src/blockstore/commitor.ts
756
+ import * as CBW from "@ipld/car/buffer-writer";
757
+ import { sha256 as hasher2 } from "multiformats/hashes/sha2";
758
+ import * as dagCodec2 from "@ipld/dag-cbor";
759
+ async function encodeCarFile(roots, t, codec3) {
760
+ let size = 0;
761
+ const headerSize = CBW.headerLength({ roots });
762
+ size += headerSize;
763
+ for (const { cid, bytes } of t.entries()) {
764
+ size += CBW.blockLength({ cid, bytes });
765
+ }
766
+ const buffer = new Uint8Array(size);
767
+ const writer = CBW.createWriter(buffer, { headerSize });
768
+ for (const r of roots) {
769
+ writer.addRoot(r);
770
+ }
771
+ for (const { cid, bytes } of t.entries()) {
772
+ writer.write({ cid, bytes });
773
+ }
774
+ writer.close();
775
+ return await encode({ value: writer.bytes, hasher: hasher2, codec: codec3 });
776
+ }
777
+ async function createCarFile(encoder, cid, t) {
778
+ return encodeCarFile([cid], t, encoder);
779
+ }
780
+ async function commitFiles(fileStore, walStore, t, done) {
781
+ const { files: roots } = makeFileCarHeader(done);
782
+ const cids = [];
783
+ const codec3 = (await fileStore.keyedCrypto()).codec();
784
+ const cars = await prepareCarFilesFiles(codec3, roots, t);
785
+ for (const car of cars) {
786
+ const { cid, bytes } = car;
787
+ await fileStore.save({ cid, bytes });
788
+ await walStore.enqueueFile(
789
+ cid
790
+ /*, !!opts.public*/
791
+ );
792
+ cids.push(cid);
793
+ }
794
+ return cids;
795
+ }
796
+ function makeFileCarHeader(result) {
797
+ const files = [];
798
+ for (const [, meta] of Object.entries(result.files || {})) {
799
+ if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
800
+ files.push(meta.cid);
801
+ }
802
+ }
803
+ return { ...result, files };
804
+ }
805
+ async function prepareCarFilesFiles(encoder, roots, t) {
806
+ return [await encodeCarFile(roots, t, encoder)];
807
+ }
808
+ function makeCarHeader(meta, cars, compact = false) {
809
+ const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
810
+ return { ...coreHeader, meta };
811
+ }
812
+ async function encodeCarHeader(fp) {
813
+ return await encode({
814
+ value: { fp },
815
+ hasher: hasher2,
816
+ codec: dagCodec2
817
+ });
818
+ }
819
+ async function commit(params, t, done, opts = { noLoader: false, compact: false }) {
820
+ const fp = makeCarHeader(done, params.carLog, !!opts.compact);
821
+ const rootBlock = await encodeCarHeader(fp);
822
+ const cars = await prepareCarFiles(params.encoder, params.threshold, rootBlock, t);
823
+ const cids = [];
824
+ for (const car of cars) {
825
+ const { cid, bytes } = car;
826
+ await params.carStore.save({ cid, bytes });
827
+ cids.push(cid);
828
+ }
829
+ const newDbMeta = { cars: cids };
830
+ await params.WALStore.enqueue(newDbMeta, opts);
831
+ await params.metaStore.save(newDbMeta);
832
+ return { cgrp: cids, header: fp };
833
+ }
834
+ async function prepareCarFiles(encoder, threshold, rootBlock, t) {
835
+ const carFiles = [];
836
+ threshold = threshold || 128e3 * 8;
837
+ let clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
838
+ clonedt.putSync(rootBlock.cid, rootBlock.bytes);
839
+ let newsize = CBW.blockLength(toCIDBlock(rootBlock));
840
+ let cidRootBlock = rootBlock;
841
+ for (const { cid, bytes } of t.entries()) {
842
+ newsize += CBW.blockLength(toCIDBlock({ cid, bytes }));
843
+ if (newsize >= threshold) {
844
+ carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
845
+ clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
846
+ clonedt.putSync(cid, bytes);
847
+ cidRootBlock = { cid, bytes };
848
+ newsize = CBW.blockLength(toCIDBlock({ cid, bytes }));
849
+ } else {
850
+ clonedt.putSync(cid, bytes);
851
+ }
852
+ }
853
+ carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
854
+ return carFiles;
855
+ }
856
+
857
+ // src/blockstore/loader.ts
858
+ import { sha256 as hasher3 } from "multiformats/hashes/sha2";
859
+
860
+ // src/blockstore/task-manager.ts
861
+ var TaskManager = class {
862
+ constructor(sthis, callback) {
863
+ this.eventsWeHandled = /* @__PURE__ */ new Set();
864
+ this.queue = [];
865
+ this.isProcessing = false;
866
+ this.logger = ensureLogger(sthis, "TaskManager");
867
+ this.callback = callback;
868
+ }
869
+ async handleEvent(cid, parents, dbMeta) {
870
+ for (const parent of parents) {
871
+ this.eventsWeHandled.add(parent.toString());
872
+ }
873
+ this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
874
+ this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
875
+ void this.processQueue();
876
+ }
877
+ async processQueue() {
878
+ if (this.isProcessing) return;
879
+ this.isProcessing = true;
880
+ const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
881
+ const first = filteredQueue[0];
882
+ if (!first) {
883
+ return;
884
+ }
885
+ try {
886
+ await this.callback(first.dbMeta);
887
+ this.eventsWeHandled.add(first.cid);
888
+ this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
889
+ } catch (err) {
890
+ if (first.retries++ > 3) {
891
+ this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
892
+ this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
893
+ }
894
+ await new Promise((resolve) => setTimeout(resolve, 50));
895
+ throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
896
+ } finally {
897
+ this.isProcessing = false;
898
+ if (this.queue.length > 0) {
899
+ void this.processQueue();
900
+ }
901
+ }
902
+ }
903
+ };
904
+
905
+ // src/blockstore/loader.ts
906
+ function carLogIncludesGroup(list, cids) {
907
+ return list.some((arr) => {
908
+ return arr.toString() === cids.toString();
909
+ });
910
+ }
911
+ function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
912
+ const byString = /* @__PURE__ */ new Map();
913
+ for (const cid of list) {
914
+ if (remove.has(cid.toString())) continue;
915
+ byString.set(cid.toString(), cid);
916
+ }
917
+ return [...byString.values()];
918
+ }
919
+ var Loader = class {
920
+ constructor(name, ebOpts, sthis) {
921
+ this.commitQueue = new CommitQueue();
922
+ this.isCompacting = false;
923
+ this.carReaders = /* @__PURE__ */ new Map();
924
+ this.seenCompacted = /* @__PURE__ */ new Set();
925
+ this.processedCars = /* @__PURE__ */ new Set();
926
+ this.carLog = [];
927
+ this.getBlockCache = /* @__PURE__ */ new Map();
928
+ this.seenMeta = /* @__PURE__ */ new Set();
929
+ this.writeLimit = pLimit(1);
930
+ this.onceReady = new ResolveOnce2();
931
+ this.name = name;
932
+ this.sthis = sthis;
933
+ this.ebOpts = defaultedBlockstoreRuntime(
934
+ sthis,
935
+ {
936
+ ...ebOpts,
937
+ name
938
+ },
939
+ "Loader"
940
+ );
941
+ this.logger = this.ebOpts.logger;
942
+ this.taskManager = new TaskManager(sthis, async (dbMeta) => {
943
+ await this.handleDbMetasFromStore([dbMeta]);
944
+ });
945
+ }
946
+ // readonly id = uuidv4();
947
+ async keyBag() {
948
+ return getKeyBag(this.sthis, this.ebOpts.keyBag);
949
+ }
950
+ async carStore() {
951
+ return this.ebOpts.storeRuntime.makeDataStore(this);
952
+ }
953
+ async fileStore() {
954
+ return this.ebOpts.storeRuntime.makeDataStore(this);
955
+ }
956
+ async WALStore() {
957
+ return this.ebOpts.storeRuntime.makeWALStore(this);
958
+ }
959
+ async metaStore() {
960
+ return this.ebOpts.storeRuntime.makeMetaStore(this);
961
+ }
962
+ async ready() {
963
+ return this.onceReady.once(async () => {
964
+ const metas = await (await this.metaStore()).load();
965
+ if (this.ebOpts.meta) {
966
+ await this.handleDbMetasFromStore([this.ebOpts.meta]);
967
+ } else if (metas) {
968
+ await this.handleDbMetasFromStore(metas);
969
+ }
970
+ });
971
+ }
972
+ async close() {
973
+ const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
974
+ await Promise.all(toClose.map((store) => store.close()));
975
+ }
976
+ async destroy() {
977
+ const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
978
+ await Promise.all(toDestroy.map((store) => store.destroy()));
979
+ }
980
+ // async snapToCar(carCid: AnyLink | string) {
981
+ // await this.ready
982
+ // if (typeof carCid === 'string') {
983
+ // carCid = CID.parse(carCid)
984
+ // }
985
+ // const carHeader = await this.loadCarHeaderFromMeta({ car: carCid, key: this.key || null })
986
+ // this.carLog = [carCid, ...carHeader.cars]
987
+ // await this.getMoreReaders(carHeader.cars)
988
+ // await this._applyCarHeader(carHeader, true)
989
+ // }
990
+ async handleDbMetasFromStore(metas) {
991
+ this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
992
+ for (const meta of metas) {
993
+ await this.writeLimit(async () => {
994
+ await this.mergeDbMetaIntoClock(meta);
995
+ });
996
+ }
997
+ }
998
+ async mergeDbMetaIntoClock(meta) {
999
+ if (this.isCompacting) {
1000
+ throw this.logger.Error().Msg("cannot merge while compacting").AsError();
1001
+ }
1002
+ if (this.seenMeta.has(meta.cars.toString())) return;
1003
+ this.seenMeta.add(meta.cars.toString());
1004
+ if (carLogIncludesGroup(this.carLog, meta.cars)) {
1005
+ return;
1006
+ }
1007
+ const carHeader = await this.loadCarHeaderFromMeta(meta);
1008
+ carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1009
+ await this.getMoreReaders(carHeader.cars.flat());
1010
+ this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
1011
+ await this.ebOpts.applyMeta?.(carHeader.meta);
1012
+ }
1013
+ // protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
1014
+ // const { key } = meta;
1015
+ // if (key) {
1016
+ // await this.setKey(key);
1017
+ // }
1018
+ // }
1019
+ async loadCarHeaderFromMeta({ cars: cids }) {
1020
+ const reader = await this.loadCar(cids[0]);
1021
+ return await parseCarFile(reader, this.logger);
1022
+ }
1023
+ // async _getKey(): Promise<string | undefined> {
1024
+ // if (this.key) return this.key;
1025
+ // // generate a random key
1026
+ // if (!this.ebOpts.public) {
1027
+ // await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
1028
+ // }
1029
+ // return this.key || undefined;
1030
+ // }
1031
+ async commitFiles(t, done) {
1032
+ await this.ready();
1033
+ const fstore = await this.fileStore();
1034
+ const wstore = await this.WALStore();
1035
+ return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
1036
+ }
1037
+ async loadFileCar(cid) {
1038
+ return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
1039
+ }
1040
+ async commit(t, done, opts = { noLoader: false, compact: false }) {
1041
+ await this.ready();
1042
+ const fstore = await this.fileStore();
1043
+ const params = {
1044
+ encoder: (await fstore.keyedCrypto()).codec(),
1045
+ carLog: this.carLog,
1046
+ carStore: fstore,
1047
+ WALStore: await this.WALStore(),
1048
+ metaStore: await this.metaStore(),
1049
+ threshold: this.ebOpts.threshold
1050
+ };
1051
+ return this.commitQueue.enqueue(async () => {
1052
+ await this.cacheTransaction(t);
1053
+ const ret = await commit(params, t, done, opts);
1054
+ await this.updateCarLog(ret.cgrp, ret.header, !!opts.compact);
1055
+ return ret.cgrp;
1056
+ });
1057
+ }
1058
+ async updateCarLog(cids, fp, compact) {
1059
+ if (compact) {
1060
+ const previousCompactCid = fp.compact[fp.compact.length - 1];
1061
+ fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1062
+ this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
1063
+ await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
1064
+ } else {
1065
+ this.carLog.unshift(cids);
1066
+ }
1067
+ }
1068
+ async cacheTransaction(t) {
1069
+ for await (const block of t.entries()) {
1070
+ const sBlock = block.cid.toString();
1071
+ if (!this.getBlockCache.has(sBlock)) {
1072
+ this.getBlockCache.set(sBlock, block);
1073
+ }
1074
+ }
1075
+ }
1076
+ async cacheCarReader(carCidStr, reader) {
1077
+ if (this.processedCars.has(carCidStr)) return;
1078
+ this.processedCars.add(carCidStr);
1079
+ for await (const block of reader.blocks()) {
1080
+ const sBlock = block.cid.toString();
1081
+ if (!this.getBlockCache.has(sBlock)) {
1082
+ this.getBlockCache.set(sBlock, block);
1083
+ }
1084
+ }
1085
+ }
1086
+ async removeCidsForCompact(cid) {
1087
+ const carHeader = await this.loadCarHeaderFromMeta({
1088
+ cars: [cid]
1089
+ });
1090
+ for (const cids of carHeader.compact) {
1091
+ for (const cid2 of cids) {
1092
+ await (await this.carStore()).remove(cid2);
1093
+ }
1094
+ }
1095
+ }
1096
+ // async flushCars() {
1097
+ // await this.ready
1098
+ // // for each cid in car log, make a dbMeta
1099
+ // for (const cid of this.carLog) {
1100
+ // const dbMeta = { car: cid, key: this.key || null } as DbMeta
1101
+ // await this.remoteWAL!.enqueue(dbMeta, { public: false })
1102
+ // }
1103
+ // }
1104
+ async *entries(cache2 = true) {
1105
+ await this.ready();
1106
+ if (cache2) {
1107
+ for (const [, block] of this.getBlockCache) {
1108
+ yield block;
1109
+ }
1110
+ } else {
1111
+ for (const [, block] of this.getBlockCache) {
1112
+ yield block;
1113
+ }
1114
+ for (const cids of this.carLog) {
1115
+ for (const cid of cids) {
1116
+ const reader = await this.loadCar(cid);
1117
+ if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
1118
+ for await (const block of reader.blocks()) {
1119
+ const sCid = block.cid.toString();
1120
+ if (!this.getBlockCache.has(sCid)) {
1121
+ yield block;
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+ }
1127
+ }
1128
+ async getBlock(cid) {
1129
+ await this.ready();
1130
+ const sCid = cid.toString();
1131
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1132
+ const getCarCid = async (carCid) => {
1133
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1134
+ const reader = await this.loadCar(carCid);
1135
+ if (!reader) {
1136
+ throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
1137
+ }
1138
+ await this.cacheCarReader(carCid.toString(), reader).catch(() => {
1139
+ return;
1140
+ });
1141
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1142
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
1143
+ };
1144
+ const getCompactCarCids = async (carCid) => {
1145
+ const reader = await this.loadCar(carCid);
1146
+ if (!reader) {
1147
+ throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
1148
+ }
1149
+ const header = await parseCarFile(reader, this.logger);
1150
+ const compacts = header.compact;
1151
+ let got2;
1152
+ const batchSize2 = 5;
1153
+ for (let i = 0; i < compacts.length; i += batchSize2) {
1154
+ const promises = [];
1155
+ for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
1156
+ for (const cid2 of compacts[j]) {
1157
+ promises.push(getCarCid(cid2));
1158
+ }
1159
+ }
1160
+ try {
1161
+ got2 = await Promise.any(promises);
1162
+ } catch {
1163
+ }
1164
+ if (got2) break;
1165
+ }
1166
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1167
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
1168
+ };
1169
+ let got;
1170
+ const batchSize = 5;
1171
+ for (let i = 0; i < this.carLog.length; i += batchSize) {
1172
+ const batch = this.carLog.slice(i, i + batchSize);
1173
+ const promises = batch.flatMap((slice) => slice.map(getCarCid));
1174
+ try {
1175
+ got = await Promise.any(promises);
1176
+ } catch {
1177
+ }
1178
+ if (got) break;
1179
+ }
1180
+ if (!got) {
1181
+ try {
1182
+ got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
1183
+ } catch {
1184
+ }
1185
+ }
1186
+ return got;
1187
+ }
1188
+ async loadCar(cid) {
1189
+ if (!this.carStore) {
1190
+ throw this.logger.Error().Msg("car store not initialized").AsError();
1191
+ }
1192
+ const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
1193
+ return loaded;
1194
+ }
1195
+ async makeDecoderAndCarReader(cid, local, remote) {
1196
+ const cidsString = cid.toString();
1197
+ let loadedCar = void 0;
1198
+ let activeStore = local;
1199
+ try {
1200
+ this.logger.Debug().Str("cid", cidsString).Msg("loading car");
1201
+ loadedCar = await local.load(cid);
1202
+ this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
1203
+ } catch (e) {
1204
+ if (remote) {
1205
+ const remoteCar = await remote.load(cid);
1206
+ if (remoteCar) {
1207
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
1208
+ await local.save(remoteCar);
1209
+ loadedCar = remoteCar;
1210
+ activeStore = remote;
1211
+ }
1212
+ } else {
1213
+ this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
1214
+ }
1215
+ }
1216
+ if (!loadedCar) {
1217
+ throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
1218
+ }
1219
+ const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher3, codec: (await activeStore.keyedCrypto()).codec() });
1220
+ const rawReader = await CarReader.fromBytes(bytes.value);
1221
+ const readerP = Promise.resolve(rawReader);
1222
+ const cachedReaderP = readerP.then(async (reader) => {
1223
+ await this.cacheCarReader(cidsString, reader).catch((e) => {
1224
+ this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
1225
+ return;
1226
+ });
1227
+ return reader;
1228
+ });
1229
+ this.carReaders.set(cidsString, cachedReaderP);
1230
+ return readerP;
1231
+ }
1232
+ //What if instead it returns an Array of CarHeader
1233
+ async storesLoadCar(cid, local, remote) {
1234
+ const cidsString = cid.toString();
1235
+ let dacr = this.carReaders.get(cidsString);
1236
+ if (!dacr) {
1237
+ dacr = this.makeDecoderAndCarReader(cid, local, remote);
1238
+ this.carReaders.set(cidsString, dacr);
1239
+ }
1240
+ return dacr;
1241
+ }
1242
+ async getMoreReaders(cids) {
1243
+ const limit = pLimit(5);
1244
+ const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
1245
+ await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
1246
+ }
1247
+ };
1248
+
1249
+ // src/runtime/keyed-crypto.ts
1250
+ var keyed_crypto_exports = {};
1251
+ __export(keyed_crypto_exports, {
1252
+ BlockIvKeyIdCodec: () => BlockIvKeyIdCodec,
1253
+ keyedCryptoFactory: () => keyedCryptoFactory
1254
+ });
1255
+ import { base58btc as base58btc2 } from "multiformats/bases/base58";
1256
+ import { sha256 as hasher4 } from "multiformats/hashes/sha2";
1257
+ import * as CBOR from "cborg";
1258
+ var generateIV = {
1259
+ random: {
1260
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1261
+ calc: async (ko, crypto, data) => {
1262
+ return crypto.randomBytes(ko.ivLength);
1263
+ },
1264
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1265
+ verify: async (ko, crypto, iv, data) => {
1266
+ return true;
1267
+ }
1268
+ },
1269
+ hash: {
1270
+ calc: async (ko, crypto, data) => {
1271
+ const hash = await hasher4.digest(data);
1272
+ const hashBytes = new Uint8Array(hash.bytes);
1273
+ const hashArray = new Uint8Array(ko.ivLength);
1274
+ for (let i = 0; i < hashBytes.length; i++) {
1275
+ hashArray[i % ko.ivLength] ^= hashBytes[i];
1276
+ }
1277
+ return hashArray;
1278
+ },
1279
+ verify: async function(ko, crypto, iv, data) {
1280
+ return ko.url.getParam("ivverify") !== "disable" && UInt8ArrayEqual(iv, await this.calc(ko, crypto, data));
1281
+ }
1282
+ }
1283
+ };
1284
+ function getGenerateIVFn(url, opts) {
1285
+ const ivhash = opts.ivCalc || url.getParam("ivhash") || "hash";
1286
+ return generateIV[ivhash] || generateIV["hash"];
1287
+ }
1288
+ var BlockIvKeyIdCodec = class {
1289
+ constructor(ko, iv, opts) {
1290
+ this.code = 3147065;
1291
+ this.name = "Fireproof@encrypted-block:aes-gcm";
1292
+ this.ko = ko;
1293
+ this.iv = iv;
1294
+ this.opts = opts || {};
1295
+ }
1296
+ async encode(data) {
1297
+ const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
1298
+ const { iv } = this.ko.algo(calcIv);
1299
+ const fprt = await this.ko.fingerPrint();
1300
+ const keyId = base58btc2.decode(fprt);
1301
+ this.ko.logger.Debug().Str("fp", fprt).Msg("encode");
1302
+ return CBOR.encode({
1303
+ iv,
1304
+ keyId,
1305
+ data: await this.ko._encrypt({ iv, bytes: data })
1306
+ });
1307
+ }
1308
+ async decode(abytes) {
1309
+ let bytes;
1310
+ if (abytes instanceof Uint8Array) {
1311
+ bytes = abytes;
1312
+ } else {
1313
+ bytes = new Uint8Array(abytes);
1314
+ }
1315
+ const { iv, keyId, data } = CBOR.decode(bytes);
1316
+ const fprt = await this.ko.fingerPrint();
1317
+ this.ko.logger.Debug().Str("fp", base58btc2.encode(keyId)).Msg("decode");
1318
+ if (base58btc2.encode(keyId) !== fprt) {
1319
+ throw this.ko.logger.Error().Str("fp", fprt).Str("keyId", base58btc2.encode(keyId)).Msg("keyId mismatch").AsError();
1320
+ }
1321
+ const result = await this.ko._decrypt({ iv, bytes: data });
1322
+ if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
1323
+ throw this.ko.logger.Error().Msg("iv missmatch").AsError();
1324
+ }
1325
+ return result;
1326
+ }
1327
+ };
1328
+ var keyedCrypto = class {
1329
+ constructor(url, key, cyopt, sthis) {
1330
+ this.ivLength = 12;
1331
+ this.isEncrypting = true;
1332
+ this.logger = ensureLogger(sthis, "keyedCrypto");
1333
+ this.crypto = cyopt;
1334
+ this.key = key;
1335
+ this.url = url;
1336
+ }
1337
+ fingerPrint() {
1338
+ return Promise.resolve(this.key.fingerPrint);
1339
+ }
1340
+ codec(iv, opts) {
1341
+ return new BlockIvKeyIdCodec(this, iv, opts);
1342
+ }
1343
+ algo(iv) {
1344
+ return {
1345
+ name: "AES-GCM",
1346
+ iv: iv || this.crypto.randomBytes(this.ivLength),
1347
+ tagLength: 128
1348
+ };
1349
+ }
1350
+ async _decrypt(data) {
1351
+ this.logger.Debug().Len(data.bytes, "bytes").Len(data.iv, "iv").Str("fp", this.key.fingerPrint).Msg("decrypting");
1352
+ return new Uint8Array(await this.crypto.decrypt(this.algo(data.iv), this.key.key, data.bytes));
1353
+ }
1354
+ async _encrypt(data) {
1355
+ this.logger.Debug().Len(data.bytes).Str("fp", this.key.fingerPrint).Msg("encrypting");
1356
+ const a = this.algo(data.iv);
1357
+ return new Uint8Array(await this.crypto.encrypt(a, this.key.key, data.bytes));
1358
+ }
1359
+ };
1360
+ var nullCodec = class {
1361
+ constructor() {
1362
+ this.code = 0;
1363
+ this.name = "Fireproof@unencrypted-block";
1364
+ }
1365
+ encode(data) {
1366
+ return data;
1367
+ }
1368
+ decode(data) {
1369
+ return data;
1370
+ }
1371
+ };
1372
+ var noCrypto = class {
1373
+ constructor(url, cyrt, sthis) {
1374
+ this.ivLength = 0;
1375
+ this.code = 0;
1376
+ this.name = "Fireproof@unencrypted-block";
1377
+ this.isEncrypting = false;
1378
+ this._fingerPrint = "noCrypto:" + Math.random();
1379
+ this.logger = ensureLogger(sthis, "noCrypto");
1380
+ this.crypto = cyrt;
1381
+ this.url = url;
1382
+ }
1383
+ fingerPrint() {
1384
+ return Promise.resolve(this._fingerPrint);
1385
+ }
1386
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1387
+ codec(iv) {
1388
+ return new nullCodec();
1389
+ }
1390
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1391
+ algo(iv) {
1392
+ return {
1393
+ name: "noCrypto",
1394
+ iv: new Uint8Array(),
1395
+ tagLength: 0
1396
+ };
1397
+ }
1398
+ _decrypt() {
1399
+ throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
1400
+ }
1401
+ _encrypt() {
1402
+ throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
1403
+ }
1404
+ };
1405
+ async function keyedCryptoFactory(url, kb, sthis) {
1406
+ const storekey = url.getParam("storekey");
1407
+ if (storekey && storekey !== "insecure") {
1408
+ let rkey = await kb.getNamedKey(storekey, true);
1409
+ if (rkey.isErr()) {
1410
+ try {
1411
+ rkey = await kb.toKeyWithFingerPrint(storekey);
1412
+ } catch (e) {
1413
+ throw sthis.logger.Error().Err(e).Str("keybag", kb.rt.id()).Str("name", storekey).Msg("getNamedKey failed").AsError();
1414
+ }
1415
+ }
1416
+ return new keyedCrypto(url, rkey.Ok(), kb.rt.crypto, sthis);
1417
+ }
1418
+ return new noCrypto(url, kb.rt.crypto, sthis);
1419
+ }
1420
+
1421
+ // src/blockstore/fragment-gateway.ts
1422
+ import { Result as Result3 } from "@adviser/cement";
1423
+ import { base58btc as base58btc3 } from "multiformats/bases/base58";
1424
+ import { encode as encode3, decode as decode3 } from "cborg";
1425
+ function getFragSize(url) {
1426
+ const fragSize = url.getParam("fragSize");
1427
+ let ret = 0;
1428
+ if (fragSize) {
1429
+ ret = parseInt(fragSize);
1430
+ }
1431
+ if (isNaN(ret) || ret <= 0) {
1432
+ ret = 0;
1433
+ }
1434
+ return ret;
1435
+ }
1436
+ async function getFrags(url, innerGW, headerSize, logger) {
1437
+ const fragSize = getFragSize(url);
1438
+ if (!fragSize) {
1439
+ const res = await innerGW.get(url);
1440
+ if (res.isErr()) {
1441
+ return [res];
1442
+ }
1443
+ const data = res.unwrap();
1444
+ return [
1445
+ Result3.Ok({
1446
+ fid: new Uint8Array(0),
1447
+ ofs: 0,
1448
+ len: data.length,
1449
+ data
1450
+ })
1451
+ ];
1452
+ }
1453
+ const firstRaw = await innerGW.get(url.build().setParam("ofs", "0").URI());
1454
+ if (firstRaw.isErr()) {
1455
+ return [firstRaw];
1456
+ }
1457
+ const firstFragment = decode3(firstRaw.unwrap());
1458
+ const blockSize = firstFragment.data.length;
1459
+ const ops = [Promise.resolve(Result3.Ok(firstFragment))];
1460
+ const fidStr = base58btc3.encode(firstFragment.fid);
1461
+ const fragUrl = url.build().setParam("fid", fidStr).setParam("len", firstFragment.len.toString()).setParam("headerSize", headerSize.toString());
1462
+ for (let ofs = blockSize; ofs < firstFragment.len; ofs += blockSize) {
1463
+ ops.push(
1464
+ (async (furl, ofs2) => {
1465
+ const raw2 = await innerGW.get(furl);
1466
+ if (raw2.isErr()) {
1467
+ return raw2;
1468
+ }
1469
+ const fragment = decode3(raw2.unwrap());
1470
+ if (base58btc3.encode(fragment.fid) !== fidStr) {
1471
+ return Result3.Err(logger.Error().Msg("Fragment fid mismatch").AsError());
1472
+ }
1473
+ if (fragment.ofs !== ofs2) {
1474
+ return Result3.Err(logger.Error().Uint64("ofs", ofs2).Msg("Fragment ofs mismatch").AsError());
1475
+ }
1476
+ return Result3.Ok(fragment);
1477
+ })(fragUrl.setParam("ofs", ofs.toString()).URI(), ofs)
1478
+ );
1479
+ }
1480
+ return Promise.all(ops);
1481
+ }
1482
+ var FragmentGateway = class {
1483
+ constructor(sthis, innerGW) {
1484
+ this.fidLength = 4;
1485
+ this.headerSize = 32;
1486
+ this.sthis = ensureSuperLog(sthis, "FragmentGateway");
1487
+ this.logger = this.sthis.logger;
1488
+ this.innerGW = innerGW;
1489
+ }
1490
+ slicer(url, body) {
1491
+ const fragSize = getFragSize(url);
1492
+ if (!fragSize) {
1493
+ return [this.innerGW.put(url, body)];
1494
+ }
1495
+ const blocksize = fragSize - this.headerSize;
1496
+ if (blocksize <= 0) {
1497
+ throw this.logger.Error().Uint64("fragSize", fragSize).Uint64("headerSize", this.headerSize).Msg("Fragment size is too small").AsError();
1498
+ }
1499
+ const ops = [];
1500
+ const fid = this.sthis.nextId(this.fidLength);
1501
+ const fragUrl = url.build().setParam("fid", fid.str).setParam("len", body.length.toString()).setParam("headerSize", this.headerSize.toString());
1502
+ for (let ofs = 0; ofs < body.length; ofs += blocksize) {
1503
+ const block = encode3({
1504
+ fid: fid.bin,
1505
+ ofs,
1506
+ len: body.length,
1507
+ data: body.slice(ofs, ofs + blocksize)
1508
+ });
1509
+ if (block.length > fragSize) {
1510
+ throw this.logger.Error().Uint64("block", block.length).Uint64("fragSize", fragSize).Msg("Block size to big").AsError();
1511
+ }
1512
+ ops.push(this.innerGW.put(fragUrl.setParam("ofs", ofs.toString()).URI(), block));
1513
+ }
1514
+ return ops;
1515
+ }
1516
+ buildUrl(baseUrl, key) {
1517
+ return this.innerGW.buildUrl(baseUrl, key);
1518
+ }
1519
+ async destroy(iurl) {
1520
+ return this.innerGW.destroy(iurl);
1521
+ }
1522
+ async start(url) {
1523
+ this.headerSize = encode3({
1524
+ fid: this.sthis.nextId(this.fidLength).bin,
1525
+ ofs: 1024 * 1024,
1526
+ // 32bit
1527
+ len: 16 * 1024 * 1024,
1528
+ // 32bit
1529
+ data: new Uint8Array(1024)
1530
+ }).length - 1024;
1531
+ return this.innerGW.start(url);
1532
+ }
1533
+ async close(url) {
1534
+ return this.innerGW.close(url);
1535
+ }
1536
+ async put(url, body) {
1537
+ await Promise.all(this.slicer(url, body));
1538
+ return Result3.Ok(void 0);
1539
+ }
1540
+ async get(url) {
1541
+ const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
1542
+ let buffer = void 0;
1543
+ for (const rfrag of rfrags) {
1544
+ if (rfrag.isErr()) {
1545
+ return Result3.Err(rfrag.Err());
1546
+ }
1547
+ const frag = rfrag.Ok();
1548
+ buffer = buffer || new Uint8Array(frag.len);
1549
+ buffer.set(frag.data, frag.ofs);
1550
+ }
1551
+ return Result3.Ok(buffer || new Uint8Array(0));
1552
+ }
1553
+ async subscribe(url, callback) {
1554
+ if (this.innerGW.subscribe) {
1555
+ return this.innerGW.subscribe(url, callback);
1556
+ } else {
1557
+ return Result3.Err(this.logger.Error().Url(url).Msg("subscribe not supported").AsError());
1558
+ }
1559
+ }
1560
+ async delete(url) {
1561
+ const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
1562
+ for (const rfrag of rfrags) {
1563
+ if (rfrag.isErr()) {
1564
+ return Result3.Err(rfrag.Err());
1565
+ }
1566
+ const frag = rfrag.Ok();
1567
+ const fidStr = base58btc3.encode(frag.fid);
1568
+ const fragUrl = url.build().setParam("fid", fidStr).setParam("len", frag.len.toString()).setParam("headerSize", this.headerSize.toString()).URI();
1569
+ await this.innerGW.delete(fragUrl);
1570
+ }
1571
+ return Result3.Ok(void 0);
1572
+ }
1573
+ };
1574
+
1575
+ // src/blockstore/meta-key-helper.ts
1576
+ import { format, parse } from "@ipld/dag-json";
1577
+ import { EventBlock, decodeEventBlock } from "@web3-storage/pail/clock";
1578
+ import { CID as CID2 } from "multiformats";
1579
+ import { base64pad } from "multiformats/bases/base64";
1580
+ import { Result as Result4 } from "@adviser/cement";
1581
+ async function decodeGatewayMetaBytesToDbMeta(sthis, byteHeads) {
1582
+ const crdtEntries = JSON.parse(sthis.txt.decode(byteHeads));
1583
+ if (!crdtEntries.length) {
1584
+ sthis.logger.Debug().Any("byteHeads", byteHeads).Msg("No CRDT entries found");
1585
+ return [];
1586
+ }
1587
+ if (!crdtEntries.map) {
1588
+ sthis.logger.Debug().Str("crdtEntries", JSON.stringify(crdtEntries)).Msg("No data in CRDT entries");
1589
+ return [];
1590
+ }
1591
+ return Promise.all(
1592
+ crdtEntries.map(async (crdtEntry) => {
1593
+ const eventBlock = await decodeEventBlock(base64pad.decode(crdtEntry.data));
1594
+ const dbMeta = parse(sthis.txt.decode(eventBlock.value.data.dbMeta));
1595
+ return {
1596
+ eventCid: eventBlock.cid,
1597
+ parents: crdtEntry.parents,
1598
+ dbMeta
1599
+ };
1600
+ })
1601
+ );
1602
+ }
1603
+ async function setCryptoKeyFromGatewayMetaPayload(uri, sthis, data) {
1604
+ try {
1605
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Setting crypto key from gateway meta payload");
1606
+ const keyInfo = await decodeGatewayMetaBytesToDbMeta(sthis, data);
1607
+ if (keyInfo.length) {
1608
+ const dbMeta = keyInfo[0].dbMeta;
1609
+ if (dbMeta.key) {
1610
+ const kb = await getKeyBag(sthis);
1611
+ const keyName = getStoreKeyName(uri);
1612
+ const res = await kb.setNamedKey(keyName, dbMeta.key);
1613
+ if (res.isErr()) {
1614
+ sthis.logger.Debug().Str("keyName", keyName).Str("dbMeta.key", dbMeta.key).Msg("Failed to set named key");
1615
+ throw res.Err();
1616
+ }
1617
+ }
1618
+ sthis.logger.Debug().Str("dbMeta.key", dbMeta.key).Str("uri", uri.toString()).Msg("Set crypto key from gateway meta payload");
1619
+ return Result4.Ok(dbMeta);
1620
+ }
1621
+ sthis.logger.Debug().Any("data", data).Msg("No crypto in gateway meta payload");
1622
+ return Result4.Ok(void 0);
1623
+ } catch (error) {
1624
+ sthis.logger.Debug().Err(error).Msg("Failed to set crypto key from gateway meta payload");
1625
+ return Result4.Err(error);
1626
+ }
1627
+ }
1628
+ async function addCryptoKeyToGatewayMetaPayload(uri, sthis, body) {
1629
+ try {
1630
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Adding crypto key to gateway meta payload");
1631
+ const keyName = getStoreKeyName(uri);
1632
+ const kb = await getKeyBag(sthis);
1633
+ const res = await kb.getNamedExtractableKey(keyName, true);
1634
+ if (res.isErr()) {
1635
+ sthis.logger.Error().Str("keyName", keyName).Msg("Failed to get named extractable key");
1636
+ throw res.Err();
1637
+ }
1638
+ const keyData = await res.Ok().extract();
1639
+ const dbMetas = await decodeGatewayMetaBytesToDbMeta(sthis, body);
1640
+ const { dbMeta, parents } = dbMetas[0];
1641
+ const parentLinks = parents.map((p) => CID2.parse(p));
1642
+ dbMeta.key = keyData.keyStr;
1643
+ const events = await Promise.all([dbMeta].map((dbMeta2) => createDbMetaEventBlock(sthis, dbMeta2, parentLinks)));
1644
+ const encoded = await encodeEventsWithParents(sthis, events, parentLinks);
1645
+ sthis.logger.Debug().Str("uri", uri.toString()).Msg("Added crypto key to gateway meta payload");
1646
+ return Result4.Ok(encoded);
1647
+ } catch (error) {
1648
+ sthis.logger.Error().Err(error).Msg("Failed to add crypto key to gateway meta payload");
1649
+ return Result4.Err(error);
1650
+ }
1651
+ }
1652
+ function getStoreKeyName(url) {
1653
+ const storeKeyName = [url.getParam("localName") || url.getParam("name")];
1654
+ const idx = url.getParam("index");
1655
+ if (idx) {
1656
+ storeKeyName.push(idx);
1657
+ }
1658
+ storeKeyName.push("data");
1659
+ return `@${storeKeyName.join(":")}@`;
1660
+ }
1661
+ async function createDbMetaEventBlock(sthis, dbMeta, parents) {
1662
+ const event = await EventBlock.create(
1663
+ {
1664
+ dbMeta: sthis.txt.encode(format(dbMeta))
1665
+ },
1666
+ parents
1667
+ );
1668
+ return event;
1669
+ }
1670
+ async function encodeEventsWithParents(sthis, events, parents) {
1671
+ const crdtEntries = events.map((event) => {
1672
+ const base64String = base64pad.encode(event.bytes);
1673
+ return {
1674
+ cid: event.cid.toString(),
1675
+ data: base64String,
1676
+ parents: parents.map((p) => p.toString())
1677
+ };
1678
+ });
1679
+ return sthis.txt.encode(JSON.stringify(crdtEntries));
1680
+ }
1681
+
1682
+ // src/blockstore/store.ts
1683
+ import pRetry from "p-retry";
1684
+ import pMap from "p-map";
1685
+ function guardVersion(url) {
1686
+ if (!url.hasParam("version")) {
1687
+ return Result5.Err(`missing version: ${url.toString()}`);
1688
+ }
1689
+ return Result5.Ok(url);
1690
+ }
1691
+ var BaseStoreImpl = class {
1692
+ constructor(name, url, opts, sthis, logger) {
1693
+ this._onStarted = [];
1694
+ this._onClosed = [];
1695
+ this.name = name;
1696
+ this._url = url;
1697
+ this.keybag = opts.keybag;
1698
+ this.sthis = sthis;
1699
+ this.logger = logger.With().Ref("url", () => this._url.toString()).Str("name", name).Logger();
1700
+ this.gateway = new FragmentGateway(this.sthis, opts.gateway);
1701
+ this.loader = opts.loader;
1702
+ }
1703
+ url() {
1704
+ return this._url;
1705
+ }
1706
+ onStarted(fn) {
1707
+ this._onStarted.push(fn);
1708
+ }
1709
+ onClosed(fn) {
1710
+ this._onClosed.push(fn);
1711
+ }
1712
+ async ready() {
1713
+ return;
1714
+ }
1715
+ async keyedCrypto() {
1716
+ return keyedCryptoFactory(this._url, await this.keybag(), this.sthis);
1717
+ }
1718
+ async start() {
1719
+ this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
1720
+ this._url = this._url.build().setParam("store", this.storeType).URI();
1721
+ const res = await this.gateway.start(this._url);
1722
+ if (res.isErr()) {
1723
+ this.logger.Error().Result("gw-start", res).Msg("started-gateway");
1724
+ return res;
1725
+ }
1726
+ this._url = res.Ok();
1727
+ const kb = await this.keybag();
1728
+ const skRes = await kb.ensureKeyFromUrl(this._url, () => {
1729
+ const idx = this._url.getParam("index");
1730
+ const storeKeyName = [this.name];
1731
+ if (idx) {
1732
+ storeKeyName.push(idx);
1733
+ }
1734
+ storeKeyName.push(this.storeType);
1735
+ return storeKeyName.join(":");
1736
+ });
1737
+ if (skRes.isErr()) {
1738
+ return skRes;
1739
+ }
1740
+ this._url = skRes.Ok();
1741
+ const version = guardVersion(this._url);
1742
+ if (version.isErr()) {
1743
+ this.logger.Error().Result("version", version).Msg("guardVersion");
1744
+ await this.close();
1745
+ return version;
1746
+ }
1747
+ if (this.ready) {
1748
+ const fn = this.ready.bind(this);
1749
+ const ready = await exception2Result(fn);
1750
+ if (ready.isErr()) {
1751
+ await this.close();
1752
+ return ready;
1753
+ }
1754
+ }
1755
+ this._onStarted.forEach((fn) => fn());
1756
+ this.logger.Debug().Msg("started");
1757
+ return version;
1758
+ }
1759
+ };
1760
+ var MetaStoreImpl = class extends BaseStoreImpl {
1761
+ // remote: boolean;
1762
+ constructor(sthis, name, url, opts) {
1763
+ super(name, url, { ...opts }, sthis, ensureLogger(sthis, "MetaStoreImpl"));
1764
+ this.storeType = "meta";
1765
+ this.subscribers = /* @__PURE__ */ new Map();
1766
+ this.parents = [];
1767
+ if (
1768
+ /*this.remote && */
1769
+ opts.gateway.subscribe
1770
+ ) {
1771
+ this.onStarted(async () => {
1772
+ this.logger.Debug().Str("url", this.url().toString()).Msg("Subscribing to the gateway");
1773
+ opts.gateway.subscribe?.(this.url(), async (message) => {
1774
+ this.logger.Debug().Msg("Received message from gateway");
1775
+ const dbMetas = await decodeGatewayMetaBytesToDbMeta(this.sthis, message);
1776
+ await Promise.all(
1777
+ dbMetas.map((dbMeta) => this.loader?.taskManager?.handleEvent(dbMeta.eventCid, dbMeta.parents, dbMeta.dbMeta))
1778
+ );
1779
+ this.updateParentsFromDbMetas(dbMetas);
1780
+ });
1781
+ });
1782
+ }
1783
+ }
1784
+ updateParentsFromDbMetas(dbMetas) {
1785
+ const cids = dbMetas.map((m) => m.eventCid);
1786
+ const dbMetaParents = dbMetas.flatMap((m) => m.parents);
1787
+ const uniqueParentsMap = new Map([...this.parents, ...cids].map((p) => [p.toString(), p]));
1788
+ const dbMetaParentsSet = new Set(dbMetaParents.map((p) => p.toString()));
1789
+ this.parents = Array.from(uniqueParentsMap.values()).filter((p) => !dbMetaParentsSet.has(p.toString()));
1790
+ }
1791
+ async handleByteHeads(byteHeads) {
1792
+ return await decodeGatewayMetaBytesToDbMeta(this.sthis, byteHeads);
1793
+ }
1794
+ async load() {
1795
+ const branch = "main";
1796
+ const url = await this.gateway.buildUrl(this.url(), branch);
1797
+ if (url.isErr()) {
1798
+ throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
1799
+ }
1800
+ const bytes = await this.gateway.get(url.Ok());
1801
+ if (bytes.isErr()) {
1802
+ if (isNotFoundError(bytes)) {
1803
+ return void 0;
1804
+ }
1805
+ throw this.logger.Error().Url(url.Ok()).Result("bytes:", bytes).Msg("gateway get").AsError();
1806
+ }
1807
+ const dbMetas = await this.handleByteHeads(bytes.Ok());
1808
+ await this.loader?.handleDbMetasFromStore(dbMetas.map((m) => m.dbMeta));
1809
+ this.updateParentsFromDbMetas(dbMetas);
1810
+ return dbMetas.map((m) => m.dbMeta);
1811
+ }
1812
+ async save(meta, branch) {
1813
+ branch = branch || "main";
1814
+ this.logger.Debug().Str("branch", branch).Any("meta", meta).Msg("saving meta");
1815
+ const event = await createDbMetaEventBlock(this.sthis, meta, this.parents);
1816
+ const bytes = await encodeEventsWithParents(this.sthis, [event], this.parents);
1817
+ const url = await this.gateway.buildUrl(this.url(), branch);
1818
+ if (url.isErr()) {
1819
+ throw this.logger.Error().Err(url.Err()).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
1820
+ }
1821
+ this.parents = [event.cid];
1822
+ const res = await this.gateway.put(url.Ok(), bytes);
1823
+ if (res.isErr()) {
1824
+ throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
1825
+ }
1826
+ return res;
1827
+ }
1828
+ async close() {
1829
+ await this.gateway.close(this.url());
1830
+ this._onClosed.forEach((fn) => fn());
1831
+ return Result5.Ok(void 0);
1832
+ }
1833
+ async destroy() {
1834
+ return this.gateway.destroy(this.url());
1835
+ }
1836
+ };
1837
+ var DataStoreImpl = class extends BaseStoreImpl {
1838
+ // readonly tag: string = "car-base";
1839
+ constructor(sthis, name, url, opts) {
1840
+ super(name, url, { ...opts }, sthis, ensureLogger(sthis, "DataStoreImpl"));
1841
+ this.storeType = "data";
1842
+ }
1843
+ async load(cid) {
1844
+ this.logger.Debug().Any("cid", cid).Msg("loading");
1845
+ const url = await this.gateway.buildUrl(this.url(), cid.toString());
1846
+ if (url.isErr()) {
1847
+ throw this.logger.Error().Err(url.Err()).Str("cid", cid.toString()).Msg("got error from gateway.buildUrl").AsError();
1848
+ }
1849
+ const res = await this.gateway.get(url.Ok());
1850
+ if (res.isErr()) {
1851
+ throw res.Err();
1852
+ }
1853
+ return { cid, bytes: res.Ok() };
1854
+ }
1855
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1856
+ async save(car, opts) {
1857
+ this.logger.Debug().Any("cid", car.cid.toString()).Msg("saving");
1858
+ const url = await this.gateway.buildUrl(this.url(), car.cid.toString());
1859
+ if (url.isErr()) {
1860
+ throw this.logger.Error().Err(url.Err()).Ref("cid", car.cid).Msg("got error from gateway.buildUrl").AsError();
1861
+ }
1862
+ const res = await this.gateway.put(url.Ok(), car.bytes);
1863
+ if (res.isErr()) {
1864
+ throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
1865
+ }
1866
+ return res.Ok();
1867
+ }
1868
+ async remove(cid) {
1869
+ const url = await this.gateway.buildUrl(this.url(), cid.toString());
1870
+ if (url.isErr()) {
1871
+ return url;
1872
+ }
1873
+ return this.gateway.delete(url.Ok());
1874
+ }
1875
+ async close() {
1876
+ await this.gateway.close(this.url());
1877
+ this._onClosed.forEach((fn) => fn());
1878
+ return Result5.Ok(void 0);
1879
+ }
1880
+ destroy() {
1881
+ return this.gateway.destroy(this.url());
1882
+ }
1883
+ };
1884
+ var WALStoreImpl = class extends BaseStoreImpl {
1885
+ constructor(loader, url, opts) {
1886
+ super(loader.name, url, { ...opts }, loader.sthis, ensureLogger(loader.sthis, "WALStoreImpl"));
1887
+ this.storeType = "wal";
1888
+ this._ready = new ResolveOnce3();
1889
+ this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
1890
+ this.processing = void 0;
1891
+ this.processQueue = new CommitQueue();
1892
+ this.loader = loader;
1893
+ }
1894
+ async ready() {
1895
+ return this._ready.once(async () => {
1896
+ const walState = await this.load().catch((e) => {
1897
+ this.logger.Error().Any("error", e).Msg("error loading wal");
1898
+ return void 0;
1899
+ });
1900
+ if (!walState) {
1901
+ this.walState.operations = [];
1902
+ this.walState.fileOperations = [];
1903
+ } else {
1904
+ this.walState.operations = walState.operations || [];
1905
+ this.walState.fileOperations = walState.fileOperations || [];
1906
+ }
1907
+ });
1908
+ }
1909
+ async enqueue(dbMeta, opts) {
1910
+ await this.ready();
1911
+ if (opts.compact) {
1912
+ this.walState.operations = [];
1913
+ this.walState.noLoaderOps = [dbMeta];
1914
+ } else if (opts.noLoader) {
1915
+ this.walState.noLoaderOps.push(dbMeta);
1916
+ } else {
1917
+ this.walState.operations.push(dbMeta);
1918
+ }
1919
+ await this.save(this.walState);
1920
+ if (!opts.noLoader) {
1921
+ void this.process();
1922
+ }
1923
+ }
1924
+ async enqueueFile(fileCid, publicFile = false) {
1925
+ await this.ready();
1926
+ this.walState.fileOperations.push({ cid: fileCid, public: publicFile });
1927
+ }
1928
+ async process() {
1929
+ await this.ready();
1930
+ if (!this.loader.remoteCarStore) return;
1931
+ await this.processQueue.enqueue(async () => {
1932
+ try {
1933
+ await this._doProcess();
1934
+ } catch (e) {
1935
+ this.logger.Error().Any("error", e).Msg("error processing wal");
1936
+ }
1937
+ if (this.walState.operations.length || this.walState.fileOperations.length || this.walState.noLoaderOps.length) {
1938
+ setTimeout(() => void this.process(), 0);
1939
+ }
1940
+ });
1941
+ }
1942
+ async _doProcess() {
1943
+ if (!this.loader.remoteCarStore) return;
1944
+ const operations = [...this.walState.operations];
1945
+ const noLoaderOps = [...this.walState.noLoaderOps];
1946
+ const fileOperations = [...this.walState.fileOperations];
1947
+ if (operations.length + noLoaderOps.length + fileOperations.length === 0) return;
1948
+ const concurrencyLimit = 3;
1949
+ const retryableUpload = (fn, description) => pRetry(fn, {
1950
+ retries: 5,
1951
+ onFailedAttempt: (error) => {
1952
+ this.logger.Warn().Msg(`Attempt ${error.attemptNumber} failed for ${description}. There are ${error.retriesLeft} retries left.`);
1953
+ }
1954
+ });
1955
+ try {
1956
+ await pMap(
1957
+ noLoaderOps,
1958
+ async (dbMeta) => {
1959
+ await retryableUpload(async () => {
1960
+ for (const cid of dbMeta.cars) {
1961
+ const car = await (await this.loader.carStore()).load(cid);
1962
+ if (!car) {
1963
+ if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
1964
+ throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
1965
+ }
1966
+ } else {
1967
+ await throwFalsy(this.loader.remoteCarStore).save(car);
1968
+ }
1969
+ }
1970
+ this.walState.noLoaderOps = this.walState.noLoaderOps.filter((op) => op !== dbMeta);
1971
+ }, `noLoaderOp with dbMeta.cars=${dbMeta.cars.toString()}`);
1972
+ },
1973
+ { concurrency: concurrencyLimit }
1974
+ );
1975
+ await pMap(
1976
+ operations,
1977
+ async (dbMeta) => {
1978
+ await retryableUpload(async () => {
1979
+ for (const cid of dbMeta.cars) {
1980
+ const car = await (await this.loader.carStore()).load(cid);
1981
+ if (!car) {
1982
+ if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
1983
+ throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
1984
+ }
1985
+ } else {
1986
+ await throwFalsy(this.loader.remoteCarStore).save(car);
1987
+ }
1988
+ }
1989
+ this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
1990
+ }, `operation with dbMeta.cars=${dbMeta.cars.toString()}`);
1991
+ },
1992
+ { concurrency: concurrencyLimit }
1993
+ );
1994
+ await pMap(
1995
+ fileOperations,
1996
+ async ({ cid: fileCid, public: publicFile }) => {
1997
+ await retryableUpload(async () => {
1998
+ const fileBlock = await (await this.loader.fileStore()).load(fileCid);
1999
+ if (!fileBlock) {
2000
+ throw this.logger.Error().Ref("cid", fileCid).Msg("missing file block").AsError();
2001
+ }
2002
+ await this.loader.remoteFileStore?.save(fileBlock, { public: publicFile });
2003
+ this.walState.fileOperations = this.walState.fileOperations.filter((op) => op.cid !== fileCid);
2004
+ }, `fileOperation with cid=${fileCid.toString()}`);
2005
+ },
2006
+ { concurrency: concurrencyLimit }
2007
+ );
2008
+ if (operations.length) {
2009
+ const lastOp = operations[operations.length - 1];
2010
+ await retryableUpload(async () => {
2011
+ await this.loader.remoteMetaStore?.save(lastOp);
2012
+ }, `remoteMetaStore save with dbMeta.cars=${lastOp.cars.toString()}`);
2013
+ }
2014
+ } catch (error) {
2015
+ this.logger.Error().Any("error", error).Msg("Processing failed");
2016
+ return;
2017
+ } finally {
2018
+ await this.save(this.walState);
2019
+ }
2020
+ }
2021
+ async load() {
2022
+ this.logger.Debug().Msg("loading");
2023
+ const filepath = await this.gateway.buildUrl(this.url(), "main");
2024
+ if (filepath.isErr()) {
2025
+ throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
2026
+ }
2027
+ const bytes = await this.gateway.get(filepath.Ok());
2028
+ if (bytes.isErr()) {
2029
+ if (isNotFoundError(bytes)) {
2030
+ return void 0;
2031
+ }
2032
+ throw this.logger.Error().Err(bytes.Err()).Msg("error get").AsError();
2033
+ }
2034
+ try {
2035
+ return bytes && parse2(this.sthis.txt.decode(bytes.Ok()));
2036
+ } catch (e) {
2037
+ throw this.logger.Error().Err(e).Msg("error parse").AsError();
2038
+ }
2039
+ }
2040
+ async save(state) {
2041
+ const filepath = await this.gateway.buildUrl(this.url(), "main");
2042
+ if (filepath.isErr()) {
2043
+ throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
2044
+ }
2045
+ let encoded;
2046
+ try {
2047
+ encoded = format2(state);
2048
+ } catch (e) {
2049
+ throw this.logger.Error().Err(e).Any("state", state).Msg("error format").AsError();
2050
+ }
2051
+ const res = await this.gateway.put(filepath.Ok(), this.sthis.txt.encode(encoded));
2052
+ if (res.isErr()) {
2053
+ throw this.logger.Error().Err(res.Err()).Str("filePath", filepath.Ok().toString()).Msg("error saving").AsError();
2054
+ }
2055
+ }
2056
+ async close() {
2057
+ await this.gateway.close(this.url());
2058
+ this._onClosed.forEach((fn) => fn());
2059
+ return Result5.Ok(void 0);
2060
+ }
2061
+ destroy() {
2062
+ return this.gateway.destroy(this.url());
2063
+ }
2064
+ };
2065
+
2066
+ // src/blockstore/store-factory.ts
2067
+ function ensureIsIndex(url, isIndex) {
2068
+ if (isIndex) {
2069
+ return url.build().setParam("index", isIndex).URI();
2070
+ }
2071
+ return url.build().delParam("index").URI();
2072
+ }
2073
+ function ensureName(name, url) {
2074
+ if (!url.hasParam("name")) {
2075
+ return url.build().setParam("name", name).URI();
2076
+ }
2077
+ return url;
2078
+ }
2079
+ var storeFactory = /* @__PURE__ */ new Map();
2080
+ function buildURL(optURL, loader) {
2081
+ const storeOpts = loader.ebOpts.store;
2082
+ const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
2083
+ let obuUrl;
2084
+ if (obuItem && obuItem.overrideBaseURL) {
2085
+ obuUrl = URI5.from(obuItem.overrideBaseURL);
2086
+ }
2087
+ const ret = ensureIsIndex(
2088
+ URI5.from(optURL || obuUrl || dataDir(loader.sthis, loader.name, storeOpts.stores?.base)),
2089
+ storeOpts.isIndex
2090
+ );
2091
+ return ret;
2092
+ }
2093
+ var onceGateway = new KeyedResolvOnce2();
2094
+ async function getGatewayFromURL(url, sthis) {
2095
+ return onceGateway.get(url.toString()).once(async () => {
2096
+ const item = storeFactory.get(url.protocol);
2097
+ if (item) {
2098
+ const ret = {
2099
+ gateway: await item.gateway(sthis),
2100
+ test: await item.test(sthis)
2101
+ };
2102
+ const res = await ret.gateway.start(url);
2103
+ if (res.isErr()) {
2104
+ sthis.logger.Error().Result("start", res).Msg("start failed");
2105
+ return void 0;
2106
+ }
2107
+ return ret;
2108
+ }
2109
+ sthis.logger.Warn().Url(url).Msg("unsupported protocol");
2110
+ return void 0;
2111
+ });
2112
+ }
2113
+ function registerStoreProtocol(item) {
2114
+ let protocol = item.protocol;
2115
+ if (!protocol.endsWith(":")) {
2116
+ protocol += ":";
2117
+ }
2118
+ if (storeFactory.has(protocol)) {
2119
+ if (!item.overrideBaseURL && storeFactory.get(protocol) !== item) {
2120
+ throw new Error(`we need a logger here`);
2121
+ return () => {
2122
+ };
2123
+ }
2124
+ }
2125
+ if (item.overrideBaseURL) {
2126
+ Array.from(storeFactory.values()).forEach((items) => {
2127
+ items.overrideBaseURL = void 0;
2128
+ });
2129
+ }
2130
+ storeFactory.set(protocol, item);
2131
+ return () => {
2132
+ storeFactory.delete(protocol);
2133
+ };
2134
+ }
2135
+ var onceDataStoreFactory = new KeyedResolvOnce2();
2136
+ async function dataStoreFactory(loader) {
2137
+ const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.data, loader)).build().setParam("store", "data").URI();
2138
+ const sthis = ensureSuperLog(loader.sthis, "dataStoreFactory", { url: url.toString() });
2139
+ return onceDataStoreFactory.get(url.toString()).once(async () => {
2140
+ const gateway = await getGatewayFromURL(url, sthis);
2141
+ if (!gateway) {
2142
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2143
+ }
2144
+ const store = new DataStoreImpl(sthis, loader.name, url, {
2145
+ gateway: gateway.gateway,
2146
+ keybag: () => getKeyBag(loader.sthis, {
2147
+ ...loader.ebOpts.keyBag
2148
+ })
2149
+ });
2150
+ return store;
2151
+ });
2152
+ }
2153
+ var onceMetaStoreFactory = new KeyedResolvOnce2();
2154
+ async function metaStoreFactory(loader) {
2155
+ const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.meta, loader)).build().setParam("store", "meta").URI();
2156
+ const sthis = ensureSuperLog(loader.sthis, "metaStoreFactory", { url: () => url.toString() });
2157
+ return onceMetaStoreFactory.get(url.toString()).once(async () => {
2158
+ sthis.logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
2159
+ const gateway = await getGatewayFromURL(url, sthis);
2160
+ if (!gateway) {
2161
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2162
+ }
2163
+ const store = new MetaStoreImpl(loader.sthis, loader.name, url, {
2164
+ gateway: gateway.gateway,
2165
+ keybag: () => getKeyBag(loader.sthis, {
2166
+ ...loader.ebOpts.keyBag
2167
+ })
2168
+ });
2169
+ return store;
2170
+ });
2171
+ }
2172
+ var onceRemoteWalFactory = new KeyedResolvOnce2();
2173
+ async function remoteWalFactory(loader) {
2174
+ const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.wal, loader)).build().setParam("store", "wal").URI();
2175
+ const sthis = ensureSuperLog(loader.sthis, "remoteWalFactory", { url: url.toString() });
2176
+ return onceRemoteWalFactory.get(url.toString()).once(async () => {
2177
+ const gateway = await getGatewayFromURL(url, sthis);
2178
+ if (!gateway) {
2179
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2180
+ }
2181
+ sthis.logger.Debug().Str("prepared", url.toString()).Msg("produced");
2182
+ const store = new WALStoreImpl(loader, url, {
2183
+ gateway: gateway.gateway,
2184
+ keybag: () => getKeyBag(loader.sthis, {
2185
+ ...loader.ebOpts.keyBag
2186
+ })
2187
+ });
2188
+ return store;
2189
+ });
2190
+ }
2191
+ async function testStoreFactory(url, sthis) {
2192
+ sthis = ensureSuperLog(sthis, "testStoreFactory");
2193
+ const gateway = await getGatewayFromURL(url, sthis);
2194
+ if (!gateway) {
2195
+ throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
2196
+ }
2197
+ return gateway.test;
2198
+ }
2199
+ async function ensureStart(store, logger) {
2200
+ const ret = await store.start();
2201
+ if (ret.isErr()) {
2202
+ throw logger.Error().Result("start", ret).Msg("start failed").AsError();
2203
+ }
2204
+ logger.Debug().Url(ret.Ok(), "prepared").Msg("produced");
2205
+ return store;
2206
+ }
2207
+ function toStoreRuntime(opts, sthis) {
2208
+ const logger = ensureLogger(sthis, "toStoreRuntime", {});
2209
+ return {
2210
+ makeMetaStore: async (loader) => {
2211
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
2212
+ return ensureStart(await (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader), logger);
2213
+ },
2214
+ makeDataStore: async (loader) => {
2215
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
2216
+ return ensureStart(await (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader), logger);
2217
+ },
2218
+ makeWALStore: async (loader) => {
2219
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeWALStore).Msg("makeRemoteWAL");
2220
+ return ensureStart(await (loader.ebOpts.store.makeWALStore || remoteWalFactory)(loader), logger);
2221
+ },
2222
+ encodeFile: opts.encodeFile || encodeFile,
2223
+ decodeFile: opts.decodeFile || decodeFile
2224
+ };
2225
+ }
2226
+ registerStoreProtocol({
2227
+ protocol: "file:",
2228
+ gateway: async (sthis) => {
2229
+ const { FileGateway } = await import("./bundle-not-impl-UH74NK5L.js");
2230
+ return new FileGateway(sthis);
2231
+ },
2232
+ test: async (sthis) => {
2233
+ const { FileTestStore } = await import("./bundle-not-impl-UH74NK5L.js");
2234
+ return new FileTestStore(sthis);
2235
+ }
2236
+ });
2237
+ registerStoreProtocol({
2238
+ protocol: "indexdb:",
2239
+ gateway: async (sthis) => {
2240
+ const { IndexDBGateway } = await import("./gateway@skip-esm-GI5PRACF.js");
2241
+ return new IndexDBGateway(sthis);
2242
+ },
2243
+ test: async (sthis) => {
2244
+ const { IndexDBTestStore } = await import("./gateway@skip-esm-GI5PRACF.js");
2245
+ return new IndexDBTestStore(sthis);
2246
+ }
2247
+ });
2248
+
2249
+ // src/blockstore/store-remote.ts
2250
+ async function RemoteDataStore(sthis, name, url, opts) {
2251
+ const ds = new DataStoreImpl(sthis, name, url, opts);
2252
+ await ds.start();
2253
+ return ds;
2254
+ }
2255
+ async function RemoteMetaStore(sthis, name, url, opts) {
2256
+ const ms = new MetaStoreImpl(
2257
+ sthis,
2258
+ name,
2259
+ url,
2260
+ opts
2261
+ /* , true*/
2262
+ );
2263
+ await ms.start();
2264
+ return ms;
2265
+ }
2266
+
2267
+ // src/blockstore/connection-base.ts
2268
+ var ConnectionBase = class {
2269
+ constructor(url, logger) {
2270
+ this.loaded = Promise.resolve();
2271
+ this.logger = logger;
2272
+ this.url = url;
2273
+ }
2274
+ async refresh() {
2275
+ await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load();
2276
+ await (await throwFalsy(this.loader).WALStore()).process();
2277
+ }
2278
+ async connect_X({ loader }) {
2279
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
2280
+ await this.connectMeta_X({ loader });
2281
+ await this.connectStorage_X({ loader });
2282
+ }
2283
+ async connectMeta_X({ loader }) {
2284
+ if (!loader) throw this.logger.Error().Msg("connectMeta_X: loader is required").AsError();
2285
+ this.loader = loader;
2286
+ await this.onConnect();
2287
+ const metaUrl = this.url.build().defParam("store", "meta").URI();
2288
+ const gateway = await getGatewayFromURL(metaUrl, this.loader.sthis);
2289
+ if (!gateway) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: gateway is required").AsError();
2290
+ const dbName = metaUrl.getParam("name");
2291
+ if (!dbName) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: name is required").AsError();
2292
+ const remote = await RemoteMetaStore(loader.sthis, dbName, metaUrl, {
2293
+ gateway: gateway.gateway,
2294
+ keybag: () => getKeyBag(loader.sthis, loader.ebOpts.keyBag),
2295
+ loader
2296
+ });
2297
+ this.loader.remoteMetaStore = remote;
2298
+ this.loaded = this.loader.ready().then(async () => {
2299
+ remote.load().then(async () => {
2300
+ (await throwFalsy(this.loader).WALStore()).process();
2301
+ });
2302
+ });
2303
+ }
2304
+ async connectStorage_X({ loader }) {
2305
+ if (!loader) throw this.logger.Error().Msg("connectStorage_X: loader is required").AsError();
2306
+ this.loader = loader;
2307
+ const dataUrl = this.url.build().defParam("store", "data").URI();
2308
+ const gateway = await getGatewayFromURL(dataUrl, this.loader.sthis);
2309
+ if (!gateway) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: gateway is required").AsError();
2310
+ const name = dataUrl.getParam("name");
2311
+ if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: name is required").AsError;
2312
+ loader.remoteCarStore = await RemoteDataStore(loader.sthis, name, this.url, {
2313
+ gateway: gateway.gateway,
2314
+ keybag: () => getKeyBag(loader.sthis, this.loader?.ebOpts.keyBag)
2315
+ });
2316
+ loader.remoteFileStore = loader.remoteCarStore;
2317
+ }
2318
+ // move this stuff to connect
2319
+ // async getDashboardURL(compact = true) {
2320
+ // const baseUrl = 'https://dashboard.fireproof.storage/'
2321
+ // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
2322
+ // // if (compact) {
2323
+ // // await this.compact()
2324
+ // // }
2325
+ // const currents = await this.loader?.metaStore?.load()
2326
+ // if (!currents) throw new Error("Can't sync empty database: save data first")
2327
+ // if (currents.length > 1)
2328
+ // throw new Error("Can't sync database with split heads: make an update first")
2329
+ // const current = currents[0]
2330
+ // const params = {
2331
+ // car: current.car.toString()
2332
+ // }
2333
+ // if (current.key) {
2334
+ // // @ts-ignore
2335
+ // params.key = current.key.toString()
2336
+ // }
2337
+ // // @ts-ignore
2338
+ // if (this.name) {
2339
+ // // @ts-ignore
2340
+ // params.name = this.name
2341
+ // }
2342
+ // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
2343
+ // console.log('Import to dashboard: ' + url.toString())
2344
+ // return url
2345
+ // }
2346
+ // openDashboard() {
2347
+ // void this.getDashboardURL().then(url => {
2348
+ // if (url) window.open(url.toString(), '_blank')
2349
+ // })
2350
+ // }
2351
+ };
2352
+
2353
+ // src/crdt-helpers.ts
2354
+ function time(tag) {
2355
+ }
2356
+ function timeEnd(tag) {
2357
+ }
2358
+ function toString(key, logger) {
2359
+ switch (typeof key) {
2360
+ case "string":
2361
+ case "number":
2362
+ return key.toString();
2363
+ default:
2364
+ throw logger.Error().Msg("Invalid key type").AsError();
2365
+ }
2366
+ }
2367
+ async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
2368
+ let result = null;
2369
+ if (updates.length > 1) {
2370
+ const batch = await Batch.create(tblocks, head);
2371
+ for (const update of updates) {
2372
+ const link = await writeDocContent(store, tblocks, update, logger);
2373
+ await batch.put(toString(update.id, logger), link);
2374
+ }
2375
+ result = await batch.commit();
2376
+ } else if (updates.length === 1) {
2377
+ const link = await writeDocContent(store, tblocks, updates[0], logger);
2378
+ result = await put(tblocks, head, toString(updates[0].id, logger), link);
2379
+ }
2380
+ if (!result) throw logger.Error().Uint64("updates.len", updates.length).Msg("Missing result").AsError();
2381
+ if (result.event) {
2382
+ for (const { cid, bytes } of [
2383
+ ...result.additions,
2384
+ // ...result.removals,
2385
+ result.event
2386
+ ]) {
2387
+ tblocks.putSync(cid, bytes);
2388
+ }
2389
+ }
2390
+ return { head: result.head };
2391
+ }
2392
+ async function writeDocContent(store, blocks, update, logger) {
2393
+ let value;
2394
+ if (update.del) {
2395
+ value = { del: true };
2396
+ } else {
2397
+ if (!update.value) throw logger.Error().Msg("Missing value").AsError();
2398
+ await processFiles(store, blocks, update.value, logger);
2399
+ value = { doc: update.value };
2400
+ }
2401
+ const block = await encode({ value, hasher: hasher5, codec });
2402
+ blocks.putSync(block.cid, block.bytes);
2403
+ return block.cid;
2404
+ }
2405
+ async function processFiles(store, blocks, doc, logger) {
2406
+ if (doc._files) {
2407
+ await processFileset(logger, store, blocks, doc._files);
2408
+ }
2409
+ if (doc._publicFiles) {
2410
+ await processFileset(
2411
+ logger,
2412
+ store,
2413
+ blocks,
2414
+ doc._publicFiles
2415
+ /*, true*/
2416
+ );
2417
+ }
2418
+ }
2419
+ async function processFileset(logger, store, blocks, files) {
2420
+ const dbBlockstore = blocks.parent;
2421
+ if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, database name is required").AsError();
2422
+ const t = new CarTransaction(dbBlockstore);
2423
+ const didPut = [];
2424
+ for (const filename in files) {
2425
+ if (File === files[filename].constructor) {
2426
+ const file = files[filename];
2427
+ const { cid, blocks: fileBlocks } = await store.encodeFile(file);
2428
+ didPut.push(filename);
2429
+ for (const block of fileBlocks) {
2430
+ t.putSync(block.cid, block.bytes);
2431
+ }
2432
+ files[filename] = { cid, type: file.type, size: file.size };
2433
+ } else {
2434
+ const { cid, type, size, car } = files[filename];
2435
+ if (cid && type && size && car) {
2436
+ files[filename] = { cid, type, size, car };
2437
+ }
2438
+ }
2439
+ }
2440
+ if (didPut.length) {
2441
+ const car = await dbBlockstore.loader.commitFiles(
2442
+ t,
2443
+ { files }
2444
+ );
2445
+ if (car) {
2446
+ for (const name of didPut) {
2447
+ files[name] = { car, ...files[name] };
2448
+ }
2449
+ }
2450
+ }
2451
+ }
2452
+ async function getValueFromCrdt(blocks, head, key, logger) {
2453
+ if (!head.length) throw logger.Debug().Msg("Getting from an empty database").AsError();
2454
+ const link = await get(blocks, head, key);
2455
+ if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
2456
+ return await getValueFromLink(blocks, link, logger);
2457
+ }
2458
+ function readFiles(blocks, { doc }) {
2459
+ if (!doc) return;
2460
+ if (doc._files) {
2461
+ readFileset(blocks, doc._files);
2462
+ }
2463
+ if (doc._publicFiles) {
2464
+ readFileset(blocks, doc._publicFiles, true);
2465
+ }
2466
+ }
2467
+ function readFileset(blocks, files, isPublic = false) {
2468
+ for (const filename in files) {
2469
+ const fileMeta = files[filename];
2470
+ if (fileMeta.cid) {
2471
+ if (isPublic) {
2472
+ fileMeta.url = `https://${fileMeta.cid.toString()}.ipfs.w3s.link/`;
2473
+ }
2474
+ if (fileMeta.car) {
2475
+ fileMeta.file = async () => await blocks.ebOpts.storeRuntime.decodeFile(
2476
+ {
2477
+ get: async (cid) => {
2478
+ return await blocks.getFile(throwFalsy(fileMeta.car), cid);
2479
+ }
2480
+ },
2481
+ fileMeta.cid,
2482
+ fileMeta
2483
+ );
2484
+ }
2485
+ }
2486
+ files[filename] = fileMeta;
2487
+ }
2488
+ }
2489
+ async function getValueFromLink(blocks, link, logger) {
2490
+ const block = await blocks.get(link);
2491
+ if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
2492
+ const { value } = await decode({ bytes: block.bytes, hasher: hasher5, codec });
2493
+ const cvalue = {
2494
+ ...value,
2495
+ cid: link
2496
+ };
2497
+ readFiles(blocks, cvalue);
2498
+ return cvalue;
2499
+ }
2500
+ var DirtyEventFetcher = class extends EventFetcher {
2501
+ constructor(logger, blocks) {
2502
+ super(blocks);
2503
+ this.logger = logger;
2504
+ }
2505
+ async get(link) {
2506
+ try {
2507
+ return super.get(link);
2508
+ } catch (e) {
2509
+ this.logger.Error().Ref("link", link.toString()).Err(e).Msg("Missing event");
2510
+ return { value: void 0 };
2511
+ }
2512
+ }
2513
+ };
2514
+ async function clockChangesSince(blocks, head, since, opts, logger) {
2515
+ const eventsFetcher = opts.dirty ? new DirtyEventFetcher(logger, blocks) : new EventFetcher(blocks);
2516
+ const keys = /* @__PURE__ */ new Set();
2517
+ const updates = await gatherUpdates(
2518
+ blocks,
2519
+ eventsFetcher,
2520
+ head,
2521
+ since,
2522
+ [],
2523
+ keys,
2524
+ /* @__PURE__ */ new Set(),
2525
+ opts.limit || Infinity,
2526
+ logger
2527
+ );
2528
+ return { result: updates.reverse(), head };
2529
+ }
2530
+ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], keys, didLinks, limit, logger) {
2531
+ if (limit <= 0) return updates;
2532
+ const sHead = head.map((l) => l.toString());
2533
+ for (const link of since) {
2534
+ if (sHead.includes(link.toString())) {
2535
+ return updates;
2536
+ }
2537
+ }
2538
+ for (const link of head) {
2539
+ if (didLinks.has(link.toString())) continue;
2540
+ didLinks.add(link.toString());
2541
+ const { value: event } = await eventsFetcher.get(link);
2542
+ if (!event) continue;
2543
+ const { type } = event.data;
2544
+ let ops = [];
2545
+ if (type === "batch") {
2546
+ ops = event.data.ops;
2547
+ } else if (type === "put") {
2548
+ ops = [event.data];
2549
+ }
2550
+ for (let i = ops.length - 1; i >= 0; i--) {
2551
+ const { key, value } = ops[i];
2552
+ if (!keys.has(key)) {
2553
+ const docValue = await getValueFromLink(blocks, value, logger);
2554
+ updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
2555
+ limit--;
2556
+ keys.add(key);
2557
+ }
2558
+ }
2559
+ if (event.parents) {
2560
+ updates = await gatherUpdates(blocks, eventsFetcher, event.parents, since, updates, keys, didLinks, limit, logger);
2561
+ }
2562
+ }
2563
+ return updates;
2564
+ }
2565
+ async function* getAllEntries(blocks, head, logger) {
2566
+ for await (const [key, link] of entries(blocks, head)) {
2567
+ const docValue = await getValueFromLink(blocks, link, logger);
2568
+ yield { id: key, value: docValue.doc, del: docValue.del };
2569
+ }
2570
+ }
2571
+ async function* clockVis(blocks, head) {
2572
+ for await (const line of vis(blocks, head)) {
2573
+ yield line;
2574
+ }
2575
+ }
2576
+ var isCompacting = false;
2577
+ async function doCompact(blockLog, head, logger) {
2578
+ if (isCompacting) {
2579
+ return;
2580
+ }
2581
+ isCompacting = true;
2582
+ time("compact head");
2583
+ for (const cid of head) {
2584
+ const bl = await blockLog.get(cid);
2585
+ if (!bl) throw logger.Error().Ref("cid", cid).Msg("Missing head block").AsError();
2586
+ }
2587
+ timeEnd("compact head");
2588
+ time("compact all entries");
2589
+ for await (const _entry of getAllEntries(blockLog, head, logger)) {
2590
+ continue;
2591
+ }
2592
+ timeEnd("compact all entries");
2593
+ time("compact clock vis");
2594
+ for await (const _line of vis(blockLog, head)) {
2595
+ }
2596
+ timeEnd("compact clock vis");
2597
+ time("compact root");
2598
+ const result = await root(blockLog, head);
2599
+ timeEnd("compact root");
2600
+ time("compact root blocks");
2601
+ for (const { cid, bytes } of [...result.additions, ...result.removals]) {
2602
+ blockLog.loggedBlocks.putSync(cid, bytes);
2603
+ }
2604
+ timeEnd("compact root blocks");
2605
+ time("compact changes");
2606
+ await clockChangesSince(blockLog, head, [], {}, logger);
2607
+ timeEnd("compact changes");
2608
+ isCompacting = false;
2609
+ }
2610
+ async function getBlock(blocks, cidString) {
2611
+ const block = await blocks.get(parse3(cidString));
2612
+ if (!block) throw new Error(`Missing block ${cidString}`);
2613
+ const { cid, value } = await decode({ bytes: block.bytes, codec, hasher: hasher5 });
2614
+ return new Block({ cid, value, bytes: block.bytes });
2615
+ }
2616
+
2617
+ // src/indexer-helpers.ts
2618
+ import { sha256 as hasher6 } from "multiformats/hashes/sha2";
2619
+ import * as codec2 from "@ipld/dag-cbor";
2620
+ import charwise from "charwise";
2621
+ import * as DbIndex from "prolly-trees/db-index";
2622
+ import { bf, simpleCompare } from "prolly-trees/utils";
2623
+ import { nocache as cache } from "prolly-trees/cache";
2624
+ var IndexTree = class {
2625
+ };
2626
+ function refCompare(aRef, bRef) {
2627
+ if (Number.isNaN(aRef)) return -1;
2628
+ if (Number.isNaN(bRef)) throw new Error("ref may not be Infinity or NaN");
2629
+ if (aRef === Infinity) return 1;
2630
+ return simpleCompare(aRef, bRef);
2631
+ }
2632
+ function compare(a, b) {
2633
+ const [aKey, aRef] = a;
2634
+ const [bKey, bRef] = b;
2635
+ const comp = simpleCompare(aKey, bKey);
2636
+ if (comp !== 0) return comp;
2637
+ return refCompare(aRef, bRef);
2638
+ }
2639
+ var byKeyOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare };
2640
+ var byIdOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare: simpleCompare };
2641
+ function indexEntriesForChanges(changes, mapFn) {
2642
+ const indexEntries = [];
2643
+ changes.forEach(({ id: key, value, del }) => {
2644
+ if (del || !value) return;
2645
+ let mapCalled = false;
2646
+ const mapReturn = mapFn({ ...value, _id: key }, (k, v) => {
2647
+ mapCalled = true;
2648
+ if (typeof k === "undefined") return;
2649
+ indexEntries.push({
2650
+ key: [charwise.encode(k), key],
2651
+ value: v || null
2652
+ });
2653
+ });
2654
+ if (!mapCalled && mapReturn) {
2655
+ indexEntries.push({
2656
+ key: [charwise.encode(mapReturn), key],
2657
+ value: null
2658
+ });
2659
+ }
2660
+ });
2661
+ return indexEntries;
2662
+ }
2663
+ function makeProllyGetBlock(blocks) {
2664
+ return async (address) => {
2665
+ const block = await blocks.get(address);
2666
+ if (!block) throw new Error(`Missing block ${address.toString()}`);
2667
+ const { cid, bytes } = block;
2668
+ return create({ cid, bytes, hasher: hasher6, codec: codec2 });
2669
+ };
2670
+ }
2671
+ async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
2672
+ if (!indexEntries.length) return inIndex;
2673
+ if (!inIndex.root) {
2674
+ if (!inIndex.cid) {
2675
+ let returnRootBlock = void 0;
2676
+ let returnNode = void 0;
2677
+ for await (const node of await DbIndex.create({
2678
+ get: makeProllyGetBlock(tblocks),
2679
+ list: indexEntries,
2680
+ ...opts
2681
+ })) {
2682
+ const block = await node.block;
2683
+ await tblocks.put(block.cid, block.bytes);
2684
+ returnRootBlock = block;
2685
+ returnNode = node;
2686
+ }
2687
+ if (!returnNode || !returnRootBlock) throw new Error("failed to create index");
2688
+ return { root: returnNode, cid: returnRootBlock.cid };
2689
+ } else {
2690
+ inIndex.root = await DbIndex.load({ cid: inIndex.cid, get: makeProllyGetBlock(tblocks), ...opts });
2691
+ }
2692
+ }
2693
+ const { root: root3, blocks: newBlocks } = await inIndex.root.bulk(indexEntries);
2694
+ if (root3) {
2695
+ for await (const block of newBlocks) {
2696
+ await tblocks.put(block.cid, block.bytes);
2697
+ }
2698
+ return { root: root3, cid: (await root3.block).cid };
2699
+ } else {
2700
+ return { root: void 0, cid: void 0 };
2701
+ }
2702
+ }
2703
+ async function loadIndex(tblocks, cid, opts) {
2704
+ return await DbIndex.load({ cid, get: makeProllyGetBlock(tblocks), ...opts });
2705
+ }
2706
+ async function applyQuery(crdt, resp, query) {
2707
+ if (query.descending) {
2708
+ resp.result = resp.result.reverse();
2709
+ }
2710
+ if (query.limit) {
2711
+ resp.result = resp.result.slice(0, query.limit);
2712
+ }
2713
+ if (query.includeDocs) {
2714
+ resp.result = await Promise.all(
2715
+ resp.result.map(async (row) => {
2716
+ const val = await crdt.get(row.id);
2717
+ const doc = val ? { ...val.doc, _id: row.id } : void 0;
2718
+ return { ...row, doc };
2719
+ })
2720
+ );
2721
+ }
2722
+ return {
2723
+ rows: resp.result.map(({ key, ...row }) => {
2724
+ return {
2725
+ key: charwise.decode(key),
2726
+ ...row
2727
+ };
2728
+ })
2729
+ };
2730
+ }
2731
+ function encodeRange(range) {
2732
+ return [charwise.encode(range[0]), charwise.encode(range[1])];
2733
+ }
2734
+ function encodeKey(key) {
2735
+ return charwise.encode(key);
2736
+ }
2737
+
2738
+ // src/indexer.ts
2739
+ function index(sthis, { _crdt }, name, mapFn, meta) {
2740
+ if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
2741
+ if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
2742
+ if (_crdt.indexers.has(name)) {
2743
+ const idx = _crdt.indexers.get(name);
2744
+ idx.applyMapFn(name, mapFn, meta);
2745
+ } else {
2746
+ const idx = new Index(sthis, _crdt, name, mapFn, meta);
2747
+ _crdt.indexers.set(name, idx);
2748
+ }
2749
+ return _crdt.indexers.get(name);
2750
+ }
2751
+ var Index = class {
2752
+ constructor(sthis, crdt, name, mapFn, meta) {
2753
+ this.mapFnString = "";
2754
+ this.byKey = new IndexTree();
2755
+ this.byId = new IndexTree();
2756
+ this.includeDocsDefault = false;
2757
+ this.logger = ensureLogger(sthis, "Index");
2758
+ this.blockstore = crdt.indexBlockstore;
2759
+ this.crdt = crdt;
2760
+ this.applyMapFn(name, mapFn, meta);
2761
+ this.name = name;
2762
+ if (!(this.mapFnString || this.initError)) throw this.logger.Error().Msg("missing mapFnString").AsError();
2763
+ }
2764
+ ready() {
2765
+ return Promise.all([this.blockstore.ready(), this.crdt.ready()]).then(() => {
2766
+ });
2767
+ }
2768
+ close() {
2769
+ return Promise.all([this.blockstore.close(), this.crdt.close()]).then(() => {
2770
+ });
2771
+ }
2772
+ destroy() {
2773
+ return Promise.all([this.blockstore.destroy(), this.crdt.destroy()]).then(() => {
2774
+ });
2775
+ }
2776
+ applyMapFn(name, mapFn, meta) {
2777
+ if (mapFn && meta) throw this.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
2778
+ if (this.name && this.name !== name) throw this.logger.Error().Msg("cannot change name").AsError();
2779
+ this.name = name;
2780
+ try {
2781
+ if (meta) {
2782
+ if (this.indexHead && this.indexHead.map((c) => c.toString()).join() !== meta.head.map((c) => c.toString()).join()) {
2783
+ throw this.logger.Error().Msg("cannot apply different head meta").AsError();
2784
+ }
2785
+ if (this.mapFnString) {
2786
+ if (this.mapFnString !== meta.map) {
2787
+ this.logger.Warn().Msg(`cannot apply different mapFn meta: old mapFnString ${this.mapFnString} new mapFnString ${meta.map}`);
2788
+ } else {
2789
+ this.byId.cid = meta.byId;
2790
+ this.byKey.cid = meta.byKey;
2791
+ this.indexHead = meta.head;
2792
+ }
2793
+ } else {
2794
+ this.mapFnString = meta.map;
2795
+ this.byId.cid = meta.byId;
2796
+ this.byKey.cid = meta.byKey;
2797
+ this.indexHead = meta.head;
2798
+ }
2799
+ } else {
2800
+ if (this.mapFn) {
2801
+ if (mapFn) {
2802
+ if (this.mapFn.toString() !== mapFn.toString()) {
2803
+ throw this.logger.Error().Msg("cannot apply different mapFn app2").AsError();
2804
+ }
2805
+ }
2806
+ } else {
2807
+ if (!mapFn) {
2808
+ mapFn = (doc) => doc[name] ?? void 0;
2809
+ }
2810
+ if (this.mapFnString) {
2811
+ if (this.mapFnString !== mapFn.toString()) {
2812
+ throw this.logger.Error().Str("mapFnString", this.mapFnString).Str("mapFn", mapFn.toString()).Msg("cannot apply different mapFn app").AsError();
2813
+ }
2814
+ } else {
2815
+ this.mapFnString = mapFn.toString();
2816
+ }
2817
+ this.mapFn = mapFn;
2818
+ }
2819
+ }
2820
+ const matches = /=>\s*(.*)/.test(this.mapFnString);
2821
+ this.includeDocsDefault = matches;
2822
+ } catch (e) {
2823
+ this.initError = e;
2824
+ }
2825
+ }
2826
+ async query(opts = {}) {
2827
+ await this.ready();
2828
+ await this._updateIndex();
2829
+ await this._hydrateIndex();
2830
+ if (!this.byKey.root) {
2831
+ return await applyQuery(this.crdt, { result: [] }, opts);
2832
+ }
2833
+ if (this.includeDocsDefault && opts.includeDocs === void 0) opts.includeDocs = true;
2834
+ if (opts.range) {
2835
+ const eRange = encodeRange(opts.range);
2836
+ return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).range(eRange[0], eRange[1]), opts);
2837
+ }
2838
+ if (opts.key) {
2839
+ const encodedKey = encodeKey(opts.key);
2840
+ return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts);
2841
+ }
2842
+ if (Array.isArray(opts.keys)) {
2843
+ const results = await Promise.all(
2844
+ opts.keys.map(async (key) => {
2845
+ const encodedKey = encodeKey(key);
2846
+ return (await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts)).rows;
2847
+ })
2848
+ );
2849
+ return { rows: results.flat() };
2850
+ }
2851
+ if (opts.prefix) {
2852
+ if (!Array.isArray(opts.prefix)) opts.prefix = [opts.prefix];
2853
+ const start = [...opts.prefix, NaN];
2854
+ const end = [...opts.prefix, Infinity];
2855
+ const encodedR = encodeRange([start, end]);
2856
+ return await applyQuery(this.crdt, await this.byKey.root.range(...encodedR), opts);
2857
+ }
2858
+ const all = await this.byKey.root.getAllEntries();
2859
+ return await applyQuery(
2860
+ this.crdt,
2861
+ {
2862
+ // @ts-expect-error getAllEntries returns a different type than range
2863
+ result: all.result.map(({ key: [k, id], value }) => ({
2864
+ key: k,
2865
+ id,
2866
+ value
2867
+ }))
2868
+ },
2869
+ opts
2870
+ );
2871
+ }
2872
+ _resetIndex() {
2873
+ this.byId = new IndexTree();
2874
+ this.byKey = new IndexTree();
2875
+ this.indexHead = void 0;
2876
+ }
2877
+ async _hydrateIndex() {
2878
+ if (this.byId.root && this.byKey.root) return;
2879
+ if (!this.byId.cid || !this.byKey.cid) return;
2880
+ this.byId.root = await loadIndex(this.blockstore, this.byId.cid, byIdOpts);
2881
+ this.byKey.root = await loadIndex(this.blockstore, this.byKey.cid, byKeyOpts);
2882
+ }
2883
+ async _updateIndex() {
2884
+ await this.ready();
2885
+ if (this.initError) throw this.initError;
2886
+ if (!this.mapFn) throw this.logger.Error().Msg("No map function defined").AsError();
2887
+ let result, head;
2888
+ if (!this.indexHead || this.indexHead.length === 0) {
2889
+ ({ result, head } = await this.crdt.allDocs());
2890
+ } else {
2891
+ ({ result, head } = await this.crdt.changes(this.indexHead));
2892
+ }
2893
+ if (result.length === 0) {
2894
+ this.indexHead = head;
2895
+ }
2896
+ let staleKeyIndexEntries = [];
2897
+ let removeIdIndexEntries = [];
2898
+ if (this.byId.root) {
2899
+ const removeIds = result.map(({ id: key }) => key);
2900
+ const { result: oldChangeEntries } = await this.byId.root.getMany(removeIds);
2901
+ staleKeyIndexEntries = oldChangeEntries.map((key) => ({ key, del: true }));
2902
+ removeIdIndexEntries = oldChangeEntries.map((key) => ({ key: key[1], del: true }));
2903
+ }
2904
+ const indexEntries = indexEntriesForChanges(result, this.mapFn);
2905
+ const byIdIndexEntries = indexEntries.map(({ key }) => ({
2906
+ key: key[1],
2907
+ value: key
2908
+ }));
2909
+ const indexerMeta = { indexes: /* @__PURE__ */ new Map() };
2910
+ for (const [name, indexer] of this.crdt.indexers) {
2911
+ if (indexer.indexHead) {
2912
+ indexerMeta.indexes?.set(name, {
2913
+ byId: indexer.byId.cid,
2914
+ byKey: indexer.byKey.cid,
2915
+ head: indexer.indexHead,
2916
+ map: indexer.mapFnString,
2917
+ name: indexer.name
2918
+ });
2919
+ }
2920
+ }
2921
+ if (result.length === 0) {
2922
+ return indexerMeta;
2923
+ }
2924
+ const { meta } = await this.blockstore.transaction(async (tblocks) => {
2925
+ this.byId = await bulkIndex(tblocks, this.byId, removeIdIndexEntries.concat(byIdIndexEntries), byIdOpts);
2926
+ this.byKey = await bulkIndex(tblocks, this.byKey, staleKeyIndexEntries.concat(indexEntries), byKeyOpts);
2927
+ this.indexHead = head;
2928
+ if (this.byId.cid && this.byKey.cid) {
2929
+ const idxMeta = {
2930
+ byId: this.byId.cid,
2931
+ byKey: this.byKey.cid,
2932
+ head,
2933
+ map: this.mapFnString,
2934
+ name: this.name
2935
+ };
2936
+ indexerMeta.indexes?.set(this.name, idxMeta);
2937
+ }
2938
+ return indexerMeta;
2939
+ });
2940
+ return meta;
2941
+ }
2942
+ };
2943
+
2944
+ // src/crdt-clock.ts
2945
+ import { advance } from "@web3-storage/pail/clock";
2946
+ import { root as root2 } from "@web3-storage/pail/crdt";
2947
+ import { ResolveOnce as ResolveOnce4 } from "@adviser/cement";
2948
+
2949
+ // src/apply-head-queue.ts
2950
+ function applyHeadQueue(worker, logger) {
2951
+ const queue = [];
2952
+ let isProcessing = false;
2953
+ async function* process() {
2954
+ if (isProcessing || queue.length === 0) return;
2955
+ isProcessing = true;
2956
+ const allUpdates = [];
2957
+ try {
2958
+ while (queue.length > 0) {
2959
+ queue.sort((a, b) => b.updates ? 1 : -1);
2960
+ const task = queue.shift();
2961
+ if (!task) continue;
2962
+ await worker(task.newHead, task.prevHead, task.updates !== void 0).catch((e) => {
2963
+ throw logger.Error().Err(e).Msg("int_applyHead worker error").AsError();
2964
+ });
2965
+ if (task.updates) {
2966
+ allUpdates.push(...task.updates);
2967
+ }
2968
+ if (!queue.some((t) => t.updates) || task.updates) {
2969
+ const allTasksHaveUpdates = queue.every((task2) => task2.updates !== null);
2970
+ yield { updates: allUpdates, all: allTasksHaveUpdates };
2971
+ allUpdates.length = 0;
2972
+ }
2973
+ }
2974
+ } finally {
2975
+ isProcessing = false;
2976
+ const generator = process();
2977
+ let result = await generator.next();
2978
+ while (!result.done) {
2979
+ result = await generator.next();
2980
+ }
2981
+ }
2982
+ }
2983
+ return {
2984
+ push(task) {
2985
+ queue.push(task);
2986
+ return process();
2987
+ },
2988
+ size() {
2989
+ return queue.length;
2990
+ }
2991
+ };
2992
+ }
2993
+
2994
+ // src/crdt-clock.ts
2995
+ var CRDTClock = class {
2996
+ constructor(blockstore) {
2997
+ // todo: track local and remote clocks independently, merge on read
2998
+ // that way we can drop the whole remote if we need to
2999
+ // should go with making sure the local clock only references locally available blockstore on write
3000
+ this.head = [];
3001
+ this.zoomers = /* @__PURE__ */ new Set();
3002
+ this.watchers = /* @__PURE__ */ new Set();
3003
+ this.emptyWatchers = /* @__PURE__ */ new Set();
3004
+ this._ready = new ResolveOnce4();
3005
+ this.blockstore = blockstore;
3006
+ this.logger = ensureLogger(blockstore.sthis, "CRDTClock");
3007
+ this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
3008
+ }
3009
+ async ready() {
3010
+ return this._ready.once(async () => {
3011
+ await this.blockstore.ready();
3012
+ });
3013
+ }
3014
+ async close() {
3015
+ await this.blockstore.close();
3016
+ }
3017
+ setHead(head) {
3018
+ this.head = head;
3019
+ }
3020
+ async applyHead(newHead, prevHead, updates) {
3021
+ for await (const { updates: updatesAcc, all } of this.applyHeadQueue.push({
3022
+ newHead,
3023
+ prevHead,
3024
+ updates
3025
+ })) {
3026
+ return this.processUpdates(updatesAcc, all, prevHead);
3027
+ }
3028
+ }
3029
+ async processUpdates(updatesAcc, all, prevHead) {
3030
+ let internalUpdates = updatesAcc;
3031
+ if (this.watchers.size && !all) {
3032
+ const changes = await clockChangesSince(throwFalsy(this.blockstore), this.head, prevHead, {}, this.logger);
3033
+ internalUpdates = changes.result;
3034
+ }
3035
+ this.zoomers.forEach((fn) => fn());
3036
+ this.notifyWatchers(internalUpdates || []);
3037
+ }
3038
+ notifyWatchers(updates) {
3039
+ this.emptyWatchers.forEach((fn) => fn());
3040
+ this.watchers.forEach((fn) => fn(updates || []));
3041
+ }
3042
+ onTick(fn) {
3043
+ this.watchers.add(fn);
3044
+ }
3045
+ onTock(fn) {
3046
+ this.emptyWatchers.add(fn);
3047
+ }
3048
+ onZoom(fn) {
3049
+ this.zoomers.add(fn);
3050
+ }
3051
+ async int_applyHead(newHead, prevHead, localUpdates) {
3052
+ const noLoader = !localUpdates;
3053
+ const ogHead = sortClockHead(this.head);
3054
+ newHead = sortClockHead(newHead);
3055
+ if (compareClockHeads(ogHead, newHead)) {
3056
+ return;
3057
+ }
3058
+ const ogPrev = sortClockHead(prevHead);
3059
+ if (compareClockHeads(ogHead, ogPrev)) {
3060
+ this.setHead(newHead);
3061
+ return;
3062
+ }
3063
+ if (!this.blockstore) {
3064
+ throw this.logger.Error().Msg("missing blockstore").AsError();
3065
+ }
3066
+ await validateBlocks(this.logger, newHead, this.blockstore);
3067
+ if (!this.transaction) {
3068
+ this.transaction = this.blockstore.openTransaction({ noLoader, add: false });
3069
+ }
3070
+ const tblocks = this.transaction;
3071
+ const advancedHead = await advanceBlocks(this.logger, newHead, tblocks, this.head);
3072
+ const result = await root2(tblocks, advancedHead);
3073
+ for (const { cid, bytes } of [
3074
+ ...result.additions
3075
+ // ...result.removals
3076
+ ]) {
3077
+ tblocks.putSync(cid, bytes);
3078
+ }
3079
+ if (!noLoader) {
3080
+ await this.blockstore.commitTransaction(tblocks, { head: advancedHead }, { add: false, noLoader });
3081
+ this.transaction = void 0;
3082
+ }
3083
+ this.setHead(advancedHead);
3084
+ }
3085
+ };
3086
+ function sortClockHead(clockHead) {
3087
+ return clockHead.sort((a, b) => a.toString().localeCompare(b.toString()));
3088
+ }
3089
+ async function validateBlocks(logger, newHead, blockstore) {
3090
+ if (!blockstore) throw logger.Error().Msg("missing blockstore");
3091
+ newHead.map(async (cid) => {
3092
+ const got = await blockstore.get(cid);
3093
+ if (!got) {
3094
+ throw logger.Error().Str("cid", cid.toString()).Msg("int_applyHead missing block").AsError();
3095
+ }
3096
+ });
3097
+ }
3098
+ function compareClockHeads(head1, head2) {
3099
+ return head1.toString() === head2.toString();
3100
+ }
3101
+ async function advanceBlocks(logger, newHead, tblocks, head) {
3102
+ for (const cid of newHead) {
3103
+ try {
3104
+ head = await advance(tblocks, head, cid);
3105
+ } catch (e) {
3106
+ logger.Debug().Err(e).Msg("failed to advance head");
3107
+ continue;
3108
+ }
3109
+ }
3110
+ return head;
3111
+ }
3112
+
3113
+ // src/crdt.ts
3114
+ var CRDT = class {
3115
+ constructor(sthis, name, opts = {}) {
3116
+ this.indexers = /* @__PURE__ */ new Map();
3117
+ this.onceReady = new ResolveOnce5();
3118
+ this.sthis = sthis;
3119
+ this.name = name;
3120
+ this.logger = ensureLogger(sthis, "CRDT");
3121
+ this.opts = opts;
3122
+ this.blockstore = blockstoreFactory(sthis, {
3123
+ name,
3124
+ applyMeta: async (meta) => {
3125
+ const crdtMeta = meta;
3126
+ if (!crdtMeta.head) throw this.logger.Error().Msg("missing head").AsError();
3127
+ await this.clock.applyHead(crdtMeta.head, []);
3128
+ },
3129
+ compact: async (blocks) => {
3130
+ await doCompact(blocks, this.clock.head, this.logger);
3131
+ return { head: this.clock.head };
3132
+ },
3133
+ autoCompact: this.opts.autoCompact || 100,
3134
+ store: { ...this.opts.store, isIndex: void 0 },
3135
+ public: this.opts.public,
3136
+ meta: this.opts.meta,
3137
+ threshold: this.opts.threshold
3138
+ });
3139
+ this.indexBlockstore = blockstoreFactory(sthis, {
3140
+ name,
3141
+ applyMeta: async (meta) => {
3142
+ const idxCarMeta = meta;
3143
+ if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
3144
+ for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
3145
+ index(this.sthis, { _crdt: this }, name2, void 0, idx);
3146
+ }
3147
+ },
3148
+ store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
3149
+ public: this.opts.public
3150
+ });
3151
+ this.clock = new CRDTClock(this.blockstore);
3152
+ this.clock.onZoom(() => {
3153
+ for (const idx of this.indexers.values()) {
3154
+ idx._resetIndex();
3155
+ }
3156
+ });
3157
+ }
3158
+ async bulk(updates) {
3159
+ await this.ready();
3160
+ const prevHead = [...this.clock.head];
3161
+ const done = await this.blockstore.transaction(async (blocks) => {
3162
+ const { head } = await applyBulkUpdateToCrdt(
3163
+ this.blockstore.ebOpts.storeRuntime,
3164
+ blocks,
3165
+ this.clock.head,
3166
+ updates,
3167
+ this.logger
3168
+ );
3169
+ updates = updates.map((dupdate) => {
3170
+ readFiles(this.blockstore, { doc: dupdate.value });
3171
+ return dupdate;
3172
+ });
3173
+ return { head };
3174
+ });
3175
+ await this.clock.applyHead(done.meta.head, prevHead, updates);
3176
+ return done.meta;
3177
+ }
3178
+ async ready() {
3179
+ return this.onceReady.once(async () => {
3180
+ try {
3181
+ await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
3182
+ } catch (e) {
3183
+ throw this.logger.Error().Err(e).Msg(`CRDT is not ready`).AsError();
3184
+ }
3185
+ });
3186
+ }
3187
+ async close() {
3188
+ await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
3189
+ }
3190
+ async destroy() {
3191
+ await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
3192
+ }
3193
+ // if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
3194
+ async allDocs() {
3195
+ await this.ready();
3196
+ const result = [];
3197
+ for await (const entry of getAllEntries(this.blockstore, this.clock.head, this.logger)) {
3198
+ result.push(entry);
3199
+ }
3200
+ return { result, head: this.clock.head };
3201
+ }
3202
+ async vis() {
3203
+ await this.ready();
3204
+ const txt = [];
3205
+ for await (const line of clockVis(this.blockstore, this.clock.head)) {
3206
+ txt.push(line);
3207
+ }
3208
+ return txt.join("\n");
3209
+ }
3210
+ async getBlock(cidString) {
3211
+ await this.ready();
3212
+ return await getBlock(this.blockstore, cidString);
3213
+ }
3214
+ async get(key) {
3215
+ await this.ready();
3216
+ const result = await getValueFromCrdt(this.blockstore, this.clock.head, key, this.logger);
3217
+ if (result.del) return void 0;
3218
+ return result;
3219
+ }
3220
+ async changes(since = [], opts = {}) {
3221
+ await this.ready();
3222
+ return await clockChangesSince(this.blockstore, this.clock.head, since, opts, this.logger);
3223
+ }
3224
+ async compact() {
3225
+ const blocks = this.blockstore;
3226
+ return await blocks.compact();
3227
+ }
3228
+ };
3229
+
3230
+ // src/database.ts
3231
+ var Database = class {
3232
+ constructor(name, opts) {
3233
+ this.opts = {};
3234
+ this._listening = false;
3235
+ this._listeners = /* @__PURE__ */ new Set();
3236
+ this._noupdate_listeners = /* @__PURE__ */ new Set();
3237
+ this._ready = new ResolveOnce6();
3238
+ this.name = name;
3239
+ this.opts = opts || this.opts;
3240
+ this.sthis = ensureSuperThis(this.opts);
3241
+ this.logger = ensureLogger(this.sthis, "Database");
3242
+ this._crdt = new CRDT(this.sthis, name, this.opts);
3243
+ this.blockstore = this._crdt.blockstore;
3244
+ this._writeQueue = writeQueue(async (updates) => {
3245
+ return await this._crdt.bulk(updates);
3246
+ });
3247
+ this._crdt.clock.onTock(() => {
3248
+ this._no_update_notify();
3249
+ });
3250
+ }
3251
+ static {
3252
+ this.databases = /* @__PURE__ */ new Map();
3253
+ }
3254
+ async close() {
3255
+ await this.ready();
3256
+ await this._crdt.close();
3257
+ await this.blockstore.close();
3258
+ }
3259
+ async destroy() {
3260
+ await this.ready();
3261
+ await this._crdt.destroy();
3262
+ await this.blockstore.destroy();
3263
+ }
3264
+ async ready() {
3265
+ return this._ready.once(async () => {
3266
+ await this.sthis.start();
3267
+ await this._crdt.ready();
3268
+ await this.blockstore.ready();
3269
+ });
3270
+ }
3271
+ async get(id) {
3272
+ if (!id) throw this.logger.Error().Str("db", this.name).Msg(`Doc id is required`).AsError();
3273
+ await this.ready();
3274
+ this.logger.Debug().Str("id", id).Msg("get");
3275
+ const got = await this._crdt.get(id).catch((e) => {
3276
+ throw new NotFoundError(`Not found: ${id} - ${e.message}`);
3277
+ });
3278
+ if (!got) throw new NotFoundError(`Not found: ${id}`);
3279
+ const { doc } = got;
3280
+ return { ...doc, _id: id };
3281
+ }
3282
+ async put(doc) {
3283
+ await this.ready();
3284
+ this.logger.Debug().Str("id", doc._id).Msg("put");
3285
+ const { _id, ...value } = doc;
3286
+ const docId = _id || this.sthis.timeOrderedNextId().str;
3287
+ const result = await this._writeQueue.push({
3288
+ id: docId,
3289
+ value: {
3290
+ ...value,
3291
+ _id: docId
3292
+ }
3293
+ });
3294
+ return { id: docId, clock: result?.head, name: this.name };
3295
+ }
3296
+ async del(id) {
3297
+ await this.ready();
3298
+ this.logger.Debug().Str("id", id).Msg("del");
3299
+ const result = await this._writeQueue.push({ id, del: true });
3300
+ return { id, clock: result?.head, name: this.name };
3301
+ }
3302
+ async changes(since = [], opts = {}) {
3303
+ await this.ready();
3304
+ this.logger.Debug().Any("since", since).Any("opts", opts).Msg("changes");
3305
+ const { result, head } = await this._crdt.changes(since, opts);
3306
+ const rows = result.map(({ id: key, value, del, clock }) => ({
3307
+ key,
3308
+ value: del ? { _id: key, _deleted: true } : { _id: key, ...value },
3309
+ clock
3310
+ }));
3311
+ return { rows, clock: head, name: this.name };
3312
+ }
3313
+ async allDocs(opts = {}) {
3314
+ await this.ready();
3315
+ this.logger.Debug().Msg("allDocs");
3316
+ const { result, head } = await this._crdt.allDocs();
3317
+ const rows = result.map(({ id: key, value, del }) => ({
3318
+ key,
3319
+ value: del ? { _id: key, _deleted: true } : { _id: key, ...value }
3320
+ }));
3321
+ return { rows, clock: head, name: this.name };
3322
+ }
3323
+ async allDocuments() {
3324
+ return this.allDocs();
3325
+ }
3326
+ subscribe(listener, updates) {
3327
+ this.logger.Debug().Bool("updates", updates).Msg("subscribe");
3328
+ if (updates) {
3329
+ if (!this._listening) {
3330
+ this._listening = true;
3331
+ this._crdt.clock.onTick((updates2) => {
3332
+ void this._notify(updates2);
3333
+ });
3334
+ }
3335
+ this._listeners.add(listener);
3336
+ return () => {
3337
+ this._listeners.delete(listener);
3338
+ };
3339
+ } else {
3340
+ this._noupdate_listeners.add(listener);
3341
+ return () => {
3342
+ this._noupdate_listeners.delete(listener);
3343
+ };
3344
+ }
3345
+ }
3346
+ // todo if we add this onto dbs in fireproof.ts then we can make index.ts a separate package
3347
+ async query(field, opts = {}) {
3348
+ await this.ready();
3349
+ this.logger.Debug().Any("field", field).Any("opts", opts).Msg("query");
3350
+ const _crdt = this._crdt;
3351
+ const idx = typeof field === "string" ? index(this.sthis, { _crdt }, field) : index(this.sthis, { _crdt }, makeName(field.toString()), field);
3352
+ return await idx.query(opts);
3353
+ }
3354
+ async compact() {
3355
+ await this.ready();
3356
+ await this._crdt.compact();
3357
+ }
3358
+ async _notify(updates) {
3359
+ await this.ready();
3360
+ if (this._listeners.size) {
3361
+ const docs = updates.map(({ id, value }) => ({ ...value, _id: id }));
3362
+ for (const listener of this._listeners) {
3363
+ await (async () => await listener(docs))().catch((e) => {
3364
+ this.logger.Error().Err(e).Msg("subscriber error");
3365
+ });
3366
+ }
3367
+ }
3368
+ }
3369
+ async _no_update_notify() {
3370
+ await this.ready();
3371
+ if (this._noupdate_listeners.size) {
3372
+ for (const listener of this._noupdate_listeners) {
3373
+ await (async () => await listener([]))().catch((e) => {
3374
+ this.logger.Error().Err(e).Msg("subscriber error");
3375
+ });
3376
+ }
3377
+ }
3378
+ }
3379
+ };
3380
+ function toSortedArray(set) {
3381
+ if (!set) return [];
3382
+ return Object.entries(set).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => ({ [k]: v }));
3383
+ }
3384
+ function fireproof(name, opts) {
3385
+ const key = JSON.stringify(
3386
+ toSortedArray({
3387
+ name,
3388
+ stores: toSortedArray(opts?.store?.stores)
3389
+ })
3390
+ );
3391
+ let db = Database.databases.get(key);
3392
+ if (!db) {
3393
+ db = new Database(name, opts);
3394
+ Database.databases.set(key, db);
3395
+ }
3396
+ return db;
3397
+ }
3398
+ function makeName(fnString) {
3399
+ const regex = /\(([^,()]+,\s*[^,()]+|\[[^\]]+\],\s*[^,()]+)\)/g;
3400
+ let found = null;
3401
+ const matches = Array.from(fnString.matchAll(regex), (match) => match[1].trim());
3402
+ if (matches.length === 0) {
3403
+ found = /=>\s*{?\s*([^{}]+)\s*}?/.exec(fnString);
3404
+ if (found && found[1].includes("return")) {
3405
+ found = null;
3406
+ }
3407
+ }
3408
+ if (!found) {
3409
+ return fnString;
3410
+ } else {
3411
+ return found[1];
3412
+ }
3413
+ }
3414
+
3415
+ // src/runtime/index.ts
3416
+ var runtime_exports = {};
3417
+ __export(runtime_exports, {
3418
+ FILESTORE_VERSION: () => FILESTORE_VERSION,
3419
+ INDEXDB_VERSION: () => INDEXDB_VERSION,
3420
+ files: () => files_exports,
3421
+ getFileName: () => getFileName,
3422
+ getFileSystem: () => getFileSystem,
3423
+ getPath: () => getPath,
3424
+ kb: () => key_bag_exports,
3425
+ kc: () => keyed_crypto_exports,
3426
+ mf: () => wait_pr_multiformats_exports,
3427
+ runtimeFn: () => runtimeFn2,
3428
+ toArrayBuffer: () => toArrayBuffer
3429
+ });
3430
+
3431
+ // src/runtime/wait-pr-multiformats/index.ts
3432
+ var wait_pr_multiformats_exports = {};
3433
+ __export(wait_pr_multiformats_exports, {
3434
+ block: () => block_exports,
3435
+ codec: () => codec_interface_exports
3436
+ });
3437
+
3438
+ // src/runtime/wait-pr-multiformats/codec-interface.ts
3439
+ var codec_interface_exports = {};
3440
+
3441
+ // src/runtime/index.ts
3442
+ import { runtimeFn as runtimeFn2 } from "@adviser/cement";
3443
+
3444
+ // src/runtime/gateways/file/version.ts
3445
+ var FILESTORE_VERSION = "v0.19-file";
3446
+
3447
+ // src/version.ts
3448
+ var PACKAGE_VERSION = Object.keys({
3449
+ "0.19.112-dev-web": "xxxx"
3450
+ })[0];
3451
+ export {
3452
+ CRDT,
3453
+ Database,
3454
+ Index,
3455
+ NotFoundError,
3456
+ PACKAGE_VERSION,
3457
+ Result,
3458
+ UInt8ArrayEqual,
3459
+ blockstore_exports as blockstore,
3460
+ blockstore_exports as bs,
3461
+ dataDir,
3462
+ ensureLogger,
3463
+ ensureSuperLog,
3464
+ ensureSuperThis,
3465
+ exceptionWrapper,
3466
+ falsyToUndef,
3467
+ fireproof,
3468
+ getKey,
3469
+ getName,
3470
+ getStore,
3471
+ index,
3472
+ isFalsy,
3473
+ isNotFoundError,
3474
+ runtime_exports as rt,
3475
+ runtime_exports as runtime,
3476
+ throwFalsy
3477
+ };
3478
+ //# sourceMappingURL=index.js.map