@fireproof/core 0.19.111 → 0.19.112-dev-web

Sign up to get free protection for your applications and to get access to all the features.
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