@fireproof/core 0.18.0 → 0.19.0-dev-use-fix

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. package/README.md +29 -15
  2. package/chunk-AZVWSRER.js +208 -0
  3. package/chunk-AZVWSRER.js.map +1 -0
  4. package/chunk-H3A2HMMM.js +164 -0
  5. package/chunk-H3A2HMMM.js.map +1 -0
  6. package/chunk-NZNG6TQT.js +370 -0
  7. package/chunk-NZNG6TQT.js.map +1 -0
  8. package/chunk-VZGT7ZYP.js +22 -0
  9. package/chunk-VZGT7ZYP.js.map +1 -0
  10. package/chunk-ZHO4NMWL.js +39 -0
  11. package/chunk-ZHO4NMWL.js.map +1 -0
  12. package/index.cjs +4706 -0
  13. package/index.cjs.map +1 -0
  14. package/index.d.cts +1012 -0
  15. package/index.d.ts +1012 -0
  16. package/index.js +2856 -0
  17. package/index.js.map +1 -0
  18. package/metafile-cjs.json +1 -0
  19. package/metafile-esm.json +1 -0
  20. package/node-sys-container-E7LADX2Z.js +29 -0
  21. package/node-sys-container-E7LADX2Z.js.map +1 -0
  22. package/package.json +23 -109
  23. package/sqlite-data-store-3ST7XOLX.js +120 -0
  24. package/sqlite-data-store-3ST7XOLX.js.map +1 -0
  25. package/sqlite-meta-store-QOIMCSJ7.js +137 -0
  26. package/sqlite-meta-store-QOIMCSJ7.js.map +1 -0
  27. package/sqlite-wal-store-JFBQPOYT.js +123 -0
  28. package/sqlite-wal-store-JFBQPOYT.js.map +1 -0
  29. package/store-file-CSS5THFH.js +193 -0
  30. package/store-file-CSS5THFH.js.map +1 -0
  31. package/store-indexdb-DR4HELVP.js +20 -0
  32. package/store-indexdb-DR4HELVP.js.map +1 -0
  33. package/store-sql-BG6SMGQJ.js +344 -0
  34. package/store-sql-BG6SMGQJ.js.map +1 -0
  35. package/tests/blockstore/loader.test.ts +265 -0
  36. package/tests/blockstore/store.test.ts +164 -0
  37. package/tests/blockstore/transaction.test.ts +121 -0
  38. package/tests/fireproof/config.test.ts +212 -0
  39. package/tests/fireproof/crdt.test.ts +434 -0
  40. package/tests/fireproof/database.test.ts +466 -0
  41. package/tests/fireproof/fireproof.test.ts +602 -0
  42. package/tests/fireproof/hello.test.ts +54 -0
  43. package/tests/fireproof/indexer.test.ts +389 -0
  44. package/tests/helpers.ts +81 -0
  45. package/tests/react/useFireproof.test.tsx +19 -0
  46. package/tests/www/gallery.html +132 -0
  47. package/tests/www/iife.html +42 -0
  48. package/tests/www/todo-aws.html +232 -0
  49. package/tests/www/todo-ipfs.html +213 -0
  50. package/tests/www/todo-local.html +214 -0
  51. package/tests/www/todo-netlify.html +227 -0
  52. package/tests/www/todo.html +236 -0
  53. package/dist/browser/fireproof.cjs +0 -1172
  54. package/dist/browser/fireproof.cjs.map +0 -1
  55. package/dist/browser/fireproof.d.cts +0 -268
  56. package/dist/browser/fireproof.d.ts +0 -268
  57. package/dist/browser/fireproof.global.js +0 -24178
  58. package/dist/browser/fireproof.global.js.map +0 -1
  59. package/dist/browser/fireproof.js +0 -1147
  60. package/dist/browser/fireproof.js.map +0 -1
  61. package/dist/browser/metafile-cjs.json +0 -1
  62. package/dist/browser/metafile-esm.json +0 -1
  63. package/dist/browser/metafile-iife.json +0 -1
  64. package/dist/memory/fireproof.cjs +0 -1172
  65. package/dist/memory/fireproof.cjs.map +0 -1
  66. package/dist/memory/fireproof.d.cts +0 -268
  67. package/dist/memory/fireproof.d.ts +0 -268
  68. package/dist/memory/fireproof.global.js +0 -24178
  69. package/dist/memory/fireproof.global.js.map +0 -1
  70. package/dist/memory/fireproof.js +0 -1147
  71. package/dist/memory/fireproof.js.map +0 -1
  72. package/dist/memory/metafile-cjs.json +0 -1
  73. package/dist/memory/metafile-esm.json +0 -1
  74. package/dist/memory/metafile-iife.json +0 -1
  75. package/dist/node/fireproof.cjs +0 -1172
  76. package/dist/node/fireproof.cjs.map +0 -1
  77. package/dist/node/fireproof.d.cts +0 -268
  78. package/dist/node/fireproof.d.ts +0 -268
  79. package/dist/node/fireproof.global.js +0 -38540
  80. package/dist/node/fireproof.global.js.map +0 -1
  81. package/dist/node/fireproof.js +0 -1138
  82. package/dist/node/fireproof.js.map +0 -1
  83. package/dist/node/metafile-cjs.json +0 -1
  84. package/dist/node/metafile-esm.json +0 -1
  85. package/dist/node/metafile-iife.json +0 -1
