@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.cjs ADDED
@@ -0,0 +1,4706 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/types.ts
34
+ function isFalsy(value) {
35
+ return value === false && value === null && value === void 0;
36
+ }
37
+ function throwFalsy(value) {
38
+ if (isFalsy(value)) {
39
+ throw new Error("value is Falsy");
40
+ }
41
+ return value;
42
+ }
43
+ function falsyToUndef(value) {
44
+ if (isFalsy(value)) {
45
+ return void 0;
46
+ }
47
+ return value;
48
+ }
49
+ var init_types = __esm({
50
+ "src/types.ts"() {
51
+ "use strict";
52
+ }
53
+ });
54
+
55
+ // src/runtime/node-sys-container.ts
56
+ var node_sys_container_exports = {};
57
+ __export(node_sys_container_exports, {
58
+ createNodeSysContainer: () => createNodeSysContainer
59
+ });
60
+ async function createNodeSysContainer() {
61
+ const nodePath = "node:path";
62
+ const nodeOS = "node:os";
63
+ const nodeURL = "node:url";
64
+ const nodeFS = "node:fs";
65
+ const fs = (await import(nodeFS)).promises;
66
+ const path = await import(nodePath);
67
+ return {
68
+ state: "node",
69
+ ...path,
70
+ ...await import(nodeOS),
71
+ ...await import(nodeURL),
72
+ ...fs,
73
+ join,
74
+ stat: fs.stat,
75
+ readdir: fs.readdir,
76
+ readfile: fs.readFile,
77
+ writefile: fs.writeFile
78
+ };
79
+ }
80
+ var init_node_sys_container = __esm({
81
+ "src/runtime/node-sys-container.ts"() {
82
+ "use strict";
83
+ init_sys_container();
84
+ }
85
+ });
86
+
87
+ // src/runtime/sys-container.ts
88
+ function join(...paths) {
89
+ return paths.map((i) => i.replace(/\/+$/, "")).join("/");
90
+ }
91
+ var stdEnv, import_uuidv7, import_cement, onceStart, envImpl, sysContainer, SysContainer;
92
+ var init_sys_container = __esm({
93
+ "src/runtime/sys-container.ts"() {
94
+ "use strict";
95
+ stdEnv = __toESM(require("std-env"), 1);
96
+ import_uuidv7 = require("uuidv7");
97
+ import_cement = require("@adviser/cement");
98
+ init_types();
99
+ onceStart = new import_cement.ResolveOnce();
100
+ envImpl = new import_cement.EnvImpl({
101
+ symbol: "FP_ENV",
102
+ presetEnv: /* @__PURE__ */ new Map([
103
+ // ["FP_DEBUG", "xxx"],
104
+ // ["FP_ENV", "development"],
105
+ ])
106
+ });
107
+ sysContainer = class {
108
+ constructor() {
109
+ this.freight = {
110
+ state: "seeded",
111
+ join,
112
+ dirname: (path) => path.split("/").slice(0, -1).join("/"),
113
+ homedir: () => {
114
+ throw new Error("SysContainer:homedir is not available in seeded state");
115
+ },
116
+ fileURLToPath: (strurl) => {
117
+ let url;
118
+ if (typeof strurl === "string") {
119
+ url = new URL(strurl);
120
+ } else {
121
+ url = strurl;
122
+ }
123
+ return url.pathname;
124
+ },
125
+ // assert: (condition: unknown, message?: string | Error) => {
126
+ // if (!condition) {
127
+ // if (message instanceof Error) {
128
+ // throw message;
129
+ // } else {
130
+ // throw new Error(message);
131
+ // }
132
+ // }
133
+ // },
134
+ mkdir: () => Promise.reject(new Error("SysContainer:mkdir is not available in seeded state")),
135
+ readdir: () => Promise.reject(new Error("SysContainer:readdir is not available in seeded state")),
136
+ rm: () => Promise.reject(new Error("SysContainer:rm is not available in seeded state")),
137
+ copyFile: () => Promise.reject(new Error("SysContainer:copyFile is not available in seeded state")),
138
+ readfile: () => Promise.reject(new Error("SysContainer:readfile is not available in seeded state")),
139
+ unlink: () => Promise.reject(new Error("SysContainer:unlink is not available in seeded state")),
140
+ writefile: () => Promise.reject(new Error("SysContainer:writefile is not available in seeded state")),
141
+ stat: () => Promise.reject(new Error("SysContainer:stat is not available in seeded state"))
142
+ };
143
+ this.id = (0, import_uuidv7.uuidv4)();
144
+ this.homedir = () => {
145
+ this.logSeeded("homedir");
146
+ return throwFalsy(this.freight).homedir();
147
+ };
148
+ this.env = envImpl;
149
+ }
150
+ async start() {
151
+ await onceStart.once(async () => {
152
+ switch (this.freight.state) {
153
+ case "seeded":
154
+ if (stdEnv.isNode) {
155
+ const { createNodeSysContainer: createNodeSysContainer2 } = await Promise.resolve().then(() => (init_node_sys_container(), node_sys_container_exports));
156
+ this.freight = await createNodeSysContainer2();
157
+ } else {
158
+ this.freight.state = "browser";
159
+ }
160
+ return;
161
+ case "browser":
162
+ case "node":
163
+ return;
164
+ }
165
+ });
166
+ }
167
+ async readdir(path, options) {
168
+ this.logSeeded("readdir");
169
+ return throwFalsy(this.freight).readdir(path, options) || [];
170
+ }
171
+ async readdirent(path, options) {
172
+ this.logSeeded("readdirent");
173
+ return throwFalsy(this.freight).readdir(path, { ...options, withFileTypes: true }) || [];
174
+ }
175
+ async readfile(path, options) {
176
+ this.logSeeded("readfile");
177
+ return throwFalsy(this.freight).readfile(path, options);
178
+ }
179
+ async mkdir(path, options) {
180
+ this.logSeeded("mkdir");
181
+ return throwFalsy(this.freight).mkdir(path, options);
182
+ }
183
+ async rm(path, options) {
184
+ this.logSeeded("rm");
185
+ return throwFalsy(this.freight).rm(path, options);
186
+ }
187
+ async unlink(path) {
188
+ this.logSeeded("unlink");
189
+ return throwFalsy(this.freight).unlink(path);
190
+ }
191
+ async writefile(path, data) {
192
+ this.logSeeded("writefile");
193
+ return throwFalsy(this.freight).writefile(path, data);
194
+ }
195
+ async copyFile(source, destination) {
196
+ this.logSeeded("copyFile");
197
+ return throwFalsy(this.freight).copyFile(source, destination);
198
+ }
199
+ async stat(path) {
200
+ this.logSeeded("stat");
201
+ return throwFalsy(this.freight).stat(path);
202
+ }
203
+ fileURLToPath(url) {
204
+ this.logSeeded("fileURLToPath");
205
+ return throwFalsy(this.freight).fileURLToPath(url);
206
+ }
207
+ dirname(path) {
208
+ this.logSeeded("dirname");
209
+ return throwFalsy(this.freight).dirname(path);
210
+ }
211
+ join(...args) {
212
+ this.logSeeded("join");
213
+ return throwFalsy(this.freight).join(...args);
214
+ }
215
+ logSeeded(method) {
216
+ if (this.freight.state === "seeded") {
217
+ const err = new Error();
218
+ console.warn(`SysContainer.${method} is not available in seeded state:`, err.stack);
219
+ }
220
+ }
221
+ };
222
+ SysContainer = new sysContainer();
223
+ }
224
+ });
225
+
226
+ // src/runtime/data-dir.ts
227
+ function dataDir(name, base) {
228
+ if (!base) {
229
+ if (import_std_env.isNode || import_std_env.isDeno) {
230
+ base = SysContainer.env.get("FP_STORAGE_URL") || `file://${SysContainer.join(SysContainer.homedir(), ".fireproof")}`;
231
+ } else {
232
+ base = `indexdb://fp`;
233
+ }
234
+ }
235
+ let url;
236
+ if (typeof base === "string") {
237
+ try {
238
+ url = new URL(base.toString());
239
+ } catch (e) {
240
+ try {
241
+ base = `file://${base}`;
242
+ url = new URL(base);
243
+ } catch (e2) {
244
+ throw new Error(`invalid base url: ${base}`);
245
+ }
246
+ }
247
+ } else {
248
+ url = base;
249
+ }
250
+ url.searchParams.set("name", name || "");
251
+ return url.toString();
252
+ }
253
+ var import_std_env;
254
+ var init_data_dir = __esm({
255
+ "src/runtime/data-dir.ts"() {
256
+ "use strict";
257
+ init_sys_container();
258
+ import_std_env = require("std-env");
259
+ }
260
+ });
261
+
262
+ // src/runtime/store-file-utils.ts
263
+ async function getPath(url, logger) {
264
+ const basePath = url.toString().replace(new RegExp(`^${url.protocol}//`), "").replace(/\?.*$/, "");
265
+ const name = url.searchParams.get("name");
266
+ if (name) {
267
+ const version = url.searchParams.get("version");
268
+ if (!version) throw logger.Error().Str("url", url.toString()).Msg(`version not found`).AsError();
269
+ return SysContainer.join(basePath, version, name);
270
+ }
271
+ return SysContainer.join(basePath);
272
+ }
273
+ function getFileName(url, key, logger) {
274
+ switch (getStore(url, logger, (...a) => a.join("/"))) {
275
+ case "data":
276
+ return key + ".car";
277
+ case "meta":
278
+ return key + ".json";
279
+ default:
280
+ throw logger.Error().Str("url", url.toString()).Msg(`unsupported store type`).AsError();
281
+ }
282
+ }
283
+ function ensureIndexName(url, name) {
284
+ if (url.searchParams.has("index")) {
285
+ name = (url.searchParams.get("index")?.replace(/[^a-zA-Z0-9]/g, "") || "idx") + "-" + name;
286
+ }
287
+ return name;
288
+ }
289
+ var init_store_file_utils = __esm({
290
+ "src/runtime/store-file-utils.ts"() {
291
+ "use strict";
292
+ init_utils();
293
+ init_sys_container();
294
+ }
295
+ });
296
+
297
+ // src/runtime/store-sql/types.ts
298
+ var DefaultSQLTableNames;
299
+ var init_types2 = __esm({
300
+ "src/runtime/store-sql/types.ts"() {
301
+ "use strict";
302
+ DefaultSQLTableNames = {
303
+ data: "Datas",
304
+ meta: "Metas",
305
+ wal: "Wals"
306
+ };
307
+ }
308
+ });
309
+
310
+ // src/runtime/store-sql/ensurer.ts
311
+ function sqlTableName(...names) {
312
+ return names.map((name) => name.replace(/^[^a-zA-Z0-9]+/, "").replace(/[^a-zA-Z0-9]+/g, "_")).filter((i) => i.length).join("_");
313
+ }
314
+ function ensureTableNames(url, opts) {
315
+ let isIndex = "";
316
+ if (url.searchParams.has("index")) {
317
+ isIndex = url.searchParams.get("index") || ".idx";
318
+ }
319
+ const ret = opts?.tableNames || DefaultSQLTableNames;
320
+ if (isIndex.length) {
321
+ return {
322
+ data: sqlTableName(isIndex, ret.data),
323
+ meta: sqlTableName(isIndex, ret.meta),
324
+ wal: sqlTableName(isIndex, ret.wal)
325
+ };
326
+ }
327
+ return {
328
+ data: sqlTableName(ret.data),
329
+ meta: sqlTableName(ret.meta),
330
+ wal: sqlTableName(ret.wal)
331
+ };
332
+ }
333
+ function ensureTextEncoder(opts) {
334
+ return opts?.textEncoder || textEncoder;
335
+ }
336
+ function ensureTextDecoder(opts) {
337
+ return opts?.textDecoder || textDecoder;
338
+ }
339
+ function url2sqlFlavor(url, logger) {
340
+ const flavor = url.protocol.replace(/:.*$/, "");
341
+ switch (flavor) {
342
+ case "sqlite":
343
+ case "mysql":
344
+ case "postgres":
345
+ return flavor;
346
+ default:
347
+ throw logger.Error().Str("flavor", flavor).Msg("unsupported protocol").AsError();
348
+ }
349
+ }
350
+ function ensureSQLOpts(url, opts, componentName, ctx) {
351
+ const logger = ensureLogger(opts, componentName, ctx);
352
+ return {
353
+ url,
354
+ sqlFlavor: url2sqlFlavor(url, logger),
355
+ tableNames: ensureTableNames(url, opts),
356
+ logger,
357
+ textEncoder: ensureTextEncoder(opts),
358
+ textDecoder: ensureTextDecoder(opts)
359
+ };
360
+ }
361
+ var textEncoder, textDecoder;
362
+ var init_ensurer = __esm({
363
+ "src/runtime/store-sql/ensurer.ts"() {
364
+ "use strict";
365
+ init_utils();
366
+ init_types2();
367
+ textEncoder = new TextEncoder();
368
+ textDecoder = new TextDecoder();
369
+ }
370
+ });
371
+
372
+ // src/runtime/store-sql/index.ts
373
+ var store_sql_exports = {};
374
+ __export(store_sql_exports, {
375
+ DefaultSQLTableNames: () => DefaultSQLTableNames,
376
+ ensureSQLOpts: () => ensureSQLOpts
377
+ });
378
+ var init_store_sql = __esm({
379
+ "src/runtime/store-sql/index.ts"() {
380
+ "use strict";
381
+ init_types2();
382
+ init_ensurer();
383
+ }
384
+ });
385
+
386
+ // src/runtime/crypto.ts
387
+ var crypto_exports = {};
388
+ __export(crypto_exports, {
389
+ toCryptoOpts: () => toCryptoOpts
390
+ });
391
+ function randomBytes(size) {
392
+ const bytes = new Uint8Array(size);
393
+ if (size > 0) {
394
+ crypto.getRandomValues(bytes);
395
+ }
396
+ return bytes;
397
+ }
398
+ function digestSHA256(data) {
399
+ return Promise.resolve(crypto.subtle.digest("SHA-256", data));
400
+ }
401
+ function toCryptoOpts(cryptoOpts = {}) {
402
+ const opts = {
403
+ importKey: cryptoOpts.importKey || crypto.subtle.importKey.bind(crypto.subtle),
404
+ encrypt: cryptoOpts.encrypt || crypto.subtle.encrypt.bind(crypto.subtle),
405
+ decrypt: cryptoOpts.decrypt || crypto.subtle.decrypt.bind(crypto.subtle),
406
+ randomBytes: cryptoOpts.randomBytes || randomBytes,
407
+ digestSHA256: cryptoOpts.digestSHA256 || digestSHA256
408
+ };
409
+ return opts;
410
+ }
411
+ var init_crypto = __esm({
412
+ "src/runtime/crypto.ts"() {
413
+ "use strict";
414
+ }
415
+ });
416
+
417
+ // src/runtime/files.ts
418
+ var files_exports = {};
419
+ __export(files_exports, {
420
+ decodeFile: () => decodeFile,
421
+ encodeFile: () => encodeFile
422
+ });
423
+ async function collect(collectable) {
424
+ const chunks = [];
425
+ await collectable.pipeTo(
426
+ new WritableStream({
427
+ write(chunk) {
428
+ chunks.push(chunk);
429
+ }
430
+ })
431
+ );
432
+ return chunks;
433
+ }
434
+ async function encodeFile(blob) {
435
+ const readable = createFileEncoderStream(blob);
436
+ const blocks = await collect(readable);
437
+ return { cid: blocks.at(-1).cid, blocks };
438
+ }
439
+ async function decodeFile(blocks, cid, meta) {
440
+ const entry = await (0, import_ipfs_unixfs_exporter.exporter)(cid.toString(), blocks, { length: meta.size });
441
+ const chunks = [];
442
+ for await (const chunk of entry.content()) {
443
+ chunks.push(chunk);
444
+ }
445
+ return new File(chunks, entry.name, { type: meta.type, lastModified: 0 });
446
+ }
447
+ function createFileEncoderStream(blob) {
448
+ const { readable, writable } = new TransformStream({}, queuingStrategy);
449
+ const unixfsWriter = UnixFS.createWriter({ writable, settings });
450
+ const fileBuilder = new UnixFSFileBuilder("", blob);
451
+ void (async () => {
452
+ await fileBuilder.finalize(unixfsWriter);
453
+ await unixfsWriter.close();
454
+ })();
455
+ return readable;
456
+ }
457
+ var UnixFS, raw, import_fixed, import_balanced, import_ipfs_unixfs_exporter, queuingStrategy, settings, UnixFSFileBuilder;
458
+ var init_files = __esm({
459
+ "src/runtime/files.ts"() {
460
+ "use strict";
461
+ UnixFS = __toESM(require("@ipld/unixfs"), 1);
462
+ raw = __toESM(require("multiformats/codecs/raw"), 1);
463
+ import_fixed = require("@ipld/unixfs/file/chunker/fixed");
464
+ import_balanced = require("@ipld/unixfs/file/layout/balanced");
465
+ import_ipfs_unixfs_exporter = require("ipfs-unixfs-exporter");
466
+ queuingStrategy = UnixFS.withCapacity();
467
+ settings = UnixFS.configure({
468
+ fileChunkEncoder: raw,
469
+ smallFileEncoder: raw,
470
+ chunker: (0, import_fixed.withMaxChunkSize)(1024 * 1024),
471
+ fileLayout: (0, import_balanced.withWidth)(1024)
472
+ });
473
+ UnixFSFileBuilder = class {
474
+ #file;
475
+ constructor(name, file) {
476
+ this.name = name;
477
+ this.#file = file;
478
+ }
479
+ async finalize(writer) {
480
+ const unixfsFileWriter = UnixFS.createFileWriter(writer);
481
+ await this.#file.stream().pipeTo(
482
+ new WritableStream({
483
+ async write(chunk) {
484
+ await unixfsFileWriter.write(chunk);
485
+ }
486
+ })
487
+ );
488
+ return await unixfsFileWriter.close();
489
+ }
490
+ };
491
+ }
492
+ });
493
+
494
+ // src/runtime/store-file-version.ts
495
+ var FILESTORE_VERSION;
496
+ var init_store_file_version = __esm({
497
+ "src/runtime/store-file-version.ts"() {
498
+ "use strict";
499
+ FILESTORE_VERSION = "v0.19-file";
500
+ }
501
+ });
502
+
503
+ // src/runtime/store-indexdb-version.ts
504
+ var INDEXDB_VERSION;
505
+ var init_store_indexdb_version = __esm({
506
+ "src/runtime/store-indexdb-version.ts"() {
507
+ "use strict";
508
+ INDEXDB_VERSION = "v0.19-indexdb";
509
+ }
510
+ });
511
+
512
+ // src/runtime/store-sql/v0.19-sqlite/version.ts
513
+ var SQLITE_VERSION;
514
+ var init_version = __esm({
515
+ "src/runtime/store-sql/v0.19-sqlite/version.ts"() {
516
+ "use strict";
517
+ SQLITE_VERSION = "v0.19-sqlite";
518
+ }
519
+ });
520
+
521
+ // src/runtime/index.ts
522
+ var runtime_exports = {};
523
+ __export(runtime_exports, {
524
+ FILESTORE_VERSION: () => FILESTORE_VERSION,
525
+ INDEXDB_VERSION: () => INDEXDB_VERSION,
526
+ SQLITE_VERSION: () => SQLITE_VERSION,
527
+ SysContainer: () => SysContainer,
528
+ crypto: () => crypto_exports,
529
+ dataDir: () => dataDir,
530
+ ensureIndexName: () => ensureIndexName,
531
+ files: () => files_exports,
532
+ getFileName: () => getFileName,
533
+ getPath: () => getPath,
534
+ join: () => join,
535
+ sql: () => store_sql_exports
536
+ });
537
+ var init_runtime = __esm({
538
+ "src/runtime/index.ts"() {
539
+ "use strict";
540
+ init_sys_container();
541
+ init_data_dir();
542
+ init_store_file_utils();
543
+ init_store_sql();
544
+ init_crypto();
545
+ init_files();
546
+ init_store_file_version();
547
+ init_store_indexdb_version();
548
+ init_version();
549
+ }
550
+ });
551
+
552
+ // src/utils.ts
553
+ function ensureLogger(optsOrLogger, componentName, ctx) {
554
+ let logger = globalLogger;
555
+ if ((0, import_cement2.IsLogger)(optsOrLogger)) {
556
+ logger = optsOrLogger;
557
+ } else if (optsOrLogger && (0, import_cement2.IsLogger)(optsOrLogger.logger)) {
558
+ logger = optsOrLogger.logger;
559
+ }
560
+ const cLogger = logger.With().Module(componentName);
561
+ const debug = [];
562
+ if (ctx) {
563
+ if ("debug" in ctx) {
564
+ if (typeof ctx.debug === "string" && ctx.debug.length > 0) {
565
+ debug.push(ctx.debug);
566
+ } else {
567
+ debug.push(componentName);
568
+ }
569
+ delete ctx.debug;
570
+ }
571
+ if ("this" in ctx) {
572
+ cLogger.Str("this", (0, import_uuidv72.uuidv7)());
573
+ delete ctx.this;
574
+ }
575
+ for (const [key, value] of Object.entries(ctx)) {
576
+ switch (typeof value) {
577
+ case "string":
578
+ cLogger.Str(key, value);
579
+ break;
580
+ case "number":
581
+ cLogger.Uint64(key, value);
582
+ break;
583
+ default:
584
+ if (value instanceof Date) {
585
+ cLogger.Str(key, value.toISOString());
586
+ } else if (value instanceof URL) {
587
+ cLogger.Str(key, value.toString());
588
+ } else if (typeof value === "function") {
589
+ cLogger.Ref(key, value);
590
+ } else {
591
+ cLogger.Any(key, value);
592
+ }
593
+ break;
594
+ }
595
+ }
596
+ }
597
+ registerFP_DEBUG.once(async () => {
598
+ SysContainer.env.onSet((key, value) => {
599
+ if (value) {
600
+ logger.SetDebug(value);
601
+ }
602
+ }, "FP_DEBUG");
603
+ }).finally(() => {
604
+ });
605
+ if (debug.length > 0) {
606
+ logger.SetDebug(debug);
607
+ }
608
+ const out = cLogger.Logger();
609
+ return out;
610
+ }
611
+ function getStore(url, logger, joiner) {
612
+ let result = url.searchParams.get("store");
613
+ if (!result) throw logger.Error().Str("url", url.toString()).Msg(`store not found`).AsError();
614
+ if (url.searchParams.has("index")) {
615
+ result = joiner(url.searchParams.get("index") || "idx", result);
616
+ }
617
+ return result;
618
+ }
619
+ function getKey(url, logger) {
620
+ const result = url.searchParams.get("key");
621
+ if (!result) throw logger.Error().Str("url", url.toString()).Msg(`key not found`).AsError();
622
+ return result;
623
+ }
624
+ function getName(url, logger) {
625
+ let result = url.searchParams.get("name");
626
+ if (!result) {
627
+ result = SysContainer.dirname(url.pathname);
628
+ if (result.length === 0) {
629
+ throw logger.Error().Str("url", url.toString()).Msg(`name not found`).AsError();
630
+ }
631
+ }
632
+ return result;
633
+ }
634
+ function exception2Result(fn) {
635
+ return fn().then((value) => import_cement2.Result.Ok(value)).catch((e) => import_cement2.Result.Err(e));
636
+ }
637
+ async function exceptionWrapper(fn) {
638
+ return fn().catch((e) => import_cement2.Result.Err(e));
639
+ }
640
+ var import_cement2, import_uuidv72, globalLogger, registerFP_DEBUG;
641
+ var init_utils = __esm({
642
+ "src/utils.ts"() {
643
+ "use strict";
644
+ import_cement2 = require("@adviser/cement");
645
+ init_runtime();
646
+ import_uuidv72 = require("uuidv7");
647
+ globalLogger = new import_cement2.LoggerImpl();
648
+ registerFP_DEBUG = new import_cement2.ResolveOnce();
649
+ }
650
+ });
651
+
652
+ // src/blockstore/gateway.ts
653
+ function isNotFoundError(e) {
654
+ if (import_cement3.Result.Is(e)) {
655
+ if (e.isOk()) return false;
656
+ e = e.Err();
657
+ }
658
+ if (e.code === "ENOENT") return true;
659
+ return false;
660
+ }
661
+ var import_cement3, NotFoundError;
662
+ var init_gateway = __esm({
663
+ "src/blockstore/gateway.ts"() {
664
+ "use strict";
665
+ import_cement3 = require("@adviser/cement");
666
+ NotFoundError = class extends Error {
667
+ constructor() {
668
+ super(...arguments);
669
+ this.code = "ENOENT";
670
+ }
671
+ };
672
+ }
673
+ });
674
+
675
+ // src/runtime/store-indexdb.ts
676
+ var store_indexdb_exports = {};
677
+ __export(store_indexdb_exports, {
678
+ IndexDBDataGateway: () => IndexDBDataGateway,
679
+ IndexDBMetaGateway: () => IndexDBMetaGateway,
680
+ IndexDBTestStore: () => IndexDBTestStore,
681
+ IndexDBWalGateway: () => IndexDBWalGateway,
682
+ getIndexDBName: () => getIndexDBName,
683
+ guardVersion: () => guardVersion
684
+ });
685
+ function ensureVersion(url) {
686
+ const ret = new URL(url.toString());
687
+ ret.searchParams.set("version", url.searchParams.get("version") || INDEXDB_VERSION);
688
+ return ret;
689
+ }
690
+ function guardVersion(url) {
691
+ if (!url.searchParams.has("version")) {
692
+ return import_cement4.Result.Err(`missing version: ${url.toString()}`);
693
+ }
694
+ return import_cement4.Result.Ok(url);
695
+ }
696
+ function sanitzeKey(key) {
697
+ if (key.length === 1) {
698
+ key = key[0];
699
+ }
700
+ return key;
701
+ }
702
+ async function connectIdb(url, logger) {
703
+ const dbName = getIndexDBName(url, logger);
704
+ const once2 = await onceIndexDB.get(dbName.fullDb).once(async () => {
705
+ const db = await (0, import_idb.openDB)(dbName.fullDb, 1, {
706
+ upgrade(db2) {
707
+ ["version", "data", "wal", "meta", "idx.data", "idx.wal", "idx.meta"].map((store) => {
708
+ db2.createObjectStore(store, {
709
+ autoIncrement: false
710
+ });
711
+ });
712
+ }
713
+ });
714
+ const found = await db.get("version", "version");
715
+ const version = url.searchParams.get("version") || INDEXDB_VERSION;
716
+ if (!found) {
717
+ await db.put("version", { version }, "version");
718
+ } else if (found.version !== version) {
719
+ logger.Warn().Str("url", url.toString()).Str("version", version).Str("found", found.version).Msg("version mismatch");
720
+ }
721
+ return { db, dbName, version };
722
+ });
723
+ url.searchParams.set("version", once2.version);
724
+ return once2.db;
725
+ }
726
+ function joinDBName(...names) {
727
+ return names.map((i) => i.replace(/^[^a-zA-Z0-9]+/g, "").replace(/[^a-zA-Z0-9]+/g, "_")).filter((i) => i.length).join(".");
728
+ }
729
+ function getIndexDBName(iurl, logger) {
730
+ const url = ensureVersion(iurl);
731
+ const fullDb = url.pathname.replace(/^\/+/, "").replace(/\?.*$/, "");
732
+ const dbName = url.searchParams.get("name");
733
+ if (!dbName) throw logger.Error().Str("url", url.toString()).Msg(`name not found`).AsError();
734
+ const result = joinDBName(fullDb, dbName);
735
+ const objStore = getStore(url, logger, joinDBName);
736
+ const connectionKey = [result, objStore].join(":");
737
+ return {
738
+ fullDb: result,
739
+ objStore,
740
+ connectionKey,
741
+ dbName
742
+ };
743
+ }
744
+ var import_idb, import_cement4, onceIndexDB, IndexDBGateway, IndexDBDataGateway, IndexDBWalGateway, IndexDBMetaGateway, txtEncoder, IndexDBTestStore;
745
+ var init_store_indexdb = __esm({
746
+ "src/runtime/store-indexdb.ts"() {
747
+ "use strict";
748
+ import_idb = require("idb");
749
+ import_cement4 = require("@adviser/cement");
750
+ init_store_indexdb_version();
751
+ init_utils();
752
+ init_gateway();
753
+ init_sys_container();
754
+ onceIndexDB = new import_cement4.KeyedResolvOnce();
755
+ IndexDBGateway = class {
756
+ constructor(logger) {
757
+ this.db = {};
758
+ this.logger = logger;
759
+ }
760
+ idb() {
761
+ this.db;
762
+ }
763
+ async start(baseURL) {
764
+ return exception2Result(async () => {
765
+ this.logger.Debug().Url(baseURL).Msg("starting");
766
+ await SysContainer.start();
767
+ this.db = await connectIdb(baseURL, this.logger);
768
+ this.logger.Debug().Url(baseURL).Msg("started");
769
+ });
770
+ }
771
+ async close() {
772
+ return import_cement4.Result.Ok(void 0);
773
+ }
774
+ async destroy(baseUrl) {
775
+ return exception2Result(async () => {
776
+ const type = getStore(baseUrl, this.logger, joinDBName);
777
+ const idb = this.db;
778
+ const trans = idb.transaction(type, "readwrite");
779
+ const object_store = trans.objectStore(type);
780
+ const toDelete = [];
781
+ for (let cursor = await object_store.openCursor(); cursor; cursor = await cursor.continue()) {
782
+ toDelete.push(cursor.primaryKey);
783
+ }
784
+ for (const key of toDelete) {
785
+ await trans.db.delete(type, key);
786
+ }
787
+ await trans.done;
788
+ });
789
+ }
790
+ async get(url) {
791
+ return exceptionWrapper(async () => {
792
+ const key = getKey(url, this.logger);
793
+ const store = getStore(url, this.logger, joinDBName);
794
+ this.logger.Debug().Url(url).Str("key", key).Str("store", store).Msg("getting");
795
+ const tx = this.db.transaction([store], "readonly");
796
+ const bytes = await tx.objectStore(store).get(sanitzeKey(key));
797
+ await tx.done;
798
+ if (!bytes) {
799
+ return import_cement4.Result.Err(new NotFoundError(`missing ${key}`));
800
+ }
801
+ return import_cement4.Result.Ok(bytes);
802
+ });
803
+ }
804
+ async put(url, value) {
805
+ return exception2Result(async () => {
806
+ const key = getKey(url, this.logger);
807
+ const store = getStore(url, this.logger, joinDBName);
808
+ this.logger.Debug().Url(url).Str("key", key).Str("store", store).Msg("putting");
809
+ const tx = this.db.transaction([store], "readwrite");
810
+ await tx.objectStore(store).put(value, sanitzeKey(key));
811
+ await tx.done;
812
+ });
813
+ }
814
+ async delete(url) {
815
+ return exception2Result(async () => {
816
+ const key = getKey(url, this.logger);
817
+ const store = getStore(url, this.logger, joinDBName);
818
+ this.logger.Debug().Url(url).Str("key", key).Str("store", store).Msg("deleting");
819
+ const tx = this.db.transaction([store], "readwrite");
820
+ await tx.objectStore(store).delete(sanitzeKey(key));
821
+ await tx.done;
822
+ return import_cement4.Result.Ok(void 0);
823
+ });
824
+ }
825
+ };
826
+ IndexDBDataGateway = class extends IndexDBGateway {
827
+ constructor(logger) {
828
+ super(ensureLogger(logger, "IndexDBDataGateway", {}));
829
+ }
830
+ buildUrl(baseUrl, key) {
831
+ const url = new URL(baseUrl.toString());
832
+ url.searchParams.set("key", key);
833
+ return Promise.resolve(import_cement4.Result.Ok(url));
834
+ }
835
+ };
836
+ IndexDBWalGateway = class extends IndexDBGateway {
837
+ constructor(logger) {
838
+ super(ensureLogger(logger, "IndexDBWalGateway", {}));
839
+ }
840
+ buildUrl(baseUrl, key) {
841
+ const url = new URL(baseUrl.toString());
842
+ url.searchParams.set("key", key);
843
+ return Promise.resolve(import_cement4.Result.Ok(url));
844
+ }
845
+ };
846
+ IndexDBMetaGateway = class extends IndexDBGateway {
847
+ constructor(logger) {
848
+ super(ensureLogger(logger, "IndexDBDataGateway", {}));
849
+ this.branches = /* @__PURE__ */ new Set();
850
+ }
851
+ async buildUrl(baseUrl, key) {
852
+ const url = new URL(baseUrl.toString());
853
+ this.branches.add(key);
854
+ url.searchParams.set("key", key);
855
+ return import_cement4.Result.Ok(url);
856
+ }
857
+ };
858
+ txtEncoder = new TextEncoder();
859
+ IndexDBTestStore = class {
860
+ constructor(logger) {
861
+ this.logger = ensureLogger(logger, "IndexDBTestStore", {});
862
+ }
863
+ async get(url, key) {
864
+ const db = await connectIdb(url, this.logger);
865
+ const store = getStore(url, this.logger, joinDBName);
866
+ this.logger.Debug().Str("key", key).Str("store", store).Msg("getting");
867
+ let bytes = await db.get(store, sanitzeKey(key));
868
+ this.logger.Debug().Str("key", key).Str("store", store).Int("len", bytes.length).Msg("got");
869
+ if (typeof bytes === "string") {
870
+ bytes = txtEncoder.encode(bytes);
871
+ }
872
+ return bytes;
873
+ }
874
+ };
875
+ }
876
+ });
877
+
878
+ // src/runtime/store-file.ts
879
+ var store_file_exports = {};
880
+ __export(store_file_exports, {
881
+ FileDataGateway: () => FileDataGateway,
882
+ FileMetaGateway: () => FileMetaGateway,
883
+ FileTestStore: () => FileTestStore,
884
+ FileWALGateway: () => FileWALGateway
885
+ });
886
+ async function ensureVersionFile(path, logger) {
887
+ let once2 = versionFiles.get(path);
888
+ if (!once2) {
889
+ once2 = new import_cement7.ResolveOnce();
890
+ versionFiles.set(path, once2);
891
+ }
892
+ await once2.once(async () => {
893
+ await SysContainer.mkdir(path, { recursive: true });
894
+ const vFile = SysContainer.join(path, "version");
895
+ const vFileStat = await SysContainer.stat(vFile).catch(() => void 0);
896
+ if (!vFileStat) {
897
+ await SysContainer.writefile(SysContainer.join(path, "version"), FILESTORE_VERSION);
898
+ return;
899
+ } else if (!vFileStat.isFile()) {
900
+ throw logger.Error().Str("file", vFile).Msg(`version file is a directory`).AsError();
901
+ }
902
+ const v = await SysContainer.readfile(vFile);
903
+ if (v.toString() !== FILESTORE_VERSION) {
904
+ console.warn(`version mismatch:${vFile}: ${v.toString()}!=${FILESTORE_VERSION}`);
905
+ }
906
+ });
907
+ return path;
908
+ }
909
+ function toArrayBuffer(buffer) {
910
+ const ab = new ArrayBuffer(buffer.length);
911
+ const view = new Uint8Array(ab);
912
+ for (let i = 0; i < buffer.length; ++i) {
913
+ view[i] = buffer[i];
914
+ }
915
+ return view;
916
+ }
917
+ var import_cement7, versionFiles, FileGateway, FileWALGateway, FileMetaGateway, FileDataGateway, FileTestStore;
918
+ var init_store_file = __esm({
919
+ "src/runtime/store-file.ts"() {
920
+ "use strict";
921
+ init_sys_container();
922
+ init_store_file_version();
923
+ import_cement7 = require("@adviser/cement");
924
+ init_utils();
925
+ init_gateway();
926
+ init_store_file_utils();
927
+ versionFiles = /* @__PURE__ */ new Map();
928
+ FileGateway = class {
929
+ constructor(logger) {
930
+ this.logger = logger;
931
+ }
932
+ start(baseURL) {
933
+ return exception2Result(async () => {
934
+ await SysContainer.start();
935
+ baseURL.searchParams.set("version", baseURL.searchParams.get("version") || FILESTORE_VERSION);
936
+ const url = await this.buildUrl(baseURL, "dummy");
937
+ if (url.isErr()) return url;
938
+ const dbdir = this.getFilePath(url.Ok());
939
+ await SysContainer.mkdir(SysContainer.dirname(dbdir), { recursive: true });
940
+ const dbroot = SysContainer.dirname(dbdir);
941
+ this.logger.Debug().Str("url", url.Ok().toString()).Str("dbroot", SysContainer.dirname(dbroot)).Msg("start");
942
+ await ensureVersionFile(dbroot, this.logger);
943
+ });
944
+ }
945
+ async close() {
946
+ return import_cement7.Result.Ok(void 0);
947
+ }
948
+ getFilePath(url) {
949
+ const path = url.toString().replace(/^file:\/\//, "").replace(/\?.*$/, "");
950
+ this.logger.Debug().Str("url", url.toString()).Str("path", path).Msg("getFilePath");
951
+ return path;
952
+ }
953
+ async put(url, body) {
954
+ return exception2Result(async () => {
955
+ const file = this.getFilePath(url);
956
+ this.logger.Debug().Str("url", url.toString()).Str("file", file).Msg("put");
957
+ await SysContainer.writefile(file, body);
958
+ });
959
+ }
960
+ async get(url) {
961
+ return exceptionWrapper(async () => {
962
+ const file = this.getFilePath(url);
963
+ try {
964
+ const res = await SysContainer.readfile(file);
965
+ this.logger.Debug().Url(url).Str("file", file).Msg("get");
966
+ return import_cement7.Result.Ok(new Uint8Array(res));
967
+ } catch (e) {
968
+ if (isNotFoundError(e)) {
969
+ return import_cement7.Result.Err(new NotFoundError(`file not found: ${file}`));
970
+ }
971
+ return import_cement7.Result.Err(e);
972
+ }
973
+ });
974
+ }
975
+ async delete(url) {
976
+ return exception2Result(async () => {
977
+ await SysContainer.unlink(this.getFilePath(url));
978
+ });
979
+ }
980
+ async destroyDir(baseURL) {
981
+ const url = await this.buildUrl(baseURL, "x");
982
+ if (url.isErr()) return url;
983
+ const filepath = SysContainer.dirname(this.getFilePath(url.Ok()));
984
+ let dir = [];
985
+ try {
986
+ dir = await SysContainer.readdir(filepath);
987
+ } catch (e) {
988
+ if (!isNotFoundError(e)) {
989
+ throw this.logger.Error().Err(e).Str("dir", filepath).Msg("destroy:readdir").AsError();
990
+ }
991
+ }
992
+ for (const file of dir) {
993
+ const pathed = SysContainer.join(filepath, file);
994
+ try {
995
+ await SysContainer.unlink(pathed);
996
+ } catch (e) {
997
+ if (!isNotFoundError(e)) {
998
+ throw this.logger.Error().Err(e).Str("file", pathed).Msg("destroy:unlink").AsError();
999
+ }
1000
+ }
1001
+ }
1002
+ return import_cement7.Result.Ok(void 0);
1003
+ }
1004
+ };
1005
+ FileWALGateway = class extends FileGateway {
1006
+ constructor(logger) {
1007
+ super(ensureLogger(logger, "FileWALGateway"));
1008
+ }
1009
+ async destroy(baseURL) {
1010
+ return this.destroyDir(baseURL);
1011
+ }
1012
+ async buildUrl(baseUrl, key) {
1013
+ const url = new URL(baseUrl.toString());
1014
+ url.pathname = SysContainer.join(await getPath(baseUrl, this.logger), ensureIndexName(baseUrl, "wal"), key + ".json");
1015
+ return import_cement7.Result.Ok(url);
1016
+ }
1017
+ };
1018
+ FileMetaGateway = class extends FileGateway {
1019
+ constructor(logger) {
1020
+ super(ensureLogger(logger, "FileMetaGateway"));
1021
+ }
1022
+ async destroy(baseURL) {
1023
+ return this.destroyDir(baseURL);
1024
+ }
1025
+ async buildUrl(baseUrl, key) {
1026
+ const url = new URL(baseUrl.toString());
1027
+ url.pathname = SysContainer.join(await getPath(baseUrl, this.logger), ensureIndexName(baseUrl, "meta"), key + ".json");
1028
+ return import_cement7.Result.Ok(url);
1029
+ }
1030
+ };
1031
+ FileDataGateway = class extends FileGateway {
1032
+ constructor(logger) {
1033
+ super(ensureLogger(logger, "FileDataGateway"));
1034
+ this.branches = /* @__PURE__ */ new Set();
1035
+ }
1036
+ async destroy(baseURL) {
1037
+ return this.destroyDir(baseURL);
1038
+ }
1039
+ async buildUrl(baseUrl, key) {
1040
+ const url = new URL(baseUrl.toString());
1041
+ url.pathname = SysContainer.join(await getPath(baseUrl, this.logger), ensureIndexName(baseUrl, "data"), key + ".car");
1042
+ return import_cement7.Result.Ok(url);
1043
+ }
1044
+ };
1045
+ FileTestStore = class {
1046
+ constructor(logger) {
1047
+ this.logger = ensureLogger(logger, "FileTestStore");
1048
+ }
1049
+ async get(url, key) {
1050
+ const logger = ensureLogger(this.logger, "get", { url: url.toString(), key });
1051
+ const dbFile = SysContainer.join(
1052
+ await getPath(url, this.logger),
1053
+ getStore(url, this.logger, SysContainer.join),
1054
+ getFileName(url, key, this.logger)
1055
+ );
1056
+ logger.Debug().Str("dbFile", dbFile).Msg("get");
1057
+ const buffer = await SysContainer.readfile(dbFile);
1058
+ logger.Debug().Str("dbFile", dbFile).Len(buffer).Msg("got");
1059
+ return toArrayBuffer(buffer);
1060
+ }
1061
+ };
1062
+ }
1063
+ });
1064
+
1065
+ // src/runtime/store-sql/sqlite-adapter-better-sqlite3.ts
1066
+ var import_cement8, onceSQLiteConnections, SQLiteConnection;
1067
+ var init_sqlite_adapter_better_sqlite3 = __esm({
1068
+ "src/runtime/store-sql/sqlite-adapter-better-sqlite3.ts"() {
1069
+ "use strict";
1070
+ import_cement8 = require("@adviser/cement");
1071
+ init_sys_container();
1072
+ init_ensurer();
1073
+ onceSQLiteConnections = new import_cement8.KeyedResolvOnce();
1074
+ SQLiteConnection = class _SQLiteConnection {
1075
+ static fromURL(url, opts = {}) {
1076
+ return new _SQLiteConnection(url, opts);
1077
+ }
1078
+ get client() {
1079
+ if (!this._client) {
1080
+ throw this.logger.Error().Msg("client not connected").AsError();
1081
+ }
1082
+ return this._client;
1083
+ }
1084
+ constructor(url, opts) {
1085
+ this.opts = ensureSQLOpts(url, opts, "SQLiteConnection", { url });
1086
+ this.logger = this.opts.logger;
1087
+ this.url = url;
1088
+ this.logger.Debug().Msg("constructor");
1089
+ }
1090
+ async connect() {
1091
+ let fName = this.url.toString().replace("sqlite://", "").replace(/\?.*$/, "");
1092
+ if (!fName) {
1093
+ throw this.logger.Error().Str("url", this.url.toString()).Msg("filename is empty").AsError();
1094
+ }
1095
+ const hasName = this.url.searchParams.get("name");
1096
+ if (hasName) {
1097
+ fName = SysContainer.join(fName, hasName);
1098
+ if (!fName.endsWith(".sqlite")) {
1099
+ fName += ".sqlite";
1100
+ }
1101
+ }
1102
+ this._client = await onceSQLiteConnections.get(fName).once(async () => {
1103
+ this.logger.Debug().Str("filename", fName).Msg("connect");
1104
+ const Sqlite3Database = (await import("better-sqlite3")).default;
1105
+ if (hasName) {
1106
+ await SysContainer.mkdir(SysContainer.dirname(fName), { recursive: true });
1107
+ }
1108
+ const db = new Sqlite3Database(fName, {
1109
+ // verbose: console.log,
1110
+ nativeBinding: "./node_modules/better-sqlite3/build/Release/better_sqlite3.node"
1111
+ });
1112
+ if (!db) {
1113
+ throw this.logger.Error().Msg("connect failed").AsError();
1114
+ }
1115
+ return db;
1116
+ });
1117
+ }
1118
+ async close() {
1119
+ this.logger.Debug().Msg("close");
1120
+ await this.client.close();
1121
+ }
1122
+ };
1123
+ }
1124
+ });
1125
+
1126
+ // src/runtime/store-sql/sql-connection-factory.ts
1127
+ function SQLConnectionFactory(databaseURL, opts = {}) {
1128
+ const logger = ensureLogger(opts, "SQLFactory");
1129
+ switch (databaseURL.protocol) {
1130
+ case "sqlite:":
1131
+ logger.Debug().Str("databaseURL", databaseURL.toString()).Msg("connecting to sqlite");
1132
+ return SQLiteConnection.fromURL(databaseURL, {
1133
+ ...opts,
1134
+ logger
1135
+ });
1136
+ default:
1137
+ throw logger.Error().Msg("unsupported protocol " + databaseURL.protocol).AsError();
1138
+ }
1139
+ }
1140
+ var init_sql_connection_factory = __esm({
1141
+ "src/runtime/store-sql/sql-connection-factory.ts"() {
1142
+ "use strict";
1143
+ init_utils();
1144
+ init_sqlite_adapter_better_sqlite3();
1145
+ }
1146
+ });
1147
+
1148
+ // src/runtime/store-sql/v0.19-sqlite/sqlite-ensure-version.ts
1149
+ async function ensureSQLiteVersion(url, dbConn) {
1150
+ const version = await once.once(async () => {
1151
+ const logger = ensureLogger(dbConn.opts, "ensureSQLiteVersion", {
1152
+ version: SQLITE_VERSION,
1153
+ url: dbConn.url.toString()
1154
+ });
1155
+ await dbConn.client.prepare(
1156
+ `CREATE TABLE IF NOT EXISTS version (
1157
+ version TEXT NOT NULL,
1158
+ updated_at TEXT NOT NULL)`
1159
+ ).run();
1160
+ const rows = await dbConn.client.prepare(`select version from version`).all();
1161
+ if (rows.length > 1) {
1162
+ throw logger.Error().Msg(`more than one version row found`).AsError();
1163
+ }
1164
+ if (rows.length === 0) {
1165
+ await dbConn.client.prepare(`insert into version (version, updated_at) values (?, ?)`).run(SQLITE_VERSION, (/* @__PURE__ */ new Date()).toISOString());
1166
+ return SQLITE_VERSION;
1167
+ }
1168
+ if (rows[0].version !== SQLITE_VERSION) {
1169
+ logger.Warn().Any("row", rows[0]).Msg(`version mismatch`);
1170
+ }
1171
+ return rows[0].version;
1172
+ });
1173
+ url.searchParams.set("version", version);
1174
+ }
1175
+ var import_cement9, once;
1176
+ var init_sqlite_ensure_version = __esm({
1177
+ "src/runtime/store-sql/v0.19-sqlite/sqlite-ensure-version.ts"() {
1178
+ "use strict";
1179
+ init_version();
1180
+ import_cement9 = require("@adviser/cement");
1181
+ init_utils();
1182
+ once = new import_cement9.ResolveOnce();
1183
+ }
1184
+ });
1185
+
1186
+ // src/runtime/store-sql/v0.19-sqlite/sqlite-wal-store.ts
1187
+ var sqlite_wal_store_exports = {};
1188
+ __export(sqlite_wal_store_exports, {
1189
+ V0_18_0SQLiteWalStore: () => V0_18_0SQLiteWalStore,
1190
+ WalSQLRecordBuilder: () => WalSQLRecordBuilder
1191
+ });
1192
+ var import_cement10, WalSQLRecordBuilder, V0_18_0SQLiteWalStore;
1193
+ var init_sqlite_wal_store = __esm({
1194
+ "src/runtime/store-sql/v0.19-sqlite/sqlite-wal-store.ts"() {
1195
+ "use strict";
1196
+ import_cement10 = require("@adviser/cement");
1197
+ init_sqlite_ensure_version();
1198
+ init_utils();
1199
+ WalSQLRecordBuilder = class _WalSQLRecordBuilder {
1200
+ #record;
1201
+ constructor(record) {
1202
+ this.#record = record;
1203
+ }
1204
+ static fromRecord(record) {
1205
+ return new _WalSQLRecordBuilder(record);
1206
+ }
1207
+ build() {
1208
+ return this.#record;
1209
+ }
1210
+ };
1211
+ V0_18_0SQLiteWalStore = class {
1212
+ constructor(dbConn) {
1213
+ this.dbConn = dbConn;
1214
+ this.textEncoder = dbConn.opts.textEncoder;
1215
+ this.logger = ensureLogger(dbConn.opts, "SQLiteWalStore");
1216
+ this.logger.Debug().Msg("constructor");
1217
+ }
1218
+ async start(url) {
1219
+ this.logger.Debug().Msg("start");
1220
+ await this.dbConn.connect();
1221
+ await ensureSQLiteVersion(url, this.dbConn);
1222
+ }
1223
+ table(url) {
1224
+ return getStore(url, this.logger, (...x) => x.join("_"));
1225
+ }
1226
+ #createTable = new import_cement10.KeyedResolvOnce();
1227
+ async createTable(url) {
1228
+ return this.#createTable.get(this.table(url)).once(async (table) => {
1229
+ await this.dbConn.client.prepare(
1230
+ `CREATE TABLE IF NOT EXISTS ${table} (
1231
+ name TEXT not null,
1232
+ branch TEXT not null,
1233
+ state BLOB NOT NULL,
1234
+ updated_at TEXT NOT NULL,
1235
+ PRIMARY KEY (name, branch)
1236
+ )`
1237
+ ).run();
1238
+ });
1239
+ }
1240
+ #insertStmt = new import_cement10.KeyedResolvOnce();
1241
+ async insertStmt(url) {
1242
+ return this.#insertStmt.get(this.table(url)).once(async (table) => {
1243
+ await this.createTable(url);
1244
+ return this.dbConn.client.prepare(`insert into ${table}
1245
+ (name, branch, state, updated_at)
1246
+ values (?, ?, ?, ?)
1247
+ ON CONFLICT(name, branch) DO UPDATE SET state=?, updated_at=?
1248
+ `);
1249
+ });
1250
+ }
1251
+ #selectStmt = new import_cement10.KeyedResolvOnce();
1252
+ async selectStmt(url) {
1253
+ return this.#selectStmt.get(this.table(url)).once(async (table) => {
1254
+ await this.createTable(url);
1255
+ return this.dbConn.client.prepare(
1256
+ `select name, branch, state, updated_at from ${table}
1257
+ where name = ? and branch = ?`
1258
+ );
1259
+ });
1260
+ }
1261
+ #deleteStmt = new import_cement10.KeyedResolvOnce();
1262
+ async deleteStmt(url) {
1263
+ return this.#deleteStmt.get(this.table(url)).once(async (table) => {
1264
+ await this.createTable(url);
1265
+ return this.dbConn.client.prepare(`delete from ${table} where name = ? and branch = ?`);
1266
+ });
1267
+ }
1268
+ async insert(url, ose) {
1269
+ const wal = WalSQLRecordBuilder.fromRecord(ose).build();
1270
+ const bufState = Buffer.from(this.textEncoder.encode(JSON.stringify(wal.state)));
1271
+ return this.insertStmt(url).then(
1272
+ (i) => i.run(ose.name, ose.branch, bufState, wal.updated_at.toISOString(), bufState, wal.updated_at.toISOString())
1273
+ );
1274
+ }
1275
+ async select(url, key) {
1276
+ const res = (await this.selectStmt(url).then((i) => i.all(key.name, key.branch))).map((irow) => {
1277
+ const row = irow;
1278
+ return {
1279
+ name: row.name,
1280
+ branch: row.branch,
1281
+ state: Uint8Array.from(row.state),
1282
+ updated_at: new Date(row.updated_at)
1283
+ };
1284
+ });
1285
+ this.logger.Debug().Str("name", key.name).Str("branch", key.branch).Uint64("res", res.length).Msg("select");
1286
+ return res;
1287
+ }
1288
+ async delete(url, key) {
1289
+ this.logger.Debug().Str("name", key.name).Str("branch", key.branch).Msg("delete");
1290
+ return this.deleteStmt(url).then((i) => i.run(key.name, key.branch));
1291
+ }
1292
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1293
+ async close(url) {
1294
+ this.logger.Debug().Msg("close");
1295
+ return import_cement10.Result.Ok(void 0);
1296
+ }
1297
+ async destroy(url) {
1298
+ return exception2Result(async () => {
1299
+ this.logger.Debug().Msg("destroy");
1300
+ await this.createTable(url);
1301
+ await this.dbConn.client.prepare(`delete from ${this.table(url)}`).run();
1302
+ });
1303
+ }
1304
+ };
1305
+ }
1306
+ });
1307
+
1308
+ // src/runtime/store-sql/v0.19-sqlite/sqlite-data-store.ts
1309
+ var sqlite_data_store_exports = {};
1310
+ __export(sqlite_data_store_exports, {
1311
+ DataSQLRecordBuilder: () => DataSQLRecordBuilder,
1312
+ V0_18_0SQLiteDataStore: () => V0_18_0SQLiteDataStore
1313
+ });
1314
+ var import_cement11, DataSQLRecordBuilder, V0_18_0SQLiteDataStore;
1315
+ var init_sqlite_data_store = __esm({
1316
+ "src/runtime/store-sql/v0.19-sqlite/sqlite-data-store.ts"() {
1317
+ "use strict";
1318
+ import_cement11 = require("@adviser/cement");
1319
+ init_sqlite_ensure_version();
1320
+ init_utils();
1321
+ DataSQLRecordBuilder = class _DataSQLRecordBuilder {
1322
+ constructor(dataRecord) {
1323
+ this.dataRecord = dataRecord;
1324
+ }
1325
+ static fromUploadParams(data, params) {
1326
+ return new _DataSQLRecordBuilder({
1327
+ name: params.name,
1328
+ car: params.car,
1329
+ data,
1330
+ updated_at: /* @__PURE__ */ new Date()
1331
+ });
1332
+ }
1333
+ build() {
1334
+ return this.dataRecord;
1335
+ }
1336
+ };
1337
+ V0_18_0SQLiteDataStore = class {
1338
+ constructor(dbConn) {
1339
+ this.dbConn = dbConn;
1340
+ this.logger = ensureLogger(dbConn.opts, "SQLiteDataStore");
1341
+ this.logger.Debug().Msg("constructor");
1342
+ }
1343
+ table(url) {
1344
+ return getStore(url, this.logger, (...x) => x.join("_"));
1345
+ }
1346
+ #createTable = new import_cement11.KeyedResolvOnce();
1347
+ async createTable(url) {
1348
+ return this.#createTable.get(this.table(url)).once(async (table) => {
1349
+ await this.dbConn.client.prepare(
1350
+ `CREATE TABLE IF NOT EXISTS ${table} (
1351
+ name TEXT NOT NULL,
1352
+ car TEXT PRIMARY KEY,
1353
+ data BLOB NOT NULL,
1354
+ updated_at TEXT NOT NULL)`
1355
+ ).run();
1356
+ });
1357
+ }
1358
+ #insertStmt = new import_cement11.KeyedResolvOnce();
1359
+ async insertStmt(url) {
1360
+ return this.#insertStmt.get(this.table(url)).once(async (table) => {
1361
+ await this.createTable(url);
1362
+ return this.dbConn.client.prepare(`
1363
+ insert into ${table}
1364
+ (name, car, data, updated_at) values (?, ?, ?, ?)
1365
+ ON CONFLICT(car) DO UPDATE SET updated_at=?`);
1366
+ });
1367
+ }
1368
+ #selectStmt = new import_cement11.KeyedResolvOnce();
1369
+ async selectStmt(url) {
1370
+ return this.#selectStmt.get(this.table(url)).once(async (table) => {
1371
+ await this.createTable(url);
1372
+ return this.dbConn.client.prepare(`select name, car, data, updated_at from ${table} where car = ?`);
1373
+ });
1374
+ }
1375
+ #deleteStmt = new import_cement11.KeyedResolvOnce();
1376
+ async deleteStmt(url) {
1377
+ return this.#deleteStmt.get(this.table(url)).once(async (table) => {
1378
+ await this.createTable(url);
1379
+ return this.dbConn.client.prepare(`delete from ${table} where car = ?`);
1380
+ });
1381
+ }
1382
+ async start(url) {
1383
+ this.logger.Debug().Msg("start-connect");
1384
+ await this.dbConn.connect();
1385
+ this.logger.Debug().Msg("start-connected");
1386
+ await ensureSQLiteVersion(url, this.dbConn);
1387
+ this.logger.Debug().Msg("start-set-version");
1388
+ }
1389
+ async insert(url, ose) {
1390
+ this.logger.Debug().Str("name", ose.name).Str("car", ose.car).Uint64("data-len", ose.data.length).Msg("insert");
1391
+ const updated_at = ose.updated_at.toISOString();
1392
+ return this.insertStmt(url).then((i) => i.run(ose.name, ose.car, Buffer.from(ose.data), updated_at, updated_at));
1393
+ }
1394
+ async select(url, car) {
1395
+ this.logger.Debug().Str("car", car).Msg("select");
1396
+ return (await this.selectStmt(url).then((i) => i.all(car))).map((irow) => {
1397
+ const row = irow;
1398
+ return {
1399
+ name: row.name,
1400
+ car: row.car,
1401
+ data: Uint8Array.from(row.data),
1402
+ updated_at: new Date(row.updated_at)
1403
+ };
1404
+ });
1405
+ }
1406
+ async delete(url, car) {
1407
+ this.logger.Debug().Str("car", car).Msg("delete");
1408
+ const ret = await this.deleteStmt(url).then((i) => i.run(car));
1409
+ return ret;
1410
+ }
1411
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1412
+ async close(url) {
1413
+ this.logger.Debug().Msg("close");
1414
+ return import_cement11.Result.Ok(void 0);
1415
+ }
1416
+ async destroy(url) {
1417
+ return exception2Result(async () => {
1418
+ this.logger.Debug().Msg("destroy");
1419
+ await this.createTable(url);
1420
+ await this.dbConn.client.prepare(`delete from ${this.table(url)}`).run();
1421
+ });
1422
+ }
1423
+ };
1424
+ }
1425
+ });
1426
+
1427
+ // src/runtime/store-sql/v0.19-sqlite/sqlite-meta-store.ts
1428
+ var sqlite_meta_store_exports = {};
1429
+ __export(sqlite_meta_store_exports, {
1430
+ MetaSQLRecordBuilder: () => MetaSQLRecordBuilder,
1431
+ V0_18_0SQLiteMetaStore: () => V0_18_0SQLiteMetaStore
1432
+ });
1433
+ var import_cement12, MetaSQLRecordBuilder, V0_18_0SQLiteMetaStore;
1434
+ var init_sqlite_meta_store = __esm({
1435
+ "src/runtime/store-sql/v0.19-sqlite/sqlite-meta-store.ts"() {
1436
+ "use strict";
1437
+ import_cement12 = require("@adviser/cement");
1438
+ init_sqlite_ensure_version();
1439
+ init_utils();
1440
+ MetaSQLRecordBuilder = class _MetaSQLRecordBuilder {
1441
+ constructor(record, textEncoder3) {
1442
+ this.record = record;
1443
+ this.textEncoder = textEncoder3;
1444
+ }
1445
+ static fromUploadMetaFnParams(data, params, textEncoder3) {
1446
+ return new _MetaSQLRecordBuilder(
1447
+ {
1448
+ name: params.name,
1449
+ branch: params.branch,
1450
+ meta: data,
1451
+ updated_at: /* @__PURE__ */ new Date()
1452
+ },
1453
+ textEncoder3
1454
+ );
1455
+ }
1456
+ static fromBytes(str, name, branch, textEncoder3) {
1457
+ return new _MetaSQLRecordBuilder(
1458
+ {
1459
+ name,
1460
+ branch,
1461
+ meta: textEncoder3.encode(str),
1462
+ updated_at: /* @__PURE__ */ new Date()
1463
+ },
1464
+ textEncoder3
1465
+ );
1466
+ }
1467
+ build() {
1468
+ return this.record;
1469
+ }
1470
+ };
1471
+ V0_18_0SQLiteMetaStore = class {
1472
+ constructor(dbConn) {
1473
+ this.dbConn = dbConn;
1474
+ this.logger = ensureLogger(dbConn.opts, "SQLiteMetaStore");
1475
+ this.logger.Debug().Msg("constructor");
1476
+ }
1477
+ async start(url) {
1478
+ this.logger.Debug().Url(url).Msg("starting");
1479
+ await this.dbConn.connect();
1480
+ await ensureSQLiteVersion(url, this.dbConn);
1481
+ this.logger.Debug().Url(url).Msg("started");
1482
+ }
1483
+ table(url) {
1484
+ return getStore(url, this.logger, (...x) => x.join("_"));
1485
+ }
1486
+ #createTable = new import_cement12.KeyedResolvOnce();
1487
+ async createTable(url) {
1488
+ return this.#createTable.get(this.table(url)).once(async (table) => {
1489
+ await this.dbConn.client.prepare(
1490
+ `CREATE TABLE IF NOT EXISTS ${table} (
1491
+ name TEXT not null,
1492
+ branch TEXT not null,
1493
+ meta BLOB NOT NULL,
1494
+ updated_at TEXT NOT NULL,
1495
+ PRIMARY KEY (name, branch)
1496
+ )`
1497
+ ).run();
1498
+ });
1499
+ }
1500
+ #insertStmt = new import_cement12.KeyedResolvOnce();
1501
+ async insertStmt(url) {
1502
+ return this.#insertStmt.get(this.table(url)).once(async (table) => {
1503
+ await this.createTable(url);
1504
+ return this.dbConn.client.prepare(`insert into ${table}
1505
+ (name, branch, meta, updated_at)
1506
+ values (?, ?, ?, ?)
1507
+ ON CONFLICT(name, branch) DO UPDATE SET meta=?, updated_at=?
1508
+ `);
1509
+ });
1510
+ }
1511
+ #selectStmt = new import_cement12.KeyedResolvOnce();
1512
+ async selectStmt(url) {
1513
+ return this.#selectStmt.get(this.table(url)).once(async (table) => {
1514
+ await this.createTable(url);
1515
+ return this.dbConn.client.prepare(`select name, branch, meta, updated_at from ${table} where name = ? and branch = ?`);
1516
+ });
1517
+ }
1518
+ #deleteStmt = new import_cement12.KeyedResolvOnce();
1519
+ async deleteStmt(url) {
1520
+ return this.#deleteStmt.get(this.table(url)).once(async (table) => {
1521
+ await this.createTable(url);
1522
+ return this.dbConn.client.prepare(`delete from ${table} where name = ? and branch = ?`);
1523
+ });
1524
+ }
1525
+ async insert(url, ose) {
1526
+ this.logger.Debug().Str("name", ose.name).Str("branch", ose.branch).Uint64("data-len", ose.meta.length).Msg("insert");
1527
+ const bufMeta = Buffer.from(ose.meta);
1528
+ return this.insertStmt(url).then(
1529
+ (i) => i.run(ose.name, ose.branch, bufMeta, ose.updated_at.toISOString(), bufMeta, ose.updated_at.toISOString())
1530
+ );
1531
+ }
1532
+ async select(url, key) {
1533
+ this.logger.Debug().Str("name", key.name).Str("branch", key.branch).Msg("select");
1534
+ return (await this.selectStmt(url).then((i) => i.all(key.name, key.branch))).map((irow) => {
1535
+ const row = irow;
1536
+ return {
1537
+ name: row.name,
1538
+ branch: row.branch,
1539
+ meta: Uint8Array.from(row.meta),
1540
+ updated_at: new Date(row.updated_at)
1541
+ };
1542
+ });
1543
+ }
1544
+ async delete(url, key) {
1545
+ this.logger.Debug().Str("name", key.name).Str("branch", key.branch).Msg("delete");
1546
+ return this.deleteStmt(url).then((i) => i.run(key.name, key.branch));
1547
+ }
1548
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1549
+ async close(url) {
1550
+ this.logger.Debug().Msg("close");
1551
+ return import_cement12.Result.Ok(void 0);
1552
+ }
1553
+ async destroy(url) {
1554
+ return exception2Result(async () => {
1555
+ this.logger.Debug().Msg("destroy");
1556
+ await this.dbConn.client.prepare(`delete from ${this.table(url)}`).run();
1557
+ });
1558
+ }
1559
+ };
1560
+ }
1561
+ });
1562
+
1563
+ // src/runtime/store-sql/store-version-factory.ts
1564
+ async function WalStoreFactory(db) {
1565
+ switch (db.opts.sqlFlavor) {
1566
+ case "sqlite": {
1567
+ const { V0_18_0SQLiteWalStore: V0_18_0SQLiteWalStore2 } = await Promise.resolve().then(() => (init_sqlite_wal_store(), sqlite_wal_store_exports));
1568
+ const store = new V0_18_0SQLiteWalStore2(db);
1569
+ return store;
1570
+ }
1571
+ default:
1572
+ throw ensureLogger(db.opts, "WalStoreFactory").Error().Msg("unsupported db connection").AsError();
1573
+ }
1574
+ }
1575
+ async function DataStoreFactory(db) {
1576
+ switch (db.opts.sqlFlavor) {
1577
+ case "sqlite": {
1578
+ const { V0_18_0SQLiteDataStore: V0_18_0SQLiteDataStore2 } = await Promise.resolve().then(() => (init_sqlite_data_store(), sqlite_data_store_exports));
1579
+ const store = new V0_18_0SQLiteDataStore2(db);
1580
+ return store;
1581
+ }
1582
+ default:
1583
+ throw ensureLogger(db.opts, "DataStoreFactory").Error().Msg("unsupported db connection").AsError();
1584
+ }
1585
+ }
1586
+ async function MetaStoreFactory(db) {
1587
+ switch (db.opts.sqlFlavor) {
1588
+ case "sqlite": {
1589
+ const { V0_18_0SQLiteMetaStore: V0_18_0SQLiteMetaStore2 } = await Promise.resolve().then(() => (init_sqlite_meta_store(), sqlite_meta_store_exports));
1590
+ const store = new V0_18_0SQLiteMetaStore2(db);
1591
+ return store;
1592
+ }
1593
+ default:
1594
+ throw ensureLogger(db.opts, "MetaStoreFactory").Error().Msg("unsupported db connection").AsError();
1595
+ }
1596
+ }
1597
+ var init_store_version_factory = __esm({
1598
+ "src/runtime/store-sql/store-version-factory.ts"() {
1599
+ "use strict";
1600
+ init_utils();
1601
+ init_version();
1602
+ }
1603
+ });
1604
+
1605
+ // src/runtime/store-sql/store-sql.ts
1606
+ var store_sql_exports2 = {};
1607
+ __export(store_sql_exports2, {
1608
+ SQLDataGateway: () => SQLDataGateway,
1609
+ SQLMetaGateway: () => SQLMetaGateway,
1610
+ SQLTestStore: () => SQLTestStore,
1611
+ SQLWalGateway: () => SQLWalGateway
1612
+ });
1613
+ var import_cement13, SQLWalGateway, SQLMetaGateway, SQLDataGateway, SQLTestStore;
1614
+ var init_store_sql2 = __esm({
1615
+ "src/runtime/store-sql/store-sql.ts"() {
1616
+ "use strict";
1617
+ import_cement13 = require("@adviser/cement");
1618
+ init_sql_connection_factory();
1619
+ init_store_version_factory();
1620
+ init_utils();
1621
+ init_gateway();
1622
+ SQLWalGateway = class {
1623
+ constructor(logger) {
1624
+ this.walSQLStore = {};
1625
+ this.logger = ensureLogger(logger, "SQLWalGateway");
1626
+ }
1627
+ buildUrl(baseUrl, key) {
1628
+ const url = new URL(baseUrl.toString());
1629
+ url.searchParams.set("key", key);
1630
+ return Promise.resolve(import_cement13.Result.Ok(url));
1631
+ }
1632
+ async start(baseUrl) {
1633
+ return exception2Result(async () => {
1634
+ this.logger.Debug().Url(baseUrl).Msg("start");
1635
+ const conn = SQLConnectionFactory(baseUrl);
1636
+ const ws = await WalStoreFactory(conn);
1637
+ await ws.start(baseUrl);
1638
+ this.walSQLStore = ws;
1639
+ });
1640
+ }
1641
+ close(baseUrl) {
1642
+ return this.walSQLStore.close(baseUrl);
1643
+ }
1644
+ destroy(baseUrl) {
1645
+ return this.walSQLStore.destroy(baseUrl);
1646
+ }
1647
+ async put(url, body) {
1648
+ return exception2Result(async () => {
1649
+ const branch = getKey(url, this.logger);
1650
+ const name = getName(url, this.logger);
1651
+ await this.walSQLStore.insert(url, {
1652
+ state: body,
1653
+ updated_at: /* @__PURE__ */ new Date(),
1654
+ name,
1655
+ branch
1656
+ });
1657
+ });
1658
+ }
1659
+ async get(url) {
1660
+ return exceptionWrapper(async () => {
1661
+ const branch = getKey(url, this.logger);
1662
+ const name = getName(url, this.logger);
1663
+ const record = await this.walSQLStore.select(url, { name, branch });
1664
+ if (record.length === 0) {
1665
+ return import_cement13.Result.Err(new NotFoundError(`not found ${name} ${branch}`));
1666
+ }
1667
+ return import_cement13.Result.Ok(record[0].state);
1668
+ });
1669
+ }
1670
+ async delete(url) {
1671
+ return exception2Result(async () => {
1672
+ const branch = getKey(url, this.logger);
1673
+ const name = getName(url, this.logger);
1674
+ await this.walSQLStore.delete(url, { name, branch });
1675
+ });
1676
+ }
1677
+ };
1678
+ SQLMetaGateway = class {
1679
+ constructor(logger) {
1680
+ this.metaSQLStore = {};
1681
+ this.logger = ensureLogger(logger, "SQLMetaGateway");
1682
+ }
1683
+ buildUrl(baseUrl, key) {
1684
+ const url = new URL(baseUrl.toString());
1685
+ url.searchParams.set("key", key);
1686
+ return Promise.resolve(import_cement13.Result.Ok(url));
1687
+ }
1688
+ async start(baseUrl) {
1689
+ return exception2Result(async () => {
1690
+ this.logger.Debug().Url(baseUrl).Msg("start");
1691
+ const conn = SQLConnectionFactory(baseUrl);
1692
+ const ws = await MetaStoreFactory(conn);
1693
+ await ws.start(baseUrl);
1694
+ this.metaSQLStore = ws;
1695
+ this.logger.Debug().Url(baseUrl).Msg("started");
1696
+ });
1697
+ }
1698
+ close(baseUrl) {
1699
+ return this.metaSQLStore.close(baseUrl);
1700
+ }
1701
+ destroy(baseUrl) {
1702
+ return this.metaSQLStore.destroy(baseUrl);
1703
+ }
1704
+ async put(url, body) {
1705
+ return exception2Result(async () => {
1706
+ const branch = getKey(url, this.logger);
1707
+ const name = getName(url, this.logger);
1708
+ await this.metaSQLStore.insert(url, {
1709
+ meta: body,
1710
+ updated_at: /* @__PURE__ */ new Date(),
1711
+ name,
1712
+ branch
1713
+ });
1714
+ });
1715
+ }
1716
+ async get(url) {
1717
+ return exceptionWrapper(async () => {
1718
+ const branch = getKey(url, this.logger);
1719
+ const name = getName(url, this.logger);
1720
+ const record = await this.metaSQLStore.select(url, {
1721
+ name,
1722
+ branch
1723
+ });
1724
+ if (record.length === 0) {
1725
+ return import_cement13.Result.Err(new NotFoundError(`not found ${name} ${branch}`));
1726
+ }
1727
+ return import_cement13.Result.Ok(record[0].meta);
1728
+ });
1729
+ }
1730
+ async delete(url) {
1731
+ return exception2Result(async () => {
1732
+ const branch = getKey(url, this.logger);
1733
+ const name = getName(url, this.logger);
1734
+ await this.metaSQLStore.delete(url, {
1735
+ name,
1736
+ branch
1737
+ });
1738
+ });
1739
+ }
1740
+ };
1741
+ SQLDataGateway = class {
1742
+ constructor(logger) {
1743
+ this.dataSQLStore = {};
1744
+ this.logger = ensureLogger(logger, "SQLDataGateway");
1745
+ }
1746
+ buildUrl(baseUrl, key) {
1747
+ const url = new URL(baseUrl.toString());
1748
+ url.searchParams.set("key", key);
1749
+ return Promise.resolve(import_cement13.Result.Ok(url));
1750
+ }
1751
+ async start(baseUrl) {
1752
+ return exception2Result(async () => {
1753
+ this.logger.Debug().Url(baseUrl).Msg("pre-sql-connection");
1754
+ const conn = SQLConnectionFactory(baseUrl);
1755
+ this.logger.Debug().Url(baseUrl).Msg("post-sql-connection");
1756
+ const ws = await DataStoreFactory(conn);
1757
+ this.logger.Debug().Url(baseUrl).Msg("post-data-store-factory");
1758
+ await ws.start(baseUrl);
1759
+ this.dataSQLStore = ws;
1760
+ this.logger.Debug().Url(baseUrl).Msg("started");
1761
+ });
1762
+ }
1763
+ close(baseUrl) {
1764
+ return this.dataSQLStore.close(baseUrl);
1765
+ }
1766
+ destroy(baseUrl) {
1767
+ return this.dataSQLStore.destroy(baseUrl);
1768
+ }
1769
+ async put(url, body) {
1770
+ return exception2Result(async () => {
1771
+ const cid = getKey(url, this.logger);
1772
+ const name = getName(url, this.logger);
1773
+ await this.dataSQLStore.insert(url, {
1774
+ data: body,
1775
+ updated_at: /* @__PURE__ */ new Date(),
1776
+ name,
1777
+ car: cid
1778
+ });
1779
+ });
1780
+ }
1781
+ async get(url) {
1782
+ return exceptionWrapper(async () => {
1783
+ const branch = getKey(url, this.logger);
1784
+ const record = await this.dataSQLStore.select(url, branch);
1785
+ if (record.length === 0) {
1786
+ return import_cement13.Result.Err(new NotFoundError(`not found ${branch}`));
1787
+ }
1788
+ return import_cement13.Result.Ok(record[0].data);
1789
+ });
1790
+ }
1791
+ async delete(url) {
1792
+ return exception2Result(async () => {
1793
+ const branch = getKey(url, this.logger);
1794
+ await this.dataSQLStore.delete(url, branch);
1795
+ return import_cement13.Result.Ok(void 0);
1796
+ });
1797
+ }
1798
+ };
1799
+ SQLTestStore = class {
1800
+ constructor(ilogger) {
1801
+ const logger = ensureLogger(ilogger, "SQLTestStore");
1802
+ this.logger = logger;
1803
+ }
1804
+ async get(url, key) {
1805
+ const conn = SQLConnectionFactory(url);
1806
+ const name = getName(url, this.logger);
1807
+ switch (url.searchParams.get("store")) {
1808
+ case "wal": {
1809
+ const sqlStore = await WalStoreFactory(conn);
1810
+ await sqlStore.start(url);
1811
+ const records = await sqlStore.select(url, {
1812
+ name,
1813
+ branch: key
1814
+ });
1815
+ return records[0].state;
1816
+ }
1817
+ case "meta": {
1818
+ const sqlStore = await MetaStoreFactory(conn);
1819
+ await sqlStore.start(url);
1820
+ const records = await sqlStore.select(url, {
1821
+ name,
1822
+ branch: key
1823
+ });
1824
+ return records[0].meta;
1825
+ }
1826
+ case "data": {
1827
+ const sqlStore = await DataStoreFactory(conn);
1828
+ await sqlStore.start(url);
1829
+ const records = await sqlStore.select(url, key);
1830
+ return records[0].data;
1831
+ }
1832
+ default:
1833
+ throw this.logger.Error().Str("key", key).Msg(`Method not implemented`);
1834
+ }
1835
+ }
1836
+ };
1837
+ }
1838
+ });
1839
+
1840
+ // src/index.ts
1841
+ var src_exports = {};
1842
+ __export(src_exports, {
1843
+ CRDT: () => CRDT,
1844
+ Database: () => Database,
1845
+ Index: () => Index,
1846
+ PACKAGE_VERSION: () => PACKAGE_VERSION,
1847
+ blockstore: () => blockstore_exports,
1848
+ bs: () => blockstore_exports,
1849
+ ensureLogger: () => ensureLogger,
1850
+ exception2Result: () => exception2Result,
1851
+ exceptionWrapper: () => exceptionWrapper,
1852
+ falsyToUndef: () => falsyToUndef,
1853
+ fireproof: () => fireproof,
1854
+ getKey: () => getKey,
1855
+ getName: () => getName,
1856
+ getStore: () => getStore,
1857
+ index: () => index,
1858
+ isFalsy: () => isFalsy,
1859
+ rt: () => runtime_exports,
1860
+ runtime: () => runtime_exports,
1861
+ throwFalsy: () => throwFalsy
1862
+ });
1863
+ module.exports = __toCommonJS(src_exports);
1864
+
1865
+ // src/database.ts
1866
+ var import_uuidv73 = require("uuidv7");
1867
+ var import_cement17 = require("@adviser/cement");
1868
+
1869
+ // src/write-queue.ts
1870
+ function writeQueue(worker, payload = Infinity, unbounded = false) {
1871
+ const queue = [];
1872
+ let isProcessing = false;
1873
+ async function process() {
1874
+ if (isProcessing || queue.length === 0) return;
1875
+ isProcessing = true;
1876
+ const tasksToProcess = queue.splice(0, payload);
1877
+ const updates = tasksToProcess.map((item) => item.task);
1878
+ if (unbounded) {
1879
+ const promises = updates.map(async (update, index2) => {
1880
+ try {
1881
+ const result = await worker([update]);
1882
+ tasksToProcess[index2].resolve(result);
1883
+ } catch (error) {
1884
+ tasksToProcess[index2].reject(error);
1885
+ }
1886
+ });
1887
+ await Promise.all(promises);
1888
+ } else {
1889
+ try {
1890
+ const result = await worker(updates);
1891
+ tasksToProcess.forEach((task) => task.resolve(result));
1892
+ } catch (error) {
1893
+ tasksToProcess.forEach((task) => task.reject(error));
1894
+ }
1895
+ }
1896
+ isProcessing = false;
1897
+ void process();
1898
+ }
1899
+ return {
1900
+ push(task) {
1901
+ return new Promise((resolve, reject) => {
1902
+ queue.push({ task, resolve, reject });
1903
+ void process();
1904
+ });
1905
+ }
1906
+ };
1907
+ }
1908
+
1909
+ // src/crdt.ts
1910
+ var import_cement16 = require("@adviser/cement");
1911
+
1912
+ // src/crdt-helpers.ts
1913
+ var import_block6 = require("multiformats/block");
1914
+ var import_link = require("multiformats/link");
1915
+ var import_sha23 = require("multiformats/hashes/sha2");
1916
+ var codec2 = __toESM(require("@ipld/dag-cbor"), 1);
1917
+ var import_crdt = require("@web3-storage/pail/crdt");
1918
+ var import_clock2 = require("@web3-storage/pail/clock");
1919
+ var Batch = __toESM(require("@web3-storage/pail/crdt/batch"), 1);
1920
+
1921
+ // src/blockstore/index.ts
1922
+ var blockstore_exports = {};
1923
+ __export(blockstore_exports, {
1924
+ BaseBlockstore: () => BaseBlockstore,
1925
+ CarTransaction: () => CarTransaction,
1926
+ CompactionFetcher: () => CompactionFetcher,
1927
+ ConnectREST: () => ConnectREST,
1928
+ ConnectionBase: () => ConnectionBase,
1929
+ DataStore: () => DataStore,
1930
+ EncryptedBlockstore: () => EncryptedBlockstore,
1931
+ Loadable: () => Loadable,
1932
+ Loader: () => Loader,
1933
+ MetaStore: () => MetaStore,
1934
+ NotFoundError: () => NotFoundError,
1935
+ RemoteWAL: () => RemoteWAL,
1936
+ isNotFoundError: () => isNotFoundError,
1937
+ parseCarFile: () => parseCarFile,
1938
+ registerStoreProtocol: () => registerStoreProtocol,
1939
+ testStoreFactory: () => testStoreFactory,
1940
+ toStoreRuntime: () => toStoreRuntime,
1941
+ toURL: () => toURL
1942
+ });
1943
+
1944
+ // src/blockstore/connection-base.ts
1945
+ var import_clock = require("@web3-storage/pail/clock");
1946
+ var import_block = require("@web3-storage/pail/block");
1947
+ init_types();
1948
+
1949
+ // src/blockstore/task-manager.ts
1950
+ init_utils();
1951
+ var TaskManager = class {
1952
+ constructor(loader) {
1953
+ this.eventsWeHandled = /* @__PURE__ */ new Set();
1954
+ this.queue = [];
1955
+ this.isProcessing = false;
1956
+ this.loader = loader;
1957
+ this.logger = ensureLogger(loader.logger, "TaskManager");
1958
+ }
1959
+ async handleEvent(eventBlock) {
1960
+ const cid = eventBlock.cid.toString();
1961
+ const parents = eventBlock.value.parents.map((cid2) => cid2.toString());
1962
+ for (const parent of parents) {
1963
+ this.eventsWeHandled.add(parent);
1964
+ }
1965
+ this.queue.push({ cid, eventBlock, retries: 0 });
1966
+ this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
1967
+ void this.processQueue();
1968
+ }
1969
+ async processQueue() {
1970
+ if (this.isProcessing) return;
1971
+ this.isProcessing = true;
1972
+ const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
1973
+ const first = filteredQueue[0];
1974
+ if (!first) {
1975
+ return;
1976
+ }
1977
+ try {
1978
+ this.loader?.remoteMetaStore?.handleByteHeads([first.eventBlock.value.data.dbMeta]);
1979
+ this.eventsWeHandled.add(first.cid);
1980
+ this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
1981
+ } catch (err) {
1982
+ if (first.retries++ > 3) {
1983
+ this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
1984
+ this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
1985
+ }
1986
+ await new Promise((resolve) => setTimeout(resolve, 50));
1987
+ throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
1988
+ } finally {
1989
+ this.isProcessing = false;
1990
+ if (this.queue.length > 0) {
1991
+ void this.processQueue();
1992
+ }
1993
+ }
1994
+ }
1995
+ };
1996
+
1997
+ // src/blockstore/connection-base.ts
1998
+ init_utils();
1999
+ var ConnectionBase = class {
2000
+ constructor(logger) {
2001
+ // readonly ready: Promise<unknown>;
2002
+ // todo move to LRU blockstore https://github.com/web3-storage/w3clock/blob/main/src/worker/block.js
2003
+ this.eventBlocks = new import_block.MemoryBlockstore();
2004
+ this.parents = [];
2005
+ this.loaded = Promise.resolve();
2006
+ this.logger = ensureLogger(logger, "ConnectionBase");
2007
+ }
2008
+ async refresh() {
2009
+ await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load("main");
2010
+ await (await throwFalsy(this.loader).remoteWAL())._process();
2011
+ }
2012
+ connect({ loader }) {
2013
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
2014
+ this.connectMeta({ loader });
2015
+ this.connectStorage({ loader });
2016
+ }
2017
+ connectMeta({ loader }) {
2018
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
2019
+ this.loader = loader;
2020
+ this.taskManager = new TaskManager(loader);
2021
+ this.onConnect();
2022
+ this.logger.Warn().Msg("connectMeta: connecting to remote meta store is disabled");
2023
+ }
2024
+ async onConnect() {
2025
+ return;
2026
+ }
2027
+ connectStorage({ loader }) {
2028
+ if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
2029
+ this.loader = loader;
2030
+ this.logger.Warn().Msg("connectStorage: connecting to remote meta store is disabled");
2031
+ }
2032
+ async createEventBlock(bytes) {
2033
+ const data = {
2034
+ dbMeta: bytes
2035
+ };
2036
+ const event = await import_clock.EventBlock.create(
2037
+ data,
2038
+ this.parents
2039
+ );
2040
+ await this.eventBlocks.put(event.cid, event.bytes);
2041
+ return event;
2042
+ }
2043
+ async decodeEventBlock(bytes) {
2044
+ const event = await (0, import_clock.decodeEventBlock)(bytes);
2045
+ return event;
2046
+ }
2047
+ // move this stuff to connect
2048
+ // async getDashboardURL(compact = true) {
2049
+ // const baseUrl = 'https://dashboard.fireproof.storage/'
2050
+ // if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
2051
+ // // if (compact) {
2052
+ // // await this.compact()
2053
+ // // }
2054
+ // const currents = await this.loader?.metaStore?.load()
2055
+ // if (!currents) throw new Error("Can't sync empty database: save data first")
2056
+ // if (currents.length > 1)
2057
+ // throw new Error("Can't sync database with split heads: make an update first")
2058
+ // const current = currents[0]
2059
+ // const params = {
2060
+ // car: current.car.toString()
2061
+ // }
2062
+ // if (current.key) {
2063
+ // // @ts-ignore
2064
+ // params.key = current.key.toString()
2065
+ // }
2066
+ // // @ts-ignore
2067
+ // if (this.name) {
2068
+ // // @ts-ignore
2069
+ // params.name = this.name
2070
+ // }
2071
+ // const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
2072
+ // console.log('Import to dashboard: ' + url.toString())
2073
+ // return url
2074
+ // }
2075
+ // openDashboard() {
2076
+ // void this.getDashboardURL().then(url => {
2077
+ // if (url) window.open(url.toString(), '_blank')
2078
+ // })
2079
+ // }
2080
+ };
2081
+
2082
+ // src/blockstore/connect-rest.ts
2083
+ init_utils();
2084
+ var ConnectREST = class extends ConnectionBase {
2085
+ constructor(base, logger) {
2086
+ super(ensureLogger(logger, "ConnectREST"));
2087
+ this.baseUrl = new URL(base);
2088
+ }
2089
+ async dataUpload(bytes, params) {
2090
+ const carCid = params.car.toString();
2091
+ const uploadURL = new URL(`/cars/${carCid}.car`, this.baseUrl);
2092
+ const done = await fetch(uploadURL, { method: "PUT", body: bytes });
2093
+ if (!done.ok) {
2094
+ throw this.logger.Error().Msg("failed to upload data " + done.statusText);
2095
+ }
2096
+ }
2097
+ async dataDownload(params) {
2098
+ const { car } = params;
2099
+ const fetchFromUrl = new URL(`/cars/${car.toString()}.car`, this.baseUrl);
2100
+ const response = await fetch(fetchFromUrl);
2101
+ if (!response.ok) {
2102
+ return void 0;
2103
+ }
2104
+ const bytes = new Uint8Array(await response.arrayBuffer());
2105
+ return bytes;
2106
+ }
2107
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2108
+ async metaUpload(bytes, params) {
2109
+ return void 0;
2110
+ }
2111
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2112
+ async metaDownload(params) {
2113
+ return [];
2114
+ }
2115
+ };
2116
+
2117
+ // src/blockstore/store-factory.ts
2118
+ var import_cement14 = require("@adviser/cement");
2119
+ init_data_dir();
2120
+ init_files();
2121
+
2122
+ // src/blockstore/store.ts
2123
+ var import_p_limit2 = __toESM(require("p-limit"), 1);
2124
+ var import_dag_json = require("@ipld/dag-json");
2125
+ var import_cement6 = require("@adviser/cement");
2126
+ init_types();
2127
+ init_gateway();
2128
+ init_utils();
2129
+ init_store_indexdb();
2130
+
2131
+ // src/blockstore/loader.ts
2132
+ var import_p_limit = __toESM(require("p-limit"), 1);
2133
+ var import_car = require("@ipld/car");
2134
+ var import_cement5 = require("@adviser/cement");
2135
+
2136
+ // src/blockstore/types.ts
2137
+ function toCIDBlock(block) {
2138
+ return block;
2139
+ }
2140
+
2141
+ // src/blockstore/loader-helpers.ts
2142
+ var import_block2 = require("multiformats/block");
2143
+ var import_sha2 = require("multiformats/hashes/sha2");
2144
+ var raw2 = __toESM(require("multiformats/codecs/raw"), 1);
2145
+ var CBW = __toESM(require("@ipld/car/buffer-writer"), 1);
2146
+ var codec = __toESM(require("@ipld/dag-cbor"), 1);
2147
+ async function encodeCarFile(roots, t) {
2148
+ let size = 0;
2149
+ const headerSize = CBW.headerLength({ roots });
2150
+ size += headerSize;
2151
+ for (const { cid, bytes } of t.entries()) {
2152
+ size += CBW.blockLength({ cid, bytes });
2153
+ }
2154
+ const buffer = new Uint8Array(size);
2155
+ const writer = CBW.createWriter(buffer, { headerSize });
2156
+ for (const r of roots) {
2157
+ writer.addRoot(r);
2158
+ }
2159
+ for (const { cid, bytes } of t.entries()) {
2160
+ writer.write({ cid, bytes });
2161
+ }
2162
+ writer.close();
2163
+ return await (0, import_block2.encode)({ value: writer.bytes, hasher: import_sha2.sha256, codec: raw2 });
2164
+ }
2165
+ async function encodeCarHeader(fp) {
2166
+ return await (0, import_block2.encode)({
2167
+ value: { fp },
2168
+ hasher: import_sha2.sha256,
2169
+ codec
2170
+ });
2171
+ }
2172
+ async function parseCarFile(reader, logger) {
2173
+ const roots = await reader.getRoots();
2174
+ const header = await reader.get(roots[0]);
2175
+ if (!header) throw logger.Error().Msg("missing header block").AsError();
2176
+ const { value } = await (0, import_block2.decode)({ bytes: header.bytes, hasher: import_sha2.sha256, codec });
2177
+ const fpvalue = value;
2178
+ if (fpvalue && !fpvalue.fp) {
2179
+ throw logger.Error().Msg("missing fp").AsError();
2180
+ }
2181
+ return fpvalue.fp;
2182
+ }
2183
+
2184
+ // src/blockstore/encrypt-helpers.ts
2185
+ var import_sha22 = require("multiformats/hashes/sha2");
2186
+ var import_multiformats2 = require("multiformats");
2187
+ var import_block3 = require("multiformats/block");
2188
+ var dagcbor = __toESM(require("@ipld/dag-cbor"), 1);
2189
+ var import_block4 = require("@web3-storage/pail/block");
2190
+ var import_utils8 = require("prolly-trees/utils");
2191
+ var import_cache = require("prolly-trees/cache");
2192
+ var import_cid_set = require("prolly-trees/cid-set");
2193
+
2194
+ // src/blockstore/encrypt-codec.ts
2195
+ var import_multiformats = require("multiformats");
2196
+ init_utils();
2197
+ function makeCodec(ilogger, crypto2, randomBytes2) {
2198
+ const logger = ensureLogger(ilogger, "makeCodec");
2199
+ const enc32 = (value) => {
2200
+ value = +value;
2201
+ const buff = new Uint8Array(4);
2202
+ buff[3] = value >>> 24;
2203
+ buff[2] = value >>> 16;
2204
+ buff[1] = value >>> 8;
2205
+ buff[0] = value & 255;
2206
+ return buff;
2207
+ };
2208
+ const readUInt32LE = (buffer) => {
2209
+ const offset = buffer.byteLength - 4;
2210
+ return (buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16) + buffer[offset + 3] * 16777216;
2211
+ };
2212
+ const concat = (buffers) => {
2213
+ const uint8Arrays = buffers.map((b) => b instanceof ArrayBuffer ? new Uint8Array(b) : b);
2214
+ const totalLength = uint8Arrays.reduce((sum, arr) => sum + arr.length, 0);
2215
+ const result = new Uint8Array(totalLength);
2216
+ let offset = 0;
2217
+ for (const arr of uint8Arrays) {
2218
+ result.set(arr, offset);
2219
+ offset += arr.length;
2220
+ }
2221
+ return result;
2222
+ };
2223
+ const encode4 = ({ iv, bytes }) => concat([iv, bytes]);
2224
+ const decode4 = (bytes) => {
2225
+ const iv = bytes.subarray(0, 12);
2226
+ bytes = bytes.slice(12);
2227
+ return { iv, bytes };
2228
+ };
2229
+ const code = 3145728 + 1337;
2230
+ async function subtleKey(key) {
2231
+ return await crypto2.importKey(
2232
+ "raw",
2233
+ // raw or jwk
2234
+ key,
2235
+ // raw data
2236
+ "AES-GCM",
2237
+ false,
2238
+ // extractable
2239
+ ["encrypt", "decrypt"]
2240
+ );
2241
+ }
2242
+ const decrypt = async ({ key, value }) => {
2243
+ const { bytes: inBytes, iv } = value;
2244
+ const cryKey = await subtleKey(key);
2245
+ const deBytes = await crypto2.decrypt(
2246
+ {
2247
+ name: "AES-GCM",
2248
+ iv,
2249
+ tagLength: 128
2250
+ },
2251
+ cryKey,
2252
+ inBytes
2253
+ );
2254
+ const bytes = new Uint8Array(deBytes);
2255
+ const len = readUInt32LE(bytes.subarray(0, 4));
2256
+ const cid = import_multiformats.CID.decode(bytes.subarray(4, 4 + len));
2257
+ return { cid, bytes: bytes.subarray(4 + len) };
2258
+ };
2259
+ const encrypt = async ({ key, cid, bytes }) => {
2260
+ const len = enc32(cid.bytes.byteLength);
2261
+ const iv = randomBytes2(12);
2262
+ const msg = concat([len, cid.bytes, bytes]);
2263
+ try {
2264
+ const cryKey = await subtleKey(key);
2265
+ const deBytes = await crypto2.encrypt(
2266
+ {
2267
+ name: "AES-GCM",
2268
+ iv,
2269
+ tagLength: 128
2270
+ },
2271
+ cryKey,
2272
+ msg
2273
+ );
2274
+ bytes = new Uint8Array(deBytes);
2275
+ } catch (e) {
2276
+ throw logger.Error().Err(e).Msg("encrypt failed").AsError();
2277
+ }
2278
+ return { value: { bytes, iv } };
2279
+ };
2280
+ const cryptoFn = (key) => {
2281
+ return { encrypt: (opts) => encrypt({ ...opts, key }), decrypt: (opts) => decrypt({ ...opts, key }) };
2282
+ };
2283
+ const name = "jchris@encrypted-block:aes-gcm";
2284
+ return { encode: encode4, decode: decode4, code, name, encrypt, decrypt, crypto: cryptoFn };
2285
+ }
2286
+
2287
+ // src/blockstore/encrypt-helpers.ts
2288
+ function carLogIncludesGroup(list, cidMatch) {
2289
+ return list.some((cid) => {
2290
+ return cid.toString() === cidMatch.toString();
2291
+ });
2292
+ }
2293
+ function makeEncDec(logger, crypto2, randomBytes2) {
2294
+ const codec4 = makeCodec(logger, crypto2, randomBytes2);
2295
+ const encrypt = async function* ({
2296
+ get: get2,
2297
+ cids,
2298
+ hasher: hasher4,
2299
+ key,
2300
+ cache: cache3,
2301
+ chunker: chunker2,
2302
+ root: root3
2303
+ }) {
2304
+ const set = /* @__PURE__ */ new Set();
2305
+ let eroot;
2306
+ if (!carLogIncludesGroup(cids, root3)) cids.push(root3);
2307
+ for (const cid of cids) {
2308
+ const unencrypted = await get2(cid);
2309
+ if (!unencrypted) throw logger.Error().Ref("cid", cid).Msg("missing cid block").AsError();
2310
+ const encrypted = await codec4.encrypt({ ...unencrypted, key });
2311
+ const block2 = await (0, import_block3.encode)({ ...encrypted, codec: codec4, hasher: hasher4 });
2312
+ yield block2;
2313
+ set.add(block2.cid.toString());
2314
+ if (unencrypted.cid.equals(root3)) eroot = block2.cid;
2315
+ }
2316
+ if (!eroot) throw logger.Error().Msg("cids does not include root").AsError();
2317
+ const list = [...set].map((s) => import_multiformats2.CID.parse(s));
2318
+ let last;
2319
+ for await (const node of (0, import_cid_set.create)({ list, get: get2, cache: cache3, chunker: chunker2, hasher: hasher4, codec: dagcbor })) {
2320
+ const block2 = await node.block;
2321
+ yield block2;
2322
+ last = block2;
2323
+ }
2324
+ if (!last) throw logger.Error().Msg("missing last block").AsError();
2325
+ const head = [eroot, last.cid];
2326
+ const block = await (0, import_block3.encode)({ value: head, codec: dagcbor, hasher: hasher4 });
2327
+ yield block;
2328
+ };
2329
+ const decrypt = async function* ({
2330
+ root: root3,
2331
+ get: get2,
2332
+ key,
2333
+ cache: cache3,
2334
+ chunker: chunker2,
2335
+ hasher: hasher4
2336
+ }) {
2337
+ const getWithDecode = async (cid) => get2(cid).then(async (block) => {
2338
+ if (!block) return;
2339
+ const decoded = await (0, import_block3.decode)({ ...block, codec: dagcbor, hasher: hasher4 });
2340
+ return decoded;
2341
+ });
2342
+ const getWithDecrypt = async (cid) => get2(cid).then(async (block) => {
2343
+ if (!block) return;
2344
+ const decoded = await (0, import_block3.decode)({ ...block, codec: codec4, hasher: hasher4 });
2345
+ return decoded;
2346
+ });
2347
+ const decodedRoot = await getWithDecode(root3);
2348
+ if (!decodedRoot) throw logger.Error().Msg("missing root").AsError();
2349
+ if (!decodedRoot.bytes) throw logger.Error().Msg("missing bytes").AsError();
2350
+ const {
2351
+ value: [eroot, tree]
2352
+ } = decodedRoot;
2353
+ const rootBlock = await get2(eroot);
2354
+ if (!rootBlock) throw logger.Error().Msg("missing root block").AsError();
2355
+ const cidset = await (0, import_cid_set.load)({ cid: tree, get: getWithDecode, cache: cache3, chunker: chunker2, codec: codec4, hasher: hasher4 });
2356
+ const { result: nodes } = await cidset.getAllEntries();
2357
+ const unwrap = async (eblock) => {
2358
+ if (!eblock) throw logger.Error().Msg("missing block").AsError();
2359
+ if (!eblock.value) {
2360
+ eblock = await (0, import_block3.decode)({ ...eblock, codec: codec4, hasher: hasher4 });
2361
+ if (!eblock.value) throw logger.Error().Msg("missing value").AsError();
2362
+ }
2363
+ const { bytes, cid } = await codec4.decrypt({ ...eblock, key }).catch((e) => {
2364
+ throw e;
2365
+ });
2366
+ const block = await (0, import_block3.create)({ cid, bytes, hasher: hasher4, codec: codec4 });
2367
+ return block;
2368
+ };
2369
+ const promises = [];
2370
+ for (const { cid } of nodes) {
2371
+ if (!rootBlock.cid.equals(cid)) promises.push(getWithDecrypt(cid).then(unwrap));
2372
+ }
2373
+ yield* promises;
2374
+ yield unwrap(rootBlock);
2375
+ };
2376
+ return { encrypt, decrypt };
2377
+ }
2378
+ var chunker = (0, import_utils8.bf)(30);
2379
+ function hexStringToUint8Array(hexString) {
2380
+ const length = hexString.length;
2381
+ const uint8Array = new Uint8Array(length / 2);
2382
+ for (let i = 0; i < length; i += 2) {
2383
+ uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
2384
+ }
2385
+ return uint8Array;
2386
+ }
2387
+ async function encryptedEncodeCarFile(logger, crypto2, key, rootCid, t) {
2388
+ const encryptionKey = hexStringToUint8Array(key);
2389
+ const encryptedBlocks = new import_block4.MemoryBlockstore();
2390
+ const cidsToEncrypt = [];
2391
+ for (const { cid, bytes } of t.entries()) {
2392
+ cidsToEncrypt.push(cid);
2393
+ const g = await t.get(cid);
2394
+ if (!g) throw logger.Error().Ref("cid", cid).Int("bytes", bytes.length).Msg("missing cid block").AsError();
2395
+ }
2396
+ let last = null;
2397
+ const { encrypt } = makeEncDec(logger, crypto2, crypto2.randomBytes);
2398
+ for await (const block of encrypt({
2399
+ cids: cidsToEncrypt,
2400
+ get: t.get.bind(t),
2401
+ key: encryptionKey,
2402
+ hasher: import_sha22.sha256,
2403
+ chunker,
2404
+ cache: import_cache.nocache,
2405
+ root: rootCid
2406
+ })) {
2407
+ await encryptedBlocks.put(block.cid, block.bytes);
2408
+ last = block;
2409
+ }
2410
+ if (!last) throw logger.Error().Msg("no blocks encrypted").AsError();
2411
+ const encryptedCar = await encodeCarFile([last.cid], encryptedBlocks);
2412
+ return encryptedCar;
2413
+ }
2414
+ async function decodeEncryptedCar(logger, crypto2, key, reader) {
2415
+ const roots = await reader.getRoots();
2416
+ const root3 = roots[0];
2417
+ return await decodeCarBlocks(logger, crypto2, root3, reader.get.bind(reader), key);
2418
+ }
2419
+ async function decodeCarBlocks(logger, crypto2, root3, get2, keyMaterial) {
2420
+ const decryptionKeyUint8 = hexStringToUint8Array(keyMaterial);
2421
+ const decryptionKey = decryptionKeyUint8.buffer.slice(0, decryptionKeyUint8.byteLength);
2422
+ const decryptedBlocks = new import_block4.MemoryBlockstore();
2423
+ let last = null;
2424
+ const { decrypt } = makeEncDec(logger, crypto2, crypto2.randomBytes);
2425
+ for await (const block of decrypt({
2426
+ root: root3,
2427
+ get: get2,
2428
+ key: decryptionKey,
2429
+ hasher: import_sha22.sha256,
2430
+ chunker,
2431
+ cache: import_cache.nocache
2432
+ })) {
2433
+ await decryptedBlocks.put(block.cid, block.bytes);
2434
+ last = block;
2435
+ }
2436
+ if (!last) throw logger.Error().Msg("no blocks decrypted").AsError();
2437
+ return { blocks: decryptedBlocks, root: last.cid };
2438
+ }
2439
+
2440
+ // src/blockstore/transaction.ts
2441
+ var import_block5 = require("@web3-storage/pail/block");
2442
+ init_types();
2443
+ init_crypto();
2444
+ init_utils();
2445
+ var CarTransaction = class extends import_block5.MemoryBlockstore {
2446
+ constructor(parent, opts = { add: true }) {
2447
+ super();
2448
+ if (opts.add) {
2449
+ parent.transactions.add(this);
2450
+ }
2451
+ this.parent = parent;
2452
+ }
2453
+ async get(cid) {
2454
+ return await this.superGet(cid) || falsyToUndef(await this.parent.get(cid));
2455
+ }
2456
+ async superGet(cid) {
2457
+ return super.get(cid);
2458
+ }
2459
+ };
2460
+ function defaultedBlockstoreRuntime(opts, component, ctx) {
2461
+ const logger = ensureLogger(opts, component, ctx);
2462
+ const store = opts.store || {};
2463
+ return {
2464
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2465
+ applyMeta: (meta, snap) => {
2466
+ return Promise.resolve();
2467
+ },
2468
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2469
+ compact: async (blocks) => {
2470
+ return {};
2471
+ },
2472
+ autoCompact: 100,
2473
+ public: false,
2474
+ name: void 0,
2475
+ threshold: 1e3 * 1e3,
2476
+ ...opts,
2477
+ logger,
2478
+ crypto: toCryptoOpts(opts.crypto),
2479
+ store,
2480
+ storeRuntime: toStoreRuntime(store, logger)
2481
+ };
2482
+ }
2483
+ var blockstoreFactory = function(opts) {
2484
+ if (opts.name) {
2485
+ return new EncryptedBlockstore(opts);
2486
+ } else {
2487
+ return new BaseBlockstore(opts);
2488
+ }
2489
+ };
2490
+ var BaseBlockstore = class {
2491
+ constructor(ebOpts = {}) {
2492
+ this.transactions = /* @__PURE__ */ new Set();
2493
+ this.ebOpts = defaultedBlockstoreRuntime(ebOpts, "BaseBlockstore");
2494
+ this.logger = this.ebOpts.logger;
2495
+ }
2496
+ // ready: Promise<void>;
2497
+ ready() {
2498
+ return Promise.resolve();
2499
+ }
2500
+ async close() {
2501
+ }
2502
+ async destroy() {
2503
+ }
2504
+ async get(cid) {
2505
+ if (!cid) throw this.logger.Error().Msg("required cid").AsError();
2506
+ for (const f of this.transactions) {
2507
+ const v = await f.superGet(cid);
2508
+ if (v) return v;
2509
+ }
2510
+ }
2511
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2512
+ async put(cid, block) {
2513
+ throw this.logger.Error().Msg("use a transaction to put").AsError();
2514
+ }
2515
+ // TransactionMeta
2516
+ async transaction(fn, _opts = {}) {
2517
+ const t = new CarTransaction(this);
2518
+ const done = await fn(t);
2519
+ this.lastTxMeta = done;
2520
+ return { t, meta: done };
2521
+ }
2522
+ async *entries() {
2523
+ const seen = /* @__PURE__ */ new Set();
2524
+ for (const t of this.transactions) {
2525
+ for await (const blk of t.entries()) {
2526
+ if (seen.has(blk.cid.toString())) continue;
2527
+ seen.add(blk.cid.toString());
2528
+ yield blk;
2529
+ }
2530
+ }
2531
+ }
2532
+ };
2533
+ var EncryptedBlockstore = class extends BaseBlockstore {
2534
+ constructor(ebOpts) {
2535
+ super(ebOpts);
2536
+ this.compacting = false;
2537
+ this.logger = ensureLogger(ebOpts, "EncryptedBlockstore");
2538
+ const { name } = ebOpts;
2539
+ if (!name) {
2540
+ throw this.logger.Error().Msg("name required").AsError();
2541
+ }
2542
+ this.name = name;
2543
+ this.loader = new Loader(this.name, ebOpts);
2544
+ }
2545
+ ready() {
2546
+ return this.loader.ready();
2547
+ }
2548
+ close() {
2549
+ return this.loader.close();
2550
+ }
2551
+ destroy() {
2552
+ return this.loader.destroy();
2553
+ }
2554
+ async get(cid) {
2555
+ const got = await super.get(cid);
2556
+ if (got) return got;
2557
+ if (!this.loader) {
2558
+ return;
2559
+ }
2560
+ return falsyToUndef(await this.loader.getBlock(cid));
2561
+ }
2562
+ async transaction(fn, opts = { noLoader: false }) {
2563
+ const { t, meta: done } = await super.transaction(fn);
2564
+ const cars = await this.loader.commit(t, done, opts);
2565
+ if (this.ebOpts.autoCompact && this.loader.carLog.length > this.ebOpts.autoCompact) {
2566
+ setTimeout(() => void this.compact(), 10);
2567
+ }
2568
+ if (cars) {
2569
+ this.transactions.delete(t);
2570
+ return { meta: done, cars, t };
2571
+ }
2572
+ throw this.logger.Error().Msg("failed to commit car files").AsError();
2573
+ }
2574
+ async getFile(car, cid, isPublic = false) {
2575
+ await this.ready();
2576
+ if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
2577
+ const reader = await this.loader.loadFileCar(car, isPublic);
2578
+ const block = await reader.get(cid);
2579
+ if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
2580
+ return block.bytes;
2581
+ }
2582
+ async compact() {
2583
+ await this.ready();
2584
+ if (!this.loader) throw this.logger.Error().Msg("loader required to compact").AsError();
2585
+ if (this.loader.carLog.length < 2) return;
2586
+ const compactFn = this.ebOpts.compact || ((blocks) => this.defaultCompact(blocks, this.logger));
2587
+ if (!compactFn || this.compacting) return;
2588
+ const blockLog = new CompactionFetcher(this);
2589
+ this.compacting = true;
2590
+ const meta = await compactFn(blockLog);
2591
+ await this.loader?.commit(blockLog.loggedBlocks, meta, {
2592
+ compact: true,
2593
+ noLoader: true
2594
+ });
2595
+ this.compacting = false;
2596
+ }
2597
+ async defaultCompact(blocks, logger) {
2598
+ if (!this.loader) {
2599
+ throw logger.Error().Msg("no loader").AsError();
2600
+ }
2601
+ if (!this.lastTxMeta) {
2602
+ throw logger.Error().Msg("no lastTxMeta").AsError();
2603
+ }
2604
+ for await (const blk of this.loader.entries(false)) {
2605
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
2606
+ }
2607
+ for (const t of this.transactions) {
2608
+ for await (const blk of t.entries()) {
2609
+ blocks.loggedBlocks.putSync(blk.cid, blk.bytes);
2610
+ }
2611
+ }
2612
+ return this.lastTxMeta;
2613
+ }
2614
+ async *entries() {
2615
+ for await (const blk of this.loader.entries()) {
2616
+ yield blk;
2617
+ }
2618
+ }
2619
+ };
2620
+ var CompactionFetcher = class {
2621
+ constructor(blocks) {
2622
+ this.blockstore = blocks;
2623
+ this.loggedBlocks = new CarTransaction(blocks);
2624
+ }
2625
+ async get(cid) {
2626
+ const block = await this.blockstore.get(cid);
2627
+ if (block) this.loggedBlocks.putSync(cid, block.bytes);
2628
+ return falsyToUndef(block);
2629
+ }
2630
+ };
2631
+
2632
+ // src/blockstore/commit-queue.ts
2633
+ var CommitQueue = class {
2634
+ constructor() {
2635
+ this.queue = [];
2636
+ this.processing = false;
2637
+ }
2638
+ async enqueue(fn) {
2639
+ return new Promise((resolve, reject) => {
2640
+ const queueFn = async () => {
2641
+ try {
2642
+ resolve(await fn());
2643
+ } catch (e) {
2644
+ reject(e);
2645
+ } finally {
2646
+ this.processing = false;
2647
+ this.processNext();
2648
+ }
2649
+ };
2650
+ this.queue.push(queueFn);
2651
+ if (!this.processing) {
2652
+ this.processNext();
2653
+ }
2654
+ });
2655
+ }
2656
+ processNext() {
2657
+ if (this.queue.length > 0 && !this.processing) {
2658
+ this.processing = true;
2659
+ const queueFn = this.queue.shift();
2660
+ if (queueFn) {
2661
+ queueFn();
2662
+ }
2663
+ }
2664
+ }
2665
+ };
2666
+
2667
+ // src/blockstore/loader.ts
2668
+ var CBW2 = __toESM(require("@ipld/car/buffer-writer"), 1);
2669
+ function carLogIncludesGroup2(list, cids) {
2670
+ return list.some((arr) => {
2671
+ return arr.toString() === cids.toString();
2672
+ });
2673
+ }
2674
+ function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
2675
+ const byString = /* @__PURE__ */ new Map();
2676
+ for (const cid of list) {
2677
+ if (remove.has(cid.toString())) continue;
2678
+ byString.set(cid.toString(), cid);
2679
+ }
2680
+ return [...byString.values()];
2681
+ }
2682
+ function toHexString(byteArray) {
2683
+ return Array.from(byteArray).map((byte) => byte.toString(16).padStart(2, "0")).join("");
2684
+ }
2685
+ var Loadable = class {
2686
+ constructor() {
2687
+ this.name = "";
2688
+ this.carLog = new Array();
2689
+ }
2690
+ };
2691
+ var Loader = class {
2692
+ constructor(name, ebOpts) {
2693
+ this.commitQueue = new CommitQueue();
2694
+ this.isCompacting = false;
2695
+ this.carReaders = /* @__PURE__ */ new Map();
2696
+ this.seenCompacted = /* @__PURE__ */ new Set();
2697
+ this.processedCars = /* @__PURE__ */ new Set();
2698
+ this.carLog = [];
2699
+ this.getBlockCache = /* @__PURE__ */ new Map();
2700
+ this.seenMeta = /* @__PURE__ */ new Set();
2701
+ this.writeLimit = (0, import_p_limit.default)(1);
2702
+ this.onceReady = new import_cement5.ResolveOnce();
2703
+ this.name = name;
2704
+ this.ebOpts = defaultedBlockstoreRuntime(
2705
+ {
2706
+ ...ebOpts,
2707
+ name
2708
+ },
2709
+ "Loader"
2710
+ );
2711
+ this.logger = this.ebOpts.logger;
2712
+ }
2713
+ // readonly id = uuidv4();
2714
+ async carStore() {
2715
+ return this.ebOpts.storeRuntime.makeDataStore(this);
2716
+ }
2717
+ async fileStore() {
2718
+ return this.ebOpts.storeRuntime.makeDataStore(this);
2719
+ }
2720
+ async remoteWAL() {
2721
+ return this.ebOpts.storeRuntime.makeRemoteWAL(this);
2722
+ }
2723
+ async metaStore() {
2724
+ return this.ebOpts.storeRuntime.makeMetaStore(this);
2725
+ }
2726
+ async ready() {
2727
+ return this.onceReady.once(async () => {
2728
+ const metas = this.ebOpts.meta ? [this.ebOpts.meta] : await (await this.metaStore()).load("main");
2729
+ if (metas) {
2730
+ await this.handleDbMetasFromStore(metas);
2731
+ }
2732
+ });
2733
+ }
2734
+ async close() {
2735
+ const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.remoteWAL()]);
2736
+ await Promise.all(toClose.map((store) => store.close()));
2737
+ }
2738
+ async destroy() {
2739
+ const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.remoteWAL()]);
2740
+ await Promise.all(toDestroy.map((store) => store.destroy()));
2741
+ }
2742
+ // async snapToCar(carCid: AnyLink | string) {
2743
+ // await this.ready
2744
+ // if (typeof carCid === 'string') {
2745
+ // carCid = CID.parse(carCid)
2746
+ // }
2747
+ // const carHeader = await this.loadCarHeaderFromMeta({ car: carCid, key: this.key || null })
2748
+ // this.carLog = [carCid, ...carHeader.cars]
2749
+ // await this.getMoreReaders(carHeader.cars)
2750
+ // await this._applyCarHeader(carHeader, true)
2751
+ // }
2752
+ async handleDbMetasFromStore(metas) {
2753
+ for (const meta of metas) {
2754
+ await this.writeLimit(async () => {
2755
+ await this.mergeDbMetaIntoClock(meta);
2756
+ });
2757
+ }
2758
+ }
2759
+ async mergeDbMetaIntoClock(meta) {
2760
+ if (this.isCompacting) {
2761
+ throw this.logger.Error().Msg("cannot merge while compacting").AsError();
2762
+ }
2763
+ if (this.seenMeta.has(meta.cars.toString())) return;
2764
+ this.seenMeta.add(meta.cars.toString());
2765
+ if (meta.key) {
2766
+ await this.setKey(meta.key);
2767
+ }
2768
+ if (carLogIncludesGroup2(this.carLog, meta.cars)) {
2769
+ return;
2770
+ }
2771
+ const carHeader = await this.loadCarHeaderFromMeta(meta);
2772
+ carHeader.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2773
+ await this.getMoreReaders(carHeader.cars.flat());
2774
+ this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
2775
+ await this.ebOpts.applyMeta?.(carHeader.meta);
2776
+ }
2777
+ async ingestKeyFromMeta(meta) {
2778
+ const { key } = meta;
2779
+ if (key) {
2780
+ await this.setKey(key);
2781
+ }
2782
+ }
2783
+ async loadCarHeaderFromMeta({ cars: cids }) {
2784
+ const reader = await this.loadCar(cids[0]);
2785
+ return await parseCarFile(reader, this.logger);
2786
+ }
2787
+ async _getKey() {
2788
+ if (this.key) return this.key;
2789
+ if (!this.ebOpts.public) {
2790
+ await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
2791
+ }
2792
+ return this.key || void 0;
2793
+ }
2794
+ async commitFiles(t, done, opts = { noLoader: false, compact: false }) {
2795
+ return this.commitQueue.enqueue(() => this._commitInternalFiles(t, done, opts));
2796
+ }
2797
+ // can these skip the queue? or have a file queue?
2798
+ async _commitInternalFiles(t, done, opts = { noLoader: false, compact: false }) {
2799
+ await this.ready();
2800
+ const { files: roots } = this.makeFileCarHeader(done);
2801
+ const cids = [];
2802
+ const cars = await this.prepareCarFilesFiles(roots, t, !!opts.public);
2803
+ for (const car of cars) {
2804
+ const { cid, bytes } = car;
2805
+ await (await this.fileStore()).save({ cid, bytes });
2806
+ await (await this.remoteWAL()).enqueueFile(cid, !!opts.public);
2807
+ cids.push(cid);
2808
+ }
2809
+ return cids;
2810
+ }
2811
+ async loadFileCar(cid, isPublic = false) {
2812
+ return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore, isPublic);
2813
+ }
2814
+ async commit(t, done, opts = { noLoader: false, compact: false }) {
2815
+ return this.commitQueue.enqueue(() => this._commitInternal(t, done, opts));
2816
+ }
2817
+ async cacheTransaction(t) {
2818
+ for await (const block of t.entries()) {
2819
+ const sBlock = block.cid.toString();
2820
+ if (!this.getBlockCache.has(sBlock)) {
2821
+ this.getBlockCache.set(sBlock, block);
2822
+ }
2823
+ }
2824
+ }
2825
+ async cacheCarReader(carCidStr, reader) {
2826
+ if (this.processedCars.has(carCidStr)) return;
2827
+ this.processedCars.add(carCidStr);
2828
+ for await (const block of reader.blocks()) {
2829
+ const sBlock = block.cid.toString();
2830
+ if (!this.getBlockCache.has(sBlock)) {
2831
+ this.getBlockCache.set(sBlock, block);
2832
+ }
2833
+ }
2834
+ }
2835
+ async _commitInternal(t, done, opts = { noLoader: false, compact: false }) {
2836
+ await this.ready();
2837
+ const fp = this.makeCarHeader(done, this.carLog, !!opts.compact);
2838
+ const rootBlock = await encodeCarHeader(fp);
2839
+ const cars = await this.prepareCarFiles(rootBlock, t, !!opts.public);
2840
+ const cids = [];
2841
+ for (const car of cars) {
2842
+ const { cid, bytes } = car;
2843
+ await (await this.carStore()).save({ cid, bytes });
2844
+ cids.push(cid);
2845
+ }
2846
+ await this.cacheTransaction(t);
2847
+ const newDbMeta = { cars: cids, key: this.key || null };
2848
+ await (await this.remoteWAL()).enqueue(newDbMeta, opts);
2849
+ await (await this.metaStore()).save(newDbMeta);
2850
+ await this.updateCarLog(cids, fp, !!opts.compact);
2851
+ return cids;
2852
+ }
2853
+ async prepareCarFilesFiles(roots, t, isPublic) {
2854
+ const theKey = isPublic ? null : await this._getKey();
2855
+ const car = theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, roots[0], t) : await encodeCarFile(roots, t);
2856
+ return [car];
2857
+ }
2858
+ async prepareCarFiles(rootBlock, t, isPublic) {
2859
+ const theKey = isPublic ? void 0 : await this._getKey();
2860
+ const carFiles = [];
2861
+ const threshold = this.ebOpts.threshold || 1e3 * 1e3;
2862
+ let clonedt = new CarTransaction(t.parent, { add: false });
2863
+ clonedt.putSync(rootBlock.cid, rootBlock.bytes);
2864
+ let newsize = CBW2.blockLength(toCIDBlock(rootBlock));
2865
+ let cidRootBlock = rootBlock;
2866
+ for (const { cid, bytes } of t.entries()) {
2867
+ newsize += CBW2.blockLength(toCIDBlock({ cid, bytes }));
2868
+ if (newsize >= threshold) {
2869
+ carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
2870
+ clonedt = new CarTransaction(t.parent, { add: false });
2871
+ clonedt.putSync(cid, bytes);
2872
+ cidRootBlock = { cid, bytes };
2873
+ newsize = CBW2.blockLength(toCIDBlock({ cid, bytes }));
2874
+ } else {
2875
+ clonedt.putSync(cid, bytes);
2876
+ }
2877
+ }
2878
+ carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
2879
+ return carFiles;
2880
+ }
2881
+ async createCarFile(theKey, cid, t) {
2882
+ try {
2883
+ return theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, cid, t) : await encodeCarFile([cid], t);
2884
+ } catch (e) {
2885
+ console.error("error creating car file", e);
2886
+ throw e;
2887
+ }
2888
+ }
2889
+ makeFileCarHeader(result) {
2890
+ const files = [];
2891
+ for (const [, meta] of Object.entries(result.files || {})) {
2892
+ if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
2893
+ files.push(meta.cid);
2894
+ }
2895
+ }
2896
+ return { ...result, files };
2897
+ }
2898
+ async updateCarLog(cids, fp, compact) {
2899
+ if (compact) {
2900
+ const previousCompactCid = fp.compact[fp.compact.length - 1];
2901
+ fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
2902
+ this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
2903
+ await this.removeCidsForCompact(previousCompactCid[0]);
2904
+ } else {
2905
+ this.carLog.unshift(cids);
2906
+ }
2907
+ }
2908
+ async removeCidsForCompact(cid) {
2909
+ const carHeader = await this.loadCarHeaderFromMeta({
2910
+ cars: [cid]
2911
+ });
2912
+ for (const cids of carHeader.compact) {
2913
+ for (const cid2 of cids) {
2914
+ await (await this.carStore()).remove(cid2);
2915
+ }
2916
+ }
2917
+ }
2918
+ // async flushCars() {
2919
+ // await this.ready
2920
+ // // for each cid in car log, make a dbMeta
2921
+ // for (const cid of this.carLog) {
2922
+ // const dbMeta = { car: cid, key: this.key || null } as DbMeta
2923
+ // await this.remoteWAL!.enqueue(dbMeta, { public: false })
2924
+ // }
2925
+ // }
2926
+ async *entries(cache3 = true) {
2927
+ await this.ready();
2928
+ if (cache3) {
2929
+ for (const [, block] of this.getBlockCache) {
2930
+ yield block;
2931
+ }
2932
+ } else {
2933
+ for (const [, block] of this.getBlockCache) {
2934
+ yield block;
2935
+ }
2936
+ for (const cids of this.carLog) {
2937
+ for (const cid of cids) {
2938
+ const reader = await this.loadCar(cid);
2939
+ if (!reader) throw this.logger.Error().Ref("cid", cid).Msg("missing car reader").AsError();
2940
+ for await (const block of reader.blocks()) {
2941
+ const sCid = block.cid.toString();
2942
+ if (!this.getBlockCache.has(sCid)) {
2943
+ yield block;
2944
+ }
2945
+ }
2946
+ }
2947
+ }
2948
+ }
2949
+ }
2950
+ async getBlock(cid) {
2951
+ await this.ready();
2952
+ const sCid = cid.toString();
2953
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2954
+ const getCarCid = async (carCid) => {
2955
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2956
+ const reader = await this.loadCar(carCid);
2957
+ if (!reader) {
2958
+ throw this.logger.Error().Ref("cid", carCid).Msg("missing car reader").AsError();
2959
+ }
2960
+ await this.cacheCarReader(carCid.toString(), reader).catch(() => {
2961
+ return;
2962
+ });
2963
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2964
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in reader").AsError();
2965
+ };
2966
+ const getCompactCarCids = async (carCid) => {
2967
+ const reader = await this.loadCar(carCid);
2968
+ if (!reader) {
2969
+ throw this.logger.Error().Str("cid", carCid.toString()).Msg("missing car reader").AsError();
2970
+ }
2971
+ const header = await parseCarFile(reader, this.logger);
2972
+ const compacts = header.compact;
2973
+ let got2;
2974
+ const batchSize2 = 5;
2975
+ for (let i = 0; i < compacts.length; i += batchSize2) {
2976
+ const promises = [];
2977
+ for (let j = i; j < Math.min(i + batchSize2, compacts.length); j++) {
2978
+ for (const cid2 of compacts[j]) {
2979
+ promises.push(getCarCid(cid2));
2980
+ }
2981
+ }
2982
+ try {
2983
+ got2 = await Promise.any(promises);
2984
+ } catch {
2985
+ }
2986
+ if (got2) break;
2987
+ }
2988
+ if (this.getBlockCache.has(sCid)) return this.getBlockCache.get(sCid);
2989
+ throw this.logger.Error().Str("cid", sCid).Msg("block not in compact reader").AsError();
2990
+ };
2991
+ let got;
2992
+ const batchSize = 5;
2993
+ for (let i = 0; i < this.carLog.length; i += batchSize) {
2994
+ const batch = this.carLog.slice(i, i + batchSize);
2995
+ const promises = batch.flatMap((slice) => slice.map(getCarCid));
2996
+ try {
2997
+ got = await Promise.any(promises);
2998
+ } catch {
2999
+ }
3000
+ if (got) break;
3001
+ }
3002
+ if (!got) {
3003
+ try {
3004
+ got = await getCompactCarCids(this.carLog[this.carLog.length - 1][0]);
3005
+ } catch {
3006
+ }
3007
+ }
3008
+ return got;
3009
+ }
3010
+ makeCarHeader(meta, cars, compact = false) {
3011
+ const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
3012
+ return { ...coreHeader, meta };
3013
+ }
3014
+ async loadCar(cid) {
3015
+ if (!this.carStore) {
3016
+ throw this.logger.Error().Msg("car store not initialized").AsError();
3017
+ }
3018
+ const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
3019
+ return loaded;
3020
+ }
3021
+ //What if instead it returns an Array of CarHeader
3022
+ async storesLoadCar(cid, local, remote, publicFiles) {
3023
+ const cidsString = cid.toString();
3024
+ if (!this.carReaders.has(cidsString)) {
3025
+ this.carReaders.set(
3026
+ cidsString,
3027
+ (async () => {
3028
+ let loadedCar = void 0;
3029
+ try {
3030
+ this.logger.Debug().Str("cid", cidsString).Msg("loading car");
3031
+ loadedCar = await local.load(cid);
3032
+ this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
3033
+ } catch (e) {
3034
+ if (remote) {
3035
+ const remoteCar = await remote.load(cid);
3036
+ if (remoteCar) {
3037
+ this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
3038
+ await local.save(remoteCar);
3039
+ loadedCar = remoteCar;
3040
+ }
3041
+ } else {
3042
+ this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
3043
+ }
3044
+ }
3045
+ if (!loadedCar) {
3046
+ throw this.logger.Error().Url(local.url).Str("cid", cidsString).Msg("missing car files").AsError();
3047
+ }
3048
+ const rawReader = await import_car.CarReader.fromBytes(loadedCar.bytes);
3049
+ const readerP = publicFiles ? Promise.resolve(rawReader) : this.ensureDecryptedReader(rawReader);
3050
+ const cachedReaderP = readerP.then(async (reader) => {
3051
+ await this.cacheCarReader(cidsString, reader).catch(() => {
3052
+ return;
3053
+ });
3054
+ return reader;
3055
+ });
3056
+ this.carReaders.set(cidsString, cachedReaderP);
3057
+ return readerP;
3058
+ })().catch((e) => {
3059
+ this.carReaders.delete(cidsString);
3060
+ throw e;
3061
+ })
3062
+ );
3063
+ }
3064
+ return this.carReaders.get(cidsString);
3065
+ }
3066
+ async ensureDecryptedReader(reader) {
3067
+ const theKey = await this._getKey();
3068
+ if (this.ebOpts.public || !(theKey && this.ebOpts.crypto)) {
3069
+ return reader;
3070
+ }
3071
+ const { blocks, root: root3 } = await decodeEncryptedCar(this.logger, this.ebOpts.crypto, theKey, reader);
3072
+ return {
3073
+ getRoots: () => [root3],
3074
+ get: blocks.get.bind(blocks),
3075
+ blocks: blocks.entries.bind(blocks)
3076
+ };
3077
+ }
3078
+ async setKey(key) {
3079
+ if (this.key && this.key !== key)
3080
+ throw this.logger.Error().Str("this.key", this.key).Str("key", key).Msg("setting key").AsError();
3081
+ this.key = key;
3082
+ const encoder = new TextEncoder();
3083
+ const data = encoder.encode(key);
3084
+ const hashBuffer = await this.ebOpts.crypto.digestSHA256(data);
3085
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
3086
+ this.keyId = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3087
+ }
3088
+ async getMoreReaders(cids) {
3089
+ const limit = (0, import_p_limit.default)(5);
3090
+ const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
3091
+ await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
3092
+ }
3093
+ };
3094
+
3095
+ // src/blockstore/store.ts
3096
+ var VersionedStore = class {
3097
+ constructor(name, url, logger) {
3098
+ this._onStarted = [];
3099
+ this._onClosed = [];
3100
+ this.name = name;
3101
+ this.url = url;
3102
+ this.logger = logger;
3103
+ }
3104
+ onStarted(fn) {
3105
+ this._onStarted.push(fn);
3106
+ }
3107
+ onClosed(fn) {
3108
+ this._onClosed.push(fn);
3109
+ }
3110
+ };
3111
+ var textEncoder2 = new TextEncoder();
3112
+ var textDecoder2 = new TextDecoder();
3113
+ var MetaStore = class extends VersionedStore {
3114
+ constructor(name, url, logger, gateway) {
3115
+ super(name, url, ensureLogger(logger, "MetaStore", {}));
3116
+ this.tag = "header-base";
3117
+ this.gateway = gateway;
3118
+ }
3119
+ makeHeader({ cars, key }) {
3120
+ const toEncode = { cars };
3121
+ if (key) toEncode.key = key;
3122
+ return (0, import_dag_json.format)(toEncode);
3123
+ }
3124
+ parseHeader(headerData) {
3125
+ const got = (0, import_dag_json.parse)(headerData);
3126
+ return got;
3127
+ }
3128
+ async start() {
3129
+ this.logger.Debug().Msg("starting");
3130
+ const res = await this.gateway.start(this.url);
3131
+ if (res.isErr()) {
3132
+ return res;
3133
+ }
3134
+ this._onStarted.forEach((fn) => fn());
3135
+ return guardVersion(this.url);
3136
+ }
3137
+ async load(branch) {
3138
+ this.logger.Debug().Str("branch", branch || "").Msg("loading");
3139
+ const url = await this.gateway.buildUrl(this.url, branch || "main");
3140
+ if (url.isErr()) {
3141
+ throw this.logger.Error().Err(url.Err()).Str("branch", branch || "").Str("url", this.url.toString()).Msg("got error from gateway.buildUrl").AsError();
3142
+ }
3143
+ const bytes = await this.gateway.get(url.Ok());
3144
+ if (bytes.isErr()) {
3145
+ if (isNotFoundError(bytes)) {
3146
+ return void 0;
3147
+ }
3148
+ throw this.logger.Error().Err(bytes.Err()).Msg("gateway get").AsError();
3149
+ }
3150
+ try {
3151
+ return [this.parseHeader(textDecoder2.decode(bytes.Ok()))];
3152
+ } catch (e) {
3153
+ throw this.logger.Error().Err(e).Msg("parseHeader").AsError();
3154
+ }
3155
+ }
3156
+ async save(meta, branch = "main") {
3157
+ this.logger.Debug().Str("branch", branch).Any("meta", meta).Msg("saving meta");
3158
+ const bytes = this.makeHeader(meta);
3159
+ const url = await this.gateway.buildUrl(this.url, branch);
3160
+ if (url.isErr()) {
3161
+ throw this.logger.Error().Err(url.Err()).Str("branch", branch).Url(this.url).Msg("got error from gateway.buildUrl").AsError();
3162
+ }
3163
+ const res = await this.gateway.put(url.Ok(), textEncoder2.encode(bytes));
3164
+ if (res.isErr()) {
3165
+ throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
3166
+ }
3167
+ return res.Ok();
3168
+ }
3169
+ async close() {
3170
+ await this.gateway.close(this.url);
3171
+ this._onClosed.forEach((fn) => fn());
3172
+ return import_cement6.Result.Ok(void 0);
3173
+ }
3174
+ async destroy() {
3175
+ return this.gateway.destroy(this.url);
3176
+ }
3177
+ };
3178
+ var DataStore = class extends VersionedStore {
3179
+ constructor(name, url, logger, gateway) {
3180
+ super(
3181
+ name,
3182
+ url,
3183
+ ensureLogger(logger, "DataStore", {
3184
+ url: () => url.toString()
3185
+ })
3186
+ );
3187
+ this.tag = "car-base";
3188
+ this.gateway = gateway;
3189
+ }
3190
+ async start() {
3191
+ this.logger.Debug().Msg("starting-gateway");
3192
+ const res = await this.gateway.start(this.url);
3193
+ if (res.isErr()) {
3194
+ this.logger.Error().Err(res.Err()).Msg("started-gateway");
3195
+ return res;
3196
+ }
3197
+ this._onStarted.forEach((fn) => fn());
3198
+ const version = guardVersion(this.url);
3199
+ if (version.isErr()) {
3200
+ this.logger.Error().Err(res.Err()).Msg("guardVersion");
3201
+ await this.close();
3202
+ return version;
3203
+ }
3204
+ this.logger.Debug().Msg("started");
3205
+ return version;
3206
+ }
3207
+ async load(cid) {
3208
+ this.logger.Debug().Any("cid", cid).Msg("loading");
3209
+ const url = await this.gateway.buildUrl(this.url, cid.toString());
3210
+ if (url.isErr()) {
3211
+ throw this.logger.Error().Err(url.Err()).Str("cid", cid.toString()).Msg("got error from gateway.buildUrl").AsError();
3212
+ }
3213
+ const res = await this.gateway.get(url.Ok());
3214
+ if (res.isErr()) {
3215
+ throw res.Err();
3216
+ }
3217
+ return { cid, bytes: res.Ok() };
3218
+ }
3219
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3220
+ async save(car, opts) {
3221
+ this.logger.Debug().Any("cid", car.cid.toString()).Msg("saving");
3222
+ const url = await this.gateway.buildUrl(this.url, car.cid.toString());
3223
+ if (url.isErr()) {
3224
+ throw this.logger.Error().Err(url.Err()).Ref("cid", car.cid).Msg("got error from gateway.buildUrl").AsError();
3225
+ }
3226
+ const res = await this.gateway.put(url.Ok(), car.bytes);
3227
+ if (res.isErr()) {
3228
+ throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
3229
+ }
3230
+ return res.Ok();
3231
+ }
3232
+ async remove(cid) {
3233
+ const url = await this.gateway.buildUrl(this.url, cid.toString());
3234
+ if (url.isErr()) {
3235
+ return url;
3236
+ }
3237
+ return this.gateway.delete(url.Ok());
3238
+ }
3239
+ async close() {
3240
+ await this.gateway.close(this.url);
3241
+ this._onClosed.forEach((fn) => fn());
3242
+ return import_cement6.Result.Ok(void 0);
3243
+ }
3244
+ destroy() {
3245
+ return this.gateway.destroy(this.url);
3246
+ }
3247
+ };
3248
+ var RemoteWAL = class extends VersionedStore {
3249
+ constructor(loader, url, logger, gateway) {
3250
+ super(loader.name, url, ensureLogger(logger, "RemoteWAL"));
3251
+ this.tag = "rwal-base";
3252
+ this._ready = new import_cement6.ResolveOnce();
3253
+ this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
3254
+ this.processing = void 0;
3255
+ this.processQueue = new CommitQueue();
3256
+ this.loader = loader;
3257
+ this.gateway = gateway;
3258
+ }
3259
+ async ready() {
3260
+ return this._ready.once(async () => {
3261
+ const walState = await this.load().catch((e) => {
3262
+ this.logger.Error().Any("error", e).Msg("error loading wal");
3263
+ return void 0;
3264
+ });
3265
+ if (!walState) {
3266
+ this.walState.operations = [];
3267
+ this.walState.fileOperations = [];
3268
+ } else {
3269
+ this.walState.operations = walState.operations || [];
3270
+ this.walState.fileOperations = walState.fileOperations || [];
3271
+ }
3272
+ });
3273
+ }
3274
+ async enqueue(dbMeta, opts) {
3275
+ await this.ready();
3276
+ if (opts.noLoader) {
3277
+ this.walState.noLoaderOps.push(dbMeta);
3278
+ } else {
3279
+ this.walState.operations.push(dbMeta);
3280
+ }
3281
+ await this.save(this.walState);
3282
+ void this._process();
3283
+ }
3284
+ async enqueueFile(fileCid, publicFile = false) {
3285
+ await this.ready();
3286
+ this.walState.fileOperations.push({ cid: fileCid, public: publicFile });
3287
+ }
3288
+ async _process() {
3289
+ await this.ready();
3290
+ if (!this.loader.remoteCarStore) return;
3291
+ await this.processQueue.enqueue(async () => {
3292
+ await this._doProcess();
3293
+ if (this.walState.operations.length || this.walState.fileOperations.length || this.walState.noLoaderOps.length) {
3294
+ setTimeout(() => void this._process(), 0);
3295
+ }
3296
+ });
3297
+ }
3298
+ async _doProcess() {
3299
+ if (!this.loader.remoteCarStore) return;
3300
+ const rmlp = (async () => {
3301
+ const operations = [...this.walState.operations];
3302
+ const fileOperations = [...this.walState.fileOperations];
3303
+ const uploads = [];
3304
+ const noLoaderOps = [...this.walState.noLoaderOps];
3305
+ const limit = (0, import_p_limit2.default)(5);
3306
+ if (operations.length + fileOperations.length + noLoaderOps.length === 0) return;
3307
+ for (const dbMeta of noLoaderOps) {
3308
+ const uploadP = limit(async () => {
3309
+ for (const cid of dbMeta.cars) {
3310
+ const car = await (await this.loader.carStore()).load(cid);
3311
+ if (!car) {
3312
+ if (carLogIncludesGroup2(this.loader.carLog, dbMeta.cars))
3313
+ throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
3314
+ } else {
3315
+ await throwFalsy(this.loader.remoteCarStore).save(car);
3316
+ }
3317
+ this.walState.noLoaderOps = this.walState.noLoaderOps.filter((op) => op !== dbMeta);
3318
+ }
3319
+ });
3320
+ uploads.push(uploadP);
3321
+ }
3322
+ for (const dbMeta of operations) {
3323
+ const uploadP = limit(async () => {
3324
+ for (const cid of dbMeta.cars) {
3325
+ const car = await (await this.loader.carStore()).load(cid).catch(() => null);
3326
+ if (!car) {
3327
+ if (carLogIncludesGroup2(this.loader.carLog, dbMeta.cars))
3328
+ throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
3329
+ } else {
3330
+ await throwFalsy(this.loader.remoteCarStore).save(car);
3331
+ }
3332
+ }
3333
+ this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
3334
+ });
3335
+ uploads.push(uploadP);
3336
+ }
3337
+ if (fileOperations.length) {
3338
+ const dbLoader = this.loader;
3339
+ for (const { cid: fileCid, public: publicFile } of fileOperations) {
3340
+ const uploadP = limit(async () => {
3341
+ const fileBlock = await (await dbLoader.fileStore()).load(fileCid);
3342
+ await dbLoader.remoteFileStore?.save(fileBlock, { public: publicFile });
3343
+ this.walState.fileOperations = this.walState.fileOperations.filter((op) => op.cid !== fileCid);
3344
+ });
3345
+ uploads.push(uploadP);
3346
+ }
3347
+ }
3348
+ try {
3349
+ const res = await Promise.allSettled(uploads);
3350
+ const errors = res.filter((r) => r.status === "rejected");
3351
+ if (errors.length) {
3352
+ throw this.logger.Error().Any(
3353
+ "errors",
3354
+ errors.map((e) => e.reason)
3355
+ ).Msg("error uploading").AsError();
3356
+ errors[0].reason;
3357
+ }
3358
+ if (operations.length) {
3359
+ const lastOp = operations[operations.length - 1];
3360
+ await this.loader.remoteMetaStore?.save(lastOp).catch((e) => {
3361
+ this.walState.operations.push(lastOp);
3362
+ throw this.logger.Error().Any("error", e).Msg("error saving remote meta").AsError();
3363
+ });
3364
+ }
3365
+ } finally {
3366
+ await this.save(this.walState);
3367
+ }
3368
+ })();
3369
+ await rmlp;
3370
+ }
3371
+ async start() {
3372
+ const res = await this.gateway.start(this.url);
3373
+ if (res.isErr()) {
3374
+ return res;
3375
+ }
3376
+ const ver = guardVersion(this.url);
3377
+ if (ver.isErr()) {
3378
+ await this.close();
3379
+ return ver;
3380
+ }
3381
+ const ready = await exception2Result(() => this.ready());
3382
+ this._onStarted.forEach((fn) => fn());
3383
+ if (ready.isErr()) {
3384
+ await this.close();
3385
+ return ready;
3386
+ }
3387
+ return ready;
3388
+ }
3389
+ async load() {
3390
+ this.logger.Debug().Msg("loading");
3391
+ const filepath = await this.gateway.buildUrl(this.url, "main");
3392
+ if (filepath.isErr()) {
3393
+ throw this.logger.Error().Err(filepath.Err()).Str("url", this.url.toString()).Msg("error building url").AsError();
3394
+ }
3395
+ const bytes = await this.gateway.get(filepath.Ok());
3396
+ if (bytes.isErr()) {
3397
+ if (isNotFoundError(bytes)) {
3398
+ return void 0;
3399
+ }
3400
+ throw this.logger.Error().Err(bytes.Err()).Msg("error get").AsError();
3401
+ }
3402
+ try {
3403
+ return bytes && (0, import_dag_json.parse)(textDecoder2.decode(bytes.Ok()));
3404
+ } catch (e) {
3405
+ throw this.logger.Error().Err(e).Msg("error parse").AsError();
3406
+ }
3407
+ }
3408
+ async save(state) {
3409
+ const filepath = await this.gateway.buildUrl(this.url, "main");
3410
+ if (filepath.isErr()) {
3411
+ throw this.logger.Error().Err(filepath.Err()).Str("url", this.url.toString()).Msg("error building url").AsError();
3412
+ }
3413
+ let encoded;
3414
+ try {
3415
+ encoded = (0, import_dag_json.format)(state);
3416
+ } catch (e) {
3417
+ throw this.logger.Error().Err(e).Any("state", state).Msg("error format").AsError();
3418
+ }
3419
+ const res = await this.gateway.put(filepath.Ok(), textEncoder2.encode(encoded));
3420
+ if (res.isErr()) {
3421
+ throw this.logger.Error().Err(res.Err()).Str("filePath", filepath.Ok().toString()).Msg("error saving").AsError();
3422
+ }
3423
+ }
3424
+ async close() {
3425
+ await this.gateway.close(this.url);
3426
+ this._onClosed.forEach((fn) => fn());
3427
+ return import_cement6.Result.Ok(void 0);
3428
+ }
3429
+ destroy() {
3430
+ return this.gateway.destroy(this.url);
3431
+ }
3432
+ };
3433
+
3434
+ // src/blockstore/store-factory.ts
3435
+ init_utils();
3436
+ function ensureIsIndex(url, isIndex) {
3437
+ if (isIndex) {
3438
+ url.searchParams.set("index", isIndex);
3439
+ return url;
3440
+ } else {
3441
+ url.searchParams.delete("index");
3442
+ return url;
3443
+ }
3444
+ }
3445
+ function toURL(pathOrUrl, isIndex) {
3446
+ if (pathOrUrl instanceof URL) return ensureIsIndex(pathOrUrl, isIndex);
3447
+ try {
3448
+ const url = new URL(pathOrUrl);
3449
+ return ensureIsIndex(url, isIndex);
3450
+ } catch (e) {
3451
+ const url = new URL(`file://${pathOrUrl}`);
3452
+ return ensureIsIndex(url, isIndex);
3453
+ }
3454
+ }
3455
+ var storeFactory = /* @__PURE__ */ new Map();
3456
+ function ensureName(name, url) {
3457
+ if (!url.searchParams.has("name")) {
3458
+ url.searchParams.set("name", name);
3459
+ }
3460
+ }
3461
+ function buildURL(optURL, loader) {
3462
+ const storeOpts = loader.ebOpts.store;
3463
+ const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
3464
+ let obuUrl;
3465
+ if (obuItem && obuItem.overrideBaseURL) {
3466
+ obuUrl = new URL(obuItem.overrideBaseURL);
3467
+ }
3468
+ return toURL(optURL || obuUrl || dataDir(loader.name, storeOpts.stores?.base), storeOpts.isIndex);
3469
+ }
3470
+ function registerStoreProtocol(item) {
3471
+ if (storeFactory.has(item.protocol)) {
3472
+ throw new Error(`protocol ${item.protocol} already registered`);
3473
+ }
3474
+ if (item.overrideBaseURL) {
3475
+ Array.from(storeFactory.values()).forEach((items) => {
3476
+ items.overrideBaseURL = void 0;
3477
+ });
3478
+ }
3479
+ storeFactory.set(item.protocol, item);
3480
+ return () => {
3481
+ storeFactory.delete(item.protocol);
3482
+ };
3483
+ }
3484
+ function runStoreFactory(url, logger, run) {
3485
+ const item = storeFactory.get(url.protocol);
3486
+ if (!item) {
3487
+ throw logger.Error().Url(url).Str("protocol", url.protocol).Any("keys", Array(storeFactory.keys())).Msg(`unsupported protocol`).AsError();
3488
+ }
3489
+ logger.Debug().Str("protocol", url.protocol).Msg("run");
3490
+ return run(item);
3491
+ }
3492
+ var onceLoadDataGateway = new import_cement14.KeyedResolvOnce();
3493
+ function loadDataGateway(url, logger) {
3494
+ return onceLoadDataGateway.get(url.protocol).once(async () => {
3495
+ return runStoreFactory(url, logger, async (item) => item.data(logger));
3496
+ });
3497
+ }
3498
+ var onceDataStoreFactory = new import_cement14.KeyedResolvOnce();
3499
+ async function dataStoreFactory(loader) {
3500
+ const url = buildURL(loader.ebOpts.store.stores?.data, loader);
3501
+ ensureName(loader.name, url);
3502
+ const logger = ensureLogger(loader.logger, "dataStoreFactory", { url: url.toString() });
3503
+ url.searchParams.set("store", "data");
3504
+ return onceDataStoreFactory.get(url.toString()).once(async () => {
3505
+ const gateway = await loadDataGateway(url, logger);
3506
+ const store = new DataStore(loader.name, url, loader.logger, gateway);
3507
+ await store.start();
3508
+ logger.Debug().Str("prepared", store.url.toString()).Msg("produced");
3509
+ return store;
3510
+ });
3511
+ }
3512
+ var onceLoadMetaGateway = new import_cement14.KeyedResolvOnce();
3513
+ function loadMetaGateway(url, logger) {
3514
+ return onceLoadMetaGateway.get(url.protocol).once(async () => {
3515
+ return runStoreFactory(url, logger, async (item) => item.meta(logger));
3516
+ });
3517
+ }
3518
+ var onceMetaStoreFactory = new import_cement14.KeyedResolvOnce();
3519
+ async function metaStoreFactory(loader) {
3520
+ const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
3521
+ ensureName(loader.name, url);
3522
+ const logger = ensureLogger(loader.logger, "metaStoreFactory", { url: () => url.toString() });
3523
+ url.searchParams.set("store", "meta");
3524
+ return onceMetaStoreFactory.get(url.toString()).once(async () => {
3525
+ logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
3526
+ const gateway = await loadMetaGateway(url, logger);
3527
+ const store = new MetaStore(loader.name, url, loader.logger, gateway);
3528
+ await store.start();
3529
+ return store;
3530
+ });
3531
+ }
3532
+ var onceWalGateway = new import_cement14.KeyedResolvOnce();
3533
+ function loadWalGateway(url, logger) {
3534
+ return onceWalGateway.get(url.protocol).once(async () => {
3535
+ return runStoreFactory(url, logger, async (item) => item.wal(logger));
3536
+ });
3537
+ }
3538
+ var onceRemoteWalFactory = new import_cement14.KeyedResolvOnce();
3539
+ async function remoteWalFactory(loader) {
3540
+ const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
3541
+ ensureName(loader.name, url);
3542
+ const logger = ensureLogger(loader.logger, "remoteWalFactory", { url: url.toString() });
3543
+ url.searchParams.set("store", "wal");
3544
+ return onceRemoteWalFactory.get(url.toString()).once(async () => {
3545
+ const gateway = await loadWalGateway(url, logger);
3546
+ logger.Debug().Str("prepared", url.toString()).Msg("produced");
3547
+ const store = new RemoteWAL(loader, url, loader.logger, gateway);
3548
+ await store.start();
3549
+ return store;
3550
+ });
3551
+ }
3552
+ async function testStoreFactory(url, ilogger) {
3553
+ const logger = ensureLogger(
3554
+ {
3555
+ logger: ilogger
3556
+ },
3557
+ "testStoreFactory"
3558
+ );
3559
+ return runStoreFactory(url, logger, async (item) => item.test(logger));
3560
+ }
3561
+ function toStoreRuntime(opts, ilogger) {
3562
+ const logger = ensureLogger(ilogger, "toStoreRuntime", {});
3563
+ return {
3564
+ makeMetaStore: (loader) => {
3565
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
3566
+ return (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader);
3567
+ },
3568
+ makeDataStore: (loader) => {
3569
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
3570
+ return (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader);
3571
+ },
3572
+ makeRemoteWAL: (loader) => {
3573
+ logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeRemoteWAL).Msg("makeRemoteWAL");
3574
+ return (loader.ebOpts.store.makeRemoteWAL || remoteWalFactory)(loader);
3575
+ },
3576
+ encodeFile: opts.encodeFile || encodeFile,
3577
+ decodeFile: opts.decodeFile || decodeFile
3578
+ };
3579
+ }
3580
+ registerStoreProtocol({
3581
+ protocol: "file:",
3582
+ data: async (logger) => {
3583
+ const { FileDataGateway: FileDataGateway2 } = await Promise.resolve().then(() => (init_store_file(), store_file_exports));
3584
+ return new FileDataGateway2(logger);
3585
+ },
3586
+ meta: async (logger) => {
3587
+ const { FileMetaGateway: FileMetaGateway2 } = await Promise.resolve().then(() => (init_store_file(), store_file_exports));
3588
+ return new FileMetaGateway2(logger);
3589
+ },
3590
+ wal: async (logger) => {
3591
+ const { FileWALGateway: FileWALGateway2 } = await Promise.resolve().then(() => (init_store_file(), store_file_exports));
3592
+ return new FileWALGateway2(logger);
3593
+ },
3594
+ test: async (logger) => {
3595
+ const { FileTestStore: FileTestStore2 } = await Promise.resolve().then(() => (init_store_file(), store_file_exports));
3596
+ return new FileTestStore2(logger);
3597
+ }
3598
+ });
3599
+ registerStoreProtocol({
3600
+ protocol: "indexdb:",
3601
+ data: async (logger) => {
3602
+ const { IndexDBDataGateway: IndexDBDataGateway2 } = await Promise.resolve().then(() => (init_store_indexdb(), store_indexdb_exports));
3603
+ return new IndexDBDataGateway2(logger);
3604
+ },
3605
+ meta: async (logger) => {
3606
+ const { IndexDBMetaGateway: IndexDBMetaGateway2 } = await Promise.resolve().then(() => (init_store_indexdb(), store_indexdb_exports));
3607
+ return new IndexDBMetaGateway2(logger);
3608
+ },
3609
+ wal: async (logger) => {
3610
+ const { IndexDBMetaGateway: IndexDBMetaGateway2 } = await Promise.resolve().then(() => (init_store_indexdb(), store_indexdb_exports));
3611
+ return new IndexDBMetaGateway2(logger);
3612
+ },
3613
+ test: async (logger) => {
3614
+ const { IndexDBTestStore: IndexDBTestStore2 } = await Promise.resolve().then(() => (init_store_indexdb(), store_indexdb_exports));
3615
+ return new IndexDBTestStore2(logger);
3616
+ }
3617
+ });
3618
+ registerStoreProtocol({
3619
+ protocol: "sqlite:",
3620
+ data: async (logger) => {
3621
+ const { SQLDataGateway: SQLDataGateway2 } = await Promise.resolve().then(() => (init_store_sql2(), store_sql_exports2));
3622
+ return new SQLDataGateway2(logger);
3623
+ },
3624
+ meta: async (logger) => {
3625
+ const { SQLMetaGateway: SQLMetaGateway2 } = await Promise.resolve().then(() => (init_store_sql2(), store_sql_exports2));
3626
+ return new SQLMetaGateway2(logger);
3627
+ },
3628
+ wal: async (logger) => {
3629
+ const { SQLWalGateway: SQLWalGateway2 } = await Promise.resolve().then(() => (init_store_sql2(), store_sql_exports2));
3630
+ return new SQLWalGateway2(logger);
3631
+ },
3632
+ test: async (logger) => {
3633
+ const { SQLTestStore: SQLTestStore2 } = await Promise.resolve().then(() => (init_store_sql2(), store_sql_exports2));
3634
+ return new SQLTestStore2(logger);
3635
+ }
3636
+ });
3637
+
3638
+ // src/blockstore/index.ts
3639
+ init_gateway();
3640
+
3641
+ // src/crdt-helpers.ts
3642
+ init_types();
3643
+ function time(tag) {
3644
+ }
3645
+ function timeEnd(tag) {
3646
+ }
3647
+ function toString(key, logger) {
3648
+ switch (typeof key) {
3649
+ case "string":
3650
+ case "number":
3651
+ return key.toString();
3652
+ default:
3653
+ throw logger.Error().Msg("Invalid key type").AsError();
3654
+ }
3655
+ }
3656
+ async function applyBulkUpdateToCrdt(store, tblocks, head, updates, logger) {
3657
+ let result = null;
3658
+ if (updates.length > 1) {
3659
+ const batch = await Batch.create(tblocks, head);
3660
+ for (const update of updates) {
3661
+ const link = await writeDocContent(store, tblocks, update, logger);
3662
+ await batch.put(toString(update.id, logger), link);
3663
+ }
3664
+ result = await batch.commit();
3665
+ } else if (updates.length === 1) {
3666
+ const link = await writeDocContent(store, tblocks, updates[0], logger);
3667
+ result = await (0, import_crdt.put)(tblocks, head, toString(updates[0].id, logger), link);
3668
+ }
3669
+ if (!result) throw logger.Error().Uint64("updates.len", updates.length).Msg("Missing result").AsError();
3670
+ if (result.event) {
3671
+ for (const { cid, bytes } of [
3672
+ ...result.additions,
3673
+ // ...result.removals,
3674
+ result.event
3675
+ ]) {
3676
+ tblocks.putSync(cid, bytes);
3677
+ }
3678
+ }
3679
+ return { head: result.head };
3680
+ }
3681
+ async function writeDocContent(store, blocks, update, logger) {
3682
+ let value;
3683
+ if (update.del) {
3684
+ value = { del: true };
3685
+ } else {
3686
+ if (!update.value) throw logger.Error().Msg("Missing value").AsError();
3687
+ await processFiles(store, blocks, update.value, logger);
3688
+ value = { doc: update.value };
3689
+ }
3690
+ const block = await (0, import_block6.encode)({ value, hasher: import_sha23.sha256, codec: codec2 });
3691
+ blocks.putSync(block.cid, block.bytes);
3692
+ return block.cid;
3693
+ }
3694
+ async function processFiles(store, blocks, doc, logger) {
3695
+ if (doc._files) {
3696
+ await processFileset(logger, store, blocks, doc._files);
3697
+ }
3698
+ if (doc._publicFiles) {
3699
+ await processFileset(logger, store, blocks, doc._publicFiles, true);
3700
+ }
3701
+ }
3702
+ async function processFileset(logger, store, blocks, files, publicFiles = false) {
3703
+ const dbBlockstore = blocks.parent;
3704
+ if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, database name is required").AsError();
3705
+ const t = new CarTransaction(dbBlockstore);
3706
+ const didPut = [];
3707
+ for (const filename in files) {
3708
+ if (File === files[filename].constructor) {
3709
+ const file = files[filename];
3710
+ const { cid, blocks: fileBlocks } = await store.encodeFile(file);
3711
+ didPut.push(filename);
3712
+ for (const block of fileBlocks) {
3713
+ t.putSync(block.cid, block.bytes);
3714
+ }
3715
+ files[filename] = { cid, type: file.type, size: file.size };
3716
+ } else {
3717
+ const { cid, type, size, car } = files[filename];
3718
+ if (cid && type && size && car) {
3719
+ files[filename] = { cid, type, size, car };
3720
+ }
3721
+ }
3722
+ }
3723
+ if (didPut.length) {
3724
+ const car = await dbBlockstore.loader.commitFiles(t, { files }, {
3725
+ public: publicFiles
3726
+ });
3727
+ if (car) {
3728
+ for (const name of didPut) {
3729
+ files[name] = { car, ...files[name] };
3730
+ }
3731
+ }
3732
+ }
3733
+ }
3734
+ async function getValueFromCrdt(blocks, head, key, logger) {
3735
+ if (!head.length) throw logger.Debug().Msg("Getting from an empty database").AsError();
3736
+ const link = await (0, import_crdt.get)(blocks, head, key);
3737
+ if (!link) throw logger.Error().Str("key", key).Msg(`Missing key`).AsError();
3738
+ return await getValueFromLink(blocks, link, logger);
3739
+ }
3740
+ function readFiles(blocks, { doc }) {
3741
+ if (!doc) return;
3742
+ if (doc._files) {
3743
+ readFileset(blocks, doc._files);
3744
+ }
3745
+ if (doc._publicFiles) {
3746
+ readFileset(blocks, doc._publicFiles, true);
3747
+ }
3748
+ }
3749
+ function readFileset(blocks, files, isPublic = false) {
3750
+ for (const filename in files) {
3751
+ const fileMeta = files[filename];
3752
+ if (fileMeta.cid) {
3753
+ if (isPublic) {
3754
+ fileMeta.url = `https://${fileMeta.cid.toString()}.ipfs.w3s.link/`;
3755
+ }
3756
+ if (fileMeta.car) {
3757
+ fileMeta.file = async () => await blocks.ebOpts.storeRuntime.decodeFile(
3758
+ {
3759
+ get: async (cid) => {
3760
+ return await blocks.getFile(throwFalsy(fileMeta.car), cid, isPublic);
3761
+ }
3762
+ },
3763
+ fileMeta.cid,
3764
+ fileMeta
3765
+ );
3766
+ }
3767
+ }
3768
+ files[filename] = fileMeta;
3769
+ }
3770
+ }
3771
+ async function getValueFromLink(blocks, link, logger) {
3772
+ const block = await blocks.get(link);
3773
+ if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
3774
+ const { value } = await (0, import_block6.decode)({ bytes: block.bytes, hasher: import_sha23.sha256, codec: codec2 });
3775
+ const cvalue = {
3776
+ ...value,
3777
+ cid: link
3778
+ };
3779
+ readFiles(blocks, cvalue);
3780
+ return cvalue;
3781
+ }
3782
+ var DirtyEventFetcher = class extends import_clock2.EventFetcher {
3783
+ async get(link) {
3784
+ try {
3785
+ return super.get(link);
3786
+ } catch (e) {
3787
+ console.error("missing event", link.toString(), e);
3788
+ return { value: void 0 };
3789
+ }
3790
+ }
3791
+ };
3792
+ async function clockChangesSince(blocks, head, since, opts, logger) {
3793
+ const eventsFetcher = opts.dirty ? new DirtyEventFetcher(blocks) : new import_clock2.EventFetcher(blocks);
3794
+ const keys = /* @__PURE__ */ new Set();
3795
+ const updates = await gatherUpdates(
3796
+ blocks,
3797
+ eventsFetcher,
3798
+ head,
3799
+ since,
3800
+ [],
3801
+ keys,
3802
+ /* @__PURE__ */ new Set(),
3803
+ opts.limit || Infinity,
3804
+ logger
3805
+ );
3806
+ return { result: updates.reverse(), head };
3807
+ }
3808
+ async function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], keys, didLinks, limit, logger) {
3809
+ if (limit <= 0) return updates;
3810
+ const sHead = head.map((l) => l.toString());
3811
+ for (const link of since) {
3812
+ if (sHead.includes(link.toString())) {
3813
+ return updates;
3814
+ }
3815
+ }
3816
+ for (const link of head) {
3817
+ if (didLinks.has(link.toString())) continue;
3818
+ didLinks.add(link.toString());
3819
+ const { value: event } = await eventsFetcher.get(link);
3820
+ if (!event) continue;
3821
+ const { type } = event.data;
3822
+ let ops = [];
3823
+ if (type === "batch") {
3824
+ ops = event.data.ops;
3825
+ } else if (type === "put") {
3826
+ ops = [event.data];
3827
+ }
3828
+ for (let i = ops.length - 1; i >= 0; i--) {
3829
+ const { key, value } = ops[i];
3830
+ if (!keys.has(key)) {
3831
+ const docValue = await getValueFromLink(blocks, value, logger);
3832
+ updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
3833
+ limit--;
3834
+ keys.add(key);
3835
+ }
3836
+ }
3837
+ if (event.parents) {
3838
+ updates = await gatherUpdates(blocks, eventsFetcher, event.parents, since, updates, keys, didLinks, limit, logger);
3839
+ }
3840
+ }
3841
+ return updates;
3842
+ }
3843
+ async function* getAllEntries(blocks, head, logger) {
3844
+ for await (const [key, link] of (0, import_crdt.entries)(blocks, head)) {
3845
+ const docValue = await getValueFromLink(blocks, link, logger);
3846
+ yield { id: key, value: docValue.doc, del: docValue.del };
3847
+ }
3848
+ }
3849
+ async function* clockVis(blocks, head) {
3850
+ for await (const line of (0, import_clock2.vis)(blocks, head)) {
3851
+ yield line;
3852
+ }
3853
+ }
3854
+ var isCompacting = false;
3855
+ async function doCompact(blockLog, head, logger) {
3856
+ if (isCompacting) {
3857
+ return;
3858
+ }
3859
+ isCompacting = true;
3860
+ time("compact head");
3861
+ for (const cid of head) {
3862
+ const bl = await blockLog.get(cid);
3863
+ if (!bl) throw logger.Error().Ref("cid", cid).Msg("Missing head block").AsError();
3864
+ }
3865
+ timeEnd("compact head");
3866
+ time("compact all entries");
3867
+ for await (const _entry of getAllEntries(blockLog, head, logger)) {
3868
+ continue;
3869
+ }
3870
+ timeEnd("compact all entries");
3871
+ time("compact clock vis");
3872
+ for await (const _line of (0, import_clock2.vis)(blockLog, head)) {
3873
+ }
3874
+ timeEnd("compact clock vis");
3875
+ time("compact root");
3876
+ const result = await (0, import_crdt.root)(blockLog, head);
3877
+ timeEnd("compact root");
3878
+ time("compact root blocks");
3879
+ for (const { cid, bytes } of [...result.additions, ...result.removals]) {
3880
+ blockLog.loggedBlocks.putSync(cid, bytes);
3881
+ }
3882
+ timeEnd("compact root blocks");
3883
+ time("compact changes");
3884
+ await clockChangesSince(blockLog, head, [], {}, logger);
3885
+ timeEnd("compact changes");
3886
+ isCompacting = false;
3887
+ }
3888
+ async function getBlock(blocks, cidString) {
3889
+ const block = await blocks.get((0, import_link.parse)(cidString));
3890
+ if (!block) throw new Error(`Missing block ${cidString}`);
3891
+ const { cid, value } = await (0, import_block6.decode)({ bytes: block.bytes, codec: codec2, hasher: import_sha23.sha256 });
3892
+ return new import_block6.Block({ cid, value, bytes: block.bytes });
3893
+ }
3894
+
3895
+ // src/indexer.ts
3896
+ init_types();
3897
+
3898
+ // src/indexer-helpers.ts
3899
+ var import_block7 = require("multiformats/block");
3900
+ var import_sha24 = require("multiformats/hashes/sha2");
3901
+ var codec3 = __toESM(require("@ipld/dag-cbor"), 1);
3902
+ var import_charwise = __toESM(require("charwise"), 1);
3903
+ var DbIndex = __toESM(require("prolly-trees/db-index"), 1);
3904
+ var import_utils20 = require("prolly-trees/utils");
3905
+ var import_cache2 = require("prolly-trees/cache");
3906
+ var IndexTree = class {
3907
+ };
3908
+ function refCompare(aRef, bRef) {
3909
+ if (Number.isNaN(aRef)) return -1;
3910
+ if (Number.isNaN(bRef)) throw new Error("ref may not be Infinity or NaN");
3911
+ if (aRef === Infinity) return 1;
3912
+ return (0, import_utils20.simpleCompare)(aRef, bRef);
3913
+ }
3914
+ function compare(a, b) {
3915
+ const [aKey, aRef] = a;
3916
+ const [bKey, bRef] = b;
3917
+ const comp = (0, import_utils20.simpleCompare)(aKey, bKey);
3918
+ if (comp !== 0) return comp;
3919
+ return refCompare(aRef, bRef);
3920
+ }
3921
+ var byKeyOpts = { cache: import_cache2.nocache, chunker: (0, import_utils20.bf)(30), codec: codec3, hasher: import_sha24.sha256, compare };
3922
+ var byIdOpts = { cache: import_cache2.nocache, chunker: (0, import_utils20.bf)(30), codec: codec3, hasher: import_sha24.sha256, compare: import_utils20.simpleCompare };
3923
+ function indexEntriesForChanges(changes, mapFn) {
3924
+ const indexEntries = [];
3925
+ changes.forEach(({ id: key, value, del }) => {
3926
+ if (del || !value) return;
3927
+ let mapCalled = false;
3928
+ const mapReturn = mapFn({ ...value, _id: key }, (k, v) => {
3929
+ mapCalled = true;
3930
+ if (typeof k === "undefined") return;
3931
+ indexEntries.push({
3932
+ key: [import_charwise.default.encode(k), key],
3933
+ value: v || null
3934
+ });
3935
+ });
3936
+ if (!mapCalled && mapReturn) {
3937
+ indexEntries.push({
3938
+ key: [import_charwise.default.encode(mapReturn), key],
3939
+ value: null
3940
+ });
3941
+ }
3942
+ });
3943
+ return indexEntries;
3944
+ }
3945
+ function makeProllyGetBlock(blocks) {
3946
+ return async (address) => {
3947
+ const block = await blocks.get(address);
3948
+ if (!block) throw new Error(`Missing block ${address.toString()}`);
3949
+ const { cid, bytes } = block;
3950
+ return (0, import_block7.create)({ cid, bytes, hasher: import_sha24.sha256, codec: codec3 });
3951
+ };
3952
+ }
3953
+ async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
3954
+ if (!indexEntries.length) return inIndex;
3955
+ if (!inIndex.root) {
3956
+ if (!inIndex.cid) {
3957
+ let returnRootBlock = void 0;
3958
+ let returnNode = void 0;
3959
+ for await (const node of await DbIndex.create({
3960
+ get: makeProllyGetBlock(tblocks),
3961
+ list: indexEntries,
3962
+ ...opts
3963
+ })) {
3964
+ const block = await node.block;
3965
+ await tblocks.put(block.cid, block.bytes);
3966
+ returnRootBlock = block;
3967
+ returnNode = node;
3968
+ }
3969
+ if (!returnNode || !returnRootBlock) throw new Error("failed to create index");
3970
+ return { root: returnNode, cid: returnRootBlock.cid };
3971
+ } else {
3972
+ inIndex.root = await DbIndex.load({ cid: inIndex.cid, get: makeProllyGetBlock(tblocks), ...opts });
3973
+ }
3974
+ }
3975
+ const { root: root3, blocks: newBlocks } = await inIndex.root.bulk(indexEntries);
3976
+ if (root3) {
3977
+ for await (const block of newBlocks) {
3978
+ await tblocks.put(block.cid, block.bytes);
3979
+ }
3980
+ return { root: root3, cid: (await root3.block).cid };
3981
+ } else {
3982
+ return { root: void 0, cid: void 0 };
3983
+ }
3984
+ }
3985
+ async function loadIndex(tblocks, cid, opts) {
3986
+ return await DbIndex.load({ cid, get: makeProllyGetBlock(tblocks), ...opts });
3987
+ }
3988
+ async function applyQuery(crdt, resp, query) {
3989
+ if (query.descending) {
3990
+ resp.result = resp.result.reverse();
3991
+ }
3992
+ if (query.limit) {
3993
+ resp.result = resp.result.slice(0, query.limit);
3994
+ }
3995
+ if (query.includeDocs) {
3996
+ resp.result = await Promise.all(
3997
+ resp.result.map(async (row) => {
3998
+ const val = await crdt.get(row.id);
3999
+ const doc = val ? { ...val.doc, _id: row.id } : void 0;
4000
+ return { ...row, doc };
4001
+ })
4002
+ );
4003
+ }
4004
+ return {
4005
+ rows: resp.result.map(({ key, ...row }) => {
4006
+ return {
4007
+ key: import_charwise.default.decode(key),
4008
+ ...row
4009
+ };
4010
+ })
4011
+ };
4012
+ }
4013
+ function encodeRange(range) {
4014
+ return [import_charwise.default.encode(range[0]), import_charwise.default.encode(range[1])];
4015
+ }
4016
+ function encodeKey(key) {
4017
+ return import_charwise.default.encode(key);
4018
+ }
4019
+
4020
+ // src/indexer.ts
4021
+ init_utils();
4022
+ function index({ _crdt }, name, mapFn, meta) {
4023
+ if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
4024
+ if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
4025
+ if (_crdt.indexers.has(name)) {
4026
+ const idx = _crdt.indexers.get(name);
4027
+ idx.applyMapFn(name, mapFn, meta);
4028
+ } else {
4029
+ const idx = new Index(_crdt, name, mapFn, meta);
4030
+ _crdt.indexers.set(name, idx);
4031
+ }
4032
+ return _crdt.indexers.get(name);
4033
+ }
4034
+ var Index = class {
4035
+ constructor(crdt, name, mapFn, meta) {
4036
+ this.mapFnString = "";
4037
+ this.byKey = new IndexTree();
4038
+ this.byId = new IndexTree();
4039
+ this.includeDocsDefault = false;
4040
+ this.logger = ensureLogger(crdt.logger, "Index");
4041
+ this.blockstore = crdt.indexBlockstore;
4042
+ this.crdt = crdt;
4043
+ this.applyMapFn(name, mapFn, meta);
4044
+ this.name = name;
4045
+ if (!(this.mapFnString || this.initError)) throw this.logger.Error().Msg("missing mapFnString").AsError();
4046
+ }
4047
+ ready() {
4048
+ return Promise.all([this.blockstore.ready(), this.crdt.ready()]).then(() => {
4049
+ });
4050
+ }
4051
+ close() {
4052
+ return Promise.all([this.blockstore.close(), this.crdt.close()]).then(() => {
4053
+ });
4054
+ }
4055
+ destroy() {
4056
+ return Promise.all([this.blockstore.destroy(), this.crdt.destroy()]).then(() => {
4057
+ });
4058
+ }
4059
+ applyMapFn(name, mapFn, meta) {
4060
+ if (mapFn && meta) throw this.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
4061
+ if (this.name && this.name !== name) throw this.logger.Error().Msg("cannot change name").AsError();
4062
+ this.name = name;
4063
+ try {
4064
+ if (meta) {
4065
+ if (this.indexHead && this.indexHead.map((c) => c.toString()).join() !== meta.head.map((c) => c.toString()).join()) {
4066
+ throw this.logger.Error().Msg("cannot apply different head meta").AsError();
4067
+ }
4068
+ if (this.mapFnString) {
4069
+ if (this.mapFnString !== meta.map) {
4070
+ this.logger.Warn().Msg(`cannot apply different mapFn meta: old mapFnString ${this.mapFnString} new mapFnString ${meta.map}`);
4071
+ } else {
4072
+ this.byId.cid = meta.byId;
4073
+ this.byKey.cid = meta.byKey;
4074
+ this.indexHead = meta.head;
4075
+ }
4076
+ } else {
4077
+ this.mapFnString = meta.map;
4078
+ this.byId.cid = meta.byId;
4079
+ this.byKey.cid = meta.byKey;
4080
+ this.indexHead = meta.head;
4081
+ }
4082
+ } else {
4083
+ if (this.mapFn) {
4084
+ if (mapFn) {
4085
+ if (this.mapFn.toString() !== mapFn.toString()) {
4086
+ throw this.logger.Error().Msg("cannot apply different mapFn app2").AsError();
4087
+ }
4088
+ }
4089
+ } else {
4090
+ if (!mapFn) {
4091
+ mapFn = (doc) => doc[name] ?? void 0;
4092
+ }
4093
+ if (this.mapFnString) {
4094
+ if (this.mapFnString !== mapFn.toString()) {
4095
+ throw this.logger.Error().Msg("cannot apply different mapFn app").AsError();
4096
+ }
4097
+ } else {
4098
+ this.mapFnString = mapFn.toString();
4099
+ }
4100
+ this.mapFn = mapFn;
4101
+ }
4102
+ }
4103
+ const matches = /=>\s*(.*)/.test(this.mapFnString);
4104
+ this.includeDocsDefault = matches;
4105
+ } catch (e) {
4106
+ this.initError = e;
4107
+ }
4108
+ }
4109
+ async query(opts = {}) {
4110
+ await this.ready();
4111
+ await this._updateIndex();
4112
+ await this._hydrateIndex();
4113
+ if (!this.byKey.root) {
4114
+ return await applyQuery(this.crdt, { result: [] }, opts);
4115
+ }
4116
+ if (this.includeDocsDefault && opts.includeDocs === void 0) opts.includeDocs = true;
4117
+ if (opts.range) {
4118
+ const eRange = encodeRange(opts.range);
4119
+ return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).range(eRange[0], eRange[1]), opts);
4120
+ }
4121
+ if (opts.key) {
4122
+ const encodedKey = encodeKey(opts.key);
4123
+ return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts);
4124
+ }
4125
+ if (Array.isArray(opts.keys)) {
4126
+ const results = await Promise.all(
4127
+ opts.keys.map(async (key) => {
4128
+ const encodedKey = encodeKey(key);
4129
+ return (await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts)).rows;
4130
+ })
4131
+ );
4132
+ return { rows: results.flat() };
4133
+ }
4134
+ if (opts.prefix) {
4135
+ if (!Array.isArray(opts.prefix)) opts.prefix = [opts.prefix];
4136
+ const start = [...opts.prefix, NaN];
4137
+ const end = [...opts.prefix, Infinity];
4138
+ const encodedR = encodeRange([start, end]);
4139
+ return await applyQuery(this.crdt, await this.byKey.root.range(...encodedR), opts);
4140
+ }
4141
+ const all = await this.byKey.root.getAllEntries();
4142
+ return await applyQuery(
4143
+ this.crdt,
4144
+ {
4145
+ // @ts-expect-error getAllEntries returns a different type than range
4146
+ result: all.result.map(({ key: [k, id], value }) => ({
4147
+ key: k,
4148
+ id,
4149
+ value
4150
+ }))
4151
+ },
4152
+ opts
4153
+ );
4154
+ }
4155
+ _resetIndex() {
4156
+ this.byId = new IndexTree();
4157
+ this.byKey = new IndexTree();
4158
+ this.indexHead = void 0;
4159
+ }
4160
+ async _hydrateIndex() {
4161
+ if (this.byId.root && this.byKey.root) return;
4162
+ if (!this.byId.cid || !this.byKey.cid) return;
4163
+ this.byId.root = await loadIndex(this.blockstore, this.byId.cid, byIdOpts);
4164
+ this.byKey.root = await loadIndex(this.blockstore, this.byKey.cid, byKeyOpts);
4165
+ }
4166
+ async _updateIndex() {
4167
+ await this.ready();
4168
+ if (this.initError) throw this.initError;
4169
+ if (!this.mapFn) throw this.logger.Error().Msg("No map function defined").AsError();
4170
+ let result, head;
4171
+ if (!this.indexHead || this.indexHead.length === 0) {
4172
+ ({ result, head } = await this.crdt.allDocs());
4173
+ } else {
4174
+ ({ result, head } = await this.crdt.changes(this.indexHead));
4175
+ }
4176
+ if (result.length === 0) {
4177
+ this.indexHead = head;
4178
+ }
4179
+ let staleKeyIndexEntries = [];
4180
+ let removeIdIndexEntries = [];
4181
+ if (this.byId.root) {
4182
+ const removeIds = result.map(({ id: key }) => key);
4183
+ const { result: oldChangeEntries } = await this.byId.root.getMany(removeIds);
4184
+ staleKeyIndexEntries = oldChangeEntries.map((key) => ({ key, del: true }));
4185
+ removeIdIndexEntries = oldChangeEntries.map((key) => ({ key: key[1], del: true }));
4186
+ }
4187
+ const indexEntries = indexEntriesForChanges(result, this.mapFn);
4188
+ const byIdIndexEntries = indexEntries.map(({ key }) => ({
4189
+ key: key[1],
4190
+ value: key
4191
+ }));
4192
+ const indexerMeta = { indexes: /* @__PURE__ */ new Map() };
4193
+ for (const [name, indexer] of this.crdt.indexers) {
4194
+ if (indexer.indexHead) {
4195
+ indexerMeta.indexes?.set(name, {
4196
+ byId: indexer.byId.cid,
4197
+ byKey: indexer.byKey.cid,
4198
+ head: indexer.indexHead,
4199
+ map: indexer.mapFnString,
4200
+ name: indexer.name
4201
+ });
4202
+ }
4203
+ }
4204
+ if (result.length === 0) {
4205
+ return indexerMeta;
4206
+ }
4207
+ const { meta } = await this.blockstore.transaction(async (tblocks) => {
4208
+ this.byId = await bulkIndex(tblocks, this.byId, removeIdIndexEntries.concat(byIdIndexEntries), byIdOpts);
4209
+ this.byKey = await bulkIndex(tblocks, this.byKey, staleKeyIndexEntries.concat(indexEntries), byKeyOpts);
4210
+ this.indexHead = head;
4211
+ if (this.byId.cid && this.byKey.cid) {
4212
+ const idxMeta = {
4213
+ byId: this.byId.cid,
4214
+ byKey: this.byKey.cid,
4215
+ head,
4216
+ map: this.mapFnString,
4217
+ name: this.name
4218
+ };
4219
+ indexerMeta.indexes?.set(this.name, idxMeta);
4220
+ }
4221
+ return indexerMeta;
4222
+ });
4223
+ return meta;
4224
+ }
4225
+ };
4226
+
4227
+ // src/crdt-clock.ts
4228
+ var import_clock3 = require("@web3-storage/pail/clock");
4229
+ var import_crdt2 = require("@web3-storage/pail/crdt");
4230
+ var import_cement15 = require("@adviser/cement");
4231
+ init_types();
4232
+
4233
+ // src/apply-head-queue.ts
4234
+ function applyHeadQueue(worker, logger) {
4235
+ const queue = [];
4236
+ let isProcessing = false;
4237
+ async function* process() {
4238
+ if (isProcessing || queue.length === 0) return;
4239
+ isProcessing = true;
4240
+ const allUpdates = [];
4241
+ try {
4242
+ while (queue.length > 0) {
4243
+ queue.sort((a, b) => b.updates ? 1 : -1);
4244
+ const task = queue.shift();
4245
+ if (!task) continue;
4246
+ await worker(task.newHead, task.prevHead, task.updates !== null).catch((e) => {
4247
+ throw logger.Error().Err(e).Msg("int_applyHead worker error").AsError();
4248
+ });
4249
+ if (task.updates) {
4250
+ allUpdates.push(...task.updates);
4251
+ }
4252
+ if (!queue.some((t) => t.updates) || task.updates) {
4253
+ const allTasksHaveUpdates = queue.every((task2) => task2.updates !== null);
4254
+ yield { updates: allUpdates, all: allTasksHaveUpdates };
4255
+ allUpdates.length = 0;
4256
+ }
4257
+ }
4258
+ } finally {
4259
+ isProcessing = false;
4260
+ const generator = process();
4261
+ let result = await generator.next();
4262
+ while (!result.done) {
4263
+ result = await generator.next();
4264
+ }
4265
+ }
4266
+ }
4267
+ return {
4268
+ push(task) {
4269
+ queue.push(task);
4270
+ return process();
4271
+ },
4272
+ size() {
4273
+ return queue.length;
4274
+ }
4275
+ };
4276
+ }
4277
+
4278
+ // src/crdt-clock.ts
4279
+ init_utils();
4280
+ var CRDTClock = class {
4281
+ constructor(blockstore) {
4282
+ // todo: track local and remote clocks independently, merge on read
4283
+ // that way we can drop the whole remote if we need to
4284
+ // should go with making sure the local clock only references locally available blockstore on write
4285
+ this.head = [];
4286
+ this.zoomers = /* @__PURE__ */ new Set();
4287
+ this.watchers = /* @__PURE__ */ new Set();
4288
+ this.emptyWatchers = /* @__PURE__ */ new Set();
4289
+ this._ready = new import_cement15.ResolveOnce();
4290
+ this.blockstore = blockstore;
4291
+ this.logger = ensureLogger(blockstore.logger, "CRDTClock");
4292
+ this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
4293
+ }
4294
+ async ready() {
4295
+ return this._ready.once(async () => {
4296
+ await this.blockstore.ready();
4297
+ });
4298
+ }
4299
+ async close() {
4300
+ await this.blockstore.close();
4301
+ }
4302
+ setHead(head) {
4303
+ this.head = head;
4304
+ }
4305
+ async applyHead(newHead, prevHead, updates) {
4306
+ for await (const { updates: updatesAcc, all } of this.applyHeadQueue.push({
4307
+ newHead,
4308
+ prevHead,
4309
+ updates
4310
+ })) {
4311
+ return this.processUpdates(updatesAcc, all, prevHead);
4312
+ }
4313
+ }
4314
+ async processUpdates(updatesAcc, all, prevHead) {
4315
+ let internalUpdates = updatesAcc;
4316
+ if (this.watchers.size && !all) {
4317
+ const changes = await clockChangesSince(throwFalsy(this.blockstore), this.head, prevHead, {}, this.logger);
4318
+ internalUpdates = changes.result;
4319
+ }
4320
+ this.zoomers.forEach((fn) => fn());
4321
+ this.notifyWatchers(internalUpdates || []);
4322
+ }
4323
+ notifyWatchers(updates) {
4324
+ this.emptyWatchers.forEach((fn) => fn());
4325
+ this.watchers.forEach((fn) => fn(updates || []));
4326
+ }
4327
+ onTick(fn) {
4328
+ this.watchers.add(fn);
4329
+ }
4330
+ onTock(fn) {
4331
+ this.emptyWatchers.add(fn);
4332
+ }
4333
+ onZoom(fn) {
4334
+ this.zoomers.add(fn);
4335
+ }
4336
+ async int_applyHead(newHead, prevHead, localUpdates) {
4337
+ const ogHead = sortClockHead(this.head);
4338
+ newHead = sortClockHead(newHead);
4339
+ if (compareClockHeads(ogHead, newHead)) {
4340
+ return;
4341
+ }
4342
+ const ogPrev = sortClockHead(prevHead);
4343
+ if (compareClockHeads(ogHead, ogPrev)) {
4344
+ this.setHead(newHead);
4345
+ return;
4346
+ }
4347
+ const noLoader = !localUpdates;
4348
+ if (!this.blockstore) {
4349
+ throw this.logger.Error().Msg("missing blockstore").AsError();
4350
+ }
4351
+ await validateBlocks(this.logger, newHead, this.blockstore);
4352
+ const { meta } = await this.blockstore.transaction(
4353
+ async (tblocks) => {
4354
+ const advancedHead = await advanceBlocks(this.logger, newHead, tblocks, this.head);
4355
+ const result = await (0, import_crdt2.root)(tblocks, advancedHead);
4356
+ for (const { cid, bytes } of [
4357
+ ...result.additions
4358
+ // ...result.removals
4359
+ ]) {
4360
+ tblocks.putSync(cid, bytes);
4361
+ }
4362
+ return { head: advancedHead };
4363
+ },
4364
+ { noLoader }
4365
+ );
4366
+ this.setHead(meta.head);
4367
+ }
4368
+ };
4369
+ function sortClockHead(clockHead) {
4370
+ return clockHead.sort((a, b) => a.toString().localeCompare(b.toString()));
4371
+ }
4372
+ async function validateBlocks(logger, newHead, blockstore) {
4373
+ if (!blockstore) throw logger.Error().Msg("missing blockstore");
4374
+ newHead.map(async (cid) => {
4375
+ const got = await blockstore.get(cid);
4376
+ if (!got) {
4377
+ throw logger.Error().Str("cid", cid.toString()).Msg("int_applyHead missing block").AsError();
4378
+ }
4379
+ });
4380
+ }
4381
+ function compareClockHeads(head1, head2) {
4382
+ return head1.toString() === head2.toString();
4383
+ }
4384
+ async function advanceBlocks(logger, newHead, tblocks, head) {
4385
+ for (const cid of newHead) {
4386
+ try {
4387
+ head = await (0, import_clock3.advance)(tblocks, head, cid);
4388
+ } catch (e) {
4389
+ logger.Debug().Err(e).Msg("failed to advance head");
4390
+ continue;
4391
+ }
4392
+ }
4393
+ return head;
4394
+ }
4395
+
4396
+ // src/crdt.ts
4397
+ init_utils();
4398
+ var CRDT = class {
4399
+ constructor(name, opts = {}) {
4400
+ this.onceReady = new import_cement16.ResolveOnce();
4401
+ this.indexers = /* @__PURE__ */ new Map();
4402
+ this.name = name;
4403
+ this.logger = ensureLogger(opts, "CRDT");
4404
+ this.opts = opts;
4405
+ this.blockstore = blockstoreFactory({
4406
+ name,
4407
+ applyMeta: async (meta) => {
4408
+ const crdtMeta = meta;
4409
+ if (!crdtMeta.head) throw this.logger.Error().Msg("missing head").AsError();
4410
+ await this.clock.applyHead(crdtMeta.head, []);
4411
+ },
4412
+ compact: async (blocks) => {
4413
+ await doCompact(blocks, this.clock.head, this.logger);
4414
+ return { head: this.clock.head };
4415
+ },
4416
+ autoCompact: this.opts.autoCompact || 100,
4417
+ crypto: this.opts.crypto,
4418
+ store: { ...this.opts.store, isIndex: void 0 },
4419
+ public: this.opts.public,
4420
+ meta: this.opts.meta,
4421
+ threshold: this.opts.threshold
4422
+ });
4423
+ this.indexBlockstore = blockstoreFactory({
4424
+ name,
4425
+ applyMeta: async (meta) => {
4426
+ const idxCarMeta = meta;
4427
+ if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
4428
+ for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
4429
+ index({ _crdt: this }, name2, void 0, idx);
4430
+ }
4431
+ },
4432
+ crypto: this.opts.crypto,
4433
+ store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
4434
+ public: this.opts.public
4435
+ });
4436
+ this.clock = new CRDTClock(this.blockstore);
4437
+ this.clock.onZoom(() => {
4438
+ for (const idx of this.indexers.values()) {
4439
+ idx._resetIndex();
4440
+ }
4441
+ });
4442
+ }
4443
+ async ready() {
4444
+ return this.onceReady.once(async () => {
4445
+ await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
4446
+ });
4447
+ }
4448
+ async close() {
4449
+ await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
4450
+ }
4451
+ async destroy() {
4452
+ await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
4453
+ }
4454
+ async bulk(updates) {
4455
+ await this.ready();
4456
+ const prevHead = [...this.clock.head];
4457
+ const done = await this.blockstore.transaction(async (blocks) => {
4458
+ const { head } = await applyBulkUpdateToCrdt(
4459
+ this.blockstore.ebOpts.storeRuntime,
4460
+ blocks,
4461
+ this.clock.head,
4462
+ updates,
4463
+ this.logger
4464
+ );
4465
+ updates = updates.map((dupdate) => {
4466
+ readFiles(this.blockstore, { doc: dupdate.value });
4467
+ return dupdate;
4468
+ });
4469
+ return { head };
4470
+ });
4471
+ await this.clock.applyHead(done.meta.head, prevHead, updates);
4472
+ return done.meta;
4473
+ }
4474
+ // if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
4475
+ async allDocs() {
4476
+ await this.ready();
4477
+ const result = [];
4478
+ for await (const entry of getAllEntries(this.blockstore, this.clock.head, this.logger)) {
4479
+ result.push(entry);
4480
+ }
4481
+ return { result, head: this.clock.head };
4482
+ }
4483
+ async vis() {
4484
+ await this.ready();
4485
+ const txt = [];
4486
+ for await (const line of clockVis(this.blockstore, this.clock.head)) {
4487
+ txt.push(line);
4488
+ }
4489
+ return txt.join("\n");
4490
+ }
4491
+ async getBlock(cidString) {
4492
+ await this.ready();
4493
+ return await getBlock(this.blockstore, cidString);
4494
+ }
4495
+ async get(key) {
4496
+ await this.ready();
4497
+ const result = await getValueFromCrdt(this.blockstore, this.clock.head, key, this.logger);
4498
+ if (result.del) return void 0;
4499
+ return result;
4500
+ }
4501
+ async changes(since = [], opts = {}) {
4502
+ await this.ready();
4503
+ return await clockChangesSince(this.blockstore, this.clock.head, since, opts, this.logger);
4504
+ }
4505
+ async compact() {
4506
+ const blocks = this.blockstore;
4507
+ return await blocks.compact();
4508
+ }
4509
+ };
4510
+
4511
+ // src/database.ts
4512
+ init_sys_container();
4513
+ init_utils();
4514
+ init_gateway();
4515
+ var Database = class {
4516
+ constructor(name, opts) {
4517
+ this.opts = {};
4518
+ this._listening = false;
4519
+ this._listeners = /* @__PURE__ */ new Set();
4520
+ this._noupdate_listeners = /* @__PURE__ */ new Set();
4521
+ this._ready = new import_cement17.ResolveOnce();
4522
+ this.name = name;
4523
+ this.opts = opts || this.opts;
4524
+ this.logger = ensureLogger(this.opts, "Database");
4525
+ this._crdt = new CRDT(name, this.opts);
4526
+ this.blockstore = this._crdt.blockstore;
4527
+ this._writeQueue = writeQueue(async (updates) => {
4528
+ return await this._crdt.bulk(updates);
4529
+ });
4530
+ this._crdt.clock.onTock(() => {
4531
+ this._no_update_notify();
4532
+ });
4533
+ }
4534
+ static {
4535
+ this.databases = /* @__PURE__ */ new Map();
4536
+ }
4537
+ async close() {
4538
+ await this.ready();
4539
+ await this._crdt.close();
4540
+ await this.blockstore.close();
4541
+ }
4542
+ async destroy() {
4543
+ await this.ready();
4544
+ await this._crdt.destroy();
4545
+ await this.blockstore.destroy();
4546
+ }
4547
+ async ready() {
4548
+ return this._ready.once(async () => {
4549
+ await SysContainer.start();
4550
+ await this._crdt.ready();
4551
+ await this.blockstore.ready();
4552
+ });
4553
+ }
4554
+ async get(id) {
4555
+ this.logger.Debug().Str("id", id).Msg("get-pre-ready");
4556
+ await this.ready();
4557
+ this.logger.Debug().Str("id", id).Msg("get-post-ready");
4558
+ const got = await this._crdt.get(id).catch((e) => {
4559
+ throw new NotFoundError(`Not found: ${id} - ${e.message}`);
4560
+ });
4561
+ if (!got) throw new NotFoundError(`Not found: ${id}`);
4562
+ const { doc } = got;
4563
+ return { ...doc, _id: id };
4564
+ }
4565
+ async put(doc) {
4566
+ this.logger.Debug().Str("id", doc._id).Msg("put-pre-ready");
4567
+ await this.ready();
4568
+ this.logger.Debug().Str("id", doc._id).Msg("put-post-ready");
4569
+ const { _id, ...value } = doc;
4570
+ const docId = _id || (0, import_uuidv73.uuidv7)();
4571
+ const result = await this._writeQueue.push({
4572
+ id: docId,
4573
+ value: {
4574
+ ...value,
4575
+ _id: docId
4576
+ }
4577
+ });
4578
+ return { id: docId, clock: result?.head };
4579
+ }
4580
+ async del(id) {
4581
+ await this.ready();
4582
+ const result = await this._writeQueue.push({ id, del: true });
4583
+ return { id, clock: result?.head };
4584
+ }
4585
+ async changes(since = [], opts = {}) {
4586
+ await this.ready();
4587
+ const { result, head } = await this._crdt.changes(since, opts);
4588
+ const rows = result.map(({ id: key, value, del, clock }) => ({
4589
+ key,
4590
+ value: del ? { _id: key, _deleted: true } : { _id: key, ...value },
4591
+ clock
4592
+ }));
4593
+ return { rows, clock: head };
4594
+ }
4595
+ async allDocs() {
4596
+ await this.ready();
4597
+ const { result, head } = await this._crdt.allDocs();
4598
+ const rows = result.map(({ id: key, value, del }) => ({
4599
+ key,
4600
+ value: del ? { _id: key, _deleted: true } : { _id: key, ...value }
4601
+ }));
4602
+ return { rows, clock: head };
4603
+ }
4604
+ async allDocuments() {
4605
+ return this.allDocs();
4606
+ }
4607
+ subscribe(listener, updates) {
4608
+ if (updates) {
4609
+ if (!this._listening) {
4610
+ this._listening = true;
4611
+ this._crdt.clock.onTick((updates2) => {
4612
+ void this._notify(updates2);
4613
+ });
4614
+ }
4615
+ this._listeners.add(listener);
4616
+ return () => {
4617
+ this._listeners.delete(listener);
4618
+ };
4619
+ } else {
4620
+ this._noupdate_listeners.add(listener);
4621
+ return () => {
4622
+ this._noupdate_listeners.delete(listener);
4623
+ };
4624
+ }
4625
+ }
4626
+ // todo if we add this onto dbs in fireproof.ts then we can make index.ts a separate package
4627
+ async query(field, opts = {}) {
4628
+ await this.ready();
4629
+ const _crdt = this._crdt;
4630
+ const idx = typeof field === "string" ? index({ _crdt }, field) : index({ _crdt }, makeName(field.toString()), field);
4631
+ return await idx.query(opts);
4632
+ }
4633
+ async compact() {
4634
+ await this.ready();
4635
+ await this._crdt.compact();
4636
+ }
4637
+ async _notify(updates) {
4638
+ await this.ready();
4639
+ if (this._listeners.size) {
4640
+ const docs = updates.map(({ id, value }) => ({ ...value, _id: id }));
4641
+ for (const listener of this._listeners) {
4642
+ await (async () => await listener(docs))().catch((e) => {
4643
+ this.logger.Error().Err(e).Msg("subscriber error");
4644
+ });
4645
+ }
4646
+ }
4647
+ }
4648
+ async _no_update_notify() {
4649
+ await this.ready();
4650
+ if (this._noupdate_listeners.size) {
4651
+ for (const listener of this._noupdate_listeners) {
4652
+ await (async () => await listener([]))().catch((e) => {
4653
+ this.logger.Error().Err(e).Msg("subscriber error");
4654
+ });
4655
+ }
4656
+ }
4657
+ }
4658
+ };
4659
+ function toSortedArray(set) {
4660
+ if (!set) return [];
4661
+ return Object.entries(set).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => ({ [k]: v }));
4662
+ }
4663
+ function fireproof(name, opts) {
4664
+ const key = JSON.stringify(
4665
+ toSortedArray({
4666
+ name,
4667
+ stores: toSortedArray(opts?.store?.stores),
4668
+ makeMetaStore: !!opts?.store?.makeMetaStore,
4669
+ makeDataStore: !!opts?.store?.makeDataStore,
4670
+ makeRemoteWAL: !!opts?.store?.makeRemoteWAL,
4671
+ encodeFile: !!opts?.store?.encodeFile,
4672
+ decodeFile: !!opts?.store?.decodeFile
4673
+ })
4674
+ );
4675
+ let db = Database.databases.get(key);
4676
+ if (!db) {
4677
+ db = new Database(name, opts);
4678
+ Database.databases.set(key, db);
4679
+ }
4680
+ return db;
4681
+ }
4682
+ function makeName(fnString) {
4683
+ const regex = /\(([^,()]+,\s*[^,()]+|\[[^\]]+\],\s*[^,()]+)\)/g;
4684
+ let found = null;
4685
+ const matches = Array.from(fnString.matchAll(regex), (match) => match[1].trim());
4686
+ if (matches.length === 0) {
4687
+ found = /=>\s*(.*)/.exec(fnString);
4688
+ }
4689
+ if (!found) {
4690
+ return fnString;
4691
+ } else {
4692
+ return found[1];
4693
+ }
4694
+ }
4695
+
4696
+ // src/index.ts
4697
+ init_types();
4698
+ init_runtime();
4699
+ init_runtime();
4700
+ init_utils();
4701
+
4702
+ // src/version.ts
4703
+ var PACKAGE_VERSION = Object.keys({
4704
+ "0.0.0-dev": "xxxx"
4705
+ })[0];
4706
+ //# sourceMappingURL=index.cjs.map