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