package/index.js ADDED
@@ -0,0 +1,2856 @@
1
+ import {
2
+ guardVersion
3
+ } from "./chunk-AZVWSRER.js";
4
+ import {
5
+ NotFoundError,
6
+ isNotFoundError
7
+ } from "./chunk-VZGT7ZYP.js";
8
+ import {
9
+ dataDir,
10
+ decodeFile,
11
+ encodeFile,
12
+ ensureLogger,
13
+ exception2Result,
14
+ exceptionWrapper,
15
+ getKey,
16
+ getName,
17
+ getStore,
18
+ runtime_exports,
19
+ toCryptoOpts
20
+ } from "./chunk-NZNG6TQT.js";
21
+ import {
22
+ SysContainer,
23
+ __export,
24
+ falsyToUndef,
25
+ isFalsy,
26
+ throwFalsy
27
+ } from "./chunk-H3A2HMMM.js";
28
+
29
+ // src/database.ts
30
+ import { uuidv7 } from "uuidv7";
31
+ import { ResolveOnce as ResolveOnce5 } from "@adviser/cement";
32
+
33
+ // src/write-queue.ts
34
+ function writeQueue(worker, payload = Infinity, unbounded = false) {
35
+ const queue = [];
36
+ let isProcessing = false;
37
+ async function process() {
38
+ if (isProcessing || queue.length === 0) return;
39
+ isProcessing = true;
40
+ const tasksToProcess = queue.splice(0, payload);
41
+ const updates = tasksToProcess.map((item) => item.task);
42
+ if (unbounded) {
43
+ const promises = updates.map(async (update, index2) => {
44
+ try {
45
+ const result = await worker([update]);
46
+ tasksToProcess[index2].resolve(result);
47
+ } catch (error) {
48
+ tasksToProcess[index2].reject(error);
49
+ }
50
+ });
51
+ await Promise.all(promises);
52
+ } else {
53
+ try {
54
+ const result = await worker(updates);
55
+ tasksToProcess.forEach((task) => task.resolve(result));
56
+ } catch (error) {
57
+ tasksToProcess.forEach((task) => task.reject(error));
58
+ }
59
+ }
60
+ isProcessing = false;
61
+ void process();
62
+ }
63
+ return {
64
+ push(task) {
65
+ return new Promise((resolve, reject) => {
66
+ queue.push({ task, resolve, reject });
67
+ void process();
68
+ });
69
+ }
70
+ };
71
+ }
72
+
73
+ // src/crdt.ts
74
+ import { ResolveOnce as ResolveOnce4 } from "@adviser/cement";
75
+
76
+ // src/crdt-helpers.ts
77
+ import { encode as encode3, decode as decode3, Block as Block2 } from "multiformats/block";
78
+ import { parse as parse2 } from "multiformats/link";
79
+ import { sha256 as hasher2 } from "multiformats/hashes/sha2";
80
+ import * as codec2 from "@ipld/dag-cbor";
81
+ import { put, get, entries, root } from "@web3-storage/pail/crdt";
82
+ import { EventFetcher, vis } from "@web3-storage/pail/clock";
83
+ import * as Batch from "@web3-storage/pail/crdt/batch";
84
+
85
+ // src/blockstore/index.ts
86
+ var blockstore_exports = {};
87
+ __export(blockstore_exports, {
88
+ BaseBlockstore: () => BaseBlockstore,
89
+ CarTransaction: () => CarTransaction,
90
+ CompactionFetcher: () => CompactionFetcher,
91
+ ConnectREST: () => ConnectREST,
92
+ ConnectionBase: () => ConnectionBase,
93
+ DataStore: () => DataStore,
94
+ EncryptedBlockstore: () => EncryptedBlockstore,
95
+ Loadable: () => Loadable,
96
+ Loader: () => Loader,
97
+ MetaStore: () => MetaStore,
98
+ NotFoundError: () => NotFoundError,
99
+ RemoteWAL: () => RemoteWAL,
100
+ isNotFoundError: () => isNotFoundError,
101
+ parseCarFile: () => parseCarFile,
102
+ registerStoreProtocol: () => registerStoreProtocol,
103
+ testStoreFactory: () => testStoreFactory,
104
+ toStoreRuntime: () => toStoreRuntime,
105
+ toURL: () => toURL
106
+ });
107
+
108
+ // src/blockstore/connection-base.ts
109
+ import { EventBlock, decodeEventBlock } from "@web3-storage/pail/clock";
110
+ import { MemoryBlockstore } from "@web3-storage/pail/block";
111
+
112
+ // src/blockstore/task-manager.ts
113
+ var TaskManager = class {
114
+ constructor(loader) {
115
+ this.eventsWeHandled = /* @__PURE__ */ new Set();
116
+ this.queue = [];
117
+ this.isProcessing = false;
118
+ this.loader = loader;
119
+ this.logger = ensureLogger(loader.logger, "TaskManager");
120
+ }
121
+ async handleEvent(eventBlock) {
122
+ const cid = eventBlock.cid.toString();
123
+ const parents = eventBlock.value.parents.map((cid2) => cid2.toString());
124
+ for (const parent of parents) {
125
+ this.eventsWeHandled.add(parent);
126
+ }
127
+ this.queue.push({ cid, eventBlock, retries: 0 });
128
+ this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
129
+ void this.processQueue();
130
+ }
131
+ async processQueue() {
132
+ if (this.isProcessing) return;
133
+ this.isProcessing = true;
134
+ const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
135
+ const first = filteredQueue[0];
136
+ if (!first) {
137
+ return;
138
+ }
139
+ try {
140
+ this.loader?.remoteMetaStore?.handleByteHeads([first.eventBlock.value.data.dbMeta]);
141
+ this.eventsWeHandled.add(first.cid);
142
+ this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
143
+ } catch (err) {
144
+ if (first.retries++ > 3) {
145
+ this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
146
+ this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
147
+ }
148
+ await new Promise((resolve) => setTimeout(resolve, 50));
149
+ throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
150
+ } finally {
151
+ this.isProcessing = false;
152
+ if (this.queue.length > 0) {
153
+ void this.processQueue();
154
+ }
155
+ }
156
+ }
157
+ };
158
+
159
+ // src/blockstore/connection-base.ts
160
+ var ConnectionBase = class {
161
+ constructor(logger) {
162
+ // readonly ready: Promise<unknown>;
163
+ // todo move to LRU blockstore https://github.com/web3-storage/w3clock/blob/main/src/worker/block.js
164
+ this.eventBlocks = new MemoryBlockstore();
165
+ this.parents = [];
166
+ this.loaded = Promise.resolve();
167
+ this.logger = ensureLogger(logger, "ConnectionBase");
168
+ }
169
+ async refresh() {
170
+ await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load("main");
171
+ await (await throwFalsy(this.loader).remoteWAL())._process();
172
+ }
173
+ connect({ loader }) {
174
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
175
+ this.connectMeta({ loader });
176
+ this.connectStorage({ loader });
177
+ }
178
+ connectMeta({ loader }) {
179
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
180
+ this.loader = loader;
181
+ this.taskManager = new TaskManager(loader);
182
+ this.onConnect();
183
+ this.logger.Warn().Msg("connectMeta: connecting to remote meta store is disabled");
184
+ }
185
+ async onConnect() {
186
+ return;
187
+ }
188
+ connectStorage({ loader }) {
189
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
190
+ this.loader = loader;
191
+ this.logger.Warn().Msg("connectStorage: connecting to remote meta store is disabled");
192
+ }
193
+ async createEventBlock(bytes) {
194
+ const data = {
195
+ dbMeta: bytes
196
+ };
197
+ const event = await EventBlock.create(
198
+ data,
199
+ this.parents
200
+ );
201
+ await this.eventBlocks.put(event.cid, event.bytes);
202
+ return event;
203
+ }
204
+ async decodeEventBlock(bytes) {
205
+ const event = await decodeEventBlock(bytes);
206
+ return event;
207
+ }
208
+ // move this stuff to connect
209
+ // async getDashboardURL(compact = true) {
210
+ // const baseUrl = 'https://dashboard.fireproof.storage/'
211
+ // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
212
+ // // if (compact) {
213
+ // // await this.compact()
214
+ // // }
215
+ // const currents = await this.loader?.metaStore?.load()
216
+ // if (!currents) throw new Error("Can't sync empty database: save data first")
217
+ // if (currents.length > 1)
218
+ // throw new Error("Can't sync database with split heads: make an update first")
219
+ // const current = currents[0]
220
+ // const params = {
221
+ // car: current.car.toString()
222
+ // }
223
+ // if (current.key) {
224
+ // // @ts-ignore
225
+ // params.key = current.key.toString()
226
+ // }
227
+ // // @ts-ignore
228
+ // if (this.name) {
229
+ // // @ts-ignore
230
+ // params.name = this.name
231
+ // }
232
+ // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
233
+ // console.log('Import to dashboard: ' + url.toString())
234
+ // return url
235
+ // }
236
+ // openDashboard() {
237
+ // void this.getDashboardURL().then(url => {
238
+ // if (url) window.open(url.toString(), '_blank')
239
+ // })
240
+ // }
241
+ };
242
+
243
+ // src/blockstore/connect-rest.ts
244
+ var ConnectREST = class extends ConnectionBase {
245
+ constructor(base, logger) {
246
+ super(ensureLogger(logger, "ConnectREST"));
247
+ this.baseUrl = new URL(base);
248
+ }
249
+ async dataUpload(bytes, params) {
250
+ const carCid = params.car.toString();
251
+ const uploadURL = new URL(`/cars/${carCid}.car`, this.baseUrl);
252
+ const done = await fetch(uploadURL, { method: "PUT", body: bytes });
253
+ if (!done.ok) {
254
+ throw this.logger.Error().Msg("failed to upload data " + done.statusText);
255
+ }
256
+ }
257
+ async dataDownload(params) {
258
+ const { car } = params;
259
+ const fetchFromUrl = new URL(`/cars/${car.toString()}.car`, this.baseUrl);
260
+ const response = await fetch(fetchFromUrl);
261
+ if (!response.ok) {
262
+ return void 0;
263
+ }
264
+ const bytes = new Uint8Array(await response.arrayBuffer());
265
+ return bytes;
266
+ }
267
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
268
+ async metaUpload(bytes, params) {
269
+ return void 0;
270
+ }
271
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
272
+ async metaDownload(params) {
273
+ return [];
274
+ }
275
+ };
276
+
277
+ // src/blockstore/store-factory.ts
278
+ import { KeyedResolvOnce } from "@adviser/cement";
279
+
280
+ // src/blockstore/store.ts
281
+ import pLimit2 from "p-limit";
282
+ import { format, parse } from "@ipld/dag-json";
283
+ import { ResolveOnce as ResolveOnce2, Result } from "@adviser/cement";
284
+
285
+ // src/blockstore/loader.ts
286
+ import pLimit from "p-limit";
287
+ import { CarReader } from "@ipld/car";
288
+ import { ResolveOnce } from "@adviser/cement";
289
+
290
+ // src/blockstore/types.ts
291
+ function toCIDBlock(block) {
292
+ return block;
293
+ }
294
+
295
+ // src/blockstore/loader-helpers.ts
296
+ import { encode, decode } from "multiformats/block";
297
+ import { sha256 as hasher } from "multiformats/hashes/sha2";
298
+ import * as raw from "multiformats/codecs/raw";
299
+ import * as CBW from "@ipld/car/buffer-writer";
300
+ import * as codec from "@ipld/dag-cbor";
301
+ async function encodeCarFile(roots, t) {
302
+ let size = 0;
303
+ const headerSize = CBW.headerLength({ roots });
304
+ size += headerSize;
305
+ for (const { cid, bytes } of t.entries()) {
306
+ size += CBW.blockLength({ cid, bytes });
307
+ }
308
+ const buffer = new Uint8Array(size);
309
+ const writer = CBW.createWriter(buffer, { headerSize });
310
+ for (const r of roots) {
311
+ writer.addRoot(r);
312
+ }
313
+ for (const { cid, bytes } of t.entries()) {
314
+ writer.write({ cid, bytes });
315
+ }
316
+ writer.close();
317
+ return await encode({ value: writer.bytes, hasher, codec: raw });
318
+ }
319
+ async function encodeCarHeader(fp) {
320
+ return await encode({
321
+ value: { fp },
322
+ hasher,
323
+ codec
324
+ });
325
+ }
326
+ async function parseCarFile(reader, logger) {
327
+ const roots = await reader.getRoots();
328
+ const header = await reader.get(roots[0]);
329
+ if (!header) throw logger.Error().Msg("missing header block").AsError();
330
+ const { value } = await decode({ bytes: header.bytes, hasher, codec });
331
+ const fpvalue = value;
332
+ if (fpvalue && !fpvalue.fp) {
333
+ throw logger.Error().Msg("missing fp").AsError();
334
+ }
335
+ return fpvalue.fp;
336
+ }
337
+
338
+ // src/blockstore/encrypt-helpers.ts
339
+ import { sha256 } from "multiformats/hashes/sha2";
340
+ import { CID as CID2 } from "multiformats";
341
+ import { encode as encode2, decode as decode2, create as mfCreate } from "multiformats/block";
342
+ import * as dagcbor from "@ipld/dag-cbor";
343
+ import { MemoryBlockstore as MemoryBlockstore2 } from "@web3-storage/pail/block";
344
+ import { bf } from "prolly-trees/utils";
345
+ import { nocache as cache } from "prolly-trees/cache";
346
+ import { create, load } from "prolly-trees/cid-set";
347
+
348
+ // src/blockstore/encrypt-codec.ts
349
+ import { CID } from "multiformats";
350
+ function makeCodec(ilogger, crypto, randomBytes) {
351
+ const logger = ensureLogger(ilogger, "makeCodec");
352
+ const enc32 = (value) => {
353
+ value = +value;
354
+ const buff = new Uint8Array(4);
355
+ buff[3] = value >>> 24;
356
+ buff[2] = value >>> 16;
357
+ buff[1] = value >>> 8;
358
+ buff[0] = value & 255;
359
+ return buff;
360
+ };
361
+ const readUInt32LE = (buffer) => {
362
+ const offset = buffer.byteLength - 4;
363
+ return (buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16) + buffer[offset + 3] * 16777216;
364
+ };
365
+ const concat = (buffers) => {
366
+ const uint8Arrays = buffers.map((b) => b instanceof ArrayBuffer ? new Uint8Array(b) : b);
367
+ const totalLength = uint8Arrays.reduce((sum, arr) => sum + arr.length, 0);
368
+ const result = new Uint8Array(totalLength);
369
+ let offset = 0;
370
+ for (const arr of uint8Arrays) {
371
+ result.set(arr, offset);
372
+ offset += arr.length;
373
+ }
374
+ return result;
375
+ };
376
+ const encode4 = ({ iv, bytes }) => concat([iv, bytes]);
377
+ const decode4 = (bytes) => {
378
+ const iv = bytes.subarray(0, 12);
379
+ bytes = bytes.slice(12);
380
+ return { iv, bytes };
381
+ };
382
+ const code = 3145728 + 1337;
383
+ async function subtleKey(key) {
384
+ return await crypto.importKey(
385
+ "raw",
386
+ // raw or jwk
387
+ key,
388
+ // raw data
389
+ "AES-GCM",
390
+ false,
391
+ // extractable
392
+ ["encrypt", "decrypt"]
393
+ );
394
+ }
395
+ const decrypt = async ({ key, value }) => {
396
+ const { bytes: inBytes, iv } = value;
397
+ const cryKey = await subtleKey(key);
398
+ const deBytes = await crypto.decrypt(
399
+ {
400
+ name: "AES-GCM",
401
+ iv,
402
+ tagLength: 128
403
+ },
404
+ cryKey,
405
+ inBytes
406
+ );
407
+ const bytes = new Uint8Array(deBytes);
408
+ const len = readUInt32LE(bytes.subarray(0, 4));
409
+ const cid = CID.decode(bytes.subarray(4, 4 + len));
410
+ return { cid, bytes: bytes.subarray(4 + len) };
411
+ };
412
+ const encrypt = async ({ key, cid, bytes }) => {
413
+ const len = enc32(cid.bytes.byteLength);
414
+ const iv = randomBytes(12);
415
+ const msg = concat([len, cid.bytes, bytes]);
416
+ try {
417
+ const cryKey = await subtleKey(key);
418
+ const deBytes = await crypto.encrypt(
419
+ {
420
+ name: "AES-GCM",
421
+ iv,
422
+ tagLength: 128
423
+ },
424
+ cryKey,
425
+ msg
426
+ );
427
+ bytes = new Uint8Array(deBytes);
428
+ } catch (e) {
429
+ throw logger.Error().Err(e).Msg("encrypt failed").AsError();
430
+ }
431
+ return { value: { bytes, iv } };
432
+ };
433
+ const cryptoFn = (key) => {
434
+ return { encrypt: (opts) => encrypt({ ...opts, key }), decrypt: (opts) => decrypt({ ...opts, key }) };
435
+ };
436
+ const name = "jchris@encrypted-block:aes-gcm";
437
+ return { encode: encode4, decode: decode4, code, name, encrypt, decrypt, crypto: cryptoFn };
438
+ }
439
+
440
+ // src/blockstore/encrypt-helpers.ts
441
+ function carLogIncludesGroup(list, cidMatch) {
442
+ return list.some((cid) => {
443
+ return cid.toString() === cidMatch.toString();
444
+ });
445
+ }
446
+ function makeEncDec(logger, crypto, randomBytes) {
447
+ const codec4 = makeCodec(logger, crypto, randomBytes);
448
+ const encrypt = async function* ({
449
+ get: get2,
450
+ cids,
451
+ hasher: hasher4,
452
+ key,
453
+ cache: cache3,
454
+ chunker: chunker2,
455
+ root: root3
456
+ }) {
457
+ const set = /* @__PURE__ */ new Set();
458
+ let eroot;
459
+ if (!carLogIncludesGroup(cids, root3)) cids.push(root3);
460
+ for (const cid of cids) {
461
+ const unencrypted = await get2(cid);
462
+ if (!unencrypted) throw logger.Error().Ref("cid", cid).Msg("missing cid block").AsError();
463
+ const encrypted = await codec4.encrypt({ ...unencrypted, key });
464
+ const block2 = await encode2({ ...encrypted, codec: codec4, hasher: hasher4 });
465
+ yield block2;
466
+ set.add(block2.cid.toString());
467
+ if (unencrypted.cid.equals(root3)) eroot = block2.cid;
468
+ }
469
+ if (!eroot) throw logger.Error().Msg("cids does not include root").AsError();
470
+ const list = [...set].map((s) => CID2.parse(s));
471
+ let last;
472
+ for await (const node of create({ list, get: get2, cache: cache3, chunker: chunker2, hasher: hasher4, codec: dagcbor })) {
473
+ const block2 = await node.block;
474
+ yield block2;
475
+ last = block2;
476
+ }
477
+ if (!last) throw logger.Error().Msg("missing last block").AsError();
478
+ const head = [eroot, last.cid];
479
+ const block = await encode2({ value: head, codec: dagcbor, hasher: hasher4 });
480
+ yield block;
481
+ };
482
+ const decrypt = async function* ({
483
+ root: root3,
484
+ get: get2,
485
+ key,
486
+ cache: cache3,
487
+ chunker: chunker2,
488
+ hasher: hasher4
489
+ }) {
490
+ const getWithDecode = async (cid) => get2(cid).then(async (block) => {
491
+ if (!block) return;
492
+ const decoded = await decode2({ ...block, codec: dagcbor, hasher: hasher4 });
493
+ return decoded;
494
+ });
495
+ const getWithDecrypt = async (cid) => get2(cid).then(async (block) => {
496
+ if (!block) return;
497
+ const decoded = await decode2({ ...block, codec: codec4, hasher: hasher4 });
498
+ return decoded;
499
+ });
500
+ const decodedRoot = await getWithDecode(root3);
501
+ if (!decodedRoot) throw logger.Error().Msg("missing root").AsError();
502
+ if (!decodedRoot.bytes) throw logger.Error().Msg("missing bytes").AsError();
503
+ const {
504
+ value: [eroot, tree]
505
+ } = decodedRoot;
506
+ const rootBlock = await get2(eroot);
507
+ if (!rootBlock) throw logger.Error().Msg("missing root block").AsError();
508
+ const cidset = await load({ cid: tree, get: getWithDecode, cache: cache3, chunker: chunker2, codec: codec4, hasher: hasher4 });
509
+ const { result: nodes } = await cidset.getAllEntries();
510
+ const unwrap = async (eblock) => {
511
+ if (!eblock) throw logger.Error().Msg("missing block").AsError();
512
+ if (!eblock.value) {
513
+ eblock = await decode2({ ...eblock, codec: codec4, hasher: hasher4 });
514
+ if (!eblock.value) throw logger.Error().Msg("missing value").AsError();
515
+ }
516
+ const { bytes, cid } = await codec4.decrypt({ ...eblock, key }).catch((e) => {
517
+ throw e;
518
+ });
519
+ const block = await mfCreate({ cid, bytes, hasher: hasher4, codec: codec4 });
520
+ return block;
521
+ };
522
+ const promises = [];
523
+ for (const { cid } of nodes) {
524
+ if (!rootBlock.cid.equals(cid)) promises.push(getWithDecrypt(cid).then(unwrap));
525
+ }
526
+ yield* promises;
527
+ yield unwrap(rootBlock);
528
+ };
529
+ return { encrypt, decrypt };
530
+ }
531
+ var chunker = bf(30);
532
+ function hexStringToUint8Array(hexString) {
533
+ const length = hexString.length;
534
+ const uint8Array = new Uint8Array(length / 2);
535
+ for (let i = 0; i < length; i += 2) {
536
+ uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
537
+ }
538
+ return uint8Array;
539
+ }
540
+ async function encryptedEncodeCarFile(logger, crypto, key, rootCid, t) {
541
+ const encryptionKey = hexStringToUint8Array(key);
542
+ const encryptedBlocks = new MemoryBlockstore2();
543
+ const cidsToEncrypt = [];
544
+ for (const { cid, bytes } of t.entries()) {
545
+ cidsToEncrypt.push(cid);
546
+ const g = await t.get(cid);
547
+ if (!g) throw logger.Error().Ref("cid", cid).Int("bytes", bytes.length).Msg("missing cid block").AsError();
548
+ }
549
+ let last = null;
550
+ const { encrypt } = makeEncDec(logger, crypto, crypto.randomBytes);
551
+ for await (const block of encrypt({
552
+ cids: cidsToEncrypt,
553
+ get: t.get.bind(t),
554
+ key: encryptionKey,
555
+ hasher: sha256,
556
+ chunker,
557
+ cache,
558
+ root: rootCid
559
+ })) {
560
+ await encryptedBlocks.put(block.cid, block.bytes);
561
+ last = block;
562
+ }
563
+ if (!last) throw logger.Error().Msg("no blocks encrypted").AsError();
564
+ const encryptedCar = await encodeCarFile([last.cid], encryptedBlocks);
565
+ return encryptedCar;
566
+ }
567
+ async function decodeEncryptedCar(logger, crypto, key, reader) {
568
+ const roots = await reader.getRoots();
569
+ const root3 = roots[0];
570
+ return await decodeCarBlocks(logger, crypto, root3, reader.get.bind(reader), key);
571
+ }
572
+ async function decodeCarBlocks(logger, crypto, root3, get2, keyMaterial) {
573
+ const decryptionKeyUint8 = hexStringToUint8Array(keyMaterial);
574
+ const decryptionKey = decryptionKeyUint8.buffer.slice(0, decryptionKeyUint8.byteLength);
575
+ const decryptedBlocks = new MemoryBlockstore2();
576
+ let last = null;
577
+ const { decrypt } = makeEncDec(logger, crypto, crypto.randomBytes);
578
+ for await (const block of decrypt({
579
+ root: root3,
580
+ get: get2,
581
+ key: decryptionKey,
582
+ hasher: sha256,
583
+ chunker,
584
+ cache
585
+ })) {
586
+ await decryptedBlocks.put(block.cid, block.bytes);
587
+ last = block;
588
+ }
589
+ if (!last) throw logger.Error().Msg("no blocks decrypted").AsError();
590
+ return { blocks: decryptedBlocks, root: last.cid };
591
+ }
592
+
593
+ // src/blockstore/transaction.ts
594
+ import { MemoryBlockstore as MemoryBlockstore3 } from "@web3-storage/pail/block";
595
+ var CarTransaction = class extends MemoryBlockstore3 {
596
+ constructor(parent, opts = { add: true }) {
597
+ super();
598
+ if (opts.add) {
599
+ parent.transactions.add(this);
600
+ }
601
+ this.parent = parent;
602
+ }
603
+ async get(cid) {
604
+ return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
605
+ }
606
+ async superGet(cid) {
607
+ return super.get(cid);
608
+ }
609
+ };
610
+ function defaultedBlockstoreRuntime(opts, component, ctx) {
611
+ const logger = ensureLogger(opts, component, ctx);
612
+ const store = opts.store || {};
613
+ return {
614
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
615
+ applyMeta: (meta, snap) => {
616
+ return Promise.resolve();
617
+ },
618
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
619
+ compact: async (blocks) => {
620
+ return {};
621
+ },
622
+ autoCompact: 100,
623
+ public: false,
624
+ name: void 0,
625
+ threshold: 1e3 * 1e3,
626
+ ...opts,
627
+ logger,
628
+ crypto: toCryptoOpts(opts.crypto),
629
+ store,
630
+ storeRuntime: toStoreRuntime(store, logger)
631
+ };
632
+ }
633
+ var blockstoreFactory = function(opts) {
634
+ if (opts.name) {
635
+ return new EncryptedBlockstore(opts);
636
+ } else {
637
+ return new BaseBlockstore(opts);
638
+ }
639
+ };
640
+ var BaseBlockstore = class {
641
+ constructor(ebOpts = {}) {
642
+ this.transactions = /* @__PURE__ */ new Set();
643
+ this.ebOpts = defaultedBlockstoreRuntime(ebOpts, "BaseBlockstore");
644
+ this.logger = this.ebOpts.logger;
645
+ }
646
+ // ready: Promise<void>;
647
+ ready() {
648
+ return Promise.resolve();
649
+ }
650
+ async close() {
651
+ }
652
+ async destroy() {
653
+ }
654
+ async get(cid) {
655
+ if (!cid) throw this.logger.Error().Msg("required cid").AsError();
656
+ for (const f of this.transactions) {
657
+ const v = await f.superGet(cid);
658
+ if (v) return v;
659
+ }
660
+ }
661
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
662
+ async put(cid, block) {
663
+ throw this.logger.Error().Msg("use a transaction to put").AsError();
664
+ }
665
+ // TransactionMeta
666
+ async transaction(fn, _opts = {}) {
667
+ const t = new CarTransaction(this);
668
+ const done = await fn(t);
669
+ this.lastTxMeta = done;
670
+ return { t, meta: done };
671
+ }
672
+ async *entries() {
673
+ const seen = /* @__PURE__ */ new Set();
674
+ for (const t of this.transactions) {
675
+ for await (const blk of t.entries()) {
676
+ if (seen.has(blk.cid.toString())) continue;
677
+ seen.add(blk.cid.toString());
678
+ yield blk;
679
+ }
680
+ }
681
+ }
682
+ };
683
+ var EncryptedBlockstore = class extends BaseBlockstore {
684
+ constructor(ebOpts) {
685
+ super(ebOpts);
686
+ this.compacting = false;
687
+ this.logger = ensureLogger(ebOpts, "EncryptedBlockstore");
688
+ const { name } = ebOpts;
689
+ if (!name) {
690
+ throw this.logger.Error().Msg("name required").AsError();
691
+ }
692
+ this.name = name;
693
+ this.loader = new Loader(this.name, ebOpts);
694
+ }
695
+ ready() {
696
+ return this.loader.ready();
697
+ }
698
+ close() {
699
+ return this.loader.close();
700
+ }
701
+ destroy() {
702
+ return this.loader.destroy();
703
+ }
704
+ async get(cid) {
705
+ const got = await super.get(cid);
706
+ if (got) return got;
707
+ if (!this.loader) {
708
+ return;
709
+ }
710
+ return falsyToUndef(await this.loader.getBlock(cid));
711
+ }
712
+ async transaction(fn, opts = { noLoader: false }) {
713
+ const { t, meta: done } = await super.transaction(fn);
714
+ const cars = await this.loader.commit(t, done, opts);
715
+ if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
716
+ setTimeout(() => void this.compact(), 10);
717
+ }
718
+ if (cars) {
719
+ this.transactions.delete(t);
720
+ return { meta: done, cars, t };
721
+ }
722
+ throw this.logger.Error().Msg("failed to commit car files").AsError();
723
+ }
724
+ async getFile(car, cid, isPublic = false) {
725
+ await this.ready();
726
+ if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
727
+ const reader = await this.loader.loadFileCar(car, isPublic);
728
+ const block = await reader.get(cid);
729
+ if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
730
+ return block.bytes;
731
+ }
732
+ async compact() {
733
+ await this.ready();
734
+ if (!this.loader) throw this.logger.Error().Msg("loader required to compact").AsError();
735
+ if (this.loader.carLog.length < 2) return;
736
+ const compactFn = this.ebOpts.compact || ((blocks) => this.defaultCompact(blocks, this.logger));
737
+ if (!compactFn || this.compacting) return;
738
+ const blockLog = new CompactionFetcher(this);
739
+ this.compacting = true;
740
+ const meta = await compactFn(blockLog);
741
+ await this.loader?.commit(blockLog.loggedBlocks, meta, {
742
+ compact: true,
743
+ noLoader: true
744
+ });
745
+ this.compacting = false;
746
+ }
747
+ async defaultCompact(blocks, logger) {
748
+ if (!this.loader) {
749
+ throw logger.Error().Msg("no loader").AsError();
750
+ }
751
+ if (!this.lastTxMeta) {
752
+ throw logger.Error().Msg("no lastTxMeta").AsError();
753
+ }
754
+ for await (const blk of this.loader.entries(false)) {
755
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
756
+ }
757
+ for (const t of this.transactions) {
758
+ for await (const blk of t.entries()) {
759
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
760
+ }
761
+ }
762
+ return this.lastTxMeta;
763
+ }
764
+ async *entries() {
765
+ for await (const blk of this.loader.entries()) {
766
+ yield blk;
767
+ }
768
+ }
769
+ };
770
+ var CompactionFetcher = class {
771
+ constructor(blocks) {
772
+ this.blockstore = blocks;
773
+ this.loggedBlocks = new CarTransaction(blocks);
774
+ }
775
+ async get(cid) {
776
+ const block = await this.blockstore.get(cid);
777
+ if (block) this.loggedBlocks.putSync(cid, block.bytes);
778
+ return falsyToUndef(block);
779
+ }
780
+ };
781
+
782
+ // src/blockstore/commit-queue.ts
783
+ var CommitQueue = class {
784
+ constructor() {
785
+ this.queue = [];
786
+ this.processing = false;
787
+ }
788
+ async enqueue(fn) {
789
+ return new Promise((resolve, reject) => {
790
+ const queueFn = async () => {
791
+ try {
792
+ resolve(await fn());
793
+ } catch (e) {
794
+ reject(e);
795
+ } finally {
796
+ this.processing = false;
797
+ this.processNext();
798
+ }
799
+ };
800
+ this.queue.push(queueFn);
801
+ if (!this.processing) {
802
+ this.processNext();
803
+ }
804
+ });
805
+ }
806
+ processNext() {
807
+ if (this.queue.length > 0 && !this.processing) {
808
+ this.processing = true;
809
+ const queueFn = this.queue.shift();
810
+ if (queueFn) {
811
+ queueFn();
812
+ }
813
+ }
814
+ }
815
+ };
816
+
817
+ // src/blockstore/loader.ts
818
+ import * as CBW2 from "@ipld/car/buffer-writer";
819
+ function carLogIncludesGroup2(list, cids) {
820
+ return list.some((arr) => {
821
+ return arr.toString() === cids.toString();
822
+ });
823
+ }
824
+ function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
825
+ const byString = /* @__PURE__ */ new Map();
826
+ for (const cid of list) {
827
+ if (remove.has(cid.toString())) continue;
828
+ byString.set(cid.toString(), cid);
829
+ }
830
+ return [...byString.values()];
831
+ }
832
+ function toHexString(byteArray) {
833
+ return Array.from(byteArray).map((byte) => byte.toString(16).padStart(2, "0")).join("");
834
+ }
835
+ var Loadable = class {
836
+ constructor() {
837
+ this.name = "";
838
+ this.carLog = new Array();
839
+ }
840
+ };
841
+ var Loader = class {
842
+ constructor(name, ebOpts) {
843
+ this.commitQueue = new CommitQueue();
844
+ this.isCompacting = false;
845
+ this.carReaders = /* @__PURE__ */ new Map();
846
+ this.seenCompacted = /* @__PURE__ */ new Set();
847
+ this.processedCars = /* @__PURE__ */ new Set();
848
+ this.carLog = [];
849
+ this.getBlockCache = /* @__PURE__ */ new Map();
850
+ this.seenMeta = /* @__PURE__ */ new Set();
851
+ this.writeLimit = pLimit(1);
852
+ this.onceReady = new ResolveOnce();
853
+ this.name = name;
854
+ this.ebOpts = defaultedBlockstoreRuntime(
855
+ {
856
+ ...ebOpts,
857
+ name
858
+ },
859
+ "Loader"
860
+ );
861
+ this.logger = this.ebOpts.logger;
862
+ }
863
+ // readonly id = uuidv4();
864
+ async carStore() {
865
+ return this.ebOpts.storeRuntime.makeDataStore(this);
866
+ }
867
+ async fileStore() {
868
+ return this.ebOpts.storeRuntime.makeDataStore(this);
869
+ }
870
+ async remoteWAL() {
871
+ return this.ebOpts.storeRuntime.makeRemoteWAL(this);
872
+ }
873
+ async metaStore() {
874
+ return this.ebOpts.storeRuntime.makeMetaStore(this);
875
+ }
876
+ async ready() {
877
+ return this.onceReady.once(async () => {
878
+ const metas = this.ebOpts.meta ? [this.ebOpts.meta] : await (await this.metaStore()).load("main");
879
+ if (metas) {
880
+ await this.handleDbMetasFromStore(metas);
881
+ }
882
+ });
883
+ }
884
+ async close() {
885
+ const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.remoteWAL()]);
886
+ await Promise.all(toClose.map((store) => store.close()));
887
+ }
888
+ async destroy() {
889
+ const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.remoteWAL()]);
890
+ await Promise.all(toDestroy.map((store) => store.destroy()));
891
+ }
892
+ // async snapToCar(carCid: AnyLink | string) {
893
+ // await this.ready
894
+ // if (typeof carCid === 'string') {
895
+ // carCid = CID.parse(carCid)
896
+ // }
897
+ // const carHeader = await this.loadCarHeaderFromMeta({ car: carCid, key: this.key || null })
898
+ // this.carLog = [carCid, ...carHeader.cars]
899
+ // await this.getMoreReaders(carHeader.cars)
900
+ // await this._applyCarHeader(carHeader, true)
901
+ // }
902
+ async handleDbMetasFromStore(metas) {
903
+ for (const meta of metas) {
904
+ await this.writeLimit(async () => {
905
+ await this.mergeDbMetaIntoClock(meta);
906
+ });
907
+ }
908
+ }
909
+ async mergeDbMetaIntoClock(meta) {
910
+ if (this.isCompacting) {
911
+ throw this.logger.Error().Msg("cannot merge while compacting").AsError();
912
+ }
913
+ if (this.seenMeta.has(meta.cars.toString())) return;
914
+ this.seenMeta.add(meta.cars.toString());
915
+ if (meta.key) {
916
+ await this.setKey(meta.key);
917
+ }
918
+ if (carLogIncludesGroup2(this.carLog, meta.cars)) {
919
+ return;
920
+ }
921
+ const carHeader = await this.loadCarHeaderFromMeta(meta);
922
+ carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
923
+ await this.getMoreReaders(carHeader.cars.flat());
924
+ this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
925
+ await this.ebOpts.applyMeta?.(carHeader.meta);
926
+ }
927
+ async ingestKeyFromMeta(meta) {
928
+ const { key } = meta;
929
+ if (key) {
930
+ await this.setKey(key);
931
+ }
932
+ }
933
+ async loadCarHeaderFromMeta({ cars: cids }) {
934
+ const reader = await this.loadCar(cids[0]);
935
+ return await parseCarFile(reader, this.logger);
936
+ }
937
+ async _getKey() {
938
+ if (this.key) return this.key;
939
+ if (!this.ebOpts.public) {
940
+ await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
941
+ }
942
+ return this.key || void 0;
943
+ }
944
+ async commitFiles(t, done, opts = { noLoader: false, compact: false }) {
945
+ return this.commitQueue.enqueue(() => this._commitInternalFiles(t, done, opts));
946
+ }
947
+ // can these skip the queue? or have a file queue?
948
+ async _commitInternalFiles(t, done, opts = { noLoader: false, compact: false }) {
949
+ await this.ready();
950
+ const { files: roots } = this.makeFileCarHeader(done);
951
+ const cids = [];
952
+ const cars = await this.prepareCarFilesFiles(roots, t, !!opts.public);
953
+ for (const car of cars) {
954
+ const { cid, bytes } = car;
955
+ await (await this.fileStore()).save({ cid, bytes });
956
+ await (await this.remoteWAL()).enqueueFile(cid, !!opts.public);
957
+ cids.push(cid);
958
+ }
959
+ return cids;
960
+ }
961
+ async loadFileCar(cid, isPublic = false) {
962
+ return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore, isPublic);
963
+ }
964
+ async commit(t, done, opts = { noLoader: false, compact: false }) {
965
+ return this.commitQueue.enqueue(() => this._commitInternal(t, done, opts));
966
+ }
967
+ async cacheTransaction(t) {
968
+ for await (const block of t.entries()) {
969
+ const sBlock = block.cid.toString();
970
+ if (!this.getBlockCache.has(sBlock)) {
971
+ this.getBlockCache.set(sBlock, block);
972
+ }
973
+ }
974
+ }
975
+ async cacheCarReader(carCidStr, reader) {
976
+ if (this.processedCars.has(carCidStr)) return;
977
+ this.processedCars.add(carCidStr);
978
+ for await (const block of reader.blocks()) {
979
+ const sBlock = block.cid.toString();
980
+ if (!this.getBlockCache.has(sBlock)) {
981
+ this.getBlockCache.set(sBlock, block);
982
+ }
983
+ }
984
+ }
985
+ async _commitInternal(t, done, opts = { noLoader: false, compact: false }) {
986
+ await this.ready();
987
+ const fp = this.makeCarHeader(done, this.carLog, !!opts.compact);
988
+ const rootBlock = await encodeCarHeader(fp);
989
+ const cars = await this.prepareCarFiles(rootBlock, t, !!opts.public);
990
+ const cids = [];
991
+ for (const car of cars) {
992
+ const { cid, bytes } = car;
993
+ await (await this.carStore()).save({ cid, bytes });
994
+ cids.push(cid);
995
+ }
996
+ await this.cacheTransaction(t);
997
+ const newDbMeta = { cars: cids, key: this.key || null };
998
+ await (await this.remoteWAL()).enqueue(newDbMeta, opts);
999
+ await (await this.metaStore()).save(newDbMeta);
1000
+ await this.updateCarLog(cids, fp, !!opts.compact);
1001
+ return cids;
1002
+ }
1003
+ async prepareCarFilesFiles(roots, t, isPublic) {
1004
+ const theKey = isPublic ? null : await this._getKey();
1005
+ const car = theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, roots[0], t) : await encodeCarFile(roots, t);
1006
+ return [car];
1007
+ }
1008
+ async prepareCarFiles(rootBlock, t, isPublic) {
1009
+ const theKey = isPublic ? void 0 : await this._getKey();
1010
+ const carFiles = [];
1011
+ const threshold = this.ebOpts.threshold || 1e3 * 1e3;
1012
+ let clonedt = new CarTransaction(t.parent, { add: false });
1013
+ clonedt.putSync(rootBlock.cid, rootBlock.bytes);
1014
+ let newsize = CBW2.blockLength(toCIDBlock(rootBlock));
1015
+ let cidRootBlock = rootBlock;
1016
+ for (const { cid, bytes } of t.entries()) {
1017
+ newsize += CBW2.blockLength(toCIDBlock({ cid, bytes }));
1018
+ if (newsize >= threshold) {
1019
+ carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
1020
+ clonedt = new CarTransaction(t.parent, { add: false });
1021
+ clonedt.putSync(cid, bytes);
1022
+ cidRootBlock = { cid, bytes };
1023
+ newsize = CBW2.blockLength(toCIDBlock({ cid, bytes }));
1024
+ } else {
1025
+ clonedt.putSync(cid, bytes);
1026
+ }
1027
+ }
1028
+ carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
1029
+ return carFiles;
1030
+ }
1031
+ async createCarFile(theKey, cid, t) {
1032
+ try {
1033
+ return theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, cid, t) : await encodeCarFile([cid], t);
1034
+ } catch (e) {
1035
+ console.error("error creating car file", e);
1036
+ throw e;
1037
+ }
1038
+ }
1039
+ makeFileCarHeader(result) {
1040
+ const files = [];
1041
+ for (const [, meta] of Object.entries(result.files || {})) {
1042
+ if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
1043
+ files.push(meta.cid);
1044
+ }
1045
+ }
1046
+ return { ...result, files };
1047
+ }
1048
+ async updateCarLog(cids, fp, compact) {
1049
+ if (compact) {
1050
+ const previousCompactCid = fp.compact[fp.compact.length - 1];
1051
+ fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
1052
+ this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
1053
+ await this.removeCidsForCompact(previousCompactCid[0]);
1054
+ } else {
1055
+ this.carLog.unshift(cids);
1056
+ }
1057
+ }
1058
+ async removeCidsForCompact(cid) {
1059
+ const carHeader = await this.loadCarHeaderFromMeta({
1060
+ cars: [cid]
1061
+ });
1062
+ for (const cids of carHeader.compact) {
1063
+ for (const cid2 of cids) {
1064
+ await (await this.carStore()).remove(cid2);
1065
+ }
1066
+ }
1067
+ }
1068
+ // async flushCars() {
1069
+ // await this.ready
1070
+ // // for each cid in car log, make a dbMeta
1071
+ // for (const cid of this.carLog) {
1072
+ // const dbMeta = { car: cid, key: this.key || null } as DbMeta
1073
+ // await this.remoteWAL!.enqueue(dbMeta, { public: false })
1074
+ // }
1075
+ // }
1076
+ async *entries(cache3 = true) {
1077
+ await this.ready();
1078
+ if (cache3) {
1079
+ for (const [, block] of this.getBlockCache) {
1080
+ yield block;
1081
+ }
1082
+ } else {
1083
+ for (const [, block] of this.getBlockCache) {
1084
+ yield block;
1085
+ }
1086
+ for (const cids of this.carLog) {
1087
+ for (const cid of cids) {
1088
+ const reader = await this.loadCar(cid);
1089
+ if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
1090
+ for await (const block of reader.blocks()) {
1091
+ const sCid = block.cid.toString();
1092
+ if (!this.getBlockCache.has(sCid)) {
1093
+ yield block;
1094
+ }
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+ }
1100
+ async getBlock(cid) {
1101
+ await this.ready();
1102
+ const sCid = cid.toString();
1103
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1104
+ const getCarCid = async (carCid) => {
1105
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1106
+ const reader = await this.loadCar(carCid);
1107
+ if (!reader) {
1108
+ throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
1109
+ }
1110
+ await this.cacheCarReader(carCid.toString(), reader).catch(() => {
1111
+ return;
1112
+ });
1113
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1114
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
1115
+ };
1116
+ const getCompactCarCids = async (carCid) => {
1117
+ const reader = await this.loadCar(carCid);
1118
+ if (!reader) {
1119
+ throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
1120
+ }
1121
+ const header = await parseCarFile(reader, this.logger);
1122
+ const compacts = header.compact;
1123
+ let got2;
1124
+ const batchSize2 = 5;
1125
+ for (let i = 0; i < compacts.length; i += batchSize2) {
1126
+ const promises = [];
1127
+ for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
1128
+ for (const cid2 of compacts[j]) {
1129
+ promises.push(getCarCid(cid2));
1130
+ }
1131
+ }
1132
+ try {
1133
+ got2 = await Promise.any(promises);
1134
+ } catch {
1135
+ }
1136
+ if (got2) break;
1137
+ }
1138
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
1139
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
1140
+ };
1141
+ let got;
1142
+ const batchSize = 5;
1143
+ for (let i = 0; i < this.carLog.length; i += batchSize) {
1144
+ const batch = this.carLog.slice(i, i + batchSize);
1145
+ const promises = batch.flatMap((slice) => slice.map(getCarCid));
1146
+ try {
1147
+ got = await Promise.any(promises);
1148
+ } catch {
1149
+ }
1150
+ if (got) break;
1151
+ }
1152
+ if (!got) {
1153
+ try {
1154
+ got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
1155
+ } catch {
1156
+ }
1157
+ }
1158
+ return got;
1159
+ }
1160
+ makeCarHeader(meta, cars, compact = false) {
1161
+ const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
1162
+ return { ...coreHeader, meta };
1163
+ }
1164
+ async loadCar(cid) {
1165
+ if (!this.carStore) {
1166
+ throw this.logger.Error().Msg("car store not initialized").AsError();
1167
+ }
1168
+ const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
1169
+ return loaded;
1170
+ }
1171
+ //What if instead it returns an Array of CarHeader
1172
+ async storesLoadCar(cid, local, remote, publicFiles) {
1173
+ const cidsString = cid.toString();
1174
+ if (!this.carReaders.has(cidsString)) {
1175
+ this.carReaders.set(
1176
+ cidsString,
1177
+ (async () => {
1178
+ let loadedCar = void 0;
1179
+ try {
1180
+ this.logger.Debug().Str("cid", cidsString).Msg("loading car");
1181
+ loadedCar = await local.load(cid);
1182
+ this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
1183
+ } catch (e) {
1184
+ if (remote) {
1185
+ const remoteCar = await remote.load(cid);
1186
+ if (remoteCar) {
1187
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
1188
+ await local.save(remoteCar);
1189
+ loadedCar = remoteCar;
1190
+ }
1191
+ } else {
1192
+ this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
1193
+ }
1194
+ }
1195
+ if (!loadedCar) {
1196
+ throw this.logger.Error().Url(local.url).Str("cid", cidsString).Msg("missing car files").AsError();
1197
+ }
1198
+ const rawReader = await CarReader.fromBytes(loadedCar.bytes);
1199
+ const readerP = publicFiles ? Promise.resolve(rawReader) : this.ensureDecryptedReader(rawReader);
1200
+ const cachedReaderP = readerP.then(async (reader) => {
1201
+ await this.cacheCarReader(cidsString, reader).catch(() => {
1202
+ return;
1203
+ });
1204
+ return reader;
1205
+ });
1206
+ this.carReaders.set(cidsString, cachedReaderP);
1207
+ return readerP;
1208
+ })().catch((e) => {
1209
+ this.carReaders.delete(cidsString);
1210
+ throw e;
1211
+ })
1212
+ );
1213
+ }
1214
+ return this.carReaders.get(cidsString);
1215
+ }
1216
+ async ensureDecryptedReader(reader) {
1217
+ const theKey = await this._getKey();
1218
+ if (this.ebOpts.public || !(theKey && this.ebOpts.crypto)) {
1219
+ return reader;
1220
+ }
1221
+ const { blocks, root: root3 } = await decodeEncryptedCar(this.logger, this.ebOpts.crypto, theKey, reader);
1222
+ return {
1223
+ getRoots: () => [root3],
1224
+ get: blocks.get.bind(blocks),
1225
+ blocks: blocks.entries.bind(blocks)
1226
+ };
1227
+ }
1228
+ async setKey(key) {
1229
+ if (this.key && this.key !== key)
1230
+ throw this.logger.Error().Str("this.key", this.key).Str("key", key).Msg("setting key").AsError();
1231
+ this.key = key;
1232
+ const encoder = new TextEncoder();
1233
+ const data = encoder.encode(key);
1234
+ const hashBuffer = await this.ebOpts.crypto.digestSHA256(data);
1235
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
1236
+ this.keyId = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
1237
+ }
1238
+ async getMoreReaders(cids) {
1239
+ const limit = pLimit(5);
1240
+ const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
1241
+ await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
1242
+ }
1243
+ };
1244
+
1245
+ // src/blockstore/store.ts
1246
+ var VersionedStore = class {
1247
+ constructor(name, url, logger) {
1248
+ this._onStarted = [];
1249
+ this._onClosed = [];
1250
+ this.name = name;
1251
+ this.url = url;
1252
+ this.logger = logger;
1253
+ }
1254
+ onStarted(fn) {
1255
+ this._onStarted.push(fn);
1256
+ }
1257
+ onClosed(fn) {
1258
+ this._onClosed.push(fn);
1259
+ }
1260
+ };
1261
+ var textEncoder = new TextEncoder();
1262
+ var textDecoder = new TextDecoder();
1263
+ var MetaStore = class extends VersionedStore {
1264
+ constructor(name, url, logger, gateway) {
1265
+ super(name, url, ensureLogger(logger, "MetaStore", {}));
1266
+ this.tag = "header-base";
1267
+ this.gateway = gateway;
1268
+ }
1269
+ makeHeader({ cars, key }) {
1270
+ const toEncode = { cars };
1271
+ if (key) toEncode.key = key;
1272
+ return format(toEncode);
1273
+ }
1274
+ parseHeader(headerData) {
1275
+ const got = parse(headerData);
1276
+ return got;
1277
+ }
1278
+ async start() {
1279
+ this.logger.Debug().Msg("starting");
1280
+ const res = await this.gateway.start(this.url);
1281
+ if (res.isErr()) {
1282
+ return res;
1283
+ }
1284
+ this._onStarted.forEach((fn) => fn());
1285
+ return guardVersion(this.url);
1286
+ }
1287
+ async load(branch) {
1288
+ this.logger.Debug().Str("branch", branch || "").Msg("loading");
1289
+ const url = await this.gateway.buildUrl(this.url, branch || "main");
1290
+ if (url.isErr()) {
1291
+ throw this.logger.Error().Err(url.Err()).Str("branch", branch || "").Str("url", this.url.toString()).Msg("got error from gateway.buildUrl").AsError();
1292
+ }
1293
+ const bytes = await this.gateway.get(url.Ok());
1294
+ if (bytes.isErr()) {
1295
+ if (isNotFoundError(bytes)) {
1296
+ return void 0;
1297
+ }
1298
+ throw this.logger.Error().Err(bytes.Err()).Msg("gateway get").AsError();
1299
+ }
1300
+ try {
1301
+ return [this.parseHeader(textDecoder.decode(bytes.Ok()))];
1302
+ } catch (e) {
1303
+ throw this.logger.Error().Err(e).Msg("parseHeader").AsError();
1304
+ }
1305
+ }
1306
+ async save(meta, branch = "main") {
1307
+ this.logger.Debug().Str("branch", branch).Any("meta", meta).Msg("saving meta");
1308
+ const bytes = this.makeHeader(meta);
1309
+ const url = await this.gateway.buildUrl(this.url, branch);
1310
+ if (url.isErr()) {
1311
+ throw this.logger.Error().Err(url.Err()).Str("branch", branch).Url(this.url).Msg("got error from gateway.buildUrl").AsError();
1312
+ }
1313
+ const res = await this.gateway.put(url.Ok(), textEncoder.encode(bytes));
1314
+ if (res.isErr()) {
1315
+ throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
1316
+ }
1317
+ return res.Ok();
1318
+ }
1319
+ async close() {
1320
+ await this.gateway.close(this.url);
1321
+ this._onClosed.forEach((fn) => fn());
1322
+ return Result.Ok(void 0);
1323
+ }
1324
+ async destroy() {
1325
+ return this.gateway.destroy(this.url);
1326
+ }
1327
+ };
1328
+ var DataStore = class extends VersionedStore {
1329
+ constructor(name, url, logger, gateway) {
1330
+ super(
1331
+ name,
1332
+ url,
1333
+ ensureLogger(logger, "DataStore", {
1334
+ url: () => url.toString()
1335
+ })
1336
+ );
1337
+ this.tag = "car-base";
1338
+ this.gateway = gateway;
1339
+ }
1340
+ async start() {
1341
+ this.logger.Debug().Msg("starting-gateway");
1342
+ const res = await this.gateway.start(this.url);
1343
+ if (res.isErr()) {
1344
+ this.logger.Error().Err(res.Err()).Msg("started-gateway");
1345
+ return res;
1346
+ }
1347
+ this._onStarted.forEach((fn) => fn());
1348
+ const version = guardVersion(this.url);
1349
+ if (version.isErr()) {
1350
+ this.logger.Error().Err(res.Err()).Msg("guardVersion");
1351
+ await this.close();
1352
+ return version;
1353
+ }
1354
+ this.logger.Debug().Msg("started");
1355
+ return version;
1356
+ }
1357
+ async load(cid) {
1358
+ this.logger.Debug().Any("cid", cid).Msg("loading");
1359
+ const url = await this.gateway.buildUrl(this.url, cid.toString());
1360
+ if (url.isErr()) {
1361
+ throw this.logger.Error().Err(url.Err()).Str("cid", cid.toString()).Msg("got error from gateway.buildUrl").AsError();
1362
+ }
1363
+ const res = await this.gateway.get(url.Ok());
1364
+ if (res.isErr()) {
1365
+ throw res.Err();
1366
+ }
1367
+ return { cid, bytes: res.Ok() };
1368
+ }
1369
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1370
+ async save(car, opts) {
1371
+ this.logger.Debug().Any("cid", car.cid.toString()).Msg("saving");
1372
+ const url = await this.gateway.buildUrl(this.url, car.cid.toString());
1373
+ if (url.isErr()) {
1374
+ throw this.logger.Error().Err(url.Err()).Ref("cid", car.cid).Msg("got error from gateway.buildUrl").AsError();
1375
+ }
1376
+ const res = await this.gateway.put(url.Ok(), car.bytes);
1377
+ if (res.isErr()) {
1378
+ throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
1379
+ }
1380
+ return res.Ok();
1381
+ }
1382
+ async remove(cid) {
1383
+ const url = await this.gateway.buildUrl(this.url, cid.toString());
1384
+ if (url.isErr()) {
1385
+ return url;
1386
+ }
1387
+ return this.gateway.delete(url.Ok());
1388
+ }
1389
+ async close() {
1390
+ await this.gateway.close(this.url);
1391
+ this._onClosed.forEach((fn) => fn());
1392
+ return Result.Ok(void 0);
1393
+ }
1394
+ destroy() {
1395
+ return this.gateway.destroy(this.url);
1396
+ }
1397
+ };
1398
+ var RemoteWAL = class extends VersionedStore {
1399
+ constructor(loader, url, logger, gateway) {
1400
+ super(loader.name, url, ensureLogger(logger, "RemoteWAL"));
1401
+ this.tag = "rwal-base";
1402
+ this._ready = new ResolveOnce2();
1403
+ this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
1404
+ this.processing = void 0;
1405
+ this.processQueue = new CommitQueue();
1406
+ this.loader = loader;
1407
+ this.gateway = gateway;
1408
+ }
1409
+ async ready() {
1410
+ return this._ready.once(async () => {
1411
+ const walState = await this.load().catch((e) => {
1412
+ this.logger.Error().Any("error", e).Msg("error loading wal");
1413
+ return void 0;
1414
+ });
1415
+ if (!walState) {
1416
+ this.walState.operations = [];
1417
+ this.walState.fileOperations = [];
1418
+ } else {
1419
+ this.walState.operations = walState.operations || [];
1420
+ this.walState.fileOperations = walState.fileOperations || [];
1421
+ }
1422
+ });
1423
+ }
1424
+ async enqueue(dbMeta, opts) {
1425
+ await this.ready();
1426
+ if (opts.noLoader) {
1427
+ this.walState.noLoaderOps.push(dbMeta);
1428
+ } else {
1429
+ this.walState.operations.push(dbMeta);
1430
+ }
1431
+ await this.save(this.walState);
1432
+ void this._process();
1433
+ }
1434
+ async enqueueFile(fileCid, publicFile = false) {
1435
+ await this.ready();
1436
+ this.walState.fileOperations.push({ cid: fileCid, public: publicFile });
1437
+ }
1438
+ async _process() {
1439
+ await this.ready();
1440
+ if (!this.loader.remoteCarStore) return;
1441
+ await this.processQueue.enqueue(async () => {
1442
+ await this._doProcess();
1443
+ if (this.walState.operations.length || this.walState.fileOperations.length || this.walState.noLoaderOps.length) {
1444
+ setTimeout(() => void this._process(), 0);
1445
+ }
1446
+ });
1447
+ }
1448
+ async _doProcess() {
1449
+ if (!this.loader.remoteCarStore) return;
1450
+ const rmlp = (async () => {
1451
+ const operations = [...this.walState.operations];
1452
+ const fileOperations = [...this.walState.fileOperations];
1453
+ const uploads = [];
1454
+ const noLoaderOps = [...this.walState.noLoaderOps];
1455
+ const limit = pLimit2(5);
1456
+ if (operations.length + fileOperations.length + noLoaderOps.length === 0) return;
1457
+ for (const dbMeta of noLoaderOps) {
1458
+ const uploadP = limit(async () => {
1459
+ for (const cid of dbMeta.cars) {
1460
+ const car = await (await this.loader.carStore()).load(cid);
1461
+ if (!car) {
1462
+ if (carLogIncludesGroup2(this.loader.carLog, dbMeta.cars))
1463
+ throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
1464
+ } else {
1465
+ await throwFalsy(this.loader.remoteCarStore).save(car);
1466
+ }
1467
+ this.walState.noLoaderOps = this.walState.noLoaderOps.filter((op) => op !== dbMeta);
1468
+ }
1469
+ });
1470
+ uploads.push(uploadP);
1471
+ }
1472
+ for (const dbMeta of operations) {
1473
+ const uploadP = limit(async () => {
1474
+ for (const cid of dbMeta.cars) {
1475
+ const car = await (await this.loader.carStore()).load(cid).catch(() => null);
1476
+ if (!car) {
1477
+ if (carLogIncludesGroup2(this.loader.carLog, dbMeta.cars))
1478
+ throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
1479
+ } else {
1480
+ await throwFalsy(this.loader.remoteCarStore).save(car);
1481
+ }
1482
+ }
1483
+ this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
1484
+ });
1485
+ uploads.push(uploadP);
1486
+ }
1487
+ if (fileOperations.length) {
1488
+ const dbLoader = this.loader;
1489
+ for (const { cid: fileCid, public: publicFile } of fileOperations) {
1490
+ const uploadP = limit(async () => {
1491
+ const fileBlock = await (await dbLoader.fileStore()).load(fileCid);
1492
+ await dbLoader.remoteFileStore?.save(fileBlock, { public: publicFile });
1493
+ this.walState.fileOperations = this.walState.fileOperations.filter((op) => op.cid !== fileCid);
1494
+ });
1495
+ uploads.push(uploadP);
1496
+ }
1497
+ }
1498
+ try {
1499
+ const res = await Promise.allSettled(uploads);
1500
+ const errors = res.filter((r) => r.status === "rejected");
1501
+ if (errors.length) {
1502
+ throw this.logger.Error().Any(
1503
+ "errors",
1504
+ errors.map((e) => e.reason)
1505
+ ).Msg("error uploading").AsError();
1506
+ errors[0].reason;
1507
+ }
1508
+ if (operations.length) {
1509
+ const lastOp = operations[operations.length - 1];
1510
+ await this.loader.remoteMetaStore?.save(lastOp).catch((e) => {
1511
+ this.walState.operations.push(lastOp);
1512
+ throw this.logger.Error().Any("error", e).Msg("error saving remote meta").AsError();
1513
+ });
1514
+ }
1515
+ } finally {
1516
+ await this.save(this.walState);
1517
+ }
1518
+ })();
1519
+ await rmlp;
1520
+ }
1521
+ async start() {
1522
+ const res = await this.gateway.start(this.url);
1523
+ if (res.isErr()) {
1524
+ return res;
1525
+ }
1526
+ const ver = guardVersion(this.url);
1527
+ if (ver.isErr()) {
1528
+ await this.close();
1529
+ return ver;
1530
+ }
1531
+ const ready = await exception2Result(() => this.ready());
1532
+ this._onStarted.forEach((fn) => fn());
1533
+ if (ready.isErr()) {
1534
+ await this.close();
1535
+ return ready;
1536
+ }
1537
+ return ready;
1538
+ }
1539
+ async load() {
1540
+ this.logger.Debug().Msg("loading");
1541
+ const filepath = await this.gateway.buildUrl(this.url, "main");
1542
+ if (filepath.isErr()) {
1543
+ throw this.logger.Error().Err(filepath.Err()).Str("url", this.url.toString()).Msg("error building url").AsError();
1544
+ }
1545
+ const bytes = await this.gateway.get(filepath.Ok());
1546
+ if (bytes.isErr()) {
1547
+ if (isNotFoundError(bytes)) {
1548
+ return void 0;
1549
+ }
1550
+ throw this.logger.Error().Err(bytes.Err()).Msg("error get").AsError();
1551
+ }
1552
+ try {
1553
+ return bytes && parse(textDecoder.decode(bytes.Ok()));
1554
+ } catch (e) {
1555
+ throw this.logger.Error().Err(e).Msg("error parse").AsError();
1556
+ }
1557
+ }
1558
+ async save(state) {
1559
+ const filepath = await this.gateway.buildUrl(this.url, "main");
1560
+ if (filepath.isErr()) {
1561
+ throw this.logger.Error().Err(filepath.Err()).Str("url", this.url.toString()).Msg("error building url").AsError();
1562
+ }
1563
+ let encoded;
1564
+ try {
1565
+ encoded = format(state);
1566
+ } catch (e) {
1567
+ throw this.logger.Error().Err(e).Any("state", state).Msg("error format").AsError();
1568
+ }
1569
+ const res = await this.gateway.put(filepath.Ok(), textEncoder.encode(encoded));
1570
+ if (res.isErr()) {
1571
+ throw this.logger.Error().Err(res.Err()).Str("filePath", filepath.Ok().toString()).Msg("error saving").AsError();
1572
+ }
1573
+ }
1574
+ async close() {
1575
+ await this.gateway.close(this.url);
1576
+ this._onClosed.forEach((fn) => fn());
1577
+ return Result.Ok(void 0);
1578
+ }
1579
+ destroy() {
1580
+ return this.gateway.destroy(this.url);
1581
+ }
1582
+ };
1583
+
1584
+ // src/blockstore/store-factory.ts
1585
+ function ensureIsIndex(url, isIndex) {
1586
+ if (isIndex) {
1587
+ url.searchParams.set("index", isIndex);
1588
+ return url;
1589
+ } else {
1590
+ url.searchParams.delete("index");
1591
+ return url;
1592
+ }
1593
+ }
1594
+ function toURL(pathOrUrl, isIndex) {
1595
+ if (pathOrUrl instanceof URL) return ensureIsIndex(pathOrUrl, isIndex);
1596
+ try {
1597
+ const url = new URL(pathOrUrl);
1598
+ return ensureIsIndex(url, isIndex);
1599
+ } catch (e) {
1600
+ const url = new URL(`file://${pathOrUrl}`);
1601
+ return ensureIsIndex(url, isIndex);
1602
+ }
1603
+ }
1604
+ var storeFactory = /* @__PURE__ */ new Map();
1605
+ function ensureName(name, url) {
1606
+ if (!url.searchParams.has("name")) {
1607
+ url.searchParams.set("name", name);
1608
+ }
1609
+ }
1610
+ function buildURL(optURL, loader) {
1611
+ const storeOpts = loader.ebOpts.store;
1612
+ const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
1613
+ let obuUrl;
1614
+ if (obuItem && obuItem.overrideBaseURL) {
1615
+ obuUrl = new URL(obuItem.overrideBaseURL);
1616
+ }
1617
+ return toURL(optURL || obuUrl || dataDir(loader.name, storeOpts.stores?.base), storeOpts.isIndex);
1618
+ }
1619
+ function registerStoreProtocol(item) {
1620
+ if (storeFactory.has(item.protocol)) {
1621
+ throw new Error(`protocol ${item.protocol} already registered`);
1622
+ }
1623
+ if (item.overrideBaseURL) {
1624
+ Array.from(storeFactory.values()).forEach((items) => {
1625
+ items.overrideBaseURL = void 0;
1626
+ });
1627
+ }
1628
+ storeFactory.set(item.protocol, item);
1629
+ return () => {
1630
+ storeFactory.delete(item.protocol);
1631
+ };
1632
+ }
1633
+ function runStoreFactory(url, logger, run) {
1634
+ const item = storeFactory.get(url.protocol);
1635
+ if (!item) {
1636
+ throw logger.Error().Url(url).Str("protocol", url.protocol).Any("keys", Array(storeFactory.keys())).Msg(`unsupported protocol`).AsError();
1637
+ }
1638
+ logger.Debug().Str("protocol", url.protocol).Msg("run");
1639
+ return run(item);
1640
+ }
1641
+ var onceLoadDataGateway = new KeyedResolvOnce();
1642
+ function loadDataGateway(url, logger) {
1643
+ return onceLoadDataGateway.get(url.protocol).once(async () => {
1644
+ return runStoreFactory(url, logger, async (item) => item.data(logger));
1645
+ });
1646
+ }
1647
+ var onceDataStoreFactory = new KeyedResolvOnce();
1648
+ async function dataStoreFactory(loader) {
1649
+ const url = buildURL(loader.ebOpts.store.stores?.data, loader);
1650
+ ensureName(loader.name, url);
1651
+ const logger = ensureLogger(loader.logger, "dataStoreFactory", { url: url.toString() });
1652
+ url.searchParams.set("store", "data");
1653
+ return onceDataStoreFactory.get(url.toString()).once(async () => {
1654
+ const gateway = await loadDataGateway(url, logger);
1655
+ const store = new DataStore(loader.name, url, loader.logger, gateway);
1656
+ await store.start();
1657
+ logger.Debug().Str("prepared", store.url.toString()).Msg("produced");
1658
+ return store;
1659
+ });
1660
+ }
1661
+ var onceLoadMetaGateway = new KeyedResolvOnce();
1662
+ function loadMetaGateway(url, logger) {
1663
+ return onceLoadMetaGateway.get(url.protocol).once(async () => {
1664
+ return runStoreFactory(url, logger, async (item) => item.meta(logger));
1665
+ });
1666
+ }
1667
+ var onceMetaStoreFactory = new KeyedResolvOnce();
1668
+ async function metaStoreFactory(loader) {
1669
+ const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
1670
+ ensureName(loader.name, url);
1671
+ const logger = ensureLogger(loader.logger, "metaStoreFactory", { url: () => url.toString() });
1672
+ url.searchParams.set("store", "meta");
1673
+ return onceMetaStoreFactory.get(url.toString()).once(async () => {
1674
+ logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
1675
+ const gateway = await loadMetaGateway(url, logger);
1676
+ const store = new MetaStore(loader.name, url, loader.logger, gateway);
1677
+ await store.start();
1678
+ return store;
1679
+ });
1680
+ }
1681
+ var onceWalGateway = new KeyedResolvOnce();
1682
+ function loadWalGateway(url, logger) {
1683
+ return onceWalGateway.get(url.protocol).once(async () => {
1684
+ return runStoreFactory(url, logger, async (item) => item.wal(logger));
1685
+ });
1686
+ }
1687
+ var onceRemoteWalFactory = new KeyedResolvOnce();
1688
+ async function remoteWalFactory(loader) {
1689
+ const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
1690
+ ensureName(loader.name, url);
1691
+ const logger = ensureLogger(loader.logger, "remoteWalFactory", { url: url.toString() });
1692
+ url.searchParams.set("store", "wal");
1693
+ return onceRemoteWalFactory.get(url.toString()).once(async () => {
1694
+ const gateway = await loadWalGateway(url, logger);
1695
+ logger.Debug().Str("prepared", url.toString()).Msg("produced");
1696
+ const store = new RemoteWAL(loader, url, loader.logger, gateway);
1697
+ await store.start();
1698
+ return store;
1699
+ });
1700
+ }
1701
+ async function testStoreFactory(url, ilogger) {
1702
+ const logger = ensureLogger(
1703
+ {
1704
+ logger: ilogger
1705
+ },
1706
+ "testStoreFactory"
1707
+ );
1708
+ return runStoreFactory(url, logger, async (item) => item.test(logger));
1709
+ }
1710
+ function toStoreRuntime(opts, ilogger) {
1711
+ const logger = ensureLogger(ilogger, "toStoreRuntime", {});
1712
+ return {
1713
+ makeMetaStore: (loader) => {
1714
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
1715
+ return (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader);
1716
+ },
1717
+ makeDataStore: (loader) => {
1718
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
1719
+ return (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader);
1720
+ },
1721
+ makeRemoteWAL: (loader) => {
1722
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeRemoteWAL).Msg("makeRemoteWAL");
1723
+ return (loader.ebOpts.store.makeRemoteWAL || remoteWalFactory)(loader);
1724
+ },
1725
+ encodeFile: opts.encodeFile || encodeFile,
1726
+ decodeFile: opts.decodeFile || decodeFile
1727
+ };
1728
+ }
1729
+ registerStoreProtocol({
1730
+ protocol: "file:",
1731
+ data: async (logger) => {
1732
+ const { FileDataGateway } = await import("./store-file-CSS5THFH.js");
1733
+ return new FileDataGateway(logger);
1734
+ },
1735
+ meta: async (logger) => {
1736
+ const { FileMetaGateway } = await import("./store-file-CSS5THFH.js");
1737
+ return new FileMetaGateway(logger);
1738
+ },
1739
+ wal: async (logger) => {
1740
+ const { FileWALGateway } = await import("./store-file-CSS5THFH.js");
1741
+ return new FileWALGateway(logger);
1742
+ },
1743
+ test: async (logger) => {
1744
+ const { FileTestStore } = await import("./store-file-CSS5THFH.js");
1745
+ return new FileTestStore(logger);
1746
+ }
1747
+ });
1748
+ registerStoreProtocol({
1749
+ protocol: "indexdb:",
1750
+ data: async (logger) => {
1751
+ const { IndexDBDataGateway } = await import("./store-indexdb-DR4HELVP.js");
1752
+ return new IndexDBDataGateway(logger);
1753
+ },
1754
+ meta: async (logger) => {
1755
+ const { IndexDBMetaGateway } = await import("./store-indexdb-DR4HELVP.js");
1756
+ return new IndexDBMetaGateway(logger);
1757
+ },
1758
+ wal: async (logger) => {
1759
+ const { IndexDBMetaGateway } = await import("./store-indexdb-DR4HELVP.js");
1760
+ return new IndexDBMetaGateway(logger);
1761
+ },
1762
+ test: async (logger) => {
1763
+ const { IndexDBTestStore } = await import("./store-indexdb-DR4HELVP.js");
1764
+ return new IndexDBTestStore(logger);
1765
+ }
1766
+ });
1767
+ registerStoreProtocol({
1768
+ protocol: "sqlite:",
1769
+ data: async (logger) => {
1770
+ const { SQLDataGateway } = await import("./store-sql-BG6SMGQJ.js");
1771
+ return new SQLDataGateway(logger);
1772
+ },
1773
+ meta: async (logger) => {
1774
+ const { SQLMetaGateway } = await import("./store-sql-BG6SMGQJ.js");
1775
+ return new SQLMetaGateway(logger);
1776
+ },
1777
+ wal: async (logger) => {
1778
+ const { SQLWalGateway } = await import("./store-sql-BG6SMGQJ.js");
1779
+ return new SQLWalGateway(logger);
1780
+ },
1781
+ test: async (logger) => {
1782
+ const { SQLTestStore } = await import("./store-sql-BG6SMGQJ.js");
1783
+ return new SQLTestStore(logger);
1784
+ }
1785
+ });
1786
+
1787
+ // src/crdt-helpers.ts
1788
+ function time(tag) {
1789
+ }
1790
+ function timeEnd(tag) {
1791
+ }
1792
+ function toString(key, logger) {
1793
+ switch (typeof key) {
1794
+ case "string":
1795
+ case "number":
1796
+ return key.toString();
1797
+ default:
1798
+ throw logger.Error().Msg("Invalid key type").AsError();
1799
+ }
1800
+ }
1801
+ async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
1802
+ let result = null;
1803
+ if (updates.length > 1) {
1804
+ const batch = await Batch.create(tblocks, head);
1805
+ for (const update of updates) {
1806
+ const link = await writeDocContent(store, tblocks, update, logger);
1807
+ await batch.put(toString(update.id, logger), link);
1808
+ }
1809
+ result = await batch.commit();
1810
+ } else if (updates.length === 1) {
1811
+ const link = await writeDocContent(store, tblocks, updates[0], logger);
1812
+ result = await put(tblocks, head, toString(updates[0].id, logger), link);
1813
+ }
1814
+ if (!result) throw logger.Error().Uint64("updates.len", updates.length).Msg("Missing result").AsError();
1815
+ if (result.event) {
1816
+ for (const { cid, bytes } of [
1817
+ ...result.additions,
1818
+ // ...result.removals,
1819
+ result.event
1820
+ ]) {
1821
+ tblocks.putSync(cid, bytes);
1822
+ }
1823
+ }
1824
+ return { head: result.head };
1825
+ }
1826
+ async function writeDocContent(store, blocks, update, logger) {
1827
+ let value;
1828
+ if (update.del) {
1829
+ value = { del: true };
1830
+ } else {
1831
+ if (!update.value) throw logger.Error().Msg("Missing value").AsError();
1832
+ await processFiles(store, blocks, update.value, logger);
1833
+ value = { doc: update.value };
1834
+ }
1835
+ const block = await encode3({ value, hasher: hasher2, codec: codec2 });
1836
+ blocks.putSync(block.cid, block.bytes);
1837
+ return block.cid;
1838
+ }
1839
+ async function processFiles(store, blocks, doc, logger) {
1840
+ if (doc._files) {
1841
+ await processFileset(logger, store, blocks, doc._files);
1842
+ }
1843
+ if (doc._publicFiles) {
1844
+ await processFileset(logger, store, blocks, doc._publicFiles, true);
1845
+ }
1846
+ }
1847
+ async function processFileset(logger, store, blocks, files, publicFiles = false) {
1848
+ const dbBlockstore = blocks.parent;
1849
+ if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, database name is required").AsError();
1850
+ const t = new CarTransaction(dbBlockstore);
1851
+ const didPut = [];
1852
+ for (const filename in files) {
1853
+ if (File === files[filename].constructor) {
1854
+ const file = files[filename];
1855
+ const { cid, blocks: fileBlocks } = await store.encodeFile(file);
1856
+ didPut.push(filename);
1857
+ for (const block of fileBlocks) {
1858
+ t.putSync(block.cid, block.bytes);
1859
+ }
1860
+ files[filename] = { cid, type: file.type, size: file.size };
1861
+ } else {
1862
+ const { cid, type, size, car } = files[filename];
1863
+ if (cid && type && size && car) {
1864
+ files[filename] = { cid, type, size, car };
1865
+ }
1866
+ }
1867
+ }
1868
+ if (didPut.length) {
1869
+ const car = await dbBlockstore.loader.commitFiles(t, { files }, {
1870
+ public: publicFiles
1871
+ });
1872
+ if (car) {
1873
+ for (const name of didPut) {
1874
+ files[name] = { car, ...files[name] };
1875
+ }
1876
+ }
1877
+ }
1878
+ }
1879
+ async function getValueFromCrdt(blocks, head, key, logger) {
1880
+ if (!head.length) throw logger.Debug().Msg("Getting from an empty database").AsError();
1881
+ const link = await get(blocks, head, key);
1882
+ if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
1883
+ return await getValueFromLink(blocks, link, logger);
1884
+ }
1885
+ function readFiles(blocks, { doc }) {
1886
+ if (!doc) return;
1887
+ if (doc._files) {
1888
+ readFileset(blocks, doc._files);
1889
+ }
1890
+ if (doc._publicFiles) {
1891
+ readFileset(blocks, doc._publicFiles, true);
1892
+ }
1893
+ }
1894
+ function readFileset(blocks, files, isPublic = false) {
1895
+ for (const filename in files) {
1896
+ const fileMeta = files[filename];
1897
+ if (fileMeta.cid) {
1898
+ if (isPublic) {
1899
+ fileMeta.url = `https://${fileMeta.cid.toString()}.ipfs.w3s.link/`;
1900
+ }
1901
+ if (fileMeta.car) {
1902
+ fileMeta.file = async () => await blocks.ebOpts.storeRuntime.decodeFile(
1903
+ {
1904
+ get: async (cid) => {
1905
+ return await blocks.getFile(throwFalsy(fileMeta.car), cid, isPublic);
1906
+ }
1907
+ },
1908
+ fileMeta.cid,
1909
+ fileMeta
1910
+ );
1911
+ }
1912
+ }
1913
+ files[filename] = fileMeta;
1914
+ }
1915
+ }
1916
+ async function getValueFromLink(blocks, link, logger) {
1917
+ const block = await blocks.get(link);
1918
+ if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
1919
+ const { value } = await decode3({ bytes: block.bytes, hasher: hasher2, codec: codec2 });
1920
+ const cvalue = {
1921
+ ...value,
1922
+ cid: link
1923
+ };
1924
+ readFiles(blocks, cvalue);
1925
+ return cvalue;
1926
+ }
1927
+ var DirtyEventFetcher = class extends EventFetcher {
1928
+ async get(link) {
1929
+ try {
1930
+ return super.get(link);
1931
+ } catch (e) {
1932
+ console.error("missing event", link.toString(), e);
1933
+ return { value: void 0 };
1934
+ }
1935
+ }
1936
+ };
1937
+ async function clockChangesSince(blocks, head, since, opts, logger) {
1938
+ const eventsFetcher = opts.dirty ? new DirtyEventFetcher(blocks) : new EventFetcher(blocks);
1939
+ const keys = /* @__PURE__ */ new Set();
1940
+ const updates = await gatherUpdates(
1941
+ blocks,
1942
+ eventsFetcher,
1943
+ head,
1944
+ since,
1945
+ [],
1946
+ keys,
1947
+ /* @__PURE__ */ new Set(),
1948
+ opts.limit || Infinity,
1949
+ logger
1950
+ );
1951
+ return { result: updates.reverse(), head };
1952
+ }
1953
+ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], keys, didLinks, limit, logger) {
1954
+ if (limit <= 0) return updates;
1955
+ const sHead = head.map((l) => l.toString());
1956
+ for (const link of since) {
1957
+ if (sHead.includes(link.toString())) {
1958
+ return updates;
1959
+ }
1960
+ }
1961
+ for (const link of head) {
1962
+ if (didLinks.has(link.toString())) continue;
1963
+ didLinks.add(link.toString());
1964
+ const { value: event } = await eventsFetcher.get(link);
1965
+ if (!event) continue;
1966
+ const { type } = event.data;
1967
+ let ops = [];
1968
+ if (type === "batch") {
1969
+ ops = event.data.ops;
1970
+ } else if (type === "put") {
1971
+ ops = [event.data];
1972
+ }
1973
+ for (let i = ops.length - 1; i >= 0; i--) {
1974
+ const { key, value } = ops[i];
1975
+ if (!keys.has(key)) {
1976
+ const docValue = await getValueFromLink(blocks, value, logger);
1977
+ updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
1978
+ limit--;
1979
+ keys.add(key);
1980
+ }
1981
+ }
1982
+ if (event.parents) {
1983
+ updates = await gatherUpdates(blocks, eventsFetcher, event.parents, since, updates, keys, didLinks, limit, logger);
1984
+ }
1985
+ }
1986
+ return updates;
1987
+ }
1988
+ async function* getAllEntries(blocks, head, logger) {
1989
+ for await (const [key, link] of entries(blocks, head)) {
1990
+ const docValue = await getValueFromLink(blocks, link, logger);
1991
+ yield { id: key, value: docValue.doc, del: docValue.del };
1992
+ }
1993
+ }
1994
+ async function* clockVis(blocks, head) {
1995
+ for await (const line of vis(blocks, head)) {
1996
+ yield line;
1997
+ }
1998
+ }
1999
+ var isCompacting = false;
2000
+ async function doCompact(blockLog, head, logger) {
2001
+ if (isCompacting) {
2002
+ return;
2003
+ }
2004
+ isCompacting = true;
2005
+ time("compact head");
2006
+ for (const cid of head) {
2007
+ const bl = await blockLog.get(cid);
2008
+ if (!bl) throw logger.Error().Ref("cid", cid).Msg("Missing head block").AsError();
2009
+ }
2010
+ timeEnd("compact head");
2011
+ time("compact all entries");
2012
+ for await (const _entry of getAllEntries(blockLog, head, logger)) {
2013
+ continue;
2014
+ }
2015
+ timeEnd("compact all entries");
2016
+ time("compact clock vis");
2017
+ for await (const _line of vis(blockLog, head)) {
2018
+ }
2019
+ timeEnd("compact clock vis");
2020
+ time("compact root");
2021
+ const result = await root(blockLog, head);
2022
+ timeEnd("compact root");
2023
+ time("compact root blocks");
2024
+ for (const { cid, bytes } of [...result.additions, ...result.removals]) {
2025
+ blockLog.loggedBlocks.putSync(cid, bytes);
2026
+ }
2027
+ timeEnd("compact root blocks");
2028
+ time("compact changes");
2029
+ await clockChangesSince(blockLog, head, [], {}, logger);
2030
+ timeEnd("compact changes");
2031
+ isCompacting = false;
2032
+ }
2033
+ async function getBlock(blocks, cidString) {
2034
+ const block = await blocks.get(parse2(cidString));
2035
+ if (!block) throw new Error(`Missing block ${cidString}`);
2036
+ const { cid, value } = await decode3({ bytes: block.bytes, codec: codec2, hasher: hasher2 });
2037
+ return new Block2({ cid, value, bytes: block.bytes });
2038
+ }
2039
+
2040
+ // src/indexer-helpers.ts
2041
+ import { create as create3 } from "multiformats/block";
2042
+ import { sha256 as hasher3 } from "multiformats/hashes/sha2";
2043
+ import * as codec3 from "@ipld/dag-cbor";
2044
+ import charwise from "charwise";
2045
+ import * as DbIndex from "prolly-trees/db-index";
2046
+ import { bf as bf2, simpleCompare } from "prolly-trees/utils";
2047
+ import { nocache as cache2 } from "prolly-trees/cache";
2048
+ var IndexTree = class {
2049
+ };
2050
+ function refCompare(aRef, bRef) {
2051
+ if (Number.isNaN(aRef)) return -1;
2052
+ if (Number.isNaN(bRef)) throw new Error("ref may not be Infinity or NaN");
2053
+ if (aRef === Infinity) return 1;
2054
+ return simpleCompare(aRef, bRef);
2055
+ }
2056
+ function compare(a, b) {
2057
+ const [aKey, aRef] = a;
2058
+ const [bKey, bRef] = b;
2059
+ const comp = simpleCompare(aKey, bKey);
2060
+ if (comp !== 0) return comp;
2061
+ return refCompare(aRef, bRef);
2062
+ }
2063
+ var byKeyOpts = { cache: cache2, chunker: bf2(30), codec: codec3, hasher: hasher3, compare };
2064
+ var byIdOpts = { cache: cache2, chunker: bf2(30), codec: codec3, hasher: hasher3, compare: simpleCompare };
2065
+ function indexEntriesForChanges(changes, mapFn) {
2066
+ const indexEntries = [];
2067
+ changes.forEach(({ id: key, value, del }) => {
2068
+ if (del || !value) return;
2069
+ let mapCalled = false;
2070
+ const mapReturn = mapFn({ ...value, _id: key }, (k, v) => {
2071
+ mapCalled = true;
2072
+ if (typeof k === "undefined") return;
2073
+ indexEntries.push({
2074
+ key: [charwise.encode(k), key],
2075
+ value: v || null
2076
+ });
2077
+ });
2078
+ if (!mapCalled && mapReturn) {
2079
+ indexEntries.push({
2080
+ key: [charwise.encode(mapReturn), key],
2081
+ value: null
2082
+ });
2083
+ }
2084
+ });
2085
+ return indexEntries;
2086
+ }
2087
+ function makeProllyGetBlock(blocks) {
2088
+ return async (address) => {
2089
+ const block = await blocks.get(address);
2090
+ if (!block) throw new Error(`Missing block ${address.toString()}`);
2091
+ const { cid, bytes } = block;
2092
+ return create3({ cid, bytes, hasher: hasher3, codec: codec3 });
2093
+ };
2094
+ }
2095
+ async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
2096
+ if (!indexEntries.length) return inIndex;
2097
+ if (!inIndex.root) {
2098
+ if (!inIndex.cid) {
2099
+ let returnRootBlock = void 0;
2100
+ let returnNode = void 0;
2101
+ for await (const node of await DbIndex.create({
2102
+ get: makeProllyGetBlock(tblocks),
2103
+ list: indexEntries,
2104
+ ...opts
2105
+ })) {
2106
+ const block = await node.block;
2107
+ await tblocks.put(block.cid, block.bytes);
2108
+ returnRootBlock = block;
2109
+ returnNode = node;
2110
+ }
2111
+ if (!returnNode || !returnRootBlock) throw new Error("failed to create index");
2112
+ return { root: returnNode, cid: returnRootBlock.cid };
2113
+ } else {
2114
+ inIndex.root = await DbIndex.load({ cid: inIndex.cid, get: makeProllyGetBlock(tblocks), ...opts });
2115
+ }
2116
+ }
2117
+ const { root: root3, blocks: newBlocks } = await inIndex.root.bulk(indexEntries);
2118
+ if (root3) {
2119
+ for await (const block of newBlocks) {
2120
+ await tblocks.put(block.cid, block.bytes);
2121
+ }
2122
+ return { root: root3, cid: (await root3.block).cid };
2123
+ } else {
2124
+ return { root: void 0, cid: void 0 };
2125
+ }
2126
+ }
2127
+ async function loadIndex(tblocks, cid, opts) {
2128
+ return await DbIndex.load({ cid, get: makeProllyGetBlock(tblocks), ...opts });
2129
+ }
2130
+ async function applyQuery(crdt, resp, query) {
2131
+ if (query.descending) {
2132
+ resp.result = resp.result.reverse();
2133
+ }
2134
+ if (query.limit) {
2135
+ resp.result = resp.result.slice(0, query.limit);
2136
+ }
2137
+ if (query.includeDocs) {
2138
+ resp.result = await Promise.all(
2139
+ resp.result.map(async (row) => {
2140
+ const val = await crdt.get(row.id);
2141
+ const doc = val ? { ...val.doc, _id: row.id } : void 0;
2142
+ return { ...row, doc };
2143
+ })
2144
+ );
2145
+ }
2146
+ return {
2147
+ rows: resp.result.map(({ key, ...row }) => {
2148
+ return {
2149
+ key: charwise.decode(key),
2150
+ ...row
2151
+ };
2152
+ })
2153
+ };
2154
+ }
2155
+ function encodeRange(range) {
2156
+ return [charwise.encode(range[0]), charwise.encode(range[1])];
2157
+ }
2158
+ function encodeKey(key) {
2159
+ return charwise.encode(key);
2160
+ }
2161
+
2162
+ // src/indexer.ts
2163
+ function index({ _crdt }, name, mapFn, meta) {
2164
+ if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
2165
+ if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
2166
+ if (_crdt.indexers.has(name)) {
2167
+ const idx = _crdt.indexers.get(name);
2168
+ idx.applyMapFn(name, mapFn, meta);
2169
+ } else {
2170
+ const idx = new Index(_crdt, name, mapFn, meta);
2171
+ _crdt.indexers.set(name, idx);
2172
+ }
2173
+ return _crdt.indexers.get(name);
2174
+ }
2175
+ var Index = class {
2176
+ constructor(crdt, name, mapFn, meta) {
2177
+ this.mapFnString = "";
2178
+ this.byKey = new IndexTree();
2179
+ this.byId = new IndexTree();
2180
+ this.includeDocsDefault = false;
2181
+ this.logger = ensureLogger(crdt.logger, "Index");
2182
+ this.blockstore = crdt.indexBlockstore;
2183
+ this.crdt = crdt;
2184
+ this.applyMapFn(name, mapFn, meta);
2185
+ this.name = name;
2186
+ if (!(this.mapFnString || this.initError)) throw this.logger.Error().Msg("missing mapFnString").AsError();
2187
+ }
2188
+ ready() {
2189
+ return Promise.all([this.blockstore.ready(), this.crdt.ready()]).then(() => {
2190
+ });
2191
+ }
2192
+ close() {
2193
+ return Promise.all([this.blockstore.close(), this.crdt.close()]).then(() => {
2194
+ });
2195
+ }
2196
+ destroy() {
2197
+ return Promise.all([this.blockstore.destroy(), this.crdt.destroy()]).then(() => {
2198
+ });
2199
+ }
2200
+ applyMapFn(name, mapFn, meta) {
2201
+ if (mapFn && meta) throw this.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
2202
+ if (this.name && this.name !== name) throw this.logger.Error().Msg("cannot change name").AsError();
2203
+ this.name = name;
2204
+ try {
2205
+ if (meta) {
2206
+ if (this.indexHead && this.indexHead.map((c) => c.toString()).join() !== meta.head.map((c) => c.toString()).join()) {
2207
+ throw this.logger.Error().Msg("cannot apply different head meta").AsError();
2208
+ }
2209
+ if (this.mapFnString) {
2210
+ if (this.mapFnString !== meta.map) {
2211
+ this.logger.Warn().Msg(`cannot apply different mapFn meta: old mapFnString ${this.mapFnString} new mapFnString ${meta.map}`);
2212
+ } else {
2213
+ this.byId.cid = meta.byId;
2214
+ this.byKey.cid = meta.byKey;
2215
+ this.indexHead = meta.head;
2216
+ }
2217
+ } else {
2218
+ this.mapFnString = meta.map;
2219
+ this.byId.cid = meta.byId;
2220
+ this.byKey.cid = meta.byKey;
2221
+ this.indexHead = meta.head;
2222
+ }
2223
+ } else {
2224
+ if (this.mapFn) {
2225
+ if (mapFn) {
2226
+ if (this.mapFn.toString() !== mapFn.toString()) {
2227
+ throw this.logger.Error().Msg("cannot apply different mapFn app2").AsError();
2228
+ }
2229
+ }
2230
+ } else {
2231
+ if (!mapFn) {
2232
+ mapFn = (doc) => doc[name] ?? void 0;
2233
+ }
2234
+ if (this.mapFnString) {
2235
+ if (this.mapFnString !== mapFn.toString()) {
2236
+ throw this.logger.Error().Msg("cannot apply different mapFn app").AsError();
2237
+ }
2238
+ } else {
2239
+ this.mapFnString = mapFn.toString();
2240
+ }
2241
+ this.mapFn = mapFn;
2242
+ }
2243
+ }
2244
+ const matches = /=>\s*(.*)/.test(this.mapFnString);
2245
+ this.includeDocsDefault = matches;
2246
+ } catch (e) {
2247
+ this.initError = e;
2248
+ }
2249
+ }
2250
+ async query(opts = {}) {
2251
+ await this.ready();
2252
+ await this._updateIndex();
2253
+ await this._hydrateIndex();
2254
+ if (!this.byKey.root) {
2255
+ return await applyQuery(this.crdt, { result: [] }, opts);
2256
+ }
2257
+ if (this.includeDocsDefault && opts.includeDocs === void 0) opts.includeDocs = true;
2258
+ if (opts.range) {
2259
+ const eRange = encodeRange(opts.range);
2260
+ return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).range(eRange[0], eRange[1]), opts);
2261
+ }
2262
+ if (opts.key) {
2263
+ const encodedKey = encodeKey(opts.key);
2264
+ return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts);
2265
+ }
2266
+ if (Array.isArray(opts.keys)) {
2267
+ const results = await Promise.all(
2268
+ opts.keys.map(async (key) => {
2269
+ const encodedKey = encodeKey(key);
2270
+ return (await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts)).rows;
2271
+ })
2272
+ );
2273
+ return { rows: results.flat() };
2274
+ }
2275
+ if (opts.prefix) {
2276
+ if (!Array.isArray(opts.prefix)) opts.prefix = [opts.prefix];
2277
+ const start = [...opts.prefix, NaN];
2278
+ const end = [...opts.prefix, Infinity];
2279
+ const encodedR = encodeRange([start, end]);
2280
+ return await applyQuery(this.crdt, await this.byKey.root.range(...encodedR), opts);
2281
+ }
2282
+ const all = await this.byKey.root.getAllEntries();
2283
+ return await applyQuery(
2284
+ this.crdt,
2285
+ {
2286
+ // @ts-expect-error getAllEntries returns a different type than range
2287
+ result: all.result.map(({ key: [k, id], value }) => ({
2288
+ key: k,
2289
+ id,
2290
+ value
2291
+ }))
2292
+ },
2293
+ opts
2294
+ );
2295
+ }
2296
+ _resetIndex() {
2297
+ this.byId = new IndexTree();
2298
+ this.byKey = new IndexTree();
2299
+ this.indexHead = void 0;
2300
+ }
2301
+ async _hydrateIndex() {
2302
+ if (this.byId.root && this.byKey.root) return;
2303
+ if (!this.byId.cid || !this.byKey.cid) return;
2304
+ this.byId.root = await loadIndex(this.blockstore, this.byId.cid, byIdOpts);
2305
+ this.byKey.root = await loadIndex(this.blockstore, this.byKey.cid, byKeyOpts);
2306
+ }
2307
+ async _updateIndex() {
2308
+ await this.ready();
2309
+ if (this.initError) throw this.initError;
2310
+ if (!this.mapFn) throw this.logger.Error().Msg("No map function defined").AsError();
2311
+ let result, head;
2312
+ if (!this.indexHead || this.indexHead.length === 0) {
2313
+ ({ result, head } = await this.crdt.allDocs());
2314
+ } else {
2315
+ ({ result, head } = await this.crdt.changes(this.indexHead));
2316
+ }
2317
+ if (result.length === 0) {
2318
+ this.indexHead = head;
2319
+ }
2320
+ let staleKeyIndexEntries = [];
2321
+ let removeIdIndexEntries = [];
2322
+ if (this.byId.root) {
2323
+ const removeIds = result.map(({ id: key }) => key);
2324
+ const { result: oldChangeEntries } = await this.byId.root.getMany(removeIds);
2325
+ staleKeyIndexEntries = oldChangeEntries.map((key) => ({ key, del: true }));
2326
+ removeIdIndexEntries = oldChangeEntries.map((key) => ({ key: key[1], del: true }));
2327
+ }
2328
+ const indexEntries = indexEntriesForChanges(result, this.mapFn);
2329
+ const byIdIndexEntries = indexEntries.map(({ key }) => ({
2330
+ key: key[1],
2331
+ value: key
2332
+ }));
2333
+ const indexerMeta = { indexes: /* @__PURE__ */ new Map() };
2334
+ for (const [name, indexer] of this.crdt.indexers) {
2335
+ if (indexer.indexHead) {
2336
+ indexerMeta.indexes?.set(name, {
2337
+ byId: indexer.byId.cid,
2338
+ byKey: indexer.byKey.cid,
2339
+ head: indexer.indexHead,
2340
+ map: indexer.mapFnString,
2341
+ name: indexer.name
2342
+ });
2343
+ }
2344
+ }
2345
+ if (result.length === 0) {
2346
+ return indexerMeta;
2347
+ }
2348
+ const { meta } = await this.blockstore.transaction(async (tblocks) => {
2349
+ this.byId = await bulkIndex(tblocks, this.byId, removeIdIndexEntries.concat(byIdIndexEntries), byIdOpts);
2350
+ this.byKey = await bulkIndex(tblocks, this.byKey, staleKeyIndexEntries.concat(indexEntries), byKeyOpts);
2351
+ this.indexHead = head;
2352
+ if (this.byId.cid && this.byKey.cid) {
2353
+ const idxMeta = {
2354
+ byId: this.byId.cid,
2355
+ byKey: this.byKey.cid,
2356
+ head,
2357
+ map: this.mapFnString,
2358
+ name: this.name
2359
+ };
2360
+ indexerMeta.indexes?.set(this.name, idxMeta);
2361
+ }
2362
+ return indexerMeta;
2363
+ });
2364
+ return meta;
2365
+ }
2366
+ };
2367
+
2368
+ // src/crdt-clock.ts
2369
+ import { advance } from "@web3-storage/pail/clock";
2370
+ import { root as root2 } from "@web3-storage/pail/crdt";
2371
+ import { ResolveOnce as ResolveOnce3 } from "@adviser/cement";
2372
+
2373
+ // src/apply-head-queue.ts
2374
+ function applyHeadQueue(worker, logger) {
2375
+ const queue = [];
2376
+ let isProcessing = false;
2377
+ async function* process() {
2378
+ if (isProcessing || queue.length === 0) return;
2379
+ isProcessing = true;
2380
+ const allUpdates = [];
2381
+ try {
2382
+ while (queue.length > 0) {
2383
+ queue.sort((a, b) => b.updates ? 1 : -1);
2384
+ const task = queue.shift();
2385
+ if (!task) continue;
2386
+ await worker(task.newHead, task.prevHead, task.updates !== null).catch((e) => {
2387
+ throw logger.Error().Err(e).Msg("int_applyHead worker error").AsError();
2388
+ });
2389
+ if (task.updates) {
2390
+ allUpdates.push(...task.updates);
2391
+ }
2392
+ if (!queue.some((t) => t.updates) || task.updates) {
2393
+ const allTasksHaveUpdates = queue.every((task2) => task2.updates !== null);
2394
+ yield { updates: allUpdates, all: allTasksHaveUpdates };
2395
+ allUpdates.length = 0;
2396
+ }
2397
+ }
2398
+ } finally {
2399
+ isProcessing = false;
2400
+ const generator = process();
2401
+ let result = await generator.next();
2402
+ while (!result.done) {
2403
+ result = await generator.next();
2404
+ }
2405
+ }
2406
+ }
2407
+ return {
2408
+ push(task) {
2409
+ queue.push(task);
2410
+ return process();
2411
+ },
2412
+ size() {
2413
+ return queue.length;
2414
+ }
2415
+ };
2416
+ }
2417
+
2418
+ // src/crdt-clock.ts
2419
+ var CRDTClock = class {
2420
+ constructor(blockstore) {
2421
+ // todo: track local and remote clocks independently, merge on read
2422
+ // that way we can drop the whole remote if we need to
2423
+ // should go with making sure the local clock only references locally available blockstore on write
2424
+ this.head = [];
2425
+ this.zoomers = /* @__PURE__ */ new Set();
2426
+ this.watchers = /* @__PURE__ */ new Set();
2427
+ this.emptyWatchers = /* @__PURE__ */ new Set();
2428
+ this._ready = new ResolveOnce3();
2429
+ this.blockstore = blockstore;
2430
+ this.logger = ensureLogger(blockstore.logger, "CRDTClock");
2431
+ this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
2432
+ }
2433
+ async ready() {
2434
+ return this._ready.once(async () => {
2435
+ await this.blockstore.ready();
2436
+ });
2437
+ }
2438
+ async close() {
2439
+ await this.blockstore.close();
2440
+ }
2441
+ setHead(head) {
2442
+ this.head = head;
2443
+ }
2444
+ async applyHead(newHead, prevHead, updates) {
2445
+ for await (const { updates: updatesAcc, all } of this.applyHeadQueue.push({
2446
+ newHead,
2447
+ prevHead,
2448
+ updates
2449
+ })) {
2450
+ return this.processUpdates(updatesAcc, all, prevHead);
2451
+ }
2452
+ }
2453
+ async processUpdates(updatesAcc, all, prevHead) {
2454
+ let internalUpdates = updatesAcc;
2455
+ if (this.watchers.size && !all) {
2456
+ const changes = await clockChangesSince(throwFalsy(this.blockstore), this.head, prevHead, {}, this.logger);
2457
+ internalUpdates = changes.result;
2458
+ }
2459
+ this.zoomers.forEach((fn) => fn());
2460
+ this.notifyWatchers(internalUpdates || []);
2461
+ }
2462
+ notifyWatchers(updates) {
2463
+ this.emptyWatchers.forEach((fn) => fn());
2464
+ this.watchers.forEach((fn) => fn(updates || []));
2465
+ }
2466
+ onTick(fn) {
2467
+ this.watchers.add(fn);
2468
+ }
2469
+ onTock(fn) {
2470
+ this.emptyWatchers.add(fn);
2471
+ }
2472
+ onZoom(fn) {
2473
+ this.zoomers.add(fn);
2474
+ }
2475
+ async int_applyHead(newHead, prevHead, localUpdates) {
2476
+ const ogHead = sortClockHead(this.head);
2477
+ newHead = sortClockHead(newHead);
2478
+ if (compareClockHeads(ogHead, newHead)) {
2479
+ return;
2480
+ }
2481
+ const ogPrev = sortClockHead(prevHead);
2482
+ if (compareClockHeads(ogHead, ogPrev)) {
2483
+ this.setHead(newHead);
2484
+ return;
2485
+ }
2486
+ const noLoader = !localUpdates;
2487
+ if (!this.blockstore) {
2488
+ throw this.logger.Error().Msg("missing blockstore").AsError();
2489
+ }
2490
+ await validateBlocks(this.logger, newHead, this.blockstore);
2491
+ const { meta } = await this.blockstore.transaction(
2492
+ async (tblocks) => {
2493
+ const advancedHead = await advanceBlocks(this.logger, newHead, tblocks, this.head);
2494
+ const result = await root2(tblocks, advancedHead);
2495
+ for (const { cid, bytes } of [
2496
+ ...result.additions
2497
+ // ...result.removals
2498
+ ]) {
2499
+ tblocks.putSync(cid, bytes);
2500
+ }
2501
+ return { head: advancedHead };
2502
+ },
2503
+ { noLoader }
2504
+ );
2505
+ this.setHead(meta.head);
2506
+ }
2507
+ };
2508
+ function sortClockHead(clockHead) {
2509
+ return clockHead.sort((a, b) => a.toString().localeCompare(b.toString()));
2510
+ }
2511
+ async function validateBlocks(logger, newHead, blockstore) {
2512
+ if (!blockstore) throw logger.Error().Msg("missing blockstore");
2513
+ newHead.map(async (cid) => {
2514
+ const got = await blockstore.get(cid);
2515
+ if (!got) {
2516
+ throw logger.Error().Str("cid", cid.toString()).Msg("int_applyHead missing block").AsError();
2517
+ }
2518
+ });
2519
+ }
2520
+ function compareClockHeads(head1, head2) {
2521
+ return head1.toString() === head2.toString();
2522
+ }
2523
+ async function advanceBlocks(logger, newHead, tblocks, head) {
2524
+ for (const cid of newHead) {
2525
+ try {
2526
+ head = await advance(tblocks, head, cid);
2527
+ } catch (e) {
2528
+ logger.Debug().Err(e).Msg("failed to advance head");
2529
+ continue;
2530
+ }
2531
+ }
2532
+ return head;
2533
+ }
2534
+
2535
+ // src/crdt.ts
2536
+ var CRDT = class {
2537
+ constructor(name, opts = {}) {
2538
+ this.onceReady = new ResolveOnce4();
2539
+ this.indexers = /* @__PURE__ */ new Map();
2540
+ this.name = name;
2541
+ this.logger = ensureLogger(opts, "CRDT");
2542
+ this.opts = opts;
2543
+ this.blockstore = blockstoreFactory({
2544
+ name,
2545
+ applyMeta: async (meta) => {
2546
+ const crdtMeta = meta;
2547
+ if (!crdtMeta.head) throw this.logger.Error().Msg("missing head").AsError();
2548
+ await this.clock.applyHead(crdtMeta.head, []);
2549
+ },
2550
+ compact: async (blocks) => {
2551
+ await doCompact(blocks, this.clock.head, this.logger);
2552
+ return { head: this.clock.head };
2553
+ },
2554
+ autoCompact: this.opts.autoCompact || 100,
2555
+ crypto: this.opts.crypto,
2556
+ store: { ...this.opts.store, isIndex: void 0 },
2557
+ public: this.opts.public,
2558
+ meta: this.opts.meta,
2559
+ threshold: this.opts.threshold
2560
+ });
2561
+ this.indexBlockstore = blockstoreFactory({
2562
+ name,
2563
+ applyMeta: async (meta) => {
2564
+ const idxCarMeta = meta;
2565
+ if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
2566
+ for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
2567
+ index({ _crdt: this }, name2, void 0, idx);
2568
+ }
2569
+ },
2570
+ crypto: this.opts.crypto,
2571
+ store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
2572
+ public: this.opts.public
2573
+ });
2574
+ this.clock = new CRDTClock(this.blockstore);
2575
+ this.clock.onZoom(() => {
2576
+ for (const idx of this.indexers.values()) {
2577
+ idx._resetIndex();
2578
+ }
2579
+ });
2580
+ }
2581
+ async ready() {
2582
+ return this.onceReady.once(async () => {
2583
+ await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
2584
+ });
2585
+ }
2586
+ async close() {
2587
+ await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
2588
+ }
2589
+ async destroy() {
2590
+ await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
2591
+ }
2592
+ async bulk(updates) {
2593
+ await this.ready();
2594
+ const prevHead = [...this.clock.head];
2595
+ const done = await this.blockstore.transaction(async (blocks) => {
2596
+ const { head } = await applyBulkUpdateToCrdt(
2597
+ this.blockstore.ebOpts.storeRuntime,
2598
+ blocks,
2599
+ this.clock.head,
2600
+ updates,
2601
+ this.logger
2602
+ );
2603
+ updates = updates.map((dupdate) => {
2604
+ readFiles(this.blockstore, { doc: dupdate.value });
2605
+ return dupdate;
2606
+ });
2607
+ return { head };
2608
+ });
2609
+ await this.clock.applyHead(done.meta.head, prevHead, updates);
2610
+ return done.meta;
2611
+ }
2612
+ // if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
2613
+ async allDocs() {
2614
+ await this.ready();
2615
+ const result = [];
2616
+ for await (const entry of getAllEntries(this.blockstore, this.clock.head, this.logger)) {
2617
+ result.push(entry);
2618
+ }
2619
+ return { result, head: this.clock.head };
2620
+ }
2621
+ async vis() {
2622
+ await this.ready();
2623
+ const txt = [];
2624
+ for await (const line of clockVis(this.blockstore, this.clock.head)) {
2625
+ txt.push(line);
2626
+ }
2627
+ return txt.join("\n");
2628
+ }
2629
+ async getBlock(cidString) {
2630
+ await this.ready();
2631
+ return await getBlock(this.blockstore, cidString);
2632
+ }
2633
+ async get(key) {
2634
+ await this.ready();
2635
+ const result = await getValueFromCrdt(this.blockstore, this.clock.head, key, this.logger);
2636
+ if (result.del) return void 0;
2637
+ return result;
2638
+ }
2639
+ async changes(since = [], opts = {}) {
2640
+ await this.ready();
2641
+ return await clockChangesSince(this.blockstore, this.clock.head, since, opts, this.logger);
2642
+ }
2643
+ async compact() {
2644
+ const blocks = this.blockstore;
2645
+ return await blocks.compact();
2646
+ }
2647
+ };
2648
+
2649
+ // src/database.ts
2650
+ var Database = class {
2651
+ constructor(name, opts) {
2652
+ this.opts = {};
2653
+ this._listening = false;
2654
+ this._listeners = /* @__PURE__ */ new Set();
2655
+ this._noupdate_listeners = /* @__PURE__ */ new Set();
2656
+ this._ready = new ResolveOnce5();
2657
+ this.name = name;
2658
+ this.opts = opts || this.opts;
2659
+ this.logger = ensureLogger(this.opts, "Database");
2660
+ this._crdt = new CRDT(name, this.opts);
2661
+ this.blockstore = this._crdt.blockstore;
2662
+ this._writeQueue = writeQueue(async (updates) => {
2663
+ return await this._crdt.bulk(updates);
2664
+ });
2665
+ this._crdt.clock.onTock(() => {
2666
+ this._no_update_notify();
2667
+ });
2668
+ }
2669
+ static {
2670
+ this.databases = /* @__PURE__ */ new Map();
2671
+ }
2672
+ async close() {
2673
+ await this.ready();
2674
+ await this._crdt.close();
2675
+ await this.blockstore.close();
2676
+ }
2677
+ async destroy() {
2678
+ await this.ready();
2679
+ await this._crdt.destroy();
2680
+ await this.blockstore.destroy();
2681
+ }
2682
+ async ready() {
2683
+ return this._ready.once(async () => {
2684
+ await SysContainer.start();
2685
+ await this._crdt.ready();
2686
+ await this.blockstore.ready();
2687
+ });
2688
+ }
2689
+ async get(id) {
2690
+ this.logger.Debug().Str("id", id).Msg("get-pre-ready");
2691
+ await this.ready();
2692
+ this.logger.Debug().Str("id", id).Msg("get-post-ready");
2693
+ const got = await this._crdt.get(id).catch((e) => {
2694
+ throw new NotFoundError(`Not found: ${id} - ${e.message}`);
2695
+ });
2696
+ if (!got) throw new NotFoundError(`Not found: ${id}`);
2697
+ const { doc } = got;
2698
+ return { ...doc, _id: id };
2699
+ }
2700
+ async put(doc) {
2701
+ this.logger.Debug().Str("id", doc._id).Msg("put-pre-ready");
2702
+ await this.ready();
2703
+ this.logger.Debug().Str("id", doc._id).Msg("put-post-ready");
2704
+ const { _id, ...value } = doc;
2705
+ const docId = _id || uuidv7();
2706
+ const result = await this._writeQueue.push({
2707
+ id: docId,
2708
+ value: {
2709
+ ...value,
2710
+ _id: docId
2711
+ }
2712
+ });
2713
+ return { id: docId, clock: result?.head };
2714
+ }
2715
+ async del(id) {
2716
+ await this.ready();
2717
+ const result = await this._writeQueue.push({ id, del: true });
2718
+ return { id, clock: result?.head };
2719
+ }
2720
+ async changes(since = [], opts = {}) {
2721
+ await this.ready();
2722
+ const { result, head } = await this._crdt.changes(since, opts);
2723
+ const rows = result.map(({ id: key, value, del, clock }) => ({
2724
+ key,
2725
+ value: del ? { _id: key, _deleted: true } : { _id: key, ...value },
2726
+ clock
2727
+ }));
2728
+ return { rows, clock: head };
2729
+ }
2730
+ async allDocs() {
2731
+ await this.ready();
2732
+ const { result, head } = await this._crdt.allDocs();
2733
+ const rows = result.map(({ id: key, value, del }) => ({
2734
+ key,
2735
+ value: del ? { _id: key, _deleted: true } : { _id: key, ...value }
2736
+ }));
2737
+ return { rows, clock: head };
2738
+ }
2739
+ async allDocuments() {
2740
+ return this.allDocs();
2741
+ }
2742
+ subscribe(listener, updates) {
2743
+ if (updates) {
2744
+ if (!this._listening) {
2745
+ this._listening = true;
2746
+ this._crdt.clock.onTick((updates2) => {
2747
+ void this._notify(updates2);
2748
+ });
2749
+ }
2750
+ this._listeners.add(listener);
2751
+ return () => {
2752
+ this._listeners.delete(listener);
2753
+ };
2754
+ } else {
2755
+ this._noupdate_listeners.add(listener);
2756
+ return () => {
2757
+ this._noupdate_listeners.delete(listener);
2758
+ };
2759
+ }
2760
+ }
2761
+ // todo if we add this onto dbs in fireproof.ts then we can make index.ts a separate package
2762
+ async query(field, opts = {}) {
2763
+ await this.ready();
2764
+ const _crdt = this._crdt;
2765
+ const idx = typeof field === "string" ? index({ _crdt }, field) : index({ _crdt }, makeName(field.toString()), field);
2766
+ return await idx.query(opts);
2767
+ }
2768
+ async compact() {
2769
+ await this.ready();
2770
+ await this._crdt.compact();
2771
+ }
2772
+ async _notify(updates) {
2773
+ await this.ready();
2774
+ if (this._listeners.size) {
2775
+ const docs = updates.map(({ id, value }) => ({ ...value, _id: id }));
2776
+ for (const listener of this._listeners) {
2777
+ await (async () => await listener(docs))().catch((e) => {
2778
+ this.logger.Error().Err(e).Msg("subscriber error");
2779
+ });
2780
+ }
2781
+ }
2782
+ }
2783
+ async _no_update_notify() {
2784
+ await this.ready();
2785
+ if (this._noupdate_listeners.size) {
2786
+ for (const listener of this._noupdate_listeners) {
2787
+ await (async () => await listener([]))().catch((e) => {
2788
+ this.logger.Error().Err(e).Msg("subscriber error");
2789
+ });
2790
+ }
2791
+ }
2792
+ }
2793
+ };
2794
+ function toSortedArray(set) {
2795
+ if (!set) return [];
2796
+ return Object.entries(set).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => ({ [k]: v }));
2797
+ }
2798
+ function fireproof(name, opts) {
2799
+ const key = JSON.stringify(
2800
+ toSortedArray({
2801
+ name,
2802
+ stores: toSortedArray(opts?.store?.stores),
2803
+ makeMetaStore: !!opts?.store?.makeMetaStore,
2804
+ makeDataStore: !!opts?.store?.makeDataStore,
2805
+ makeRemoteWAL: !!opts?.store?.makeRemoteWAL,
2806
+ encodeFile: !!opts?.store?.encodeFile,
2807
+ decodeFile: !!opts?.store?.decodeFile
2808
+ })
2809
+ );
2810
+ let db = Database.databases.get(key);
2811
+ if (!db) {
2812
+ db = new Database(name, opts);
2813
+ Database.databases.set(key, db);
2814
+ }
2815
+ return db;
2816
+ }
2817
+ function makeName(fnString) {
2818
+ const regex = /\(([^,()]+,\s*[^,()]+|\[[^\]]+\],\s*[^,()]+)\)/g;
2819
+ let found = null;
2820
+ const matches = Array.from(fnString.matchAll(regex), (match) => match[1].trim());
2821
+ if (matches.length === 0) {
2822
+ found = /=>\s*(.*)/.exec(fnString);
2823
+ }
2824
+ if (!found) {
2825
+ return fnString;
2826
+ } else {
2827
+ return found[1];
2828
+ }
2829
+ }
2830
+
2831
+ // src/version.ts
2832
+ var PACKAGE_VERSION = Object.keys({
2833
+ "0.0.0-dev": "xxxx"
2834
+ })[0];
2835
+ export {
2836
+ CRDT,
2837
+ Database,
2838
+ Index,
2839
+ PACKAGE_VERSION,
2840
+ blockstore_exports as blockstore,
2841
+ blockstore_exports as bs,
2842
+ ensureLogger,
2843
+ exception2Result,
2844
+ exceptionWrapper,
2845
+ falsyToUndef,
2846
+ fireproof,
2847
+ getKey,
2848
+ getName,
2849
+ getStore,
2850
+ index,
2851
+ isFalsy,
2852
+ runtime_exports as rt,
2853
+ runtime_exports as runtime,
2854
+ throwFalsy
2855
+ };
2856
+ //# sourceMappingURL=index.js.map