@fireproof/core 0.19.8-dev-getcon → 0.19.8-dev-series-1
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 +34 -0
- package/chunk-7EWIAXTM.js +7 -0
- package/chunk-7EWIAXTM.js.map +1 -0
- package/chunk-OFGPKRCM.js +290 -0
- package/chunk-OFGPKRCM.js.map +1 -0
- package/chunk-PB4BKL4O.js +7 -0
- package/chunk-PB4BKL4O.js.map +1 -0
- package/chunk-WS3YRPIA.js +75 -0
- package/chunk-WS3YRPIA.js.map +1 -0
- package/deno.json +20 -0
- package/gateway-5FCWPX5W.js +144 -0
- package/gateway-5FCWPX5W.js.map +1 -0
- package/{store-indexdb-WLRSICCB.js → gateway-H7UD6TNB.js} +49 -82
- package/gateway-H7UD6TNB.js.map +1 -0
- package/index.cjs +2317 -1838
- package/index.cjs.map +1 -1
- package/index.d.cts +663 -535
- package/index.d.ts +663 -535
- package/index.global.js +26291 -20733
- package/index.global.js.map +1 -1
- package/index.js +1618 -1032
- package/index.js.map +1 -1
- package/key-bag-file-WADZBHYG.js +54 -0
- package/key-bag-file-WADZBHYG.js.map +1 -0
- package/key-bag-indexdb-PGVAI3FJ.js +50 -0
- package/key-bag-indexdb-PGVAI3FJ.js.map +1 -0
- package/mem-filesystem-YPPJV7Q2.js +41 -0
- package/mem-filesystem-YPPJV7Q2.js.map +1 -0
- package/metafile-cjs.json +1 -1
- package/metafile-esm.json +1 -1
- package/metafile-iife.json +1 -1
- package/node-filesystem-INX4ZTHE.js +45 -0
- package/node-filesystem-INX4ZTHE.js.map +1 -0
- package/package.json +12 -8
- package/tests/blockstore/fragment-gateway.test.ts +107 -0
- package/tests/blockstore/keyed-crypto.test.ts +332 -0
- package/tests/blockstore/loader.test.ts +24 -19
- package/tests/blockstore/store.test.ts +51 -40
- package/tests/blockstore/transaction.test.ts +19 -15
- package/tests/fireproof/all-gateway.test.ts +394 -0
- package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.car +0 -0
- package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.ts +316 -0
- package/tests/fireproof/config.test.ts +94 -78
- package/tests/fireproof/crdt.test.ts +34 -28
- package/tests/fireproof/database.test.ts +22 -14
- package/tests/fireproof/fireproof.test.fixture.ts +133 -0
- package/tests/fireproof/fireproof.test.ts +331 -219
- package/tests/fireproof/hello.test.ts +34 -18
- package/tests/fireproof/indexer.test.ts +34 -27
- package/tests/fireproof/utils.test.ts +84 -0
- package/tests/helpers.ts +28 -57
- package/tests/www/todo-local.html +1 -1
- package/tests/www/todo.html +12 -15
- package/utils-QO2HIWGI.js +14 -0
- package/utils-QO2HIWGI.js.map +1 -0
- package/chunk-BNL4PVBF.js +0 -314
- package/chunk-BNL4PVBF.js.map +0 -1
- package/chunk-JW2QT6BF.js +0 -184
- package/chunk-JW2QT6BF.js.map +0 -1
- package/node-sys-container-MIEX6ELJ.js +0 -29
- package/node-sys-container-MIEX6ELJ.js.map +0 -1
- package/store-file-VJ6BI4II.js +0 -191
- package/store-file-VJ6BI4II.js.map +0 -1
- package/store-indexdb-WLRSICCB.js.map +0 -1
package/index.js
CHANGED
@@ -1,30 +1,33 @@
|
|
1
|
+
import {
|
2
|
+
FILESTORE_VERSION
|
3
|
+
} from "./chunk-7EWIAXTM.js";
|
4
|
+
import {
|
5
|
+
getFileName,
|
6
|
+
getFileSystem,
|
7
|
+
getPath,
|
8
|
+
toArrayBuffer
|
9
|
+
} from "./chunk-WS3YRPIA.js";
|
10
|
+
import {
|
11
|
+
INDEXDB_VERSION
|
12
|
+
} from "./chunk-PB4BKL4O.js";
|
1
13
|
import {
|
2
14
|
NotFoundError,
|
3
15
|
Result,
|
16
|
+
UInt8ArrayEqual,
|
17
|
+
__export,
|
4
18
|
dataDir,
|
5
|
-
decodeFile,
|
6
|
-
encodeFile,
|
7
19
|
ensureLogger,
|
8
|
-
|
20
|
+
ensureSuperLog,
|
21
|
+
ensureSuperThis,
|
9
22
|
exceptionWrapper,
|
10
23
|
getKey,
|
11
24
|
getName,
|
12
25
|
getStore,
|
13
|
-
isNotFoundError
|
14
|
-
|
15
|
-
toCryptoOpts
|
16
|
-
} from "./chunk-BNL4PVBF.js";
|
17
|
-
import {
|
18
|
-
SysContainer,
|
19
|
-
__export,
|
20
|
-
falsyToUndef,
|
21
|
-
isFalsy,
|
22
|
-
throwFalsy
|
23
|
-
} from "./chunk-JW2QT6BF.js";
|
26
|
+
isNotFoundError
|
27
|
+
} from "./chunk-OFGPKRCM.js";
|
24
28
|
|
25
29
|
// src/database.ts
|
26
|
-
import {
|
27
|
-
import { ResolveOnce as ResolveOnce5 } from "@adviser/cement";
|
30
|
+
import { ResolveOnce as ResolveOnce6 } from "@adviser/cement";
|
28
31
|
|
29
32
|
// src/write-queue.ts
|
30
33
|
function writeQueue(worker, payload = Infinity, unbounded = false) {
|
@@ -67,13 +70,83 @@ function writeQueue(worker, payload = Infinity, unbounded = false) {
|
|
67
70
|
}
|
68
71
|
|
69
72
|
// src/crdt.ts
|
70
|
-
import { ResolveOnce as
|
73
|
+
import { ResolveOnce as ResolveOnce5 } from "@adviser/cement";
|
74
|
+
|
75
|
+
// src/runtime/wait-pr-multiformats/block.ts
|
76
|
+
var block_exports = {};
|
77
|
+
__export(block_exports, {
|
78
|
+
Block: () => Block,
|
79
|
+
create: () => create,
|
80
|
+
createUnsafe: () => createUnsafe,
|
81
|
+
decode: () => decode,
|
82
|
+
encode: () => encode
|
83
|
+
});
|
84
|
+
import { bytes as binary, CID } from "multiformats";
|
85
|
+
import { Block as mfBlock } from "multiformats/block";
|
86
|
+
var Block = mfBlock;
|
87
|
+
async function decode({
|
88
|
+
bytes,
|
89
|
+
codec: codec3,
|
90
|
+
hasher: hasher7
|
91
|
+
}) {
|
92
|
+
if (bytes == null) throw new Error('Missing required argument "bytes"');
|
93
|
+
if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
|
94
|
+
const value = await Promise.resolve(codec3.decode(bytes));
|
95
|
+
const hash = await hasher7.digest(bytes);
|
96
|
+
const cid = CID.create(1, codec3.code, hash);
|
97
|
+
return new mfBlock({ value, bytes, cid });
|
98
|
+
}
|
99
|
+
async function encode({
|
100
|
+
value,
|
101
|
+
codec: codec3,
|
102
|
+
hasher: hasher7
|
103
|
+
}) {
|
104
|
+
if (typeof value === "undefined") throw new Error('Missing required argument "value"');
|
105
|
+
if (codec3 == null || hasher7 == null) throw new Error("Missing required argument: codec or hasher");
|
106
|
+
const bytes = await Promise.resolve(codec3.encode(value));
|
107
|
+
const hash = await hasher7.digest(bytes);
|
108
|
+
const cid = CID.create(1, codec3.code, hash);
|
109
|
+
return new mfBlock({ value, bytes, cid });
|
110
|
+
}
|
111
|
+
async function create({
|
112
|
+
bytes,
|
113
|
+
cid,
|
114
|
+
hasher: hasher7,
|
115
|
+
codec: codec3
|
116
|
+
}) {
|
117
|
+
if (bytes == null) throw new Error('Missing required argument "bytes"');
|
118
|
+
if (hasher7 == null) throw new Error('Missing required argument "hasher"');
|
119
|
+
const value = await Promise.resolve(codec3.decode(bytes));
|
120
|
+
const hash = await hasher7.digest(bytes);
|
121
|
+
if (!binary.equals(cid.multihash.bytes, hash.bytes)) {
|
122
|
+
throw new Error("CID hash does not match bytes");
|
123
|
+
}
|
124
|
+
return createUnsafe({
|
125
|
+
bytes,
|
126
|
+
cid,
|
127
|
+
value,
|
128
|
+
codec: codec3
|
129
|
+
});
|
130
|
+
}
|
131
|
+
async function createUnsafe({
|
132
|
+
bytes,
|
133
|
+
cid,
|
134
|
+
value: maybeValue,
|
135
|
+
codec: codec3
|
136
|
+
}) {
|
137
|
+
const value = await Promise.resolve(maybeValue !== void 0 ? maybeValue : codec3?.decode(bytes));
|
138
|
+
if (value === void 0) throw new Error('Missing required argument, must either provide "value" or "codec"');
|
139
|
+
return new Block({
|
140
|
+
cid,
|
141
|
+
bytes,
|
142
|
+
value
|
143
|
+
});
|
144
|
+
}
|
71
145
|
|
72
146
|
// src/crdt-helpers.ts
|
73
|
-
import {
|
74
|
-
import {
|
75
|
-
import
|
76
|
-
import * as codec2 from "@ipld/dag-cbor";
|
147
|
+
import { parse as parse3 } from "multiformats/link";
|
148
|
+
import { sha256 as hasher5 } from "multiformats/hashes/sha2";
|
149
|
+
import * as codec from "@ipld/dag-cbor";
|
77
150
|
import { put, get, entries, root } from "@web3-storage/pail/crdt";
|
78
151
|
import { EventFetcher, vis } from "@web3-storage/pail/clock";
|
79
152
|
import * as Batch from "@web3-storage/pail/crdt/batch";
|
@@ -84,512 +157,147 @@ __export(blockstore_exports, {
|
|
84
157
|
BaseBlockstore: () => BaseBlockstore,
|
85
158
|
CarTransaction: () => CarTransaction,
|
86
159
|
CompactionFetcher: () => CompactionFetcher,
|
87
|
-
ConnectREST: () => ConnectREST,
|
88
160
|
ConnectionBase: () => ConnectionBase,
|
89
|
-
DataStore: () => DataStore,
|
90
161
|
EncryptedBlockstore: () => EncryptedBlockstore,
|
91
|
-
|
162
|
+
FragmentGateway: () => FragmentGateway,
|
92
163
|
Loader: () => Loader,
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
isNotFoundError: () => isNotFoundError,
|
164
|
+
addCryptoKeyToGatewayMetaPayload: () => addCryptoKeyToGatewayMetaPayload,
|
165
|
+
ensureStart: () => ensureStart,
|
166
|
+
getGatewayFromURL: () => getGatewayFromURL,
|
97
167
|
parseCarFile: () => parseCarFile,
|
98
168
|
registerStoreProtocol: () => registerStoreProtocol,
|
169
|
+
setCryptoKeyFromGatewayMetaPayload: () => setCryptoKeyFromGatewayMetaPayload,
|
99
170
|
testStoreFactory: () => testStoreFactory,
|
100
|
-
|
101
|
-
|
171
|
+
toCIDBlock: () => toCIDBlock,
|
172
|
+
toStoreRuntime: () => toStoreRuntime
|
102
173
|
});
|
103
174
|
|
104
|
-
// src/blockstore/
|
105
|
-
|
106
|
-
|
175
|
+
// src/blockstore/types.ts
|
176
|
+
function toCIDBlock(block) {
|
177
|
+
return block;
|
178
|
+
}
|
107
179
|
|
108
|
-
// src/blockstore/
|
109
|
-
|
110
|
-
constructor(loader) {
|
111
|
-
this.eventsWeHandled = /* @__PURE__ */ new Set();
|
112
|
-
this.queue = [];
|
113
|
-
this.isProcessing = false;
|
114
|
-
this.loader = loader;
|
115
|
-
this.logger = ensureLogger(loader.logger, "TaskManager");
|
116
|
-
}
|
117
|
-
async handleEvent(eventBlock) {
|
118
|
-
const cid = eventBlock.cid.toString();
|
119
|
-
const parents = eventBlock.value.parents.map((cid2) => cid2.toString());
|
120
|
-
for (const parent of parents) {
|
121
|
-
this.eventsWeHandled.add(parent);
|
122
|
-
}
|
123
|
-
this.queue.push({ cid, eventBlock, retries: 0 });
|
124
|
-
this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
|
125
|
-
void this.processQueue();
|
126
|
-
}
|
127
|
-
async processQueue() {
|
128
|
-
if (this.isProcessing) return;
|
129
|
-
this.isProcessing = true;
|
130
|
-
const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
|
131
|
-
const first = filteredQueue[0];
|
132
|
-
if (!first) {
|
133
|
-
return;
|
134
|
-
}
|
135
|
-
try {
|
136
|
-
this.loader?.remoteMetaStore?.handleByteHeads([first.eventBlock.value.data.dbMeta]);
|
137
|
-
this.eventsWeHandled.add(first.cid);
|
138
|
-
this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
|
139
|
-
} catch (err) {
|
140
|
-
if (first.retries++ > 3) {
|
141
|
-
this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
|
142
|
-
this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
|
143
|
-
}
|
144
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
145
|
-
throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
|
146
|
-
} finally {
|
147
|
-
this.isProcessing = false;
|
148
|
-
if (this.queue.length > 0) {
|
149
|
-
void this.processQueue();
|
150
|
-
}
|
151
|
-
}
|
152
|
-
}
|
153
|
-
};
|
180
|
+
// src/blockstore/store-factory.ts
|
181
|
+
import { KeyedResolvOnce as KeyedResolvOnce2, URI as URI5 } from "@adviser/cement";
|
154
182
|
|
155
|
-
// src/
|
156
|
-
var
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
183
|
+
// src/runtime/files.ts
|
184
|
+
var files_exports = {};
|
185
|
+
__export(files_exports, {
|
186
|
+
decodeFile: () => decodeFile,
|
187
|
+
encodeFile: () => encodeFile
|
188
|
+
});
|
189
|
+
import * as UnixFS from "@ipld/unixfs";
|
190
|
+
import * as raw from "multiformats/codecs/raw";
|
191
|
+
import { withMaxChunkSize } from "@ipld/unixfs/file/chunker/fixed";
|
192
|
+
import { withWidth } from "@ipld/unixfs/file/layout/balanced";
|
193
|
+
import { exporter } from "ipfs-unixfs-exporter";
|
194
|
+
var queuingStrategy = UnixFS.withCapacity();
|
195
|
+
var settings = UnixFS.configure({
|
196
|
+
fileChunkEncoder: raw,
|
197
|
+
smallFileEncoder: raw,
|
198
|
+
chunker: withMaxChunkSize(1024 * 1024),
|
199
|
+
fileLayout: withWidth(1024)
|
200
|
+
});
|
201
|
+
async function collect(collectable) {
|
202
|
+
const chunks = [];
|
203
|
+
await collectable.pipeTo(
|
204
|
+
new WritableStream({
|
205
|
+
write(chunk) {
|
206
|
+
chunks.push(chunk);
|
207
|
+
}
|
208
|
+
})
|
209
|
+
);
|
210
|
+
return chunks;
|
211
|
+
}
|
212
|
+
async function encodeFile(blob) {
|
213
|
+
const readable = createFileEncoderStream(blob);
|
214
|
+
const blocks = await collect(readable);
|
215
|
+
return { cid: blocks.at(-1).cid, blocks };
|
216
|
+
}
|
217
|
+
async function decodeFile(blocks, cid, meta) {
|
218
|
+
const entry = await exporter(cid.toString(), blocks, { length: meta.size });
|
219
|
+
const chunks = [];
|
220
|
+
for await (const chunk of entry.content()) {
|
221
|
+
chunks.push(chunk);
|
222
|
+
}
|
223
|
+
return new File(chunks, entry.name, { type: meta.type, lastModified: 0 });
|
224
|
+
}
|
225
|
+
function createFileEncoderStream(blob) {
|
226
|
+
const { readable, writable } = new TransformStream({}, queuingStrategy);
|
227
|
+
const unixfsWriter = UnixFS.createWriter({ writable, settings });
|
228
|
+
const fileBuilder = new UnixFSFileBuilder("", blob);
|
229
|
+
void (async () => {
|
230
|
+
await fileBuilder.finalize(unixfsWriter);
|
231
|
+
await unixfsWriter.close();
|
232
|
+
})();
|
233
|
+
return readable;
|
234
|
+
}
|
235
|
+
var UnixFSFileBuilder = class {
|
236
|
+
#file;
|
237
|
+
constructor(name, file) {
|
238
|
+
this.name = name;
|
239
|
+
this.#file = file;
|
240
|
+
}
|
241
|
+
async finalize(writer) {
|
242
|
+
const unixfsFileWriter = UnixFS.createFileWriter(writer);
|
243
|
+
await this.#file.stream().pipeTo(
|
244
|
+
new WritableStream({
|
245
|
+
async write(chunk) {
|
246
|
+
await unixfsFileWriter.write(chunk);
|
247
|
+
}
|
248
|
+
})
|
196
249
|
);
|
197
|
-
await
|
198
|
-
return event;
|
199
|
-
}
|
200
|
-
async decodeEventBlock(bytes) {
|
201
|
-
const event = await decodeEventBlock(bytes);
|
202
|
-
return event;
|
250
|
+
return await unixfsFileWriter.close();
|
203
251
|
}
|
204
|
-
// move this stuff to connect
|
205
|
-
// async getDashboardURL(compact = true) {
|
206
|
-
// const baseUrl = 'https://dashboard.fireproof.storage/'
|
207
|
-
// if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
|
208
|
-
// // if (compact) {
|
209
|
-
// // await this.compact()
|
210
|
-
// // }
|
211
|
-
// const currents = await this.loader?.metaStore?.load()
|
212
|
-
// if (!currents) throw new Error("Can't sync empty database: save data first")
|
213
|
-
// if (currents.length > 1)
|
214
|
-
// throw new Error("Can't sync database with split heads: make an update first")
|
215
|
-
// const current = currents[0]
|
216
|
-
// const params = {
|
217
|
-
// car: current.car.toString()
|
218
|
-
// }
|
219
|
-
// if (current.key) {
|
220
|
-
// // @ts-ignore
|
221
|
-
// params.key = current.key.toString()
|
222
|
-
// }
|
223
|
-
// // @ts-ignore
|
224
|
-
// if (this.name) {
|
225
|
-
// // @ts-ignore
|
226
|
-
// params.name = this.name
|
227
|
-
// }
|
228
|
-
// const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
|
229
|
-
// console.log('Import to dashboard: ' + url.toString())
|
230
|
-
// return url
|
231
|
-
// }
|
232
|
-
// openDashboard() {
|
233
|
-
// void this.getDashboardURL().then(url => {
|
234
|
-
// if (url) window.open(url.toString(), '_blank')
|
235
|
-
// })
|
236
|
-
// }
|
237
252
|
};
|
238
253
|
|
239
|
-
// src/blockstore/
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
}
|
252
|
-
}
|
253
|
-
async dataDownload(params) {
|
254
|
-
const { car } = params;
|
255
|
-
const fetchFromUrl = new URL(`/cars/${car.toString()}.car`, this.baseUrl);
|
256
|
-
const response = await fetch(fetchFromUrl);
|
257
|
-
if (!response.ok) {
|
258
|
-
return void 0;
|
259
|
-
}
|
260
|
-
const bytes = new Uint8Array(await response.arrayBuffer());
|
261
|
-
return bytes;
|
254
|
+
// src/blockstore/store.ts
|
255
|
+
import pLimit2 from "p-limit";
|
256
|
+
import { format as format2, parse as parse2 } from "@ipld/dag-json";
|
257
|
+
import { exception2Result, ResolveOnce as ResolveOnce3, Result as Result5 } from "@adviser/cement";
|
258
|
+
|
259
|
+
// src/types.ts
|
260
|
+
function isFalsy(value) {
|
261
|
+
return value === false && value === null && value === void 0;
|
262
|
+
}
|
263
|
+
function throwFalsy(value) {
|
264
|
+
if (isFalsy(value)) {
|
265
|
+
throw new Error("value is Falsy");
|
262
266
|
}
|
263
|
-
|
264
|
-
|
267
|
+
return value;
|
268
|
+
}
|
269
|
+
function falsyToUndef(value) {
|
270
|
+
if (isFalsy(value)) {
|
265
271
|
return void 0;
|
266
272
|
}
|
267
|
-
|
268
|
-
|
269
|
-
return [];
|
270
|
-
}
|
271
|
-
};
|
272
|
-
|
273
|
-
// src/blockstore/store-factory.ts
|
274
|
-
import { KeyedResolvOnce } from "@adviser/cement";
|
275
|
-
|
276
|
-
// src/blockstore/store.ts
|
277
|
-
import pLimit2 from "p-limit";
|
278
|
-
import { format, parse } from "@ipld/dag-json";
|
279
|
-
import { ResolveOnce as ResolveOnce2, Result as Result2 } from "@adviser/cement";
|
273
|
+
return value;
|
274
|
+
}
|
280
275
|
|
281
276
|
// src/blockstore/loader.ts
|
282
277
|
import pLimit from "p-limit";
|
283
278
|
import { CarReader } from "@ipld/car";
|
284
|
-
import { ResolveOnce } from "@adviser/cement";
|
285
|
-
|
286
|
-
// src/blockstore/types.ts
|
287
|
-
function toCIDBlock(block) {
|
288
|
-
return block;
|
289
|
-
}
|
279
|
+
import { ResolveOnce as ResolveOnce2 } from "@adviser/cement";
|
290
280
|
|
291
281
|
// src/blockstore/loader-helpers.ts
|
292
|
-
import { encode, decode } from "multiformats/block";
|
293
282
|
import { sha256 as hasher } from "multiformats/hashes/sha2";
|
294
|
-
import * as
|
295
|
-
import * as CBW from "@ipld/car/buffer-writer";
|
296
|
-
import * as codec from "@ipld/dag-cbor";
|
297
|
-
async function encodeCarFile(roots, t) {
|
298
|
-
let size = 0;
|
299
|
-
const headerSize = CBW.headerLength({ roots });
|
300
|
-
size += headerSize;
|
301
|
-
for (const { cid, bytes } of t.entries()) {
|
302
|
-
size += CBW.blockLength({ cid, bytes });
|
303
|
-
}
|
304
|
-
const buffer = new Uint8Array(size);
|
305
|
-
const writer = CBW.createWriter(buffer, { headerSize });
|
306
|
-
for (const r of roots) {
|
307
|
-
writer.addRoot(r);
|
308
|
-
}
|
309
|
-
for (const { cid, bytes } of t.entries()) {
|
310
|
-
writer.write({ cid, bytes });
|
311
|
-
}
|
312
|
-
writer.close();
|
313
|
-
return await encode({ value: writer.bytes, hasher, codec: raw });
|
314
|
-
}
|
315
|
-
async function encodeCarHeader(fp) {
|
316
|
-
return await encode({
|
317
|
-
value: { fp },
|
318
|
-
hasher,
|
319
|
-
codec
|
320
|
-
});
|
321
|
-
}
|
283
|
+
import * as dagCodec from "@ipld/dag-cbor";
|
322
284
|
async function parseCarFile(reader, logger) {
|
323
285
|
const roots = await reader.getRoots();
|
324
286
|
const header = await reader.get(roots[0]);
|
325
287
|
if (!header) throw logger.Error().Msg("missing header block").AsError();
|
326
|
-
const
|
327
|
-
const fpvalue = value;
|
288
|
+
const dec = await decode({ bytes: header.bytes, hasher, codec: dagCodec });
|
289
|
+
const fpvalue = dec.value;
|
328
290
|
if (fpvalue && !fpvalue.fp) {
|
329
291
|
throw logger.Error().Msg("missing fp").AsError();
|
330
292
|
}
|
331
293
|
return fpvalue.fp;
|
332
294
|
}
|
333
295
|
|
334
|
-
// src/blockstore/encrypt-helpers.ts
|
335
|
-
import { sha256 } from "multiformats/hashes/sha2";
|
336
|
-
import { CID as CID2 } from "multiformats";
|
337
|
-
import { encode as encode2, decode as decode2, create as mfCreate } from "multiformats/block";
|
338
|
-
import * as dagcbor from "@ipld/dag-cbor";
|
339
|
-
import { MemoryBlockstore as MemoryBlockstore2 } from "@web3-storage/pail/block";
|
340
|
-
import { bf } from "prolly-trees/utils";
|
341
|
-
import { nocache as cache } from "prolly-trees/cache";
|
342
|
-
import { create, load } from "prolly-trees/cid-set";
|
343
|
-
|
344
|
-
// src/blockstore/encrypt-codec.ts
|
345
|
-
import { CID } from "multiformats";
|
346
|
-
function makeCodec(ilogger, crypto, randomBytes) {
|
347
|
-
const logger = ensureLogger(ilogger, "makeCodec");
|
348
|
-
const enc32 = (value) => {
|
349
|
-
value = +value;
|
350
|
-
const buff = new Uint8Array(4);
|
351
|
-
buff[3] = value >>> 24;
|
352
|
-
buff[2] = value >>> 16;
|
353
|
-
buff[1] = value >>> 8;
|
354
|
-
buff[0] = value & 255;
|
355
|
-
return buff;
|
356
|
-
};
|
357
|
-
const readUInt32LE = (buffer) => {
|
358
|
-
const offset = buffer.byteLength - 4;
|
359
|
-
return (buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16) + buffer[offset + 3] * 16777216;
|
360
|
-
};
|
361
|
-
const concat = (buffers) => {
|
362
|
-
const uint8Arrays = buffers.map((b) => b instanceof ArrayBuffer ? new Uint8Array(b) : b);
|
363
|
-
const totalLength = uint8Arrays.reduce((sum, arr) => sum + arr.length, 0);
|
364
|
-
const result = new Uint8Array(totalLength);
|
365
|
-
let offset = 0;
|
366
|
-
for (const arr of uint8Arrays) {
|
367
|
-
result.set(arr, offset);
|
368
|
-
offset += arr.length;
|
369
|
-
}
|
370
|
-
return result;
|
371
|
-
};
|
372
|
-
const encode4 = ({ iv, bytes }) => concat([iv, bytes]);
|
373
|
-
const decode4 = (bytes) => {
|
374
|
-
const iv = bytes.subarray(0, 12);
|
375
|
-
bytes = bytes.slice(12);
|
376
|
-
return { iv, bytes };
|
377
|
-
};
|
378
|
-
const code = 3145728 + 1337;
|
379
|
-
async function subtleKey(key) {
|
380
|
-
return await crypto.importKey(
|
381
|
-
"raw",
|
382
|
-
// raw or jwk
|
383
|
-
key,
|
384
|
-
// raw data
|
385
|
-
"AES-GCM",
|
386
|
-
false,
|
387
|
-
// extractable
|
388
|
-
["encrypt", "decrypt"]
|
389
|
-
);
|
390
|
-
}
|
391
|
-
const decrypt = async ({ key, value }) => {
|
392
|
-
const { bytes: inBytes, iv } = value;
|
393
|
-
const cryKey = await subtleKey(key);
|
394
|
-
const deBytes = await crypto.decrypt(
|
395
|
-
{
|
396
|
-
name: "AES-GCM",
|
397
|
-
iv,
|
398
|
-
tagLength: 128
|
399
|
-
},
|
400
|
-
cryKey,
|
401
|
-
inBytes
|
402
|
-
);
|
403
|
-
const bytes = new Uint8Array(deBytes);
|
404
|
-
const len = readUInt32LE(bytes.subarray(0, 4));
|
405
|
-
const cid = CID.decode(bytes.subarray(4, 4 + len));
|
406
|
-
return { cid, bytes: bytes.subarray(4 + len) };
|
407
|
-
};
|
408
|
-
const encrypt = async ({ key, cid, bytes }) => {
|
409
|
-
const len = enc32(cid.bytes.byteLength);
|
410
|
-
const iv = randomBytes(12);
|
411
|
-
const msg = concat([len, cid.bytes, bytes]);
|
412
|
-
try {
|
413
|
-
const cryKey = await subtleKey(key);
|
414
|
-
const deBytes = await crypto.encrypt(
|
415
|
-
{
|
416
|
-
name: "AES-GCM",
|
417
|
-
iv,
|
418
|
-
tagLength: 128
|
419
|
-
},
|
420
|
-
cryKey,
|
421
|
-
msg
|
422
|
-
);
|
423
|
-
bytes = new Uint8Array(deBytes);
|
424
|
-
} catch (e) {
|
425
|
-
throw logger.Error().Err(e).Msg("encrypt failed").AsError();
|
426
|
-
}
|
427
|
-
return { value: { bytes, iv } };
|
428
|
-
};
|
429
|
-
const cryptoFn = (key) => {
|
430
|
-
return { encrypt: (opts) => encrypt({ ...opts, key }), decrypt: (opts) => decrypt({ ...opts, key }) };
|
431
|
-
};
|
432
|
-
const name = "jchris@encrypted-block:aes-gcm";
|
433
|
-
return { encode: encode4, decode: decode4, code, name, encrypt, decrypt, crypto: cryptoFn };
|
434
|
-
}
|
435
|
-
|
436
|
-
// src/blockstore/encrypt-helpers.ts
|
437
|
-
function carLogIncludesGroup(list, cidMatch) {
|
438
|
-
return list.some((cid) => {
|
439
|
-
return cid.toString() === cidMatch.toString();
|
440
|
-
});
|
441
|
-
}
|
442
|
-
function makeEncDec(logger, crypto, randomBytes) {
|
443
|
-
const codec4 = makeCodec(logger, crypto, randomBytes);
|
444
|
-
const encrypt = async function* ({
|
445
|
-
get: get2,
|
446
|
-
cids,
|
447
|
-
hasher: hasher4,
|
448
|
-
key,
|
449
|
-
cache: cache3,
|
450
|
-
chunker: chunker2,
|
451
|
-
root: root3
|
452
|
-
}) {
|
453
|
-
const set = /* @__PURE__ */ new Set();
|
454
|
-
let eroot;
|
455
|
-
if (!carLogIncludesGroup(cids, root3)) cids.push(root3);
|
456
|
-
for (const cid of cids) {
|
457
|
-
const unencrypted = await get2(cid);
|
458
|
-
if (!unencrypted) throw logger.Error().Ref("cid", cid).Msg("missing cid block").AsError();
|
459
|
-
const encrypted = await codec4.encrypt({ ...unencrypted, key });
|
460
|
-
const block2 = await encode2({ ...encrypted, codec: codec4, hasher: hasher4 });
|
461
|
-
yield block2;
|
462
|
-
set.add(block2.cid.toString());
|
463
|
-
if (unencrypted.cid.equals(root3)) eroot = block2.cid;
|
464
|
-
}
|
465
|
-
if (!eroot) throw logger.Error().Msg("cids does not include root").AsError();
|
466
|
-
const list = [...set].map((s) => CID2.parse(s));
|
467
|
-
let last;
|
468
|
-
for await (const node of create({ list, get: get2, cache: cache3, chunker: chunker2, hasher: hasher4, codec: dagcbor })) {
|
469
|
-
const block2 = await node.block;
|
470
|
-
yield block2;
|
471
|
-
last = block2;
|
472
|
-
}
|
473
|
-
if (!last) throw logger.Error().Msg("missing last block").AsError();
|
474
|
-
const head = [eroot, last.cid];
|
475
|
-
const block = await encode2({ value: head, codec: dagcbor, hasher: hasher4 });
|
476
|
-
yield block;
|
477
|
-
};
|
478
|
-
const decrypt = async function* ({
|
479
|
-
root: root3,
|
480
|
-
get: get2,
|
481
|
-
key,
|
482
|
-
cache: cache3,
|
483
|
-
chunker: chunker2,
|
484
|
-
hasher: hasher4
|
485
|
-
}) {
|
486
|
-
const getWithDecode = async (cid) => get2(cid).then(async (block) => {
|
487
|
-
if (!block) return;
|
488
|
-
const decoded = await decode2({ ...block, codec: dagcbor, hasher: hasher4 });
|
489
|
-
return decoded;
|
490
|
-
});
|
491
|
-
const getWithDecrypt = async (cid) => get2(cid).then(async (block) => {
|
492
|
-
if (!block) return;
|
493
|
-
const decoded = await decode2({ ...block, codec: codec4, hasher: hasher4 });
|
494
|
-
return decoded;
|
495
|
-
});
|
496
|
-
const decodedRoot = await getWithDecode(root3);
|
497
|
-
if (!decodedRoot) throw logger.Error().Msg("missing root").AsError();
|
498
|
-
if (!decodedRoot.bytes) throw logger.Error().Msg("missing bytes").AsError();
|
499
|
-
const {
|
500
|
-
value: [eroot, tree]
|
501
|
-
} = decodedRoot;
|
502
|
-
const rootBlock = await get2(eroot);
|
503
|
-
if (!rootBlock) throw logger.Error().Msg("missing root block").AsError();
|
504
|
-
const cidset = await load({ cid: tree, get: getWithDecode, cache: cache3, chunker: chunker2, codec: codec4, hasher: hasher4 });
|
505
|
-
const { result: nodes } = await cidset.getAllEntries();
|
506
|
-
const unwrap = async (eblock) => {
|
507
|
-
if (!eblock) throw logger.Error().Msg("missing block").AsError();
|
508
|
-
if (!eblock.value) {
|
509
|
-
eblock = await decode2({ ...eblock, codec: codec4, hasher: hasher4 });
|
510
|
-
if (!eblock.value) throw logger.Error().Msg("missing value").AsError();
|
511
|
-
}
|
512
|
-
const { bytes, cid } = await codec4.decrypt({ ...eblock, key }).catch((e) => {
|
513
|
-
throw e;
|
514
|
-
});
|
515
|
-
const block = await mfCreate({ cid, bytes, hasher: hasher4, codec: codec4 });
|
516
|
-
return block;
|
517
|
-
};
|
518
|
-
const promises = [];
|
519
|
-
for (const { cid } of nodes) {
|
520
|
-
if (!rootBlock.cid.equals(cid)) promises.push(getWithDecrypt(cid).then(unwrap));
|
521
|
-
}
|
522
|
-
yield* promises;
|
523
|
-
yield unwrap(rootBlock);
|
524
|
-
};
|
525
|
-
return { encrypt, decrypt };
|
526
|
-
}
|
527
|
-
var chunker = bf(30);
|
528
|
-
function hexStringToUint8Array(hexString) {
|
529
|
-
const length = hexString.length;
|
530
|
-
const uint8Array = new Uint8Array(length / 2);
|
531
|
-
for (let i = 0; i < length; i += 2) {
|
532
|
-
uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
|
533
|
-
}
|
534
|
-
return uint8Array;
|
535
|
-
}
|
536
|
-
async function encryptedEncodeCarFile(logger, crypto, key, rootCid, t) {
|
537
|
-
const encryptionKey = hexStringToUint8Array(key);
|
538
|
-
const encryptedBlocks = new MemoryBlockstore2();
|
539
|
-
const cidsToEncrypt = [];
|
540
|
-
for (const { cid, bytes } of t.entries()) {
|
541
|
-
cidsToEncrypt.push(cid);
|
542
|
-
const g = await t.get(cid);
|
543
|
-
if (!g) throw logger.Error().Ref("cid", cid).Int("bytes", bytes.length).Msg("missing cid block").AsError();
|
544
|
-
}
|
545
|
-
let last = null;
|
546
|
-
const { encrypt } = makeEncDec(logger, crypto, crypto.randomBytes);
|
547
|
-
for await (const block of encrypt({
|
548
|
-
cids: cidsToEncrypt,
|
549
|
-
get: t.get.bind(t),
|
550
|
-
key: encryptionKey,
|
551
|
-
hasher: sha256,
|
552
|
-
chunker,
|
553
|
-
cache,
|
554
|
-
root: rootCid
|
555
|
-
})) {
|
556
|
-
await encryptedBlocks.put(block.cid, block.bytes);
|
557
|
-
last = block;
|
558
|
-
}
|
559
|
-
if (!last) throw logger.Error().Msg("no blocks encrypted").AsError();
|
560
|
-
const encryptedCar = await encodeCarFile([last.cid], encryptedBlocks);
|
561
|
-
return encryptedCar;
|
562
|
-
}
|
563
|
-
async function decodeEncryptedCar(logger, crypto, key, reader) {
|
564
|
-
const roots = await reader.getRoots();
|
565
|
-
const root3 = roots[0];
|
566
|
-
return await decodeCarBlocks(logger, crypto, root3, reader.get.bind(reader), key);
|
567
|
-
}
|
568
|
-
async function decodeCarBlocks(logger, crypto, root3, get2, keyMaterial) {
|
569
|
-
const decryptionKeyUint8 = hexStringToUint8Array(keyMaterial);
|
570
|
-
const decryptionKey = decryptionKeyUint8.buffer.slice(0, decryptionKeyUint8.byteLength);
|
571
|
-
const decryptedBlocks = new MemoryBlockstore2();
|
572
|
-
let last = null;
|
573
|
-
const { decrypt } = makeEncDec(logger, crypto, crypto.randomBytes);
|
574
|
-
for await (const block of decrypt({
|
575
|
-
root: root3,
|
576
|
-
get: get2,
|
577
|
-
key: decryptionKey,
|
578
|
-
hasher: sha256,
|
579
|
-
chunker,
|
580
|
-
cache
|
581
|
-
})) {
|
582
|
-
await decryptedBlocks.put(block.cid, block.bytes);
|
583
|
-
last = block;
|
584
|
-
}
|
585
|
-
if (!last) throw logger.Error().Msg("no blocks decrypted").AsError();
|
586
|
-
return { blocks: decryptedBlocks, root: last.cid };
|
587
|
-
}
|
588
|
-
|
589
296
|
// src/blockstore/transaction.ts
|
590
|
-
import { MemoryBlockstore
|
591
|
-
|
592
|
-
|
297
|
+
import { MemoryBlockstore } from "@web3-storage/pail/block";
|
298
|
+
import { toCryptoRuntime } from "@adviser/cement";
|
299
|
+
var CarTransaction = class extends MemoryBlockstore {
|
300
|
+
constructor(parent, opts = { add: true, noLoader: false }) {
|
593
301
|
super();
|
594
302
|
if (opts.add) {
|
595
303
|
parent.transactions.add(this);
|
@@ -603,8 +311,8 @@ var CarTransaction = class extends MemoryBlockstore3 {
|
|
603
311
|
return super.get(cid);
|
604
312
|
}
|
605
313
|
};
|
606
|
-
function defaultedBlockstoreRuntime(opts, component, ctx) {
|
607
|
-
const logger = ensureLogger(
|
314
|
+
function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
|
315
|
+
const logger = ensureLogger(sthis, component, ctx);
|
608
316
|
const store = opts.store || {};
|
609
317
|
return {
|
610
318
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
@@ -621,22 +329,24 @@ function defaultedBlockstoreRuntime(opts, component, ctx) {
|
|
621
329
|
threshold: 1e3 * 1e3,
|
622
330
|
...opts,
|
623
331
|
logger,
|
624
|
-
|
332
|
+
keyBag: opts.keyBag || {},
|
333
|
+
crypto: toCryptoRuntime(opts.crypto),
|
625
334
|
store,
|
626
|
-
storeRuntime: toStoreRuntime(store,
|
335
|
+
storeRuntime: toStoreRuntime(store, sthis)
|
627
336
|
};
|
628
337
|
}
|
629
|
-
|
338
|
+
function blockstoreFactory(sthis, opts) {
|
630
339
|
if (opts.name) {
|
631
|
-
return new EncryptedBlockstore(opts);
|
340
|
+
return new EncryptedBlockstore(sthis, opts);
|
632
341
|
} else {
|
633
342
|
return new BaseBlockstore(opts);
|
634
343
|
}
|
635
|
-
}
|
344
|
+
}
|
636
345
|
var BaseBlockstore = class {
|
637
346
|
constructor(ebOpts = {}) {
|
638
347
|
this.transactions = /* @__PURE__ */ new Set();
|
639
|
-
this.
|
348
|
+
this.sthis = ensureSuperThis(ebOpts);
|
349
|
+
this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
|
640
350
|
this.logger = this.ebOpts.logger;
|
641
351
|
}
|
642
352
|
// ready: Promise<void>;
|
@@ -659,8 +369,8 @@ var BaseBlockstore = class {
|
|
659
369
|
throw this.logger.Error().Msg("use a transaction to put").AsError();
|
660
370
|
}
|
661
371
|
// TransactionMeta
|
662
|
-
async transaction(fn, _opts
|
663
|
-
const t = new CarTransaction(this);
|
372
|
+
async transaction(fn, _opts) {
|
373
|
+
const t = new CarTransaction(this, _opts);
|
664
374
|
const done = await fn(t);
|
665
375
|
this.lastTxMeta = done;
|
666
376
|
return { t, meta: done };
|
@@ -677,16 +387,16 @@ var BaseBlockstore = class {
|
|
677
387
|
}
|
678
388
|
};
|
679
389
|
var EncryptedBlockstore = class extends BaseBlockstore {
|
680
|
-
constructor(ebOpts) {
|
390
|
+
constructor(sthis, ebOpts) {
|
681
391
|
super(ebOpts);
|
682
392
|
this.compacting = false;
|
683
|
-
this.logger = ensureLogger(
|
393
|
+
this.logger = ensureLogger(this.sthis, "EncryptedBlockstore");
|
684
394
|
const { name } = ebOpts;
|
685
395
|
if (!name) {
|
686
396
|
throw this.logger.Error().Msg("name required").AsError();
|
687
397
|
}
|
688
398
|
this.name = name;
|
689
|
-
this.loader = new Loader(this.name, ebOpts);
|
399
|
+
this.loader = new Loader(this.name, ebOpts, sthis);
|
690
400
|
}
|
691
401
|
ready() {
|
692
402
|
return this.loader.ready();
|
@@ -717,10 +427,13 @@ var EncryptedBlockstore = class extends BaseBlockstore {
|
|
717
427
|
}
|
718
428
|
throw this.logger.Error().Msg("failed to commit car files").AsError();
|
719
429
|
}
|
720
|
-
async getFile(car, cid
|
430
|
+
async getFile(car, cid) {
|
721
431
|
await this.ready();
|
722
432
|
if (!this.loader) throw this.logger.Error().Msg("loader required to get file, database must be named").AsError();
|
723
|
-
const reader = await this.loader.loadFileCar(
|
433
|
+
const reader = await this.loader.loadFileCar(
|
434
|
+
car
|
435
|
+
/*, isPublic */
|
436
|
+
);
|
724
437
|
const block = await reader.get(cid);
|
725
438
|
if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
|
726
439
|
return block.bytes;
|
@@ -776,10 +489,20 @@ var CompactionFetcher = class {
|
|
776
489
|
};
|
777
490
|
|
778
491
|
// src/blockstore/commit-queue.ts
|
492
|
+
import { Future } from "@adviser/cement";
|
779
493
|
var CommitQueue = class {
|
780
494
|
constructor() {
|
781
495
|
this.queue = [];
|
782
496
|
this.processing = false;
|
497
|
+
this._waitIdleItems = /* @__PURE__ */ new Set();
|
498
|
+
}
|
499
|
+
waitIdle() {
|
500
|
+
if (this.queue.length === 0 && !this.processing) {
|
501
|
+
return Promise.resolve();
|
502
|
+
}
|
503
|
+
const fn = new Future();
|
504
|
+
this._waitIdleItems.add(fn);
|
505
|
+
return fn.asPromise();
|
783
506
|
}
|
784
507
|
async enqueue(fn) {
|
785
508
|
return new Promise((resolve, reject) => {
|
@@ -804,15 +527,370 @@ var CommitQueue = class {
|
|
804
527
|
this.processing = true;
|
805
528
|
const queueFn = this.queue.shift();
|
806
529
|
if (queueFn) {
|
807
|
-
queueFn()
|
530
|
+
queueFn().finally(() => {
|
531
|
+
});
|
532
|
+
}
|
533
|
+
}
|
534
|
+
if (this.queue.length === 0 && !this.processing) {
|
535
|
+
const toResolve = Array.from(this._waitIdleItems);
|
536
|
+
this._waitIdleItems.clear();
|
537
|
+
toResolve.map((fn) => fn.resolve());
|
538
|
+
}
|
539
|
+
}
|
540
|
+
};
|
541
|
+
|
542
|
+
// src/runtime/key-bag.ts
|
543
|
+
var key_bag_exports = {};
|
544
|
+
__export(key_bag_exports, {
|
545
|
+
KeyBag: () => KeyBag,
|
546
|
+
getKeyBag: () => getKeyBag,
|
547
|
+
registerKeyBagProviderFactory: () => registerKeyBagProviderFactory
|
548
|
+
});
|
549
|
+
import {
|
550
|
+
KeyedResolvOnce,
|
551
|
+
ResolveOnce,
|
552
|
+
ResolveSeq,
|
553
|
+
Result as Result2,
|
554
|
+
runtimeFn,
|
555
|
+
toCryptoRuntime as toCryptoRuntime2,
|
556
|
+
URI
|
557
|
+
} from "@adviser/cement";
|
558
|
+
import { base58btc } from "multiformats/bases/base58";
|
559
|
+
var KeyBag = class {
|
560
|
+
constructor(rt) {
|
561
|
+
this.rt = rt;
|
562
|
+
this._warnOnce = new ResolveOnce();
|
563
|
+
this._seq = new ResolveSeq();
|
564
|
+
this.logger = ensureLogger(rt.sthis, "KeyBag");
|
565
|
+
this.logger.Debug().Msg("KeyBag created");
|
566
|
+
}
|
567
|
+
async subtleKey(key) {
|
568
|
+
const extractable = this.rt.url.getParam("extractKey") === "_deprecated_internal_api";
|
569
|
+
if (extractable) {
|
570
|
+
this._warnOnce.once(
|
571
|
+
() => this.logger.Warn().Msg("extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!")
|
572
|
+
);
|
573
|
+
}
|
574
|
+
return await this.rt.crypto.importKey(
|
575
|
+
"raw",
|
576
|
+
// raw or jwk
|
577
|
+
base58btc.decode(key),
|
578
|
+
// hexStringToUint8Array(key), // raw data
|
579
|
+
"AES-GCM",
|
580
|
+
extractable,
|
581
|
+
["encrypt", "decrypt"]
|
582
|
+
);
|
583
|
+
}
|
584
|
+
async ensureKeyFromUrl(url, keyFactory) {
|
585
|
+
const storeKey = url.getParam("storekey");
|
586
|
+
if (storeKey === "insecure") {
|
587
|
+
return Result2.Ok(url);
|
588
|
+
}
|
589
|
+
if (!storeKey) {
|
590
|
+
const keyName = `@${keyFactory()}@`;
|
591
|
+
const ret = await this.getNamedKey(keyName);
|
592
|
+
if (ret.isErr()) {
|
593
|
+
return ret;
|
594
|
+
}
|
595
|
+
const urb = url.build().setParam("storekey", keyName);
|
596
|
+
return Result2.Ok(urb.URI());
|
597
|
+
}
|
598
|
+
if (storeKey.startsWith("@") && storeKey.endsWith("@")) {
|
599
|
+
const ret = await this.getNamedKey(storeKey);
|
600
|
+
if (ret.isErr()) {
|
601
|
+
return ret;
|
602
|
+
}
|
603
|
+
}
|
604
|
+
return Result2.Ok(url);
|
605
|
+
}
|
606
|
+
async toKeyWithFingerPrint(keyStr) {
|
607
|
+
const material = base58btc.decode(keyStr);
|
608
|
+
const key = await this.subtleKey(keyStr);
|
609
|
+
const fpr = await this.rt.crypto.digestSHA256(material);
|
610
|
+
return Result2.Ok({
|
611
|
+
key,
|
612
|
+
fingerPrint: base58btc.encode(new Uint8Array(fpr))
|
613
|
+
});
|
614
|
+
}
|
615
|
+
async setNamedKey(name, key) {
|
616
|
+
return this._seq.add(() => this._setNamedKey(name, key));
|
617
|
+
}
|
618
|
+
// avoid deadlock
|
619
|
+
async _setNamedKey(name, key) {
|
620
|
+
const item = {
|
621
|
+
name,
|
622
|
+
key
|
623
|
+
};
|
624
|
+
const bag = await this.rt.getBag();
|
625
|
+
this.logger.Debug().Str("name", name).Msg("setNamedKey");
|
626
|
+
await bag.set(name, item);
|
627
|
+
return await this.toKeyWithFingerPrint(item.key);
|
628
|
+
}
|
629
|
+
async getNamedExtractableKey(name, failIfNotFound = false) {
|
630
|
+
const ret = await this.getNamedKey(name, failIfNotFound);
|
631
|
+
if (ret.isErr()) {
|
632
|
+
return ret;
|
633
|
+
}
|
634
|
+
const named = ret.Ok();
|
635
|
+
return Result2.Ok({
|
636
|
+
...named,
|
637
|
+
extract: async () => {
|
638
|
+
const ext = new Uint8Array(await this.rt.crypto.exportKey("raw", named.key));
|
639
|
+
return {
|
640
|
+
key: ext,
|
641
|
+
keyStr: base58btc.encode(ext)
|
642
|
+
};
|
643
|
+
}
|
644
|
+
});
|
645
|
+
}
|
646
|
+
async getNamedKey(name, failIfNotFound = false) {
|
647
|
+
const id = this.rt.sthis.nextId(4).str;
|
648
|
+
return this._seq.add(async () => {
|
649
|
+
const bag = await this.rt.getBag();
|
650
|
+
const named = await bag.get(name);
|
651
|
+
if (named) {
|
652
|
+
const fpr = await this.toKeyWithFingerPrint(named.key);
|
653
|
+
this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", fpr).Msg("fingerPrint getNamedKey");
|
654
|
+
return fpr;
|
655
|
+
}
|
656
|
+
if (failIfNotFound) {
|
657
|
+
this.logger.Debug().Str("id", id).Str("name", name).Msg("failIfNotFound getNamedKey");
|
658
|
+
return Result2.Err(new Error(`Key not found: ${name}`));
|
659
|
+
}
|
660
|
+
const ret = await this._setNamedKey(name, base58btc.encode(this.rt.crypto.randomBytes(this.rt.keyLength)));
|
661
|
+
this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", ret).Msg("createKey getNamedKey-post");
|
662
|
+
return ret;
|
663
|
+
});
|
664
|
+
}
|
665
|
+
};
|
666
|
+
var keyBagProviderFactories = new Map(
|
667
|
+
[
|
668
|
+
{
|
669
|
+
protocol: "file:",
|
670
|
+
factory: async (url, sthis) => {
|
671
|
+
const { KeyBagProviderFile } = await import("./key-bag-file-WADZBHYG.js");
|
672
|
+
return new KeyBagProviderFile(url, sthis);
|
673
|
+
}
|
674
|
+
},
|
675
|
+
{
|
676
|
+
protocol: "indexdb:",
|
677
|
+
factory: async (url, sthis) => {
|
678
|
+
const { KeyBagProviderIndexDB } = await import("./key-bag-indexdb-PGVAI3FJ.js");
|
679
|
+
return new KeyBagProviderIndexDB(url, sthis);
|
680
|
+
}
|
681
|
+
}
|
682
|
+
].map((i) => [i.protocol, i])
|
683
|
+
);
|
684
|
+
function registerKeyBagProviderFactory(item) {
|
685
|
+
const protocol = item.protocol.endsWith(":") ? item.protocol : item.protocol + ":";
|
686
|
+
keyBagProviderFactories.set(protocol, {
|
687
|
+
...item,
|
688
|
+
protocol
|
689
|
+
});
|
690
|
+
}
|
691
|
+
function defaultKeyBagOpts(sthis, kbo) {
|
692
|
+
if (kbo.keyRuntime) {
|
693
|
+
return kbo.keyRuntime;
|
694
|
+
}
|
695
|
+
const logger = ensureLogger(sthis, "KeyBag");
|
696
|
+
let url;
|
697
|
+
if (kbo.url) {
|
698
|
+
url = URI.from(kbo.url);
|
699
|
+
logger.Debug().Url(url).Msg("from opts");
|
700
|
+
} else {
|
701
|
+
let bagFnameOrUrl = sthis.env.get("FP_KEYBAG_URL");
|
702
|
+
if (runtimeFn().isBrowser) {
|
703
|
+
url = URI.from(bagFnameOrUrl || "indexdb://fp-keybag");
|
704
|
+
} else {
|
705
|
+
if (!bagFnameOrUrl) {
|
706
|
+
const home = sthis.env.get("HOME");
|
707
|
+
bagFnameOrUrl = `${home}/.fireproof/keybag`;
|
708
|
+
url = URI.from(`file://${bagFnameOrUrl}`);
|
709
|
+
} else {
|
710
|
+
url = URI.from(bagFnameOrUrl);
|
711
|
+
}
|
712
|
+
}
|
713
|
+
logger.Debug().Url(url).Msg("from env");
|
714
|
+
}
|
715
|
+
const kitem = keyBagProviderFactories.get(url.protocol);
|
716
|
+
if (!kitem) {
|
717
|
+
throw logger.Error().Url(url).Msg("unsupported protocol").AsError();
|
718
|
+
}
|
719
|
+
const getBag = async () => kitem.factory(url, sthis);
|
720
|
+
if (url.hasParam("masterkey")) {
|
721
|
+
throw logger.Error().Url(url).Msg("masterkey is not supported").AsError();
|
722
|
+
}
|
723
|
+
return {
|
724
|
+
url,
|
725
|
+
crypto: kbo.crypto || toCryptoRuntime2({}),
|
726
|
+
sthis,
|
727
|
+
logger,
|
728
|
+
keyLength: kbo.keyLength || 16,
|
729
|
+
getBag,
|
730
|
+
id: () => {
|
731
|
+
return url.toString();
|
732
|
+
}
|
733
|
+
};
|
734
|
+
}
|
735
|
+
var _keyBags = new KeyedResolvOnce();
|
736
|
+
async function getKeyBag(sthis, kbo = {}) {
|
737
|
+
await sthis.start();
|
738
|
+
const rt = defaultKeyBagOpts(sthis, kbo);
|
739
|
+
return _keyBags.get(rt.id()).once(async () => new KeyBag(rt));
|
740
|
+
}
|
741
|
+
|
742
|
+
// src/blockstore/commitor.ts
|
743
|
+
import * as CBW from "@ipld/car/buffer-writer";
|
744
|
+
import { sha256 as hasher2 } from "multiformats/hashes/sha2";
|
745
|
+
import * as dagCodec2 from "@ipld/dag-cbor";
|
746
|
+
async function encodeCarFile(roots, t, codec3) {
|
747
|
+
let size = 0;
|
748
|
+
const headerSize = CBW.headerLength({ roots });
|
749
|
+
size += headerSize;
|
750
|
+
for (const { cid, bytes } of t.entries()) {
|
751
|
+
size += CBW.blockLength({ cid, bytes });
|
752
|
+
}
|
753
|
+
const buffer = new Uint8Array(size);
|
754
|
+
const writer = CBW.createWriter(buffer, { headerSize });
|
755
|
+
for (const r of roots) {
|
756
|
+
writer.addRoot(r);
|
757
|
+
}
|
758
|
+
for (const { cid, bytes } of t.entries()) {
|
759
|
+
writer.write({ cid, bytes });
|
760
|
+
}
|
761
|
+
writer.close();
|
762
|
+
return await encode({ value: writer.bytes, hasher: hasher2, codec: codec3 });
|
763
|
+
}
|
764
|
+
async function createCarFile(encoder, cid, t) {
|
765
|
+
return encodeCarFile([cid], t, encoder);
|
766
|
+
}
|
767
|
+
async function commitFiles(fileStore, walStore, t, done) {
|
768
|
+
const { files: roots } = makeFileCarHeader(done);
|
769
|
+
const cids = [];
|
770
|
+
const codec3 = (await fileStore.keyedCrypto()).codec();
|
771
|
+
const cars = await prepareCarFilesFiles(codec3, roots, t);
|
772
|
+
for (const car of cars) {
|
773
|
+
const { cid, bytes } = car;
|
774
|
+
await fileStore.save({ cid, bytes });
|
775
|
+
await walStore.enqueueFile(
|
776
|
+
cid
|
777
|
+
/*, !!opts.public*/
|
778
|
+
);
|
779
|
+
cids.push(cid);
|
780
|
+
}
|
781
|
+
return cids;
|
782
|
+
}
|
783
|
+
function makeFileCarHeader(result) {
|
784
|
+
const files = [];
|
785
|
+
for (const [, meta] of Object.entries(result.files || {})) {
|
786
|
+
if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
|
787
|
+
files.push(meta.cid);
|
788
|
+
}
|
789
|
+
}
|
790
|
+
return { ...result, files };
|
791
|
+
}
|
792
|
+
async function prepareCarFilesFiles(encoder, roots, t) {
|
793
|
+
return [await encodeCarFile(roots, t, encoder)];
|
794
|
+
}
|
795
|
+
function makeCarHeader(meta, cars, compact = false) {
|
796
|
+
const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
|
797
|
+
return { ...coreHeader, meta };
|
798
|
+
}
|
799
|
+
async function encodeCarHeader(fp) {
|
800
|
+
return await encode({
|
801
|
+
value: { fp },
|
802
|
+
hasher: hasher2,
|
803
|
+
codec: dagCodec2
|
804
|
+
});
|
805
|
+
}
|
806
|
+
async function commit(params, t, done, opts = { noLoader: false, compact: false }) {
|
807
|
+
const fp = makeCarHeader(done, params.carLog, !!opts.compact);
|
808
|
+
const rootBlock = await encodeCarHeader(fp);
|
809
|
+
const cars = await prepareCarFiles(params.encoder, params.threshold, rootBlock, t);
|
810
|
+
const cids = [];
|
811
|
+
for (const car of cars) {
|
812
|
+
const { cid, bytes } = car;
|
813
|
+
await params.carStore.save({ cid, bytes });
|
814
|
+
cids.push(cid);
|
815
|
+
}
|
816
|
+
const newDbMeta = { cars: cids };
|
817
|
+
await params.WALStore.enqueue(newDbMeta, opts);
|
818
|
+
await params.metaStore.save(newDbMeta);
|
819
|
+
return { cgrp: cids, header: fp };
|
820
|
+
}
|
821
|
+
async function prepareCarFiles(encoder, threshold, rootBlock, t) {
|
822
|
+
const carFiles = [];
|
823
|
+
threshold = threshold || 128e3 * 8;
|
824
|
+
let clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
|
825
|
+
clonedt.putSync(rootBlock.cid, rootBlock.bytes);
|
826
|
+
let newsize = CBW.blockLength(toCIDBlock(rootBlock));
|
827
|
+
let cidRootBlock = rootBlock;
|
828
|
+
for (const { cid, bytes } of t.entries()) {
|
829
|
+
newsize += CBW.blockLength(toCIDBlock({ cid, bytes }));
|
830
|
+
if (newsize >= threshold) {
|
831
|
+
carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
|
832
|
+
clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
|
833
|
+
clonedt.putSync(cid, bytes);
|
834
|
+
cidRootBlock = { cid, bytes };
|
835
|
+
newsize = CBW.blockLength(toCIDBlock({ cid, bytes }));
|
836
|
+
} else {
|
837
|
+
clonedt.putSync(cid, bytes);
|
838
|
+
}
|
839
|
+
}
|
840
|
+
carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
|
841
|
+
return carFiles;
|
842
|
+
}
|
843
|
+
|
844
|
+
// src/blockstore/loader.ts
|
845
|
+
import { sha256 as hasher3 } from "multiformats/hashes/sha2";
|
846
|
+
|
847
|
+
// src/blockstore/task-manager.ts
|
848
|
+
var TaskManager = class {
|
849
|
+
constructor(sthis, callback) {
|
850
|
+
this.eventsWeHandled = /* @__PURE__ */ new Set();
|
851
|
+
this.queue = [];
|
852
|
+
this.isProcessing = false;
|
853
|
+
this.logger = ensureLogger(sthis, "TaskManager");
|
854
|
+
this.callback = callback;
|
855
|
+
}
|
856
|
+
async handleEvent(cid, parents, dbMeta) {
|
857
|
+
for (const parent of parents) {
|
858
|
+
this.eventsWeHandled.add(parent.toString());
|
859
|
+
}
|
860
|
+
this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
|
861
|
+
this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
|
862
|
+
void this.processQueue();
|
863
|
+
}
|
864
|
+
async processQueue() {
|
865
|
+
if (this.isProcessing) return;
|
866
|
+
this.isProcessing = true;
|
867
|
+
const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
|
868
|
+
const first = filteredQueue[0];
|
869
|
+
if (!first) {
|
870
|
+
return;
|
871
|
+
}
|
872
|
+
try {
|
873
|
+
await this.callback(first.dbMeta);
|
874
|
+
this.eventsWeHandled.add(first.cid);
|
875
|
+
this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
|
876
|
+
} catch (err) {
|
877
|
+
if (first.retries++ > 3) {
|
878
|
+
this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
|
879
|
+
this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
|
880
|
+
}
|
881
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
882
|
+
throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
|
883
|
+
} finally {
|
884
|
+
this.isProcessing = false;
|
885
|
+
if (this.queue.length > 0) {
|
886
|
+
void this.processQueue();
|
808
887
|
}
|
809
888
|
}
|
810
889
|
}
|
811
890
|
};
|
812
891
|
|
813
892
|
// src/blockstore/loader.ts
|
814
|
-
|
815
|
-
function carLogIncludesGroup2(list, cids) {
|
893
|
+
function carLogIncludesGroup(list, cids) {
|
816
894
|
return list.some((arr) => {
|
817
895
|
return arr.toString() === cids.toString();
|
818
896
|
});
|
@@ -825,17 +903,8 @@ function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
|
|
825
903
|
}
|
826
904
|
return [...byString.values()];
|
827
905
|
}
|
828
|
-
function toHexString(byteArray) {
|
829
|
-
return Array.from(byteArray).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
830
|
-
}
|
831
|
-
var Loadable = class {
|
832
|
-
constructor() {
|
833
|
-
this.name = "";
|
834
|
-
this.carLog = new Array();
|
835
|
-
}
|
836
|
-
};
|
837
906
|
var Loader = class {
|
838
|
-
constructor(name, ebOpts) {
|
907
|
+
constructor(name, ebOpts, sthis) {
|
839
908
|
this.commitQueue = new CommitQueue();
|
840
909
|
this.isCompacting = false;
|
841
910
|
this.carReaders = /* @__PURE__ */ new Map();
|
@@ -845,9 +914,11 @@ var Loader = class {
|
|
845
914
|
this.getBlockCache = /* @__PURE__ */ new Map();
|
846
915
|
this.seenMeta = /* @__PURE__ */ new Set();
|
847
916
|
this.writeLimit = pLimit(1);
|
848
|
-
this.onceReady = new
|
917
|
+
this.onceReady = new ResolveOnce2();
|
849
918
|
this.name = name;
|
919
|
+
this.sthis = sthis;
|
850
920
|
this.ebOpts = defaultedBlockstoreRuntime(
|
921
|
+
sthis,
|
851
922
|
{
|
852
923
|
...ebOpts,
|
853
924
|
name
|
@@ -855,34 +926,42 @@ var Loader = class {
|
|
855
926
|
"Loader"
|
856
927
|
);
|
857
928
|
this.logger = this.ebOpts.logger;
|
929
|
+
this.taskManager = new TaskManager(sthis, async (dbMeta) => {
|
930
|
+
await this.handleDbMetasFromStore([dbMeta]);
|
931
|
+
});
|
858
932
|
}
|
859
933
|
// readonly id = uuidv4();
|
934
|
+
async keyBag() {
|
935
|
+
return getKeyBag(this.sthis, this.ebOpts.keyBag);
|
936
|
+
}
|
860
937
|
async carStore() {
|
861
938
|
return this.ebOpts.storeRuntime.makeDataStore(this);
|
862
939
|
}
|
863
940
|
async fileStore() {
|
864
941
|
return this.ebOpts.storeRuntime.makeDataStore(this);
|
865
942
|
}
|
866
|
-
async
|
867
|
-
return this.ebOpts.storeRuntime.
|
943
|
+
async WALStore() {
|
944
|
+
return this.ebOpts.storeRuntime.makeWALStore(this);
|
868
945
|
}
|
869
946
|
async metaStore() {
|
870
947
|
return this.ebOpts.storeRuntime.makeMetaStore(this);
|
871
948
|
}
|
872
949
|
async ready() {
|
873
950
|
return this.onceReady.once(async () => {
|
874
|
-
const metas =
|
875
|
-
if (
|
951
|
+
const metas = await (await this.metaStore()).load();
|
952
|
+
if (this.ebOpts.meta) {
|
953
|
+
await this.handleDbMetasFromStore([this.ebOpts.meta]);
|
954
|
+
} else if (metas) {
|
876
955
|
await this.handleDbMetasFromStore(metas);
|
877
956
|
}
|
878
957
|
});
|
879
958
|
}
|
880
959
|
async close() {
|
881
|
-
const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.
|
960
|
+
const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
|
882
961
|
await Promise.all(toClose.map((store) => store.close()));
|
883
962
|
}
|
884
963
|
async destroy() {
|
885
|
-
const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.
|
964
|
+
const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
|
886
965
|
await Promise.all(toDestroy.map((store) => store.destroy()));
|
887
966
|
}
|
888
967
|
// async snapToCar(carCid: AnyLink | string) {
|
@@ -896,6 +975,7 @@ var Loader = class {
|
|
896
975
|
// await this._applyCarHeader(carHeader, true)
|
897
976
|
// }
|
898
977
|
async handleDbMetasFromStore(metas) {
|
978
|
+
this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
|
899
979
|
for (const meta of metas) {
|
900
980
|
await this.writeLimit(async () => {
|
901
981
|
await this.mergeDbMetaIntoClock(meta);
|
@@ -908,10 +988,7 @@ var Loader = class {
|
|
908
988
|
}
|
909
989
|
if (this.seenMeta.has(meta.cars.toString())) return;
|
910
990
|
this.seenMeta.add(meta.cars.toString());
|
911
|
-
if (meta.
|
912
|
-
await this.setKey(meta.key);
|
913
|
-
}
|
914
|
-
if (carLogIncludesGroup2(this.carLog, meta.cars)) {
|
991
|
+
if (carLogIncludesGroup(this.carLog, meta.cars)) {
|
915
992
|
return;
|
916
993
|
}
|
917
994
|
const carHeader = await this.loadCarHeaderFromMeta(meta);
|
@@ -920,45 +997,60 @@ var Loader = class {
|
|
920
997
|
this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
|
921
998
|
await this.ebOpts.applyMeta?.(carHeader.meta);
|
922
999
|
}
|
923
|
-
async ingestKeyFromMeta(meta) {
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
}
|
1000
|
+
// protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
|
1001
|
+
// const { key } = meta;
|
1002
|
+
// if (key) {
|
1003
|
+
// await this.setKey(key);
|
1004
|
+
// }
|
1005
|
+
// }
|
929
1006
|
async loadCarHeaderFromMeta({ cars: cids }) {
|
930
1007
|
const reader = await this.loadCar(cids[0]);
|
931
1008
|
return await parseCarFile(reader, this.logger);
|
932
1009
|
}
|
933
|
-
async _getKey() {
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
}
|
943
|
-
// can these skip the queue? or have a file queue?
|
944
|
-
async _commitInternalFiles(t, done, opts = { noLoader: false, compact: false }) {
|
1010
|
+
// async _getKey(): Promise<string | undefined> {
|
1011
|
+
// if (this.key) return this.key;
|
1012
|
+
// // generate a random key
|
1013
|
+
// if (!this.ebOpts.public) {
|
1014
|
+
// await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
|
1015
|
+
// }
|
1016
|
+
// return this.key || undefined;
|
1017
|
+
// }
|
1018
|
+
async commitFiles(t, done) {
|
945
1019
|
await this.ready();
|
946
|
-
const
|
947
|
-
const
|
948
|
-
|
949
|
-
for (const car of cars) {
|
950
|
-
const { cid, bytes } = car;
|
951
|
-
await (await this.fileStore()).save({ cid, bytes });
|
952
|
-
await (await this.remoteWAL()).enqueueFile(cid, !!opts.public);
|
953
|
-
cids.push(cid);
|
954
|
-
}
|
955
|
-
return cids;
|
1020
|
+
const fstore = await this.fileStore();
|
1021
|
+
const wstore = await this.WALStore();
|
1022
|
+
return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
|
956
1023
|
}
|
957
|
-
async loadFileCar(cid
|
958
|
-
return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore
|
1024
|
+
async loadFileCar(cid) {
|
1025
|
+
return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
|
959
1026
|
}
|
960
1027
|
async commit(t, done, opts = { noLoader: false, compact: false }) {
|
961
|
-
|
1028
|
+
await this.ready();
|
1029
|
+
const fstore = await this.fileStore();
|
1030
|
+
const params = {
|
1031
|
+
encoder: (await fstore.keyedCrypto()).codec(),
|
1032
|
+
carLog: this.carLog,
|
1033
|
+
carStore: fstore,
|
1034
|
+
WALStore: await this.WALStore(),
|
1035
|
+
metaStore: await this.metaStore(),
|
1036
|
+
threshold: this.ebOpts.threshold
|
1037
|
+
};
|
1038
|
+
return this.commitQueue.enqueue(async () => {
|
1039
|
+
await this.cacheTransaction(t);
|
1040
|
+
const ret = await commit(params, t, done, opts);
|
1041
|
+
await this.updateCarLog(ret.cgrp, ret.header, !!opts.compact);
|
1042
|
+
return ret.cgrp;
|
1043
|
+
});
|
1044
|
+
}
|
1045
|
+
async updateCarLog(cids, fp, compact) {
|
1046
|
+
if (compact) {
|
1047
|
+
const previousCompactCid = fp.compact[fp.compact.length - 1];
|
1048
|
+
fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
|
1049
|
+
this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
|
1050
|
+
await this.removeCidsForCompact(previousCompactCid[0]).catch((e) => e);
|
1051
|
+
} else {
|
1052
|
+
this.carLog.unshift(cids);
|
1053
|
+
}
|
962
1054
|
}
|
963
1055
|
async cacheTransaction(t) {
|
964
1056
|
for await (const block of t.entries()) {
|
@@ -978,79 +1070,6 @@ var Loader = class {
|
|
978
1070
|
}
|
979
1071
|
}
|
980
1072
|
}
|
981
|
-
async _commitInternal(t, done, opts = { noLoader: false, compact: false }) {
|
982
|
-
await this.ready();
|
983
|
-
const fp = this.makeCarHeader(done, this.carLog, !!opts.compact);
|
984
|
-
const rootBlock = await encodeCarHeader(fp);
|
985
|
-
const cars = await this.prepareCarFiles(rootBlock, t, !!opts.public);
|
986
|
-
const cids = [];
|
987
|
-
for (const car of cars) {
|
988
|
-
const { cid, bytes } = car;
|
989
|
-
await (await this.carStore()).save({ cid, bytes });
|
990
|
-
cids.push(cid);
|
991
|
-
}
|
992
|
-
await this.cacheTransaction(t);
|
993
|
-
const newDbMeta = { cars: cids, key: this.key || null };
|
994
|
-
await (await this.remoteWAL()).enqueue(newDbMeta, opts);
|
995
|
-
await (await this.metaStore()).save(newDbMeta);
|
996
|
-
await this.updateCarLog(cids, fp, !!opts.compact);
|
997
|
-
return cids;
|
998
|
-
}
|
999
|
-
async prepareCarFilesFiles(roots, t, isPublic) {
|
1000
|
-
const theKey = isPublic ? null : await this._getKey();
|
1001
|
-
const car = theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, roots[0], t) : await encodeCarFile(roots, t);
|
1002
|
-
return [car];
|
1003
|
-
}
|
1004
|
-
async prepareCarFiles(rootBlock, t, isPublic) {
|
1005
|
-
const theKey = isPublic ? void 0 : await this._getKey();
|
1006
|
-
const carFiles = [];
|
1007
|
-
const threshold = this.ebOpts.threshold || 1e3 * 1e3;
|
1008
|
-
let clonedt = new CarTransaction(t.parent, { add: false });
|
1009
|
-
clonedt.putSync(rootBlock.cid, rootBlock.bytes);
|
1010
|
-
let newsize = CBW2.blockLength(toCIDBlock(rootBlock));
|
1011
|
-
let cidRootBlock = rootBlock;
|
1012
|
-
for (const { cid, bytes } of t.entries()) {
|
1013
|
-
newsize += CBW2.blockLength(toCIDBlock({ cid, bytes }));
|
1014
|
-
if (newsize >= threshold) {
|
1015
|
-
carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
|
1016
|
-
clonedt = new CarTransaction(t.parent, { add: false });
|
1017
|
-
clonedt.putSync(cid, bytes);
|
1018
|
-
cidRootBlock = { cid, bytes };
|
1019
|
-
newsize = CBW2.blockLength(toCIDBlock({ cid, bytes }));
|
1020
|
-
} else {
|
1021
|
-
clonedt.putSync(cid, bytes);
|
1022
|
-
}
|
1023
|
-
}
|
1024
|
-
carFiles.push(await this.createCarFile(theKey, cidRootBlock.cid, clonedt));
|
1025
|
-
return carFiles;
|
1026
|
-
}
|
1027
|
-
async createCarFile(theKey, cid, t) {
|
1028
|
-
try {
|
1029
|
-
return theKey && this.ebOpts.crypto ? await encryptedEncodeCarFile(this.logger, this.ebOpts.crypto, theKey, cid, t) : await encodeCarFile([cid], t);
|
1030
|
-
} catch (e) {
|
1031
|
-
console.error("error creating car file", e);
|
1032
|
-
throw e;
|
1033
|
-
}
|
1034
|
-
}
|
1035
|
-
makeFileCarHeader(result) {
|
1036
|
-
const files = [];
|
1037
|
-
for (const [, meta] of Object.entries(result.files || {})) {
|
1038
|
-
if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
|
1039
|
-
files.push(meta.cid);
|
1040
|
-
}
|
1041
|
-
}
|
1042
|
-
return { ...result, files };
|
1043
|
-
}
|
1044
|
-
async updateCarLog(cids, fp, compact) {
|
1045
|
-
if (compact) {
|
1046
|
-
const previousCompactCid = fp.compact[fp.compact.length - 1];
|
1047
|
-
fp.compact.map((c) => c.toString()).forEach(this.seenCompacted.add, this.seenCompacted);
|
1048
|
-
this.carLog = [...uniqueCids([...this.carLog, ...fp.cars, cids], this.seenCompacted)];
|
1049
|
-
await this.removeCidsForCompact(previousCompactCid[0]);
|
1050
|
-
} else {
|
1051
|
-
this.carLog.unshift(cids);
|
1052
|
-
}
|
1053
|
-
}
|
1054
1073
|
async removeCidsForCompact(cid) {
|
1055
1074
|
const carHeader = await this.loadCarHeaderFromMeta({
|
1056
1075
|
cars: [cid]
|
@@ -1069,9 +1088,9 @@ var Loader = class {
|
|
1069
1088
|
// await this.remoteWAL!.enqueue(dbMeta, { public: false })
|
1070
1089
|
// }
|
1071
1090
|
// }
|
1072
|
-
async *entries(
|
1091
|
+
async *entries(cache2 = true) {
|
1073
1092
|
await this.ready();
|
1074
|
-
if (
|
1093
|
+
if (cache2) {
|
1075
1094
|
for (const [, block] of this.getBlockCache) {
|
1076
1095
|
yield block;
|
1077
1096
|
}
|
@@ -1153,105 +1172,521 @@ var Loader = class {
|
|
1153
1172
|
}
|
1154
1173
|
return got;
|
1155
1174
|
}
|
1156
|
-
makeCarHeader(meta, cars, compact = false) {
|
1157
|
-
const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
|
1158
|
-
return { ...coreHeader, meta };
|
1159
|
-
}
|
1160
1175
|
async loadCar(cid) {
|
1161
1176
|
if (!this.carStore) {
|
1162
1177
|
throw this.logger.Error().Msg("car store not initialized").AsError();
|
1163
1178
|
}
|
1164
|
-
const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
|
1165
|
-
return loaded;
|
1179
|
+
const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
|
1180
|
+
return loaded;
|
1181
|
+
}
|
1182
|
+
async makeDecoderAndCarReader(cid, local, remote) {
|
1183
|
+
const cidsString = cid.toString();
|
1184
|
+
let loadedCar = void 0;
|
1185
|
+
let activeStore = local;
|
1186
|
+
try {
|
1187
|
+
this.logger.Debug().Str("cid", cidsString).Msg("loading car");
|
1188
|
+
loadedCar = await local.load(cid);
|
1189
|
+
this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
|
1190
|
+
} catch (e) {
|
1191
|
+
if (remote) {
|
1192
|
+
const remoteCar = await remote.load(cid);
|
1193
|
+
if (remoteCar) {
|
1194
|
+
this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
|
1195
|
+
await local.save(remoteCar);
|
1196
|
+
loadedCar = remoteCar;
|
1197
|
+
activeStore = remote;
|
1198
|
+
}
|
1199
|
+
} else {
|
1200
|
+
this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
|
1201
|
+
}
|
1202
|
+
}
|
1203
|
+
if (!loadedCar) {
|
1204
|
+
throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
|
1205
|
+
}
|
1206
|
+
const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher3, codec: (await activeStore.keyedCrypto()).codec() });
|
1207
|
+
const rawReader = await CarReader.fromBytes(bytes.value);
|
1208
|
+
const readerP = Promise.resolve(rawReader);
|
1209
|
+
const cachedReaderP = readerP.then(async (reader) => {
|
1210
|
+
await this.cacheCarReader(cidsString, reader).catch((e) => {
|
1211
|
+
this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
|
1212
|
+
return;
|
1213
|
+
});
|
1214
|
+
return reader;
|
1215
|
+
});
|
1216
|
+
this.carReaders.set(cidsString, cachedReaderP);
|
1217
|
+
return readerP;
|
1218
|
+
}
|
1219
|
+
//What if instead it returns an Array of CarHeader
|
1220
|
+
async storesLoadCar(cid, local, remote) {
|
1221
|
+
const cidsString = cid.toString();
|
1222
|
+
let dacr = this.carReaders.get(cidsString);
|
1223
|
+
if (!dacr) {
|
1224
|
+
dacr = this.makeDecoderAndCarReader(cid, local, remote);
|
1225
|
+
this.carReaders.set(cidsString, dacr);
|
1226
|
+
}
|
1227
|
+
return dacr;
|
1228
|
+
}
|
1229
|
+
async getMoreReaders(cids) {
|
1230
|
+
const limit = pLimit(5);
|
1231
|
+
const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
|
1232
|
+
await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
|
1233
|
+
}
|
1234
|
+
};
|
1235
|
+
|
1236
|
+
// src/runtime/keyed-crypto.ts
|
1237
|
+
var keyed_crypto_exports = {};
|
1238
|
+
__export(keyed_crypto_exports, {
|
1239
|
+
BlockIvKeyIdCodec: () => BlockIvKeyIdCodec,
|
1240
|
+
keyedCryptoFactory: () => keyedCryptoFactory
|
1241
|
+
});
|
1242
|
+
import { base58btc as base58btc2 } from "multiformats/bases/base58";
|
1243
|
+
import { sha256 as hasher4 } from "multiformats/hashes/sha2";
|
1244
|
+
import * as CBOR from "cborg";
|
1245
|
+
var generateIV = {
|
1246
|
+
random: {
|
1247
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1248
|
+
calc: async (ko, crypto, data) => {
|
1249
|
+
return crypto.randomBytes(ko.ivLength);
|
1250
|
+
},
|
1251
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1252
|
+
verify: async (ko, crypto, iv, data) => {
|
1253
|
+
return true;
|
1254
|
+
}
|
1255
|
+
},
|
1256
|
+
hash: {
|
1257
|
+
calc: async (ko, crypto, data) => {
|
1258
|
+
const hash = await hasher4.digest(data);
|
1259
|
+
const hashBytes = new Uint8Array(hash.bytes);
|
1260
|
+
const hashArray = new Uint8Array(ko.ivLength);
|
1261
|
+
for (let i = 0; i < hashBytes.length; i++) {
|
1262
|
+
hashArray[i % ko.ivLength] ^= hashBytes[i];
|
1263
|
+
}
|
1264
|
+
return hashArray;
|
1265
|
+
},
|
1266
|
+
verify: async function(ko, crypto, iv, data) {
|
1267
|
+
return ko.url.getParam("ivverify") !== "disable" && UInt8ArrayEqual(iv, await this.calc(ko, crypto, data));
|
1268
|
+
}
|
1269
|
+
}
|
1270
|
+
};
|
1271
|
+
function getGenerateIVFn(url, opts) {
|
1272
|
+
const ivhash = opts.ivCalc || url.getParam("ivhash") || "hash";
|
1273
|
+
return generateIV[ivhash] || generateIV["hash"];
|
1274
|
+
}
|
1275
|
+
var BlockIvKeyIdCodec = class {
|
1276
|
+
constructor(ko, iv, opts) {
|
1277
|
+
this.code = 3147065;
|
1278
|
+
this.name = "Fireproof@encrypted-block:aes-gcm";
|
1279
|
+
this.ko = ko;
|
1280
|
+
this.iv = iv;
|
1281
|
+
this.opts = opts || {};
|
1282
|
+
}
|
1283
|
+
async encode(data) {
|
1284
|
+
const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
|
1285
|
+
const { iv } = this.ko.algo(calcIv);
|
1286
|
+
const fprt = await this.ko.fingerPrint();
|
1287
|
+
const keyId = base58btc2.decode(fprt);
|
1288
|
+
this.ko.logger.Debug().Str("fp", fprt).Msg("encode");
|
1289
|
+
return CBOR.encode({
|
1290
|
+
iv,
|
1291
|
+
keyId,
|
1292
|
+
data: await this.ko._encrypt({ iv, bytes: data })
|
1293
|
+
});
|
1294
|
+
}
|
1295
|
+
async decode(abytes) {
|
1296
|
+
let bytes;
|
1297
|
+
if (abytes instanceof Uint8Array) {
|
1298
|
+
bytes = abytes;
|
1299
|
+
} else {
|
1300
|
+
bytes = new Uint8Array(abytes);
|
1301
|
+
}
|
1302
|
+
const { iv, keyId, data } = CBOR.decode(bytes);
|
1303
|
+
const fprt = await this.ko.fingerPrint();
|
1304
|
+
this.ko.logger.Debug().Str("fp", base58btc2.encode(keyId)).Msg("decode");
|
1305
|
+
if (base58btc2.encode(keyId) !== fprt) {
|
1306
|
+
throw this.ko.logger.Error().Str("fp", fprt).Str("keyId", base58btc2.encode(keyId)).Msg("keyId mismatch").AsError();
|
1307
|
+
}
|
1308
|
+
const result = await this.ko._decrypt({ iv, bytes: data });
|
1309
|
+
if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
|
1310
|
+
throw this.ko.logger.Error().Msg("iv missmatch").AsError();
|
1311
|
+
}
|
1312
|
+
return result;
|
1313
|
+
}
|
1314
|
+
};
|
1315
|
+
var keyedCrypto = class {
|
1316
|
+
constructor(url, key, cyopt, sthis) {
|
1317
|
+
this.ivLength = 12;
|
1318
|
+
this.isEncrypting = true;
|
1319
|
+
this.logger = ensureLogger(sthis, "keyedCrypto");
|
1320
|
+
this.crypto = cyopt;
|
1321
|
+
this.key = key;
|
1322
|
+
this.url = url;
|
1323
|
+
}
|
1324
|
+
fingerPrint() {
|
1325
|
+
return Promise.resolve(this.key.fingerPrint);
|
1326
|
+
}
|
1327
|
+
codec(iv, opts) {
|
1328
|
+
return new BlockIvKeyIdCodec(this, iv, opts);
|
1329
|
+
}
|
1330
|
+
algo(iv) {
|
1331
|
+
return {
|
1332
|
+
name: "AES-GCM",
|
1333
|
+
iv: iv || this.crypto.randomBytes(this.ivLength),
|
1334
|
+
tagLength: 128
|
1335
|
+
};
|
1336
|
+
}
|
1337
|
+
async _decrypt(data) {
|
1338
|
+
this.logger.Debug().Len(data.bytes, "bytes").Len(data.iv, "iv").Str("fp", this.key.fingerPrint).Msg("decrypting");
|
1339
|
+
return new Uint8Array(await this.crypto.decrypt(this.algo(data.iv), this.key.key, data.bytes));
|
1340
|
+
}
|
1341
|
+
async _encrypt(data) {
|
1342
|
+
this.logger.Debug().Len(data.bytes).Str("fp", this.key.fingerPrint).Msg("encrypting");
|
1343
|
+
const a = this.algo(data.iv);
|
1344
|
+
return new Uint8Array(await this.crypto.encrypt(a, this.key.key, data.bytes));
|
1345
|
+
}
|
1346
|
+
};
|
1347
|
+
var nullCodec = class {
|
1348
|
+
constructor() {
|
1349
|
+
this.code = 0;
|
1350
|
+
this.name = "Fireproof@unencrypted-block";
|
1351
|
+
}
|
1352
|
+
encode(data) {
|
1353
|
+
return data;
|
1354
|
+
}
|
1355
|
+
decode(data) {
|
1356
|
+
return data;
|
1357
|
+
}
|
1358
|
+
};
|
1359
|
+
var noCrypto = class {
|
1360
|
+
constructor(url, cyrt, sthis) {
|
1361
|
+
this.ivLength = 0;
|
1362
|
+
this.code = 0;
|
1363
|
+
this.name = "Fireproof@unencrypted-block";
|
1364
|
+
this.isEncrypting = false;
|
1365
|
+
this._fingerPrint = "noCrypto:" + Math.random();
|
1366
|
+
this.logger = ensureLogger(sthis, "noCrypto");
|
1367
|
+
this.crypto = cyrt;
|
1368
|
+
this.url = url;
|
1369
|
+
}
|
1370
|
+
fingerPrint() {
|
1371
|
+
return Promise.resolve(this._fingerPrint);
|
1372
|
+
}
|
1373
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1374
|
+
codec(iv) {
|
1375
|
+
return new nullCodec();
|
1376
|
+
}
|
1377
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1378
|
+
algo(iv) {
|
1379
|
+
return {
|
1380
|
+
name: "noCrypto",
|
1381
|
+
iv: new Uint8Array(),
|
1382
|
+
tagLength: 0
|
1383
|
+
};
|
1384
|
+
}
|
1385
|
+
_decrypt() {
|
1386
|
+
throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
|
1387
|
+
}
|
1388
|
+
_encrypt() {
|
1389
|
+
throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
|
1390
|
+
}
|
1391
|
+
};
|
1392
|
+
async function keyedCryptoFactory(url, kb, sthis) {
|
1393
|
+
const storekey = url.getParam("storekey");
|
1394
|
+
if (storekey && storekey !== "insecure") {
|
1395
|
+
let rkey = await kb.getNamedKey(storekey, true);
|
1396
|
+
if (rkey.isErr()) {
|
1397
|
+
try {
|
1398
|
+
rkey = await kb.toKeyWithFingerPrint(storekey);
|
1399
|
+
} catch (e) {
|
1400
|
+
throw sthis.logger.Error().Err(e).Str("keybag", kb.rt.id()).Str("name", storekey).Msg("getNamedKey failed").AsError();
|
1401
|
+
}
|
1402
|
+
}
|
1403
|
+
return new keyedCrypto(url, rkey.Ok(), kb.rt.crypto, sthis);
|
1404
|
+
}
|
1405
|
+
return new noCrypto(url, kb.rt.crypto, sthis);
|
1406
|
+
}
|
1407
|
+
|
1408
|
+
// src/blockstore/fragment-gateway.ts
|
1409
|
+
import { Result as Result3 } from "@adviser/cement";
|
1410
|
+
import { base58btc as base58btc3 } from "multiformats/bases/base58";
|
1411
|
+
import { encode as encode3, decode as decode3 } from "cborg";
|
1412
|
+
function getFragSize(url) {
|
1413
|
+
const fragSize = url.getParam("fragSize");
|
1414
|
+
let ret = 0;
|
1415
|
+
if (fragSize) {
|
1416
|
+
ret = parseInt(fragSize);
|
1417
|
+
}
|
1418
|
+
if (isNaN(ret) || ret <= 0) {
|
1419
|
+
ret = 0;
|
1420
|
+
}
|
1421
|
+
return ret;
|
1422
|
+
}
|
1423
|
+
async function getFrags(url, innerGW, headerSize, logger) {
|
1424
|
+
const fragSize = getFragSize(url);
|
1425
|
+
if (!fragSize) {
|
1426
|
+
const res = await innerGW.get(url);
|
1427
|
+
if (res.isErr()) {
|
1428
|
+
return [res];
|
1429
|
+
}
|
1430
|
+
const data = res.unwrap();
|
1431
|
+
return [
|
1432
|
+
Result3.Ok({
|
1433
|
+
fid: new Uint8Array(0),
|
1434
|
+
ofs: 0,
|
1435
|
+
len: data.length,
|
1436
|
+
data
|
1437
|
+
})
|
1438
|
+
];
|
1439
|
+
}
|
1440
|
+
const firstRaw = await innerGW.get(url.build().setParam("ofs", "0").URI());
|
1441
|
+
if (firstRaw.isErr()) {
|
1442
|
+
return [firstRaw];
|
1443
|
+
}
|
1444
|
+
const firstFragment = decode3(firstRaw.unwrap());
|
1445
|
+
const blockSize = firstFragment.data.length;
|
1446
|
+
const ops = [Promise.resolve(Result3.Ok(firstFragment))];
|
1447
|
+
const fidStr = base58btc3.encode(firstFragment.fid);
|
1448
|
+
const fragUrl = url.build().setParam("fid", fidStr).setParam("len", firstFragment.len.toString()).setParam("headerSize", headerSize.toString());
|
1449
|
+
for (let ofs = blockSize; ofs < firstFragment.len; ofs += blockSize) {
|
1450
|
+
ops.push(
|
1451
|
+
(async (furl, ofs2) => {
|
1452
|
+
const raw2 = await innerGW.get(furl);
|
1453
|
+
if (raw2.isErr()) {
|
1454
|
+
return raw2;
|
1455
|
+
}
|
1456
|
+
const fragment = decode3(raw2.unwrap());
|
1457
|
+
if (base58btc3.encode(fragment.fid) !== fidStr) {
|
1458
|
+
return Result3.Err(logger.Error().Msg("Fragment fid mismatch").AsError());
|
1459
|
+
}
|
1460
|
+
if (fragment.ofs !== ofs2) {
|
1461
|
+
return Result3.Err(logger.Error().Uint64("ofs", ofs2).Msg("Fragment ofs mismatch").AsError());
|
1462
|
+
}
|
1463
|
+
return Result3.Ok(fragment);
|
1464
|
+
})(fragUrl.setParam("ofs", ofs.toString()).URI(), ofs)
|
1465
|
+
);
|
1466
|
+
}
|
1467
|
+
return Promise.all(ops);
|
1468
|
+
}
|
1469
|
+
var FragmentGateway = class {
|
1470
|
+
constructor(sthis, innerGW) {
|
1471
|
+
this.fidLength = 4;
|
1472
|
+
this.headerSize = 32;
|
1473
|
+
this.sthis = ensureSuperLog(sthis, "FragmentGateway");
|
1474
|
+
this.logger = this.sthis.logger;
|
1475
|
+
this.innerGW = innerGW;
|
1476
|
+
}
|
1477
|
+
slicer(url, body) {
|
1478
|
+
const fragSize = getFragSize(url);
|
1479
|
+
if (!fragSize) {
|
1480
|
+
return [this.innerGW.put(url, body)];
|
1481
|
+
}
|
1482
|
+
const blocksize = fragSize - this.headerSize;
|
1483
|
+
if (blocksize <= 0) {
|
1484
|
+
throw this.logger.Error().Uint64("fragSize", fragSize).Uint64("headerSize", this.headerSize).Msg("Fragment size is too small").AsError();
|
1485
|
+
}
|
1486
|
+
const ops = [];
|
1487
|
+
const fid = this.sthis.nextId(this.fidLength);
|
1488
|
+
const fragUrl = url.build().setParam("fid", fid.str).setParam("len", body.length.toString()).setParam("headerSize", this.headerSize.toString());
|
1489
|
+
for (let ofs = 0; ofs < body.length; ofs += blocksize) {
|
1490
|
+
const block = encode3({
|
1491
|
+
fid: fid.bin,
|
1492
|
+
ofs,
|
1493
|
+
len: body.length,
|
1494
|
+
data: body.slice(ofs, ofs + blocksize)
|
1495
|
+
});
|
1496
|
+
if (block.length > fragSize) {
|
1497
|
+
throw this.logger.Error().Uint64("block", block.length).Uint64("fragSize", fragSize).Msg("Block size to big").AsError();
|
1498
|
+
}
|
1499
|
+
ops.push(this.innerGW.put(fragUrl.setParam("ofs", ofs.toString()).URI(), block));
|
1500
|
+
}
|
1501
|
+
return ops;
|
1502
|
+
}
|
1503
|
+
buildUrl(baseUrl, key) {
|
1504
|
+
return this.innerGW.buildUrl(baseUrl, key);
|
1505
|
+
}
|
1506
|
+
async destroy(iurl) {
|
1507
|
+
return this.innerGW.destroy(iurl);
|
1508
|
+
}
|
1509
|
+
async start(url) {
|
1510
|
+
this.headerSize = encode3({
|
1511
|
+
fid: this.sthis.nextId(this.fidLength).bin,
|
1512
|
+
ofs: 1024 * 1024,
|
1513
|
+
// 32bit
|
1514
|
+
len: 16 * 1024 * 1024,
|
1515
|
+
// 32bit
|
1516
|
+
data: new Uint8Array(1024)
|
1517
|
+
}).length - 1024;
|
1518
|
+
return this.innerGW.start(url);
|
1519
|
+
}
|
1520
|
+
async close(url) {
|
1521
|
+
return this.innerGW.close(url);
|
1522
|
+
}
|
1523
|
+
async put(url, body) {
|
1524
|
+
await Promise.all(this.slicer(url, body));
|
1525
|
+
return Result3.Ok(void 0);
|
1526
|
+
}
|
1527
|
+
async get(url) {
|
1528
|
+
const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
|
1529
|
+
let buffer = void 0;
|
1530
|
+
for (const rfrag of rfrags) {
|
1531
|
+
if (rfrag.isErr()) {
|
1532
|
+
return Result3.Err(rfrag.Err());
|
1533
|
+
}
|
1534
|
+
const frag = rfrag.Ok();
|
1535
|
+
buffer = buffer || new Uint8Array(frag.len);
|
1536
|
+
buffer.set(frag.data, frag.ofs);
|
1537
|
+
}
|
1538
|
+
return Result3.Ok(buffer || new Uint8Array(0));
|
1539
|
+
}
|
1540
|
+
async subscribe(url, callback) {
|
1541
|
+
if (this.innerGW.subscribe) {
|
1542
|
+
return this.innerGW.subscribe(url, callback);
|
1543
|
+
} else {
|
1544
|
+
return Result3.Err(this.logger.Error().Url(url).Msg("subscribe not supported").AsError());
|
1545
|
+
}
|
1546
|
+
}
|
1547
|
+
async delete(url) {
|
1548
|
+
const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
|
1549
|
+
for (const rfrag of rfrags) {
|
1550
|
+
if (rfrag.isErr()) {
|
1551
|
+
return Result3.Err(rfrag.Err());
|
1552
|
+
}
|
1553
|
+
const frag = rfrag.Ok();
|
1554
|
+
const fidStr = base58btc3.encode(frag.fid);
|
1555
|
+
const fragUrl = url.build().setParam("fid", fidStr).setParam("len", frag.len.toString()).setParam("headerSize", this.headerSize.toString()).URI();
|
1556
|
+
await this.innerGW.delete(fragUrl);
|
1557
|
+
}
|
1558
|
+
return Result3.Ok(void 0);
|
1166
1559
|
}
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1560
|
+
};
|
1561
|
+
|
1562
|
+
// src/blockstore/meta-key-helper.ts
|
1563
|
+
import { format, parse } from "@ipld/dag-json";
|
1564
|
+
import { EventBlock, decodeEventBlock } from "@web3-storage/pail/clock";
|
1565
|
+
import { CID as CID2 } from "multiformats";
|
1566
|
+
import { base64pad } from "multiformats/bases/base64";
|
1567
|
+
import { Result as Result4 } from "@adviser/cement";
|
1568
|
+
async function decodeGatewayMetaBytesToDbMeta(sthis, byteHeads) {
|
1569
|
+
const crdtEntries = JSON.parse(sthis.txt.decode(byteHeads));
|
1570
|
+
if (!crdtEntries.length) {
|
1571
|
+
sthis.logger.Debug().Str("byteHeads", new TextDecoder().decode(byteHeads)).Msg("No CRDT entries found");
|
1572
|
+
return [];
|
1573
|
+
}
|
1574
|
+
if (!crdtEntries.map) {
|
1575
|
+
sthis.logger.Debug().Str("crdtEntries", JSON.stringify(crdtEntries)).Msg("No data in CRDT entries");
|
1576
|
+
return [];
|
1577
|
+
}
|
1578
|
+
return Promise.all(
|
1579
|
+
crdtEntries.map(async (crdtEntry) => {
|
1580
|
+
const eventBlock = await decodeEventBlock(base64pad.decode(crdtEntry.data));
|
1581
|
+
const dbMeta = parse(sthis.txt.decode(eventBlock.value.data.dbMeta));
|
1582
|
+
return {
|
1583
|
+
eventCid: eventBlock.cid,
|
1584
|
+
parents: crdtEntry.parents,
|
1585
|
+
dbMeta
|
1586
|
+
};
|
1587
|
+
})
|
1588
|
+
);
|
1589
|
+
}
|
1590
|
+
async function setCryptoKeyFromGatewayMetaPayload(uri, sthis, data) {
|
1591
|
+
try {
|
1592
|
+
sthis.logger.Debug().Str("uri", uri.toString()).Msg("Setting crypto key from gateway meta payload");
|
1593
|
+
const keyInfo = await decodeGatewayMetaBytesToDbMeta(sthis, data);
|
1594
|
+
if (keyInfo.length) {
|
1595
|
+
const dbMeta = keyInfo[0].dbMeta;
|
1596
|
+
if (dbMeta.key) {
|
1597
|
+
const kb = await getKeyBag(sthis);
|
1598
|
+
const keyName = getStoreKeyName(uri);
|
1599
|
+
const res = await kb.setNamedKey(keyName, dbMeta.key);
|
1600
|
+
if (res.isErr()) {
|
1601
|
+
sthis.logger.Debug().Str("keyName", keyName).Str("dbMeta.key", dbMeta.key).Msg("Failed to set named key");
|
1602
|
+
throw res.Err();
|
1603
|
+
}
|
1604
|
+
}
|
1605
|
+
sthis.logger.Debug().Str("dbMeta.key", dbMeta.key).Str("uri", uri.toString()).Msg("Set crypto key from gateway meta payload");
|
1606
|
+
return Result4.Ok(dbMeta);
|
1209
1607
|
}
|
1210
|
-
|
1608
|
+
sthis.logger.Debug().Str("data", new TextDecoder().decode(data)).Msg("No crypto in gateway meta payload");
|
1609
|
+
return Result4.Ok(void 0);
|
1610
|
+
} catch (error) {
|
1611
|
+
sthis.logger.Debug().Err(error).Msg("Failed to set crypto key from gateway meta payload");
|
1612
|
+
return Result4.Err(error);
|
1211
1613
|
}
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1614
|
+
}
|
1615
|
+
async function addCryptoKeyToGatewayMetaPayload(uri, sthis, body) {
|
1616
|
+
try {
|
1617
|
+
sthis.logger.Debug().Str("uri", uri.toString()).Msg("Adding crypto key to gateway meta payload");
|
1618
|
+
const keyName = getStoreKeyName(uri);
|
1619
|
+
const kb = await getKeyBag(sthis);
|
1620
|
+
const res = await kb.getNamedExtractableKey(keyName, true);
|
1621
|
+
if (res.isErr()) {
|
1622
|
+
sthis.logger.Error().Str("keyName", keyName).Msg("Failed to get named extractable key");
|
1623
|
+
throw res.Err();
|
1216
1624
|
}
|
1217
|
-
const
|
1625
|
+
const keyData = await res.Ok().extract();
|
1626
|
+
const dbMetas = await decodeGatewayMetaBytesToDbMeta(sthis, body);
|
1627
|
+
const { dbMeta, parents } = dbMetas[0];
|
1628
|
+
const parentLinks = parents.map((p) => CID2.parse(p));
|
1629
|
+
dbMeta.key = keyData.keyStr;
|
1630
|
+
const events = await Promise.all([dbMeta].map((dbMeta2) => createDbMetaEventBlock(sthis, dbMeta2, parentLinks)));
|
1631
|
+
const encoded = await encodeEventsWithParents(sthis, events, parentLinks);
|
1632
|
+
sthis.logger.Debug().Str("uri", uri.toString()).Msg("Added crypto key to gateway meta payload");
|
1633
|
+
return Result4.Ok(encoded);
|
1634
|
+
} catch (error) {
|
1635
|
+
sthis.logger.Error().Err(error).Msg("Failed to add crypto key to gateway meta payload");
|
1636
|
+
return Result4.Err(error);
|
1637
|
+
}
|
1638
|
+
}
|
1639
|
+
function getStoreKeyName(url) {
|
1640
|
+
const storeKeyName = [url.getParam("localName") || url.getParam("name")];
|
1641
|
+
const idx = url.getParam("index");
|
1642
|
+
if (idx) {
|
1643
|
+
storeKeyName.push(idx);
|
1644
|
+
}
|
1645
|
+
storeKeyName.push("data");
|
1646
|
+
return `@${storeKeyName.join(":")}@`;
|
1647
|
+
}
|
1648
|
+
async function createDbMetaEventBlock(sthis, dbMeta, parents) {
|
1649
|
+
const event = await EventBlock.create(
|
1650
|
+
{
|
1651
|
+
dbMeta: sthis.txt.encode(format(dbMeta))
|
1652
|
+
},
|
1653
|
+
parents
|
1654
|
+
);
|
1655
|
+
return event;
|
1656
|
+
}
|
1657
|
+
async function encodeEventsWithParents(sthis, events, parents) {
|
1658
|
+
const crdtEntries = events.map((event) => {
|
1659
|
+
const base64String = base64pad.encode(event.bytes);
|
1218
1660
|
return {
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1661
|
+
cid: event.cid.toString(),
|
1662
|
+
data: base64String,
|
1663
|
+
parents: parents.map((p) => p.toString())
|
1222
1664
|
};
|
1223
|
-
}
|
1224
|
-
|
1225
|
-
|
1226
|
-
throw this.logger.Error().Str("this.key", this.key).Str("key", key).Msg("setting key").AsError();
|
1227
|
-
this.key = key;
|
1228
|
-
const encoder = new TextEncoder();
|
1229
|
-
const data = encoder.encode(key);
|
1230
|
-
const hashBuffer = await this.ebOpts.crypto.digestSHA256(data);
|
1231
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
1232
|
-
this.keyId = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
1233
|
-
}
|
1234
|
-
async getMoreReaders(cids) {
|
1235
|
-
const limit = pLimit(5);
|
1236
|
-
const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
|
1237
|
-
await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
|
1238
|
-
}
|
1239
|
-
};
|
1665
|
+
});
|
1666
|
+
return sthis.txt.encode(JSON.stringify(crdtEntries));
|
1667
|
+
}
|
1240
1668
|
|
1241
1669
|
// src/blockstore/store.ts
|
1242
1670
|
function guardVersion(url) {
|
1243
|
-
if (!url.
|
1244
|
-
return
|
1671
|
+
if (!url.hasParam("version")) {
|
1672
|
+
return Result5.Err(`missing version: ${url.toString()}`);
|
1245
1673
|
}
|
1246
|
-
return
|
1674
|
+
return Result5.Ok(url);
|
1247
1675
|
}
|
1248
|
-
var
|
1249
|
-
constructor(name, url, logger) {
|
1676
|
+
var BaseStoreImpl = class {
|
1677
|
+
constructor(name, url, opts, sthis, logger) {
|
1250
1678
|
this._onStarted = [];
|
1251
1679
|
this._onClosed = [];
|
1252
1680
|
this.name = name;
|
1253
|
-
this.
|
1254
|
-
this.
|
1681
|
+
this._url = url;
|
1682
|
+
this.keybag = opts.keybag;
|
1683
|
+
this.sthis = sthis;
|
1684
|
+
this.logger = logger.With().Ref("url", () => this._url.toString()).Str("name", name).Logger();
|
1685
|
+
this.gateway = new FragmentGateway(this.sthis, opts.gateway);
|
1686
|
+
this.loader = opts.loader;
|
1687
|
+
}
|
1688
|
+
url() {
|
1689
|
+
return this._url;
|
1255
1690
|
}
|
1256
1691
|
onStarted(fn) {
|
1257
1692
|
this._onStarted.push(fn);
|
@@ -1259,38 +1694,93 @@ var VersionedStore = class {
|
|
1259
1694
|
onClosed(fn) {
|
1260
1695
|
this._onClosed.push(fn);
|
1261
1696
|
}
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
super(name, url, ensureLogger(logger, "MetaStore", {}));
|
1268
|
-
this.tag = "header-base";
|
1269
|
-
this.gateway = gateway;
|
1270
|
-
}
|
1271
|
-
makeHeader({ cars, key }) {
|
1272
|
-
const toEncode = { cars };
|
1273
|
-
if (key) toEncode.key = key;
|
1274
|
-
return format(toEncode);
|
1275
|
-
}
|
1276
|
-
parseHeader(headerData) {
|
1277
|
-
const got = parse(headerData);
|
1278
|
-
return got;
|
1697
|
+
async ready() {
|
1698
|
+
return;
|
1699
|
+
}
|
1700
|
+
async keyedCrypto() {
|
1701
|
+
return keyedCryptoFactory(this._url, await this.keybag(), this.sthis);
|
1279
1702
|
}
|
1280
1703
|
async start() {
|
1281
|
-
this.logger.Debug().Msg("starting");
|
1282
|
-
|
1704
|
+
this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
|
1705
|
+
this._url = this._url.build().setParam("store", this.storeType).URI();
|
1706
|
+
const res = await this.gateway.start(this._url);
|
1283
1707
|
if (res.isErr()) {
|
1708
|
+
this.logger.Error().Result("gw-start", res).Msg("started-gateway");
|
1284
1709
|
return res;
|
1285
1710
|
}
|
1711
|
+
this._url = res.Ok();
|
1712
|
+
const kb = await this.keybag();
|
1713
|
+
const skRes = await kb.ensureKeyFromUrl(this._url, () => {
|
1714
|
+
const idx = this._url.getParam("index");
|
1715
|
+
const storeKeyName = [this.name];
|
1716
|
+
if (idx) {
|
1717
|
+
storeKeyName.push(idx);
|
1718
|
+
}
|
1719
|
+
storeKeyName.push(this.storeType);
|
1720
|
+
return storeKeyName.join(":");
|
1721
|
+
});
|
1722
|
+
if (skRes.isErr()) {
|
1723
|
+
return skRes;
|
1724
|
+
}
|
1725
|
+
this._url = skRes.Ok();
|
1726
|
+
const version = guardVersion(this._url);
|
1727
|
+
if (version.isErr()) {
|
1728
|
+
this.logger.Error().Result("version", version).Msg("guardVersion");
|
1729
|
+
await this.close();
|
1730
|
+
return version;
|
1731
|
+
}
|
1732
|
+
if (this.ready) {
|
1733
|
+
const fn = this.ready.bind(this);
|
1734
|
+
const ready = await exception2Result(fn);
|
1735
|
+
if (ready.isErr()) {
|
1736
|
+
await this.close();
|
1737
|
+
return ready;
|
1738
|
+
}
|
1739
|
+
}
|
1286
1740
|
this._onStarted.forEach((fn) => fn());
|
1287
|
-
|
1741
|
+
this.logger.Debug().Msg("started");
|
1742
|
+
return version;
|
1743
|
+
}
|
1744
|
+
};
|
1745
|
+
var MetaStoreImpl = class extends BaseStoreImpl {
|
1746
|
+
// remote: boolean;
|
1747
|
+
constructor(sthis, name, url, opts) {
|
1748
|
+
super(name, url, { ...opts }, sthis, ensureLogger(sthis, "MetaStoreImpl"));
|
1749
|
+
this.storeType = "meta";
|
1750
|
+
this.subscribers = /* @__PURE__ */ new Map();
|
1751
|
+
this.parents = [];
|
1752
|
+
if (
|
1753
|
+
/*this.remote && */
|
1754
|
+
opts.gateway.subscribe
|
1755
|
+
) {
|
1756
|
+
this.onStarted(async () => {
|
1757
|
+
this.logger.Debug().Str("url", this.url().toString()).Msg("Subscribing to the gateway");
|
1758
|
+
opts.gateway.subscribe?.(this.url(), async (message) => {
|
1759
|
+
this.logger.Debug().Msg("Received message from gateway");
|
1760
|
+
const dbMetas = await decodeGatewayMetaBytesToDbMeta(this.sthis, message);
|
1761
|
+
await Promise.all(
|
1762
|
+
dbMetas.map((dbMeta) => this.loader?.taskManager?.handleEvent(dbMeta.eventCid, dbMeta.parents, dbMeta.dbMeta))
|
1763
|
+
);
|
1764
|
+
this.updateParentsFromDbMetas(dbMetas);
|
1765
|
+
});
|
1766
|
+
});
|
1767
|
+
}
|
1768
|
+
}
|
1769
|
+
updateParentsFromDbMetas(dbMetas) {
|
1770
|
+
const cids = dbMetas.map((m) => m.eventCid);
|
1771
|
+
const dbMetaParents = dbMetas.flatMap((m) => m.parents);
|
1772
|
+
const uniqueParentsMap = new Map([...this.parents, ...cids].map((p) => [p.toString(), p]));
|
1773
|
+
const dbMetaParentsSet = new Set(dbMetaParents.map((p) => p.toString()));
|
1774
|
+
this.parents = Array.from(uniqueParentsMap.values()).filter((p) => !dbMetaParentsSet.has(p.toString()));
|
1288
1775
|
}
|
1289
|
-
async
|
1290
|
-
this.
|
1291
|
-
|
1776
|
+
async handleByteHeads(byteHeads) {
|
1777
|
+
return await decodeGatewayMetaBytesToDbMeta(this.sthis, byteHeads);
|
1778
|
+
}
|
1779
|
+
async load() {
|
1780
|
+
const branch = "main";
|
1781
|
+
const url = await this.gateway.buildUrl(this.url(), branch);
|
1292
1782
|
if (url.isErr()) {
|
1293
|
-
throw this.logger.Error().Result("
|
1783
|
+
throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
|
1294
1784
|
}
|
1295
1785
|
const bytes = await this.gateway.get(url.Ok());
|
1296
1786
|
if (bytes.isErr()) {
|
@@ -1299,66 +1789,45 @@ var MetaStore = class extends VersionedStore {
|
|
1299
1789
|
}
|
1300
1790
|
throw this.logger.Error().Url(url.Ok()).Result("bytes:", bytes).Msg("gateway get").AsError();
|
1301
1791
|
}
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
}
|
1792
|
+
const dbMetas = await this.handleByteHeads(bytes.Ok());
|
1793
|
+
await this.loader?.handleDbMetasFromStore(dbMetas.map((m) => m.dbMeta));
|
1794
|
+
this.updateParentsFromDbMetas(dbMetas);
|
1795
|
+
return dbMetas.map((m) => m.dbMeta);
|
1307
1796
|
}
|
1308
|
-
async save(meta, branch
|
1797
|
+
async save(meta, branch) {
|
1798
|
+
branch = branch || "main";
|
1309
1799
|
this.logger.Debug().Str("branch", branch).Any("meta", meta).Msg("saving meta");
|
1310
|
-
const
|
1311
|
-
const
|
1800
|
+
const event = await createDbMetaEventBlock(this.sthis, meta, this.parents);
|
1801
|
+
const bytes = await encodeEventsWithParents(this.sthis, [event], this.parents);
|
1802
|
+
const url = await this.gateway.buildUrl(this.url(), branch);
|
1312
1803
|
if (url.isErr()) {
|
1313
|
-
throw this.logger.Error().Err(url.Err()).Str("branch", branch).
|
1804
|
+
throw this.logger.Error().Err(url.Err()).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
|
1314
1805
|
}
|
1315
|
-
|
1806
|
+
this.parents = [event.cid];
|
1807
|
+
const res = await this.gateway.put(url.Ok(), bytes);
|
1316
1808
|
if (res.isErr()) {
|
1317
1809
|
throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
|
1318
1810
|
}
|
1319
|
-
return res
|
1811
|
+
return res;
|
1320
1812
|
}
|
1321
1813
|
async close() {
|
1322
|
-
await this.gateway.close(this.url);
|
1814
|
+
await this.gateway.close(this.url());
|
1323
1815
|
this._onClosed.forEach((fn) => fn());
|
1324
|
-
return
|
1816
|
+
return Result5.Ok(void 0);
|
1325
1817
|
}
|
1326
1818
|
async destroy() {
|
1327
|
-
return this.gateway.destroy(this.url);
|
1819
|
+
return this.gateway.destroy(this.url());
|
1328
1820
|
}
|
1329
1821
|
};
|
1330
|
-
var
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
ensureLogger(logger, "DataStore", {
|
1336
|
-
url: () => url.toString()
|
1337
|
-
})
|
1338
|
-
);
|
1339
|
-
this.tag = "car-base";
|
1340
|
-
this.gateway = gateway;
|
1341
|
-
}
|
1342
|
-
async start() {
|
1343
|
-
this.logger.Debug().Msg("starting-gateway");
|
1344
|
-
const res = await this.gateway.start(this.url);
|
1345
|
-
if (res.isErr()) {
|
1346
|
-
this.logger.Error().Result("gw-start", res).Msg("started-gateway");
|
1347
|
-
return res;
|
1348
|
-
}
|
1349
|
-
this._onStarted.forEach((fn) => fn());
|
1350
|
-
const version = guardVersion(this.url);
|
1351
|
-
if (version.isErr()) {
|
1352
|
-
this.logger.Error().Result("version", version).Msg("guardVersion");
|
1353
|
-
await this.close();
|
1354
|
-
return version;
|
1355
|
-
}
|
1356
|
-
this.logger.Debug().Msg("started");
|
1357
|
-
return version;
|
1822
|
+
var DataStoreImpl = class extends BaseStoreImpl {
|
1823
|
+
// readonly tag: string = "car-base";
|
1824
|
+
constructor(sthis, name, url, opts) {
|
1825
|
+
super(name, url, { ...opts }, sthis, ensureLogger(sthis, "DataStoreImpl"));
|
1826
|
+
this.storeType = "data";
|
1358
1827
|
}
|
1359
1828
|
async load(cid) {
|
1360
1829
|
this.logger.Debug().Any("cid", cid).Msg("loading");
|
1361
|
-
const url = await this.gateway.buildUrl(this.url, cid.toString());
|
1830
|
+
const url = await this.gateway.buildUrl(this.url(), cid.toString());
|
1362
1831
|
if (url.isErr()) {
|
1363
1832
|
throw this.logger.Error().Err(url.Err()).Str("cid", cid.toString()).Msg("got error from gateway.buildUrl").AsError();
|
1364
1833
|
}
|
@@ -1371,7 +1840,7 @@ var DataStore = class extends VersionedStore {
|
|
1371
1840
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1372
1841
|
async save(car, opts) {
|
1373
1842
|
this.logger.Debug().Any("cid", car.cid.toString()).Msg("saving");
|
1374
|
-
const url = await this.gateway.buildUrl(this.url, car.cid.toString());
|
1843
|
+
const url = await this.gateway.buildUrl(this.url(), car.cid.toString());
|
1375
1844
|
if (url.isErr()) {
|
1376
1845
|
throw this.logger.Error().Err(url.Err()).Ref("cid", car.cid).Msg("got error from gateway.buildUrl").AsError();
|
1377
1846
|
}
|
@@ -1382,31 +1851,30 @@ var DataStore = class extends VersionedStore {
|
|
1382
1851
|
return res.Ok();
|
1383
1852
|
}
|
1384
1853
|
async remove(cid) {
|
1385
|
-
const url = await this.gateway.buildUrl(this.url, cid.toString());
|
1854
|
+
const url = await this.gateway.buildUrl(this.url(), cid.toString());
|
1386
1855
|
if (url.isErr()) {
|
1387
1856
|
return url;
|
1388
1857
|
}
|
1389
1858
|
return this.gateway.delete(url.Ok());
|
1390
1859
|
}
|
1391
1860
|
async close() {
|
1392
|
-
await this.gateway.close(this.url);
|
1861
|
+
await this.gateway.close(this.url());
|
1393
1862
|
this._onClosed.forEach((fn) => fn());
|
1394
|
-
return
|
1863
|
+
return Result5.Ok(void 0);
|
1395
1864
|
}
|
1396
1865
|
destroy() {
|
1397
|
-
return this.gateway.destroy(this.url);
|
1866
|
+
return this.gateway.destroy(this.url());
|
1398
1867
|
}
|
1399
1868
|
};
|
1400
|
-
var
|
1401
|
-
constructor(loader, url,
|
1402
|
-
super(loader.name, url, ensureLogger(
|
1403
|
-
this.
|
1404
|
-
this._ready = new
|
1869
|
+
var WALStoreImpl = class extends BaseStoreImpl {
|
1870
|
+
constructor(loader, url, opts) {
|
1871
|
+
super(loader.name, url, { ...opts }, loader.sthis, ensureLogger(loader.sthis, "WALStoreImpl"));
|
1872
|
+
this.storeType = "wal";
|
1873
|
+
this._ready = new ResolveOnce3();
|
1405
1874
|
this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
|
1406
1875
|
this.processing = void 0;
|
1407
1876
|
this.processQueue = new CommitQueue();
|
1408
1877
|
this.loader = loader;
|
1409
|
-
this.gateway = gateway;
|
1410
1878
|
}
|
1411
1879
|
async ready() {
|
1412
1880
|
return this._ready.once(async () => {
|
@@ -1425,25 +1893,34 @@ var RemoteWAL = class extends VersionedStore {
|
|
1425
1893
|
}
|
1426
1894
|
async enqueue(dbMeta, opts) {
|
1427
1895
|
await this.ready();
|
1428
|
-
if (opts.
|
1896
|
+
if (opts.compact) {
|
1897
|
+
this.walState.operations = [];
|
1898
|
+
this.walState.noLoaderOps = [dbMeta];
|
1899
|
+
} else if (opts.noLoader) {
|
1429
1900
|
this.walState.noLoaderOps.push(dbMeta);
|
1430
1901
|
} else {
|
1431
1902
|
this.walState.operations.push(dbMeta);
|
1432
1903
|
}
|
1433
1904
|
await this.save(this.walState);
|
1434
|
-
|
1905
|
+
if (!opts.noLoader) {
|
1906
|
+
void this.process();
|
1907
|
+
}
|
1435
1908
|
}
|
1436
1909
|
async enqueueFile(fileCid, publicFile = false) {
|
1437
1910
|
await this.ready();
|
1438
1911
|
this.walState.fileOperations.push({ cid: fileCid, public: publicFile });
|
1439
1912
|
}
|
1440
|
-
async
|
1913
|
+
async process() {
|
1441
1914
|
await this.ready();
|
1442
1915
|
if (!this.loader.remoteCarStore) return;
|
1443
1916
|
await this.processQueue.enqueue(async () => {
|
1444
|
-
|
1917
|
+
try {
|
1918
|
+
await this._doProcess();
|
1919
|
+
} catch (e) {
|
1920
|
+
this.logger.Error().Any("error", e).Msg("error processing wal");
|
1921
|
+
}
|
1445
1922
|
if (this.walState.operations.length || this.walState.fileOperations.length || this.walState.noLoaderOps.length) {
|
1446
|
-
setTimeout(() => void this.
|
1923
|
+
setTimeout(() => void this.process(), 0);
|
1447
1924
|
}
|
1448
1925
|
});
|
1449
1926
|
}
|
@@ -1454,14 +1931,14 @@ var RemoteWAL = class extends VersionedStore {
|
|
1454
1931
|
const fileOperations = [...this.walState.fileOperations];
|
1455
1932
|
const uploads = [];
|
1456
1933
|
const noLoaderOps = [...this.walState.noLoaderOps];
|
1457
|
-
const limit = pLimit2(
|
1934
|
+
const limit = pLimit2(3);
|
1458
1935
|
if (operations.length + fileOperations.length + noLoaderOps.length === 0) return;
|
1459
1936
|
for (const dbMeta of noLoaderOps) {
|
1460
1937
|
const uploadP = limit(async () => {
|
1461
1938
|
for (const cid of dbMeta.cars) {
|
1462
1939
|
const car = await (await this.loader.carStore()).load(cid);
|
1463
1940
|
if (!car) {
|
1464
|
-
if (
|
1941
|
+
if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars))
|
1465
1942
|
throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
|
1466
1943
|
} else {
|
1467
1944
|
await throwFalsy(this.loader.remoteCarStore).save(car);
|
@@ -1476,7 +1953,7 @@ var RemoteWAL = class extends VersionedStore {
|
|
1476
1953
|
for (const cid of dbMeta.cars) {
|
1477
1954
|
const car = await (await this.loader.carStore()).load(cid).catch(() => null);
|
1478
1955
|
if (!car) {
|
1479
|
-
if (
|
1956
|
+
if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars))
|
1480
1957
|
throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
|
1481
1958
|
} else {
|
1482
1959
|
await throwFalsy(this.loader.remoteCarStore).save(car);
|
@@ -1501,11 +1978,7 @@ var RemoteWAL = class extends VersionedStore {
|
|
1501
1978
|
const res = await Promise.allSettled(uploads);
|
1502
1979
|
const errors = res.filter((r) => r.status === "rejected");
|
1503
1980
|
if (errors.length) {
|
1504
|
-
throw this.logger.Error().Any(
|
1505
|
-
"errors",
|
1506
|
-
errors.map((e) => e.reason)
|
1507
|
-
).Msg("error uploading").AsError();
|
1508
|
-
errors[0].reason;
|
1981
|
+
throw this.logger.Error().Any("errors", errors).Msg("error uploading").AsError();
|
1509
1982
|
}
|
1510
1983
|
if (operations.length) {
|
1511
1984
|
const lastOp = operations[operations.length - 1];
|
@@ -1520,29 +1993,11 @@ var RemoteWAL = class extends VersionedStore {
|
|
1520
1993
|
})();
|
1521
1994
|
await rmlp;
|
1522
1995
|
}
|
1523
|
-
async start() {
|
1524
|
-
const res = await this.gateway.start(this.url);
|
1525
|
-
if (res.isErr()) {
|
1526
|
-
return res;
|
1527
|
-
}
|
1528
|
-
const ver = guardVersion(this.url);
|
1529
|
-
if (ver.isErr()) {
|
1530
|
-
await this.close();
|
1531
|
-
return ver;
|
1532
|
-
}
|
1533
|
-
const ready = await exception2Result(() => this.ready());
|
1534
|
-
this._onStarted.forEach((fn) => fn());
|
1535
|
-
if (ready.isErr()) {
|
1536
|
-
await this.close();
|
1537
|
-
return ready;
|
1538
|
-
}
|
1539
|
-
return ready;
|
1540
|
-
}
|
1541
1996
|
async load() {
|
1542
1997
|
this.logger.Debug().Msg("loading");
|
1543
|
-
const filepath = await this.gateway.buildUrl(this.url, "main");
|
1998
|
+
const filepath = await this.gateway.buildUrl(this.url(), "main");
|
1544
1999
|
if (filepath.isErr()) {
|
1545
|
-
throw this.logger.Error().Err(filepath.Err()).
|
2000
|
+
throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
|
1546
2001
|
}
|
1547
2002
|
const bytes = await this.gateway.get(filepath.Ok());
|
1548
2003
|
if (bytes.isErr()) {
|
@@ -1552,71 +2007,83 @@ var RemoteWAL = class extends VersionedStore {
|
|
1552
2007
|
throw this.logger.Error().Err(bytes.Err()).Msg("error get").AsError();
|
1553
2008
|
}
|
1554
2009
|
try {
|
1555
|
-
return bytes &&
|
2010
|
+
return bytes && parse2(this.sthis.txt.decode(bytes.Ok()));
|
1556
2011
|
} catch (e) {
|
1557
2012
|
throw this.logger.Error().Err(e).Msg("error parse").AsError();
|
1558
2013
|
}
|
1559
2014
|
}
|
1560
2015
|
async save(state) {
|
1561
|
-
const filepath = await this.gateway.buildUrl(this.url, "main");
|
2016
|
+
const filepath = await this.gateway.buildUrl(this.url(), "main");
|
1562
2017
|
if (filepath.isErr()) {
|
1563
|
-
throw this.logger.Error().Err(filepath.Err()).
|
2018
|
+
throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
|
1564
2019
|
}
|
1565
2020
|
let encoded;
|
1566
2021
|
try {
|
1567
|
-
encoded =
|
2022
|
+
encoded = format2(state);
|
1568
2023
|
} catch (e) {
|
1569
2024
|
throw this.logger.Error().Err(e).Any("state", state).Msg("error format").AsError();
|
1570
2025
|
}
|
1571
|
-
const res = await this.gateway.put(filepath.Ok(),
|
2026
|
+
const res = await this.gateway.put(filepath.Ok(), this.sthis.txt.encode(encoded));
|
1572
2027
|
if (res.isErr()) {
|
1573
2028
|
throw this.logger.Error().Err(res.Err()).Str("filePath", filepath.Ok().toString()).Msg("error saving").AsError();
|
1574
2029
|
}
|
1575
2030
|
}
|
1576
2031
|
async close() {
|
1577
|
-
await this.gateway.close(this.url);
|
2032
|
+
await this.gateway.close(this.url());
|
1578
2033
|
this._onClosed.forEach((fn) => fn());
|
1579
|
-
return
|
2034
|
+
return Result5.Ok(void 0);
|
1580
2035
|
}
|
1581
2036
|
destroy() {
|
1582
|
-
return this.gateway.destroy(this.url);
|
2037
|
+
return this.gateway.destroy(this.url());
|
1583
2038
|
}
|
1584
2039
|
};
|
1585
2040
|
|
1586
2041
|
// src/blockstore/store-factory.ts
|
1587
2042
|
function ensureIsIndex(url, isIndex) {
|
1588
2043
|
if (isIndex) {
|
1589
|
-
url.
|
1590
|
-
return url;
|
1591
|
-
} else {
|
1592
|
-
url.searchParams.delete("index");
|
1593
|
-
return url;
|
1594
|
-
}
|
1595
|
-
}
|
1596
|
-
function toURL(pathOrUrl, isIndex) {
|
1597
|
-
if (pathOrUrl instanceof URL) return ensureIsIndex(pathOrUrl, isIndex);
|
1598
|
-
try {
|
1599
|
-
const url = new URL(pathOrUrl);
|
1600
|
-
return ensureIsIndex(url, isIndex);
|
1601
|
-
} catch (e) {
|
1602
|
-
const url = new URL(`file://${pathOrUrl}`);
|
1603
|
-
return ensureIsIndex(url, isIndex);
|
2044
|
+
return url.build().setParam("index", isIndex).URI();
|
1604
2045
|
}
|
2046
|
+
return url.build().delParam("index").URI();
|
1605
2047
|
}
|
1606
|
-
var storeFactory = /* @__PURE__ */ new Map();
|
1607
2048
|
function ensureName(name, url) {
|
1608
|
-
if (!url.
|
1609
|
-
url.
|
2049
|
+
if (!url.hasParam("name")) {
|
2050
|
+
return url.build().setParam("name", name).URI();
|
1610
2051
|
}
|
2052
|
+
return url;
|
1611
2053
|
}
|
2054
|
+
var storeFactory = /* @__PURE__ */ new Map();
|
1612
2055
|
function buildURL(optURL, loader) {
|
1613
2056
|
const storeOpts = loader.ebOpts.store;
|
1614
2057
|
const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
|
1615
2058
|
let obuUrl;
|
1616
2059
|
if (obuItem && obuItem.overrideBaseURL) {
|
1617
|
-
obuUrl =
|
2060
|
+
obuUrl = URI5.from(obuItem.overrideBaseURL);
|
1618
2061
|
}
|
1619
|
-
|
2062
|
+
const ret = ensureIsIndex(
|
2063
|
+
URI5.from(optURL || obuUrl || dataDir(loader.sthis, loader.name, storeOpts.stores?.base)),
|
2064
|
+
storeOpts.isIndex
|
2065
|
+
);
|
2066
|
+
return ret;
|
2067
|
+
}
|
2068
|
+
var onceGateway = new KeyedResolvOnce2();
|
2069
|
+
async function getGatewayFromURL(url, sthis) {
|
2070
|
+
return onceGateway.get(url.toString()).once(async () => {
|
2071
|
+
const item = storeFactory.get(url.protocol);
|
2072
|
+
if (item) {
|
2073
|
+
const ret = {
|
2074
|
+
gateway: await item.gateway(sthis),
|
2075
|
+
test: await item.test(sthis)
|
2076
|
+
};
|
2077
|
+
const res = await ret.gateway.start(url);
|
2078
|
+
if (res.isErr()) {
|
2079
|
+
sthis.logger.Error().Result("start", res).Msg("start failed");
|
2080
|
+
return void 0;
|
2081
|
+
}
|
2082
|
+
return ret;
|
2083
|
+
}
|
2084
|
+
sthis.logger.Warn().Url(url).Msg("unsupported protocol");
|
2085
|
+
return void 0;
|
2086
|
+
});
|
1620
2087
|
}
|
1621
2088
|
function registerStoreProtocol(item) {
|
1622
2089
|
let protocol = item.protocol;
|
@@ -1625,8 +2092,7 @@ function registerStoreProtocol(item) {
|
|
1625
2092
|
}
|
1626
2093
|
if (storeFactory.has(protocol)) {
|
1627
2094
|
if (!item.overrideBaseURL && storeFactory.get(protocol) !== item) {
|
1628
|
-
|
1629
|
-
logger.Warn().Msg(`protocol ${protocol} already registered`);
|
2095
|
+
throw new Error(`we need a logger here`);
|
1630
2096
|
return () => {
|
1631
2097
|
};
|
1632
2098
|
}
|
@@ -1641,106 +2107,92 @@ function registerStoreProtocol(item) {
|
|
1641
2107
|
storeFactory.delete(protocol);
|
1642
2108
|
};
|
1643
2109
|
}
|
1644
|
-
|
1645
|
-
const item = storeFactory.get(url.protocol);
|
1646
|
-
if (!item) {
|
1647
|
-
throw logger.Error().Url(url).Str("protocol", url.protocol).Any("keys", Array(storeFactory.keys())).Msg(`unsupported protocol`).AsError();
|
1648
|
-
}
|
1649
|
-
logger.Debug().Str("protocol", url.protocol).Msg("run");
|
1650
|
-
return run(item);
|
1651
|
-
}
|
1652
|
-
var onceLoadDataGateway = new KeyedResolvOnce();
|
1653
|
-
function loadDataGateway(url, logger) {
|
1654
|
-
return onceLoadDataGateway.get(url.protocol).once(async () => {
|
1655
|
-
return runStoreFactory(url, logger, async (item) => item.data(logger));
|
1656
|
-
});
|
1657
|
-
}
|
1658
|
-
var onceDataStoreFactory = new KeyedResolvOnce();
|
2110
|
+
var onceDataStoreFactory = new KeyedResolvOnce2();
|
1659
2111
|
async function dataStoreFactory(loader) {
|
1660
|
-
const url = buildURL(loader.ebOpts.store.stores?.data, loader);
|
1661
|
-
|
1662
|
-
const logger = ensureLogger(loader.logger, "dataStoreFactory", { url: url.toString() });
|
1663
|
-
url.searchParams.set("store", "data");
|
2112
|
+
const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.data, loader)).build().setParam("store", "data").URI();
|
2113
|
+
const sthis = ensureSuperLog(loader.sthis, "dataStoreFactory", { url: url.toString() });
|
1664
2114
|
return onceDataStoreFactory.get(url.toString()).once(async () => {
|
1665
|
-
const gateway = await
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
2115
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2116
|
+
if (!gateway) {
|
2117
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2118
|
+
}
|
2119
|
+
const store = new DataStoreImpl(sthis, loader.name, url, {
|
2120
|
+
gateway: gateway.gateway,
|
2121
|
+
keybag: () => getKeyBag(loader.sthis, {
|
2122
|
+
...loader.ebOpts.keyBag
|
2123
|
+
})
|
2124
|
+
});
|
1672
2125
|
return store;
|
1673
2126
|
});
|
1674
2127
|
}
|
1675
|
-
var
|
1676
|
-
function loadMetaGateway(url, logger) {
|
1677
|
-
return onceLoadMetaGateway.get(url.protocol).once(async () => {
|
1678
|
-
return runStoreFactory(url, logger, async (item) => item.meta(logger));
|
1679
|
-
});
|
1680
|
-
}
|
1681
|
-
var onceMetaStoreFactory = new KeyedResolvOnce();
|
2128
|
+
var onceMetaStoreFactory = new KeyedResolvOnce2();
|
1682
2129
|
async function metaStoreFactory(loader) {
|
1683
|
-
const url = buildURL(loader.ebOpts.store.stores?.meta, loader);
|
1684
|
-
|
1685
|
-
const logger = ensureLogger(loader.logger, "metaStoreFactory", { url: () => url.toString() });
|
1686
|
-
url.searchParams.set("store", "meta");
|
2130
|
+
const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.meta, loader)).build().setParam("store", "meta").URI();
|
2131
|
+
const sthis = ensureSuperLog(loader.sthis, "metaStoreFactory", { url: () => url.toString() });
|
1687
2132
|
return onceMetaStoreFactory.get(url.toString()).once(async () => {
|
1688
|
-
logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
|
1689
|
-
const gateway = await
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
2133
|
+
sthis.logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
|
2134
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2135
|
+
if (!gateway) {
|
2136
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2137
|
+
}
|
2138
|
+
const store = new MetaStoreImpl(loader.sthis, loader.name, url, {
|
2139
|
+
gateway: gateway.gateway,
|
2140
|
+
keybag: () => getKeyBag(loader.sthis, {
|
2141
|
+
...loader.ebOpts.keyBag
|
2142
|
+
})
|
2143
|
+
});
|
1695
2144
|
return store;
|
1696
2145
|
});
|
1697
2146
|
}
|
1698
|
-
var
|
1699
|
-
function loadWalGateway(url, logger) {
|
1700
|
-
return onceWalGateway.get(url.protocol).once(async () => {
|
1701
|
-
return runStoreFactory(url, logger, async (item) => item.wal(logger));
|
1702
|
-
});
|
1703
|
-
}
|
1704
|
-
var onceRemoteWalFactory = new KeyedResolvOnce();
|
2147
|
+
var onceRemoteWalFactory = new KeyedResolvOnce2();
|
1705
2148
|
async function remoteWalFactory(loader) {
|
1706
|
-
const url = buildURL(loader.ebOpts.store.stores?.
|
1707
|
-
|
1708
|
-
const logger = ensureLogger(loader.logger, "remoteWalFactory", { url: url.toString() });
|
1709
|
-
url.searchParams.set("store", "wal");
|
2149
|
+
const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.wal, loader)).build().setParam("store", "wal").URI();
|
2150
|
+
const sthis = ensureSuperLog(loader.sthis, "remoteWalFactory", { url: url.toString() });
|
1710
2151
|
return onceRemoteWalFactory.get(url.toString()).once(async () => {
|
1711
|
-
const gateway = await
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
2152
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2153
|
+
if (!gateway) {
|
2154
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2155
|
+
}
|
2156
|
+
sthis.logger.Debug().Str("prepared", url.toString()).Msg("produced");
|
2157
|
+
const store = new WALStoreImpl(loader, url, {
|
2158
|
+
gateway: gateway.gateway,
|
2159
|
+
keybag: () => getKeyBag(loader.sthis, {
|
2160
|
+
...loader.ebOpts.keyBag
|
2161
|
+
})
|
2162
|
+
});
|
1718
2163
|
return store;
|
1719
2164
|
});
|
1720
2165
|
}
|
1721
|
-
async function testStoreFactory(url,
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
2166
|
+
async function testStoreFactory(url, sthis) {
|
2167
|
+
sthis = ensureSuperLog(sthis, "testStoreFactory");
|
2168
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2169
|
+
if (!gateway) {
|
2170
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2171
|
+
}
|
2172
|
+
return gateway.test;
|
2173
|
+
}
|
2174
|
+
async function ensureStart(store, logger) {
|
2175
|
+
const ret = await store.start();
|
2176
|
+
if (ret.isErr()) {
|
2177
|
+
throw logger.Error().Result("start", ret).Msg("start failed").AsError();
|
2178
|
+
}
|
2179
|
+
logger.Debug().Url(ret.Ok(), "prepared").Msg("produced");
|
2180
|
+
return store;
|
1729
2181
|
}
|
1730
|
-
function toStoreRuntime(opts,
|
1731
|
-
const logger = ensureLogger(
|
2182
|
+
function toStoreRuntime(opts, sthis) {
|
2183
|
+
const logger = ensureLogger(sthis, "toStoreRuntime", {});
|
1732
2184
|
return {
|
1733
|
-
makeMetaStore: (loader) => {
|
2185
|
+
makeMetaStore: async (loader) => {
|
1734
2186
|
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
|
1735
|
-
return (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader);
|
2187
|
+
return ensureStart(await (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader), logger);
|
1736
2188
|
},
|
1737
|
-
makeDataStore: (loader) => {
|
2189
|
+
makeDataStore: async (loader) => {
|
1738
2190
|
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
|
1739
|
-
return (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader);
|
2191
|
+
return ensureStart(await (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader), logger);
|
1740
2192
|
},
|
1741
|
-
|
1742
|
-
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.
|
1743
|
-
return (loader.ebOpts.store.
|
2193
|
+
makeWALStore: async (loader) => {
|
2194
|
+
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeWALStore).Msg("makeRemoteWAL");
|
2195
|
+
return ensureStart(await (loader.ebOpts.store.makeWALStore || remoteWalFactory)(loader), logger);
|
1744
2196
|
},
|
1745
2197
|
encodeFile: opts.encodeFile || encodeFile,
|
1746
2198
|
decodeFile: opts.decodeFile || decodeFile
|
@@ -1748,43 +2200,131 @@ function toStoreRuntime(opts, ilogger) {
|
|
1748
2200
|
}
|
1749
2201
|
registerStoreProtocol({
|
1750
2202
|
protocol: "file:",
|
1751
|
-
|
1752
|
-
const {
|
1753
|
-
return new
|
2203
|
+
gateway: async (sthis) => {
|
2204
|
+
const { FileGateway } = await import("./gateway-5FCWPX5W.js");
|
2205
|
+
return new FileGateway(sthis);
|
1754
2206
|
},
|
1755
|
-
|
1756
|
-
const {
|
1757
|
-
return new
|
1758
|
-
},
|
1759
|
-
wal: async (logger) => {
|
1760
|
-
const { FileWALGateway } = await import("./store-file-VJ6BI4II.js");
|
1761
|
-
return new FileWALGateway(logger);
|
1762
|
-
},
|
1763
|
-
test: async (logger) => {
|
1764
|
-
const { FileTestStore } = await import("./store-file-VJ6BI4II.js");
|
1765
|
-
return new FileTestStore(logger);
|
2207
|
+
test: async (sthis) => {
|
2208
|
+
const { FileTestStore } = await import("./gateway-5FCWPX5W.js");
|
2209
|
+
return new FileTestStore(sthis);
|
1766
2210
|
}
|
1767
2211
|
});
|
1768
2212
|
registerStoreProtocol({
|
1769
2213
|
protocol: "indexdb:",
|
1770
|
-
|
1771
|
-
const {
|
1772
|
-
return new
|
1773
|
-
},
|
1774
|
-
meta: async (logger) => {
|
1775
|
-
const { IndexDBMetaGateway } = await import("./store-indexdb-WLRSICCB.js");
|
1776
|
-
return new IndexDBMetaGateway(logger);
|
1777
|
-
},
|
1778
|
-
wal: async (logger) => {
|
1779
|
-
const { IndexDBMetaGateway } = await import("./store-indexdb-WLRSICCB.js");
|
1780
|
-
return new IndexDBMetaGateway(logger);
|
2214
|
+
gateway: async (sthis) => {
|
2215
|
+
const { IndexDBGateway } = await import("./gateway-H7UD6TNB.js");
|
2216
|
+
return new IndexDBGateway(sthis);
|
1781
2217
|
},
|
1782
|
-
test: async (
|
1783
|
-
const { IndexDBTestStore } = await import("./
|
1784
|
-
return new IndexDBTestStore(
|
2218
|
+
test: async (sthis) => {
|
2219
|
+
const { IndexDBTestStore } = await import("./gateway-H7UD6TNB.js");
|
2220
|
+
return new IndexDBTestStore(sthis);
|
1785
2221
|
}
|
1786
2222
|
});
|
1787
2223
|
|
2224
|
+
// src/blockstore/store-remote.ts
|
2225
|
+
async function RemoteDataStore(sthis, name, url, opts) {
|
2226
|
+
const ds = new DataStoreImpl(sthis, name, url, opts);
|
2227
|
+
await ds.start();
|
2228
|
+
return ds;
|
2229
|
+
}
|
2230
|
+
async function RemoteMetaStore(sthis, name, url, opts) {
|
2231
|
+
const ms = new MetaStoreImpl(
|
2232
|
+
sthis,
|
2233
|
+
name,
|
2234
|
+
url,
|
2235
|
+
opts
|
2236
|
+
/* , true*/
|
2237
|
+
);
|
2238
|
+
await ms.start();
|
2239
|
+
return ms;
|
2240
|
+
}
|
2241
|
+
|
2242
|
+
// src/blockstore/connection-base.ts
|
2243
|
+
var ConnectionBase = class {
|
2244
|
+
constructor(url, logger) {
|
2245
|
+
this.loaded = Promise.resolve();
|
2246
|
+
this.logger = logger;
|
2247
|
+
this.url = url;
|
2248
|
+
}
|
2249
|
+
async refresh() {
|
2250
|
+
await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load();
|
2251
|
+
await (await throwFalsy(this.loader).WALStore()).process();
|
2252
|
+
}
|
2253
|
+
async connect_X({ loader }) {
|
2254
|
+
if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
|
2255
|
+
await this.connectMeta_X({ loader });
|
2256
|
+
await this.connectStorage_X({ loader });
|
2257
|
+
}
|
2258
|
+
async connectMeta_X({ loader }) {
|
2259
|
+
if (!loader) throw this.logger.Error().Msg("connectMeta_X: loader is required").AsError();
|
2260
|
+
this.loader = loader;
|
2261
|
+
await this.onConnect();
|
2262
|
+
const metaUrl = this.url.build().defParam("store", "meta").URI();
|
2263
|
+
const gateway = await getGatewayFromURL(metaUrl, this.loader.sthis);
|
2264
|
+
if (!gateway) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: gateway is required").AsError();
|
2265
|
+
const dbName = metaUrl.getParam("name");
|
2266
|
+
if (!dbName) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: name is required").AsError();
|
2267
|
+
const remote = await RemoteMetaStore(loader.sthis, dbName, metaUrl, {
|
2268
|
+
gateway: gateway.gateway,
|
2269
|
+
keybag: () => getKeyBag(loader.sthis, loader.ebOpts.keyBag),
|
2270
|
+
loader
|
2271
|
+
});
|
2272
|
+
this.loader.remoteMetaStore = remote;
|
2273
|
+
this.loaded = this.loader.ready().then(async () => {
|
2274
|
+
remote.load().then(async () => {
|
2275
|
+
(await throwFalsy(this.loader).WALStore()).process();
|
2276
|
+
});
|
2277
|
+
});
|
2278
|
+
}
|
2279
|
+
async connectStorage_X({ loader }) {
|
2280
|
+
if (!loader) throw this.logger.Error().Msg("connectStorage_X: loader is required").AsError();
|
2281
|
+
this.loader = loader;
|
2282
|
+
const dataUrl = this.url.build().defParam("store", "data").URI();
|
2283
|
+
const gateway = await getGatewayFromURL(dataUrl, this.loader.sthis);
|
2284
|
+
if (!gateway) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: gateway is required").AsError();
|
2285
|
+
const name = dataUrl.getParam("name");
|
2286
|
+
if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: name is required").AsError;
|
2287
|
+
loader.remoteCarStore = await RemoteDataStore(loader.sthis, name, this.url, {
|
2288
|
+
gateway: gateway.gateway,
|
2289
|
+
keybag: () => getKeyBag(loader.sthis, this.loader?.ebOpts.keyBag)
|
2290
|
+
});
|
2291
|
+
loader.remoteFileStore = loader.remoteCarStore;
|
2292
|
+
}
|
2293
|
+
// move this stuff to connect
|
2294
|
+
// async getDashboardURL(compact = true) {
|
2295
|
+
// const baseUrl = 'https://dashboard.fireproof.storage/'
|
2296
|
+
// if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
|
2297
|
+
// // if (compact) {
|
2298
|
+
// // await this.compact()
|
2299
|
+
// // }
|
2300
|
+
// const currents = await this.loader?.metaStore?.load()
|
2301
|
+
// if (!currents) throw new Error("Can't sync empty database: save data first")
|
2302
|
+
// if (currents.length > 1)
|
2303
|
+
// throw new Error("Can't sync database with split heads: make an update first")
|
2304
|
+
// const current = currents[0]
|
2305
|
+
// const params = {
|
2306
|
+
// car: current.car.toString()
|
2307
|
+
// }
|
2308
|
+
// if (current.key) {
|
2309
|
+
// // @ts-ignore
|
2310
|
+
// params.key = current.key.toString()
|
2311
|
+
// }
|
2312
|
+
// // @ts-ignore
|
2313
|
+
// if (this.name) {
|
2314
|
+
// // @ts-ignore
|
2315
|
+
// params.name = this.name
|
2316
|
+
// }
|
2317
|
+
// const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
|
2318
|
+
// console.log('Import to dashboard: ' + url.toString())
|
2319
|
+
// return url
|
2320
|
+
// }
|
2321
|
+
// openDashboard() {
|
2322
|
+
// void this.getDashboardURL().then(url => {
|
2323
|
+
// if (url) window.open(url.toString(), '_blank')
|
2324
|
+
// })
|
2325
|
+
// }
|
2326
|
+
};
|
2327
|
+
|
1788
2328
|
// src/crdt-helpers.ts
|
1789
2329
|
function time(tag) {
|
1790
2330
|
}
|
@@ -1833,7 +2373,7 @@ async function writeDocContent(store, blocks, update, logger) {
|
|
1833
2373
|
await processFiles(store, blocks, update.value, logger);
|
1834
2374
|
value = { doc: update.value };
|
1835
2375
|
}
|
1836
|
-
const block = await
|
2376
|
+
const block = await encode({ value, hasher: hasher5, codec });
|
1837
2377
|
blocks.putSync(block.cid, block.bytes);
|
1838
2378
|
return block.cid;
|
1839
2379
|
}
|
@@ -1842,10 +2382,16 @@ async function processFiles(store, blocks, doc, logger) {
|
|
1842
2382
|
await processFileset(logger, store, blocks, doc._files);
|
1843
2383
|
}
|
1844
2384
|
if (doc._publicFiles) {
|
1845
|
-
await processFileset(
|
2385
|
+
await processFileset(
|
2386
|
+
logger,
|
2387
|
+
store,
|
2388
|
+
blocks,
|
2389
|
+
doc._publicFiles
|
2390
|
+
/*, true*/
|
2391
|
+
);
|
1846
2392
|
}
|
1847
2393
|
}
|
1848
|
-
async function processFileset(logger, store, blocks, files
|
2394
|
+
async function processFileset(logger, store, blocks, files) {
|
1849
2395
|
const dbBlockstore = blocks.parent;
|
1850
2396
|
if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, database name is required").AsError();
|
1851
2397
|
const t = new CarTransaction(dbBlockstore);
|
@@ -1867,9 +2413,10 @@ async function processFileset(logger, store, blocks, files, publicFiles = false)
|
|
1867
2413
|
}
|
1868
2414
|
}
|
1869
2415
|
if (didPut.length) {
|
1870
|
-
const car = await dbBlockstore.loader.commitFiles(
|
1871
|
-
|
1872
|
-
|
2416
|
+
const car = await dbBlockstore.loader.commitFiles(
|
2417
|
+
t,
|
2418
|
+
{ files }
|
2419
|
+
);
|
1873
2420
|
if (car) {
|
1874
2421
|
for (const name of didPut) {
|
1875
2422
|
files[name] = { car, ...files[name] };
|
@@ -1903,7 +2450,7 @@ function readFileset(blocks, files, isPublic = false) {
|
|
1903
2450
|
fileMeta.file = async () => await blocks.ebOpts.storeRuntime.decodeFile(
|
1904
2451
|
{
|
1905
2452
|
get: async (cid) => {
|
1906
|
-
return await blocks.getFile(throwFalsy(fileMeta.car), cid
|
2453
|
+
return await blocks.getFile(throwFalsy(fileMeta.car), cid);
|
1907
2454
|
}
|
1908
2455
|
},
|
1909
2456
|
fileMeta.cid,
|
@@ -1917,7 +2464,7 @@ function readFileset(blocks, files, isPublic = false) {
|
|
1917
2464
|
async function getValueFromLink(blocks, link, logger) {
|
1918
2465
|
const block = await blocks.get(link);
|
1919
2466
|
if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
|
1920
|
-
const { value } = await
|
2467
|
+
const { value } = await decode({ bytes: block.bytes, hasher: hasher5, codec });
|
1921
2468
|
const cvalue = {
|
1922
2469
|
...value,
|
1923
2470
|
cid: link
|
@@ -1926,17 +2473,21 @@ async function getValueFromLink(blocks, link, logger) {
|
|
1926
2473
|
return cvalue;
|
1927
2474
|
}
|
1928
2475
|
var DirtyEventFetcher = class extends EventFetcher {
|
2476
|
+
constructor(logger, blocks) {
|
2477
|
+
super(blocks);
|
2478
|
+
this.logger = logger;
|
2479
|
+
}
|
1929
2480
|
async get(link) {
|
1930
2481
|
try {
|
1931
2482
|
return super.get(link);
|
1932
2483
|
} catch (e) {
|
1933
|
-
|
2484
|
+
this.logger.Error().Ref("link", link.toString()).Err(e).Msg("Missing event");
|
1934
2485
|
return { value: void 0 };
|
1935
2486
|
}
|
1936
2487
|
}
|
1937
2488
|
};
|
1938
2489
|
async function clockChangesSince(blocks, head, since, opts, logger) {
|
1939
|
-
const eventsFetcher = opts.dirty ? new DirtyEventFetcher(blocks) : new EventFetcher(blocks);
|
2490
|
+
const eventsFetcher = opts.dirty ? new DirtyEventFetcher(logger, blocks) : new EventFetcher(blocks);
|
1940
2491
|
const keys = /* @__PURE__ */ new Set();
|
1941
2492
|
const updates = await gatherUpdates(
|
1942
2493
|
blocks,
|
@@ -2032,20 +2583,19 @@ async function doCompact(blockLog, head, logger) {
|
|
2032
2583
|
isCompacting = false;
|
2033
2584
|
}
|
2034
2585
|
async function getBlock(blocks, cidString) {
|
2035
|
-
const block = await blocks.get(
|
2586
|
+
const block = await blocks.get(parse3(cidString));
|
2036
2587
|
if (!block) throw new Error(`Missing block ${cidString}`);
|
2037
|
-
const { cid, value } = await
|
2038
|
-
return new
|
2588
|
+
const { cid, value } = await decode({ bytes: block.bytes, codec, hasher: hasher5 });
|
2589
|
+
return new Block({ cid, value, bytes: block.bytes });
|
2039
2590
|
}
|
2040
2591
|
|
2041
2592
|
// src/indexer-helpers.ts
|
2042
|
-
import {
|
2043
|
-
import
|
2044
|
-
import * as codec3 from "@ipld/dag-cbor";
|
2593
|
+
import { sha256 as hasher6 } from "multiformats/hashes/sha2";
|
2594
|
+
import * as codec2 from "@ipld/dag-cbor";
|
2045
2595
|
import charwise from "charwise";
|
2046
2596
|
import * as DbIndex from "prolly-trees/db-index";
|
2047
|
-
import { bf
|
2048
|
-
import { nocache as
|
2597
|
+
import { bf, simpleCompare } from "prolly-trees/utils";
|
2598
|
+
import { nocache as cache } from "prolly-trees/cache";
|
2049
2599
|
var IndexTree = class {
|
2050
2600
|
};
|
2051
2601
|
function refCompare(aRef, bRef) {
|
@@ -2061,8 +2611,8 @@ function compare(a, b) {
|
|
2061
2611
|
if (comp !== 0) return comp;
|
2062
2612
|
return refCompare(aRef, bRef);
|
2063
2613
|
}
|
2064
|
-
var byKeyOpts = { cache
|
2065
|
-
var byIdOpts = { cache
|
2614
|
+
var byKeyOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare };
|
2615
|
+
var byIdOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare: simpleCompare };
|
2066
2616
|
function indexEntriesForChanges(changes, mapFn) {
|
2067
2617
|
const indexEntries = [];
|
2068
2618
|
changes.forEach(({ id: key, value, del }) => {
|
@@ -2090,7 +2640,7 @@ function makeProllyGetBlock(blocks) {
|
|
2090
2640
|
const block = await blocks.get(address);
|
2091
2641
|
if (!block) throw new Error(`Missing block ${address.toString()}`);
|
2092
2642
|
const { cid, bytes } = block;
|
2093
|
-
return
|
2643
|
+
return create({ cid, bytes, hasher: hasher6, codec: codec2 });
|
2094
2644
|
};
|
2095
2645
|
}
|
2096
2646
|
async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
|
@@ -2161,25 +2711,25 @@ function encodeKey(key) {
|
|
2161
2711
|
}
|
2162
2712
|
|
2163
2713
|
// src/indexer.ts
|
2164
|
-
function index({ _crdt }, name, mapFn, meta) {
|
2714
|
+
function index(sthis, { _crdt }, name, mapFn, meta) {
|
2165
2715
|
if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
|
2166
2716
|
if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
|
2167
2717
|
if (_crdt.indexers.has(name)) {
|
2168
2718
|
const idx = _crdt.indexers.get(name);
|
2169
2719
|
idx.applyMapFn(name, mapFn, meta);
|
2170
2720
|
} else {
|
2171
|
-
const idx = new Index(_crdt, name, mapFn, meta);
|
2721
|
+
const idx = new Index(sthis, _crdt, name, mapFn, meta);
|
2172
2722
|
_crdt.indexers.set(name, idx);
|
2173
2723
|
}
|
2174
2724
|
return _crdt.indexers.get(name);
|
2175
2725
|
}
|
2176
2726
|
var Index = class {
|
2177
|
-
constructor(crdt, name, mapFn, meta) {
|
2727
|
+
constructor(sthis, crdt, name, mapFn, meta) {
|
2178
2728
|
this.mapFnString = "";
|
2179
2729
|
this.byKey = new IndexTree();
|
2180
2730
|
this.byId = new IndexTree();
|
2181
2731
|
this.includeDocsDefault = false;
|
2182
|
-
this.logger = ensureLogger(
|
2732
|
+
this.logger = ensureLogger(sthis, "Index");
|
2183
2733
|
this.blockstore = crdt.indexBlockstore;
|
2184
2734
|
this.crdt = crdt;
|
2185
2735
|
this.applyMapFn(name, mapFn, meta);
|
@@ -2234,7 +2784,7 @@ var Index = class {
|
|
2234
2784
|
}
|
2235
2785
|
if (this.mapFnString) {
|
2236
2786
|
if (this.mapFnString !== mapFn.toString()) {
|
2237
|
-
throw this.logger.Error().Msg("cannot apply different mapFn app").AsError();
|
2787
|
+
throw this.logger.Error().Str("mapFnString", this.mapFnString).Str("mapFn", mapFn.toString()).Msg("cannot apply different mapFn app").AsError();
|
2238
2788
|
}
|
2239
2789
|
} else {
|
2240
2790
|
this.mapFnString = mapFn.toString();
|
@@ -2369,7 +2919,7 @@ var Index = class {
|
|
2369
2919
|
// src/crdt-clock.ts
|
2370
2920
|
import { advance } from "@web3-storage/pail/clock";
|
2371
2921
|
import { root as root2 } from "@web3-storage/pail/crdt";
|
2372
|
-
import { ResolveOnce as
|
2922
|
+
import { ResolveOnce as ResolveOnce4 } from "@adviser/cement";
|
2373
2923
|
|
2374
2924
|
// src/apply-head-queue.ts
|
2375
2925
|
function applyHeadQueue(worker, logger) {
|
@@ -2384,7 +2934,7 @@ function applyHeadQueue(worker, logger) {
|
|
2384
2934
|
queue.sort((a, b) => b.updates ? 1 : -1);
|
2385
2935
|
const task = queue.shift();
|
2386
2936
|
if (!task) continue;
|
2387
|
-
await worker(task.newHead, task.prevHead, task.updates !==
|
2937
|
+
await worker(task.newHead, task.prevHead, task.updates !== void 0).catch((e) => {
|
2388
2938
|
throw logger.Error().Err(e).Msg("int_applyHead worker error").AsError();
|
2389
2939
|
});
|
2390
2940
|
if (task.updates) {
|
@@ -2426,9 +2976,9 @@ var CRDTClock = class {
|
|
2426
2976
|
this.zoomers = /* @__PURE__ */ new Set();
|
2427
2977
|
this.watchers = /* @__PURE__ */ new Set();
|
2428
2978
|
this.emptyWatchers = /* @__PURE__ */ new Set();
|
2429
|
-
this._ready = new
|
2979
|
+
this._ready = new ResolveOnce4();
|
2430
2980
|
this.blockstore = blockstore;
|
2431
|
-
this.logger = ensureLogger(blockstore.
|
2981
|
+
this.logger = ensureLogger(blockstore.sthis, "CRDTClock");
|
2432
2982
|
this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
|
2433
2983
|
}
|
2434
2984
|
async ready() {
|
@@ -2474,6 +3024,7 @@ var CRDTClock = class {
|
|
2474
3024
|
this.zoomers.add(fn);
|
2475
3025
|
}
|
2476
3026
|
async int_applyHead(newHead, prevHead, localUpdates) {
|
3027
|
+
const noLoader = !localUpdates;
|
2477
3028
|
const ogHead = sortClockHead(this.head);
|
2478
3029
|
newHead = sortClockHead(newHead);
|
2479
3030
|
if (compareClockHeads(ogHead, newHead)) {
|
@@ -2484,7 +3035,6 @@ var CRDTClock = class {
|
|
2484
3035
|
this.setHead(newHead);
|
2485
3036
|
return;
|
2486
3037
|
}
|
2487
|
-
const noLoader = !localUpdates;
|
2488
3038
|
if (!this.blockstore) {
|
2489
3039
|
throw this.logger.Error().Msg("missing blockstore").AsError();
|
2490
3040
|
}
|
@@ -2501,7 +3051,7 @@ var CRDTClock = class {
|
|
2501
3051
|
}
|
2502
3052
|
return { head: advancedHead };
|
2503
3053
|
},
|
2504
|
-
{ noLoader }
|
3054
|
+
{ noLoader, add: false }
|
2505
3055
|
);
|
2506
3056
|
this.setHead(meta.head);
|
2507
3057
|
}
|
@@ -2535,13 +3085,14 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
|
|
2535
3085
|
|
2536
3086
|
// src/crdt.ts
|
2537
3087
|
var CRDT = class {
|
2538
|
-
constructor(name, opts = {}) {
|
2539
|
-
this.onceReady = new ResolveOnce4();
|
3088
|
+
constructor(sthis, name, opts = {}) {
|
2540
3089
|
this.indexers = /* @__PURE__ */ new Map();
|
3090
|
+
this.onceReady = new ResolveOnce5();
|
3091
|
+
this.sthis = sthis;
|
2541
3092
|
this.name = name;
|
2542
|
-
this.logger = ensureLogger(
|
3093
|
+
this.logger = ensureLogger(sthis, "CRDT");
|
2543
3094
|
this.opts = opts;
|
2544
|
-
this.blockstore = blockstoreFactory({
|
3095
|
+
this.blockstore = blockstoreFactory(sthis, {
|
2545
3096
|
name,
|
2546
3097
|
applyMeta: async (meta) => {
|
2547
3098
|
const crdtMeta = meta;
|
@@ -2553,22 +3104,20 @@ var CRDT = class {
|
|
2553
3104
|
return { head: this.clock.head };
|
2554
3105
|
},
|
2555
3106
|
autoCompact: this.opts.autoCompact || 100,
|
2556
|
-
crypto: this.opts.crypto,
|
2557
3107
|
store: { ...this.opts.store, isIndex: void 0 },
|
2558
3108
|
public: this.opts.public,
|
2559
3109
|
meta: this.opts.meta,
|
2560
3110
|
threshold: this.opts.threshold
|
2561
3111
|
});
|
2562
|
-
this.indexBlockstore = blockstoreFactory({
|
3112
|
+
this.indexBlockstore = blockstoreFactory(sthis, {
|
2563
3113
|
name,
|
2564
3114
|
applyMeta: async (meta) => {
|
2565
3115
|
const idxCarMeta = meta;
|
2566
3116
|
if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
|
2567
3117
|
for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
|
2568
|
-
index({ _crdt: this }, name2, void 0, idx);
|
3118
|
+
index(this.sthis, { _crdt: this }, name2, void 0, idx);
|
2569
3119
|
}
|
2570
3120
|
},
|
2571
|
-
crypto: this.opts.crypto,
|
2572
3121
|
store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
|
2573
3122
|
public: this.opts.public
|
2574
3123
|
});
|
@@ -2579,17 +3128,6 @@ var CRDT = class {
|
|
2579
3128
|
}
|
2580
3129
|
});
|
2581
3130
|
}
|
2582
|
-
async ready() {
|
2583
|
-
return this.onceReady.once(async () => {
|
2584
|
-
await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
|
2585
|
-
});
|
2586
|
-
}
|
2587
|
-
async close() {
|
2588
|
-
await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
|
2589
|
-
}
|
2590
|
-
async destroy() {
|
2591
|
-
await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
|
2592
|
-
}
|
2593
3131
|
async bulk(updates) {
|
2594
3132
|
await this.ready();
|
2595
3133
|
const prevHead = [...this.clock.head];
|
@@ -2610,6 +3148,21 @@ var CRDT = class {
|
|
2610
3148
|
await this.clock.applyHead(done.meta.head, prevHead, updates);
|
2611
3149
|
return done.meta;
|
2612
3150
|
}
|
3151
|
+
async ready() {
|
3152
|
+
return this.onceReady.once(async () => {
|
3153
|
+
try {
|
3154
|
+
await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
|
3155
|
+
} catch (e) {
|
3156
|
+
throw this.logger.Error().Err(e).Msg(`CRDT is not ready`).AsError();
|
3157
|
+
}
|
3158
|
+
});
|
3159
|
+
}
|
3160
|
+
async close() {
|
3161
|
+
await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
|
3162
|
+
}
|
3163
|
+
async destroy() {
|
3164
|
+
await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
|
3165
|
+
}
|
2613
3166
|
// if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
|
2614
3167
|
async allDocs() {
|
2615
3168
|
await this.ready();
|
@@ -2654,11 +3207,12 @@ var Database = class {
|
|
2654
3207
|
this._listening = false;
|
2655
3208
|
this._listeners = /* @__PURE__ */ new Set();
|
2656
3209
|
this._noupdate_listeners = /* @__PURE__ */ new Set();
|
2657
|
-
this._ready = new
|
3210
|
+
this._ready = new ResolveOnce6();
|
2658
3211
|
this.name = name;
|
2659
3212
|
this.opts = opts || this.opts;
|
2660
|
-
this.
|
2661
|
-
this.
|
3213
|
+
this.sthis = ensureSuperThis(this.opts);
|
3214
|
+
this.logger = ensureLogger(this.sthis, "Database");
|
3215
|
+
this._crdt = new CRDT(this.sthis, name, this.opts);
|
2662
3216
|
this.blockstore = this._crdt.blockstore;
|
2663
3217
|
this._writeQueue = writeQueue(async (updates) => {
|
2664
3218
|
return await this._crdt.bulk(updates);
|
@@ -2682,7 +3236,7 @@ var Database = class {
|
|
2682
3236
|
}
|
2683
3237
|
async ready() {
|
2684
3238
|
return this._ready.once(async () => {
|
2685
|
-
await
|
3239
|
+
await this.sthis.start();
|
2686
3240
|
await this._crdt.ready();
|
2687
3241
|
await this.blockstore.ready();
|
2688
3242
|
});
|
@@ -2702,7 +3256,7 @@ var Database = class {
|
|
2702
3256
|
await this.ready();
|
2703
3257
|
this.logger.Debug().Str("id", doc._id).Msg("put");
|
2704
3258
|
const { _id, ...value } = doc;
|
2705
|
-
const docId = _id ||
|
3259
|
+
const docId = _id || this.sthis.timeOrderedNextId().str;
|
2706
3260
|
const result = await this._writeQueue.push({
|
2707
3261
|
id: docId,
|
2708
3262
|
value: {
|
@@ -2767,7 +3321,7 @@ var Database = class {
|
|
2767
3321
|
await this.ready();
|
2768
3322
|
this.logger.Debug().Any("field", field).Any("opts", opts).Msg("query");
|
2769
3323
|
const _crdt = this._crdt;
|
2770
|
-
const idx = typeof field === "string" ? index({ _crdt }, field) : index({ _crdt }, makeName(field.toString()), field);
|
3324
|
+
const idx = typeof field === "string" ? index(this.sthis, { _crdt }, field) : index(this.sthis, { _crdt }, makeName(field.toString()), field);
|
2771
3325
|
return await idx.query(opts);
|
2772
3326
|
}
|
2773
3327
|
async compact() {
|
@@ -2804,12 +3358,7 @@ function fireproof(name, opts) {
|
|
2804
3358
|
const key = JSON.stringify(
|
2805
3359
|
toSortedArray({
|
2806
3360
|
name,
|
2807
|
-
stores: toSortedArray(opts?.store?.stores)
|
2808
|
-
makeMetaStore: !!opts?.store?.makeMetaStore,
|
2809
|
-
makeDataStore: !!opts?.store?.makeDataStore,
|
2810
|
-
makeRemoteWAL: !!opts?.store?.makeRemoteWAL,
|
2811
|
-
encodeFile: !!opts?.store?.encodeFile,
|
2812
|
-
decodeFile: !!opts?.store?.decodeFile
|
3361
|
+
stores: toSortedArray(opts?.store?.stores)
|
2813
3362
|
})
|
2814
3363
|
);
|
2815
3364
|
let db = Database.databases.get(key);
|
@@ -2824,7 +3373,10 @@ function makeName(fnString) {
|
|
2824
3373
|
let found = null;
|
2825
3374
|
const matches = Array.from(fnString.matchAll(regex), (match) => match[1].trim());
|
2826
3375
|
if (matches.length === 0) {
|
2827
|
-
found = /=>\s*(
|
3376
|
+
found = /=>\s*{?\s*([^{}]+)\s*}?/.exec(fnString);
|
3377
|
+
if (found && found[1].includes("return")) {
|
3378
|
+
found = null;
|
3379
|
+
}
|
2828
3380
|
}
|
2829
3381
|
if (!found) {
|
2830
3382
|
return fnString;
|
@@ -2833,20 +3385,53 @@ function makeName(fnString) {
|
|
2833
3385
|
}
|
2834
3386
|
}
|
2835
3387
|
|
3388
|
+
// src/runtime/index.ts
|
3389
|
+
var runtime_exports = {};
|
3390
|
+
__export(runtime_exports, {
|
3391
|
+
FILESTORE_VERSION: () => FILESTORE_VERSION,
|
3392
|
+
INDEXDB_VERSION: () => INDEXDB_VERSION,
|
3393
|
+
files: () => files_exports,
|
3394
|
+
getFileName: () => getFileName,
|
3395
|
+
getFileSystem: () => getFileSystem,
|
3396
|
+
getPath: () => getPath,
|
3397
|
+
kb: () => key_bag_exports,
|
3398
|
+
kc: () => keyed_crypto_exports,
|
3399
|
+
mf: () => wait_pr_multiformats_exports,
|
3400
|
+
runtimeFn: () => runtimeFn2,
|
3401
|
+
toArrayBuffer: () => toArrayBuffer
|
3402
|
+
});
|
3403
|
+
|
3404
|
+
// src/runtime/wait-pr-multiformats/index.ts
|
3405
|
+
var wait_pr_multiformats_exports = {};
|
3406
|
+
__export(wait_pr_multiformats_exports, {
|
3407
|
+
block: () => block_exports,
|
3408
|
+
codec: () => codec_interface_exports
|
3409
|
+
});
|
3410
|
+
|
3411
|
+
// src/runtime/wait-pr-multiformats/codec-interface.ts
|
3412
|
+
var codec_interface_exports = {};
|
3413
|
+
|
3414
|
+
// src/runtime/index.ts
|
3415
|
+
import { runtimeFn as runtimeFn2 } from "@adviser/cement";
|
3416
|
+
|
2836
3417
|
// src/version.ts
|
2837
3418
|
var PACKAGE_VERSION = Object.keys({
|
2838
|
-
"0.19.8-dev-
|
3419
|
+
"0.19.8-dev-series-1": "xxxx"
|
2839
3420
|
})[0];
|
2840
3421
|
export {
|
2841
3422
|
CRDT,
|
2842
3423
|
Database,
|
2843
3424
|
Index,
|
3425
|
+
NotFoundError,
|
2844
3426
|
PACKAGE_VERSION,
|
2845
3427
|
Result,
|
3428
|
+
UInt8ArrayEqual,
|
2846
3429
|
blockstore_exports as blockstore,
|
2847
3430
|
blockstore_exports as bs,
|
3431
|
+
dataDir,
|
2848
3432
|
ensureLogger,
|
2849
|
-
|
3433
|
+
ensureSuperLog,
|
3434
|
+
ensureSuperThis,
|
2850
3435
|
exceptionWrapper,
|
2851
3436
|
falsyToUndef,
|
2852
3437
|
fireproof,
|
@@ -2855,6 +3440,7 @@ export {
|
|
2855
3440
|
getStore,
|
2856
3441
|
index,
|
2857
3442
|
isFalsy,
|
3443
|
+
isNotFoundError,
|
2858
3444
|
runtime_exports as rt,
|
2859
3445
|
runtime_exports as runtime,
|
2860
3446
|
throwFalsy
|