@fireproof/core 0.19.8-dev-global → 0.19.8-dev-series-2
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 +22 -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 +2365 -1875
- package/index.cjs.map +1 -1
- package/index.d.cts +663 -535
- package/index.d.ts +663 -535
- package/index.global.js +26742 -20717
- package/index.global.js.map +1 -1
- package/index.js +1691 -1094
- 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 +14 -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,146 @@ __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
|
-
throw this.logger.Error().Msg("failed to upload data " + done.statusText);
|
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 { format as format2, parse as parse2 } from "@ipld/dag-json";
|
256
|
+
import { exception2Result, ResolveOnce as ResolveOnce3, Result as Result5 } from "@adviser/cement";
|
257
|
+
|
258
|
+
// src/types.ts
|
259
|
+
function isFalsy(value) {
|
260
|
+
return value === false && value === null && value === void 0;
|
261
|
+
}
|
262
|
+
function throwFalsy(value) {
|
263
|
+
if (isFalsy(value)) {
|
264
|
+
throw new Error("value is Falsy");
|
262
265
|
}
|
263
|
-
|
264
|
-
|
266
|
+
return value;
|
267
|
+
}
|
268
|
+
function falsyToUndef(value) {
|
269
|
+
if (isFalsy(value)) {
|
265
270
|
return void 0;
|
266
271
|
}
|
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";
|
272
|
+
return value;
|
273
|
+
}
|
280
274
|
|
281
275
|
// src/blockstore/loader.ts
|
282
276
|
import pLimit from "p-limit";
|
283
277
|
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
|
-
}
|
278
|
+
import { ResolveOnce as ResolveOnce2 } from "@adviser/cement";
|
290
279
|
|
291
280
|
// src/blockstore/loader-helpers.ts
|
292
|
-
import { encode, decode } from "multiformats/block";
|
293
281
|
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
|
-
}
|
282
|
+
import * as dagCodec from "@ipld/dag-cbor";
|
322
283
|
async function parseCarFile(reader, logger) {
|
323
284
|
const roots = await reader.getRoots();
|
324
285
|
const header = await reader.get(roots[0]);
|
325
286
|
if (!header) throw logger.Error().Msg("missing header block").AsError();
|
326
|
-
const
|
327
|
-
const fpvalue = value;
|
287
|
+
const dec = await decode({ bytes: header.bytes, hasher, codec: dagCodec });
|
288
|
+
const fpvalue = dec.value;
|
328
289
|
if (fpvalue && !fpvalue.fp) {
|
329
290
|
throw logger.Error().Msg("missing fp").AsError();
|
330
291
|
}
|
331
292
|
return fpvalue.fp;
|
332
293
|
}
|
333
294
|
|
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
295
|
// src/blockstore/transaction.ts
|
590
|
-
import { MemoryBlockstore
|
591
|
-
|
592
|
-
|
296
|
+
import { MemoryBlockstore } from "@web3-storage/pail/block";
|
297
|
+
import { toCryptoRuntime } from "@adviser/cement";
|
298
|
+
var CarTransaction = class extends MemoryBlockstore {
|
299
|
+
constructor(parent, opts = { add: true, noLoader: false }) {
|
593
300
|
super();
|
594
301
|
if (opts.add) {
|
595
302
|
parent.transactions.add(this);
|
@@ -603,8 +310,8 @@ var CarTransaction = class extends MemoryBlockstore3 {
|
|
603
310
|
return super.get(cid);
|
604
311
|
}
|
605
312
|
};
|
606
|
-
function defaultedBlockstoreRuntime(opts, component, ctx) {
|
607
|
-
const logger = ensureLogger(
|
313
|
+
function defaultedBlockstoreRuntime(sthis, opts, component, ctx) {
|
314
|
+
const logger = ensureLogger(sthis, component, ctx);
|
608
315
|
const store = opts.store || {};
|
609
316
|
return {
|
610
317
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
@@ -621,22 +328,24 @@ function defaultedBlockstoreRuntime(opts, component, ctx) {
|
|
621
328
|
threshold: 1e3 * 1e3,
|
622
329
|
...opts,
|
623
330
|
logger,
|
624
|
-
|
331
|
+
keyBag: opts.keyBag || {},
|
332
|
+
crypto: toCryptoRuntime(opts.crypto),
|
625
333
|
store,
|
626
|
-
storeRuntime: toStoreRuntime(store,
|
334
|
+
storeRuntime: toStoreRuntime(store, sthis)
|
627
335
|
};
|
628
336
|
}
|
629
|
-
|
337
|
+
function blockstoreFactory(sthis, opts) {
|
630
338
|
if (opts.name) {
|
631
|
-
return new EncryptedBlockstore(opts);
|
339
|
+
return new EncryptedBlockstore(sthis, opts);
|
632
340
|
} else {
|
633
341
|
return new BaseBlockstore(opts);
|
634
342
|
}
|
635
|
-
}
|
343
|
+
}
|
636
344
|
var BaseBlockstore = class {
|
637
345
|
constructor(ebOpts = {}) {
|
638
346
|
this.transactions = /* @__PURE__ */ new Set();
|
639
|
-
this.
|
347
|
+
this.sthis = ensureSuperThis(ebOpts);
|
348
|
+
this.ebOpts = defaultedBlockstoreRuntime(this.sthis, ebOpts, "BaseBlockstore");
|
640
349
|
this.logger = this.ebOpts.logger;
|
641
350
|
}
|
642
351
|
// ready: Promise<void>;
|
@@ -659,8 +368,8 @@ var BaseBlockstore = class {
|
|
659
368
|
throw this.logger.Error().Msg("use a transaction to put").AsError();
|
660
369
|
}
|
661
370
|
// TransactionMeta
|
662
|
-
async transaction(fn, _opts
|
663
|
-
const t = new CarTransaction(this);
|
371
|
+
async transaction(fn, _opts) {
|
372
|
+
const t = new CarTransaction(this, _opts);
|
664
373
|
const done = await fn(t);
|
665
374
|
this.lastTxMeta = done;
|
666
375
|
return { t, meta: done };
|
@@ -677,16 +386,16 @@ var BaseBlockstore = class {
|
|
677
386
|
}
|
678
387
|
};
|
679
388
|
var EncryptedBlockstore = class extends BaseBlockstore {
|
680
|
-
constructor(ebOpts) {
|
389
|
+
constructor(sthis, ebOpts) {
|
681
390
|
super(ebOpts);
|
682
391
|
this.compacting = false;
|
683
|
-
this.logger = ensureLogger(
|
392
|
+
this.logger = ensureLogger(this.sthis, "EncryptedBlockstore");
|
684
393
|
const { name } = ebOpts;
|
685
394
|
if (!name) {
|
686
395
|
throw this.logger.Error().Msg("name required").AsError();
|
687
396
|
}
|
688
397
|
this.name = name;
|
689
|
-
this.loader = new Loader(this.name, ebOpts);
|
398
|
+
this.loader = new Loader(this.name, ebOpts, sthis);
|
690
399
|
}
|
691
400
|
ready() {
|
692
401
|
return this.loader.ready();
|
@@ -717,10 +426,13 @@ var EncryptedBlockstore = class extends BaseBlockstore {
|
|
717
426
|
}
|
718
427
|
throw this.logger.Error().Msg("failed to commit car files").AsError();
|
719
428
|
}
|
720
|
-
async getFile(car, cid
|
429
|
+
async getFile(car, cid) {
|
721
430
|
await this.ready();
|
722
431
|
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(
|
432
|
+
const reader = await this.loader.loadFileCar(
|
433
|
+
car
|
434
|
+
/*, isPublic */
|
435
|
+
);
|
724
436
|
const block = await reader.get(cid);
|
725
437
|
if (!block) throw this.logger.Error().Str("cid", cid.toString()).Msg(`Missing block`).AsError();
|
726
438
|
return block.bytes;
|
@@ -776,10 +488,20 @@ var CompactionFetcher = class {
|
|
776
488
|
};
|
777
489
|
|
778
490
|
// src/blockstore/commit-queue.ts
|
491
|
+
import { Future } from "@adviser/cement";
|
779
492
|
var CommitQueue = class {
|
780
493
|
constructor() {
|
781
494
|
this.queue = [];
|
782
495
|
this.processing = false;
|
496
|
+
this._waitIdleItems = /* @__PURE__ */ new Set();
|
497
|
+
}
|
498
|
+
waitIdle() {
|
499
|
+
if (this.queue.length === 0 && !this.processing) {
|
500
|
+
return Promise.resolve();
|
501
|
+
}
|
502
|
+
const fn = new Future();
|
503
|
+
this._waitIdleItems.add(fn);
|
504
|
+
return fn.asPromise();
|
783
505
|
}
|
784
506
|
async enqueue(fn) {
|
785
507
|
return new Promise((resolve, reject) => {
|
@@ -804,38 +526,384 @@ var CommitQueue = class {
|
|
804
526
|
this.processing = true;
|
805
527
|
const queueFn = this.queue.shift();
|
806
528
|
if (queueFn) {
|
807
|
-
queueFn()
|
529
|
+
queueFn().finally(() => {
|
530
|
+
});
|
808
531
|
}
|
809
532
|
}
|
533
|
+
if (this.queue.length === 0 && !this.processing) {
|
534
|
+
const toResolve = Array.from(this._waitIdleItems);
|
535
|
+
this._waitIdleItems.clear();
|
536
|
+
toResolve.map((fn) => fn.resolve());
|
537
|
+
}
|
810
538
|
}
|
811
539
|
};
|
812
540
|
|
813
|
-
// src/
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
}
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
541
|
+
// src/runtime/key-bag.ts
|
542
|
+
var key_bag_exports = {};
|
543
|
+
__export(key_bag_exports, {
|
544
|
+
KeyBag: () => KeyBag,
|
545
|
+
getKeyBag: () => getKeyBag,
|
546
|
+
registerKeyBagProviderFactory: () => registerKeyBagProviderFactory
|
547
|
+
});
|
548
|
+
import {
|
549
|
+
KeyedResolvOnce,
|
550
|
+
ResolveOnce,
|
551
|
+
ResolveSeq,
|
552
|
+
Result as Result2,
|
553
|
+
runtimeFn,
|
554
|
+
toCryptoRuntime as toCryptoRuntime2,
|
555
|
+
URI
|
556
|
+
} from "@adviser/cement";
|
557
|
+
import { base58btc } from "multiformats/bases/base58";
|
558
|
+
var KeyBag = class {
|
559
|
+
constructor(rt) {
|
560
|
+
this.rt = rt;
|
561
|
+
this._warnOnce = new ResolveOnce();
|
562
|
+
this._seq = new ResolveSeq();
|
563
|
+
this.logger = ensureLogger(rt.sthis, "KeyBag");
|
564
|
+
this.logger.Debug().Msg("KeyBag created");
|
565
|
+
}
|
566
|
+
async subtleKey(key) {
|
567
|
+
const extractable = this.rt.url.getParam("extractKey") === "_deprecated_internal_api";
|
568
|
+
if (extractable) {
|
569
|
+
this._warnOnce.once(
|
570
|
+
() => this.logger.Warn().Msg("extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!")
|
571
|
+
);
|
572
|
+
}
|
573
|
+
return await this.rt.crypto.importKey(
|
574
|
+
"raw",
|
575
|
+
// raw or jwk
|
576
|
+
base58btc.decode(key),
|
577
|
+
// hexStringToUint8Array(key), // raw data
|
578
|
+
"AES-GCM",
|
579
|
+
extractable,
|
580
|
+
["encrypt", "decrypt"]
|
581
|
+
);
|
825
582
|
}
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
}
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
583
|
+
async ensureKeyFromUrl(url, keyFactory) {
|
584
|
+
const storeKey = url.getParam("storekey");
|
585
|
+
if (storeKey === "insecure") {
|
586
|
+
return Result2.Ok(url);
|
587
|
+
}
|
588
|
+
if (!storeKey) {
|
589
|
+
const keyName = `@${keyFactory()}@`;
|
590
|
+
const ret = await this.getNamedKey(keyName);
|
591
|
+
if (ret.isErr()) {
|
592
|
+
return ret;
|
593
|
+
}
|
594
|
+
const urb = url.build().setParam("storekey", keyName);
|
595
|
+
return Result2.Ok(urb.URI());
|
596
|
+
}
|
597
|
+
if (storeKey.startsWith("@") && storeKey.endsWith("@")) {
|
598
|
+
const ret = await this.getNamedKey(storeKey);
|
599
|
+
if (ret.isErr()) {
|
600
|
+
return ret;
|
601
|
+
}
|
602
|
+
}
|
603
|
+
return Result2.Ok(url);
|
604
|
+
}
|
605
|
+
async toKeyWithFingerPrint(keyStr) {
|
606
|
+
const material = base58btc.decode(keyStr);
|
607
|
+
const key = await this.subtleKey(keyStr);
|
608
|
+
const fpr = await this.rt.crypto.digestSHA256(material);
|
609
|
+
return Result2.Ok({
|
610
|
+
key,
|
611
|
+
fingerPrint: base58btc.encode(new Uint8Array(fpr))
|
612
|
+
});
|
613
|
+
}
|
614
|
+
async setNamedKey(name, key) {
|
615
|
+
return this._seq.add(() => this._setNamedKey(name, key));
|
616
|
+
}
|
617
|
+
// avoid deadlock
|
618
|
+
async _setNamedKey(name, key) {
|
619
|
+
const item = {
|
620
|
+
name,
|
621
|
+
key
|
622
|
+
};
|
623
|
+
const bag = await this.rt.getBag();
|
624
|
+
this.logger.Debug().Str("name", name).Msg("setNamedKey");
|
625
|
+
await bag.set(name, item);
|
626
|
+
return await this.toKeyWithFingerPrint(item.key);
|
627
|
+
}
|
628
|
+
async getNamedExtractableKey(name, failIfNotFound = false) {
|
629
|
+
const ret = await this.getNamedKey(name, failIfNotFound);
|
630
|
+
if (ret.isErr()) {
|
631
|
+
return ret;
|
632
|
+
}
|
633
|
+
const named = ret.Ok();
|
634
|
+
return Result2.Ok({
|
635
|
+
...named,
|
636
|
+
extract: async () => {
|
637
|
+
const ext = new Uint8Array(await this.rt.crypto.exportKey("raw", named.key));
|
638
|
+
return {
|
639
|
+
key: ext,
|
640
|
+
keyStr: base58btc.encode(ext)
|
641
|
+
};
|
642
|
+
}
|
643
|
+
});
|
644
|
+
}
|
645
|
+
async getNamedKey(name, failIfNotFound = false) {
|
646
|
+
const id = this.rt.sthis.nextId(4).str;
|
647
|
+
return this._seq.add(async () => {
|
648
|
+
const bag = await this.rt.getBag();
|
649
|
+
const named = await bag.get(name);
|
650
|
+
if (named) {
|
651
|
+
const fpr = await this.toKeyWithFingerPrint(named.key);
|
652
|
+
this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", fpr).Msg("fingerPrint getNamedKey");
|
653
|
+
return fpr;
|
654
|
+
}
|
655
|
+
if (failIfNotFound) {
|
656
|
+
this.logger.Debug().Str("id", id).Str("name", name).Msg("failIfNotFound getNamedKey");
|
657
|
+
return Result2.Err(new Error(`Key not found: ${name}`));
|
658
|
+
}
|
659
|
+
const ret = await this._setNamedKey(name, base58btc.encode(this.rt.crypto.randomBytes(this.rt.keyLength)));
|
660
|
+
this.logger.Debug().Str("id", id).Str("name", name).Result("fpr", ret).Msg("createKey getNamedKey-post");
|
661
|
+
return ret;
|
662
|
+
});
|
835
663
|
}
|
836
664
|
};
|
665
|
+
var keyBagProviderFactories = new Map(
|
666
|
+
[
|
667
|
+
{
|
668
|
+
protocol: "file:",
|
669
|
+
factory: async (url, sthis) => {
|
670
|
+
const { KeyBagProviderFile } = await import("./key-bag-file-WADZBHYG.js");
|
671
|
+
return new KeyBagProviderFile(url, sthis);
|
672
|
+
}
|
673
|
+
},
|
674
|
+
{
|
675
|
+
protocol: "indexdb:",
|
676
|
+
factory: async (url, sthis) => {
|
677
|
+
const { KeyBagProviderIndexDB } = await import("./key-bag-indexdb-PGVAI3FJ.js");
|
678
|
+
return new KeyBagProviderIndexDB(url, sthis);
|
679
|
+
}
|
680
|
+
}
|
681
|
+
].map((i) => [i.protocol, i])
|
682
|
+
);
|
683
|
+
function registerKeyBagProviderFactory(item) {
|
684
|
+
const protocol = item.protocol.endsWith(":") ? item.protocol : item.protocol + ":";
|
685
|
+
keyBagProviderFactories.set(protocol, {
|
686
|
+
...item,
|
687
|
+
protocol
|
688
|
+
});
|
689
|
+
}
|
690
|
+
function defaultKeyBagOpts(sthis, kbo) {
|
691
|
+
if (kbo.keyRuntime) {
|
692
|
+
return kbo.keyRuntime;
|
693
|
+
}
|
694
|
+
const logger = ensureLogger(sthis, "KeyBag");
|
695
|
+
let url;
|
696
|
+
if (kbo.url) {
|
697
|
+
url = URI.from(kbo.url);
|
698
|
+
logger.Debug().Url(url).Msg("from opts");
|
699
|
+
} else {
|
700
|
+
let bagFnameOrUrl = sthis.env.get("FP_KEYBAG_URL");
|
701
|
+
if (runtimeFn().isBrowser) {
|
702
|
+
url = URI.from(bagFnameOrUrl || "indexdb://fp-keybag");
|
703
|
+
} else {
|
704
|
+
if (!bagFnameOrUrl) {
|
705
|
+
const home = sthis.env.get("HOME");
|
706
|
+
bagFnameOrUrl = `${home}/.fireproof/keybag`;
|
707
|
+
url = URI.from(`file://${bagFnameOrUrl}`);
|
708
|
+
} else {
|
709
|
+
url = URI.from(bagFnameOrUrl);
|
710
|
+
}
|
711
|
+
}
|
712
|
+
logger.Debug().Url(url).Msg("from env");
|
713
|
+
}
|
714
|
+
const kitem = keyBagProviderFactories.get(url.protocol);
|
715
|
+
if (!kitem) {
|
716
|
+
throw logger.Error().Url(url).Msg("unsupported protocol").AsError();
|
717
|
+
}
|
718
|
+
const getBag = async () => kitem.factory(url, sthis);
|
719
|
+
if (url.hasParam("masterkey")) {
|
720
|
+
throw logger.Error().Url(url).Msg("masterkey is not supported").AsError();
|
721
|
+
}
|
722
|
+
return {
|
723
|
+
url,
|
724
|
+
crypto: kbo.crypto || toCryptoRuntime2({}),
|
725
|
+
sthis,
|
726
|
+
logger,
|
727
|
+
keyLength: kbo.keyLength || 16,
|
728
|
+
getBag,
|
729
|
+
id: () => {
|
730
|
+
return url.toString();
|
731
|
+
}
|
732
|
+
};
|
733
|
+
}
|
734
|
+
var _keyBags = new KeyedResolvOnce();
|
735
|
+
async function getKeyBag(sthis, kbo = {}) {
|
736
|
+
await sthis.start();
|
737
|
+
const rt = defaultKeyBagOpts(sthis, kbo);
|
738
|
+
return _keyBags.get(rt.id()).once(async () => new KeyBag(rt));
|
739
|
+
}
|
740
|
+
|
741
|
+
// src/blockstore/commitor.ts
|
742
|
+
import * as CBW from "@ipld/car/buffer-writer";
|
743
|
+
import { sha256 as hasher2 } from "multiformats/hashes/sha2";
|
744
|
+
import * as dagCodec2 from "@ipld/dag-cbor";
|
745
|
+
async function encodeCarFile(roots, t, codec3) {
|
746
|
+
let size = 0;
|
747
|
+
const headerSize = CBW.headerLength({ roots });
|
748
|
+
size += headerSize;
|
749
|
+
for (const { cid, bytes } of t.entries()) {
|
750
|
+
size += CBW.blockLength({ cid, bytes });
|
751
|
+
}
|
752
|
+
const buffer = new Uint8Array(size);
|
753
|
+
const writer = CBW.createWriter(buffer, { headerSize });
|
754
|
+
for (const r of roots) {
|
755
|
+
writer.addRoot(r);
|
756
|
+
}
|
757
|
+
for (const { cid, bytes } of t.entries()) {
|
758
|
+
writer.write({ cid, bytes });
|
759
|
+
}
|
760
|
+
writer.close();
|
761
|
+
return await encode({ value: writer.bytes, hasher: hasher2, codec: codec3 });
|
762
|
+
}
|
763
|
+
async function createCarFile(encoder, cid, t) {
|
764
|
+
return encodeCarFile([cid], t, encoder);
|
765
|
+
}
|
766
|
+
async function commitFiles(fileStore, walStore, t, done) {
|
767
|
+
const { files: roots } = makeFileCarHeader(done);
|
768
|
+
const cids = [];
|
769
|
+
const codec3 = (await fileStore.keyedCrypto()).codec();
|
770
|
+
const cars = await prepareCarFilesFiles(codec3, roots, t);
|
771
|
+
for (const car of cars) {
|
772
|
+
const { cid, bytes } = car;
|
773
|
+
await fileStore.save({ cid, bytes });
|
774
|
+
await walStore.enqueueFile(
|
775
|
+
cid
|
776
|
+
/*, !!opts.public*/
|
777
|
+
);
|
778
|
+
cids.push(cid);
|
779
|
+
}
|
780
|
+
return cids;
|
781
|
+
}
|
782
|
+
function makeFileCarHeader(result) {
|
783
|
+
const files = [];
|
784
|
+
for (const [, meta] of Object.entries(result.files || {})) {
|
785
|
+
if (meta && typeof meta === "object" && "cid" in meta && meta !== null) {
|
786
|
+
files.push(meta.cid);
|
787
|
+
}
|
788
|
+
}
|
789
|
+
return { ...result, files };
|
790
|
+
}
|
791
|
+
async function prepareCarFilesFiles(encoder, roots, t) {
|
792
|
+
return [await encodeCarFile(roots, t, encoder)];
|
793
|
+
}
|
794
|
+
function makeCarHeader(meta, cars, compact = false) {
|
795
|
+
const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
|
796
|
+
return { ...coreHeader, meta };
|
797
|
+
}
|
798
|
+
async function encodeCarHeader(fp) {
|
799
|
+
return await encode({
|
800
|
+
value: { fp },
|
801
|
+
hasher: hasher2,
|
802
|
+
codec: dagCodec2
|
803
|
+
});
|
804
|
+
}
|
805
|
+
async function commit(params, t, done, opts = { noLoader: false, compact: false }) {
|
806
|
+
const fp = makeCarHeader(done, params.carLog, !!opts.compact);
|
807
|
+
const rootBlock = await encodeCarHeader(fp);
|
808
|
+
const cars = await prepareCarFiles(params.encoder, params.threshold, rootBlock, t);
|
809
|
+
const cids = [];
|
810
|
+
for (const car of cars) {
|
811
|
+
const { cid, bytes } = car;
|
812
|
+
await params.carStore.save({ cid, bytes });
|
813
|
+
cids.push(cid);
|
814
|
+
}
|
815
|
+
const newDbMeta = { cars: cids };
|
816
|
+
await params.WALStore.enqueue(newDbMeta, opts);
|
817
|
+
await params.metaStore.save(newDbMeta);
|
818
|
+
return { cgrp: cids, header: fp };
|
819
|
+
}
|
820
|
+
async function prepareCarFiles(encoder, threshold, rootBlock, t) {
|
821
|
+
const carFiles = [];
|
822
|
+
threshold = threshold || 128e3 * 8;
|
823
|
+
let clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
|
824
|
+
clonedt.putSync(rootBlock.cid, rootBlock.bytes);
|
825
|
+
let newsize = CBW.blockLength(toCIDBlock(rootBlock));
|
826
|
+
let cidRootBlock = rootBlock;
|
827
|
+
for (const { cid, bytes } of t.entries()) {
|
828
|
+
newsize += CBW.blockLength(toCIDBlock({ cid, bytes }));
|
829
|
+
if (newsize >= threshold) {
|
830
|
+
carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
|
831
|
+
clonedt = new CarTransaction(t.parent, { add: false, noLoader: false });
|
832
|
+
clonedt.putSync(cid, bytes);
|
833
|
+
cidRootBlock = { cid, bytes };
|
834
|
+
newsize = CBW.blockLength(toCIDBlock({ cid, bytes }));
|
835
|
+
} else {
|
836
|
+
clonedt.putSync(cid, bytes);
|
837
|
+
}
|
838
|
+
}
|
839
|
+
carFiles.push(await createCarFile(encoder, cidRootBlock.cid, clonedt));
|
840
|
+
return carFiles;
|
841
|
+
}
|
842
|
+
|
843
|
+
// src/blockstore/loader.ts
|
844
|
+
import { sha256 as hasher3 } from "multiformats/hashes/sha2";
|
845
|
+
|
846
|
+
// src/blockstore/task-manager.ts
|
847
|
+
var TaskManager = class {
|
848
|
+
constructor(sthis, callback) {
|
849
|
+
this.eventsWeHandled = /* @__PURE__ */ new Set();
|
850
|
+
this.queue = [];
|
851
|
+
this.isProcessing = false;
|
852
|
+
this.logger = ensureLogger(sthis, "TaskManager");
|
853
|
+
this.callback = callback;
|
854
|
+
}
|
855
|
+
async handleEvent(cid, parents, dbMeta) {
|
856
|
+
for (const parent of parents) {
|
857
|
+
this.eventsWeHandled.add(parent.toString());
|
858
|
+
}
|
859
|
+
this.queue.push({ cid: cid.toString(), dbMeta, retries: 0 });
|
860
|
+
this.queue = this.queue.filter(({ cid: cid2 }) => !this.eventsWeHandled.has(cid2));
|
861
|
+
void this.processQueue();
|
862
|
+
}
|
863
|
+
async processQueue() {
|
864
|
+
if (this.isProcessing) return;
|
865
|
+
this.isProcessing = true;
|
866
|
+
const filteredQueue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
|
867
|
+
const first = filteredQueue[0];
|
868
|
+
if (!first) {
|
869
|
+
return;
|
870
|
+
}
|
871
|
+
try {
|
872
|
+
await this.callback(first.dbMeta);
|
873
|
+
this.eventsWeHandled.add(first.cid);
|
874
|
+
this.queue = this.queue.filter(({ cid }) => !this.eventsWeHandled.has(cid));
|
875
|
+
} catch (err) {
|
876
|
+
if (first.retries++ > 3) {
|
877
|
+
this.logger.Error().Str("cid", first.cid).Msg("failed to process event block after 3 retries");
|
878
|
+
this.queue = this.queue.filter(({ cid }) => cid !== first.cid);
|
879
|
+
}
|
880
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
881
|
+
throw this.logger.Error().Err(err).Msg("failed to process event block").AsError();
|
882
|
+
} finally {
|
883
|
+
this.isProcessing = false;
|
884
|
+
if (this.queue.length > 0) {
|
885
|
+
void this.processQueue();
|
886
|
+
}
|
887
|
+
}
|
888
|
+
}
|
889
|
+
};
|
890
|
+
|
891
|
+
// src/blockstore/loader.ts
|
892
|
+
function carLogIncludesGroup(list, cids) {
|
893
|
+
return list.some((arr) => {
|
894
|
+
return arr.toString() === cids.toString();
|
895
|
+
});
|
896
|
+
}
|
897
|
+
function uniqueCids(list, remove = /* @__PURE__ */ new Set()) {
|
898
|
+
const byString = /* @__PURE__ */ new Map();
|
899
|
+
for (const cid of list) {
|
900
|
+
if (remove.has(cid.toString())) continue;
|
901
|
+
byString.set(cid.toString(), cid);
|
902
|
+
}
|
903
|
+
return [...byString.values()];
|
904
|
+
}
|
837
905
|
var Loader = class {
|
838
|
-
constructor(name, ebOpts) {
|
906
|
+
constructor(name, ebOpts, sthis) {
|
839
907
|
this.commitQueue = new CommitQueue();
|
840
908
|
this.isCompacting = false;
|
841
909
|
this.carReaders = /* @__PURE__ */ new Map();
|
@@ -845,9 +913,11 @@ var Loader = class {
|
|
845
913
|
this.getBlockCache = /* @__PURE__ */ new Map();
|
846
914
|
this.seenMeta = /* @__PURE__ */ new Set();
|
847
915
|
this.writeLimit = pLimit(1);
|
848
|
-
this.onceReady = new
|
916
|
+
this.onceReady = new ResolveOnce2();
|
849
917
|
this.name = name;
|
918
|
+
this.sthis = sthis;
|
850
919
|
this.ebOpts = defaultedBlockstoreRuntime(
|
920
|
+
sthis,
|
851
921
|
{
|
852
922
|
...ebOpts,
|
853
923
|
name
|
@@ -855,34 +925,42 @@ var Loader = class {
|
|
855
925
|
"Loader"
|
856
926
|
);
|
857
927
|
this.logger = this.ebOpts.logger;
|
928
|
+
this.taskManager = new TaskManager(sthis, async (dbMeta) => {
|
929
|
+
await this.handleDbMetasFromStore([dbMeta]);
|
930
|
+
});
|
858
931
|
}
|
859
932
|
// readonly id = uuidv4();
|
933
|
+
async keyBag() {
|
934
|
+
return getKeyBag(this.sthis, this.ebOpts.keyBag);
|
935
|
+
}
|
860
936
|
async carStore() {
|
861
937
|
return this.ebOpts.storeRuntime.makeDataStore(this);
|
862
938
|
}
|
863
939
|
async fileStore() {
|
864
940
|
return this.ebOpts.storeRuntime.makeDataStore(this);
|
865
941
|
}
|
866
|
-
async
|
867
|
-
return this.ebOpts.storeRuntime.
|
942
|
+
async WALStore() {
|
943
|
+
return this.ebOpts.storeRuntime.makeWALStore(this);
|
868
944
|
}
|
869
945
|
async metaStore() {
|
870
946
|
return this.ebOpts.storeRuntime.makeMetaStore(this);
|
871
947
|
}
|
872
948
|
async ready() {
|
873
949
|
return this.onceReady.once(async () => {
|
874
|
-
const metas =
|
875
|
-
if (
|
950
|
+
const metas = await (await this.metaStore()).load();
|
951
|
+
if (this.ebOpts.meta) {
|
952
|
+
await this.handleDbMetasFromStore([this.ebOpts.meta]);
|
953
|
+
} else if (metas) {
|
876
954
|
await this.handleDbMetasFromStore(metas);
|
877
955
|
}
|
878
956
|
});
|
879
957
|
}
|
880
958
|
async close() {
|
881
|
-
const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.
|
959
|
+
const toClose = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
|
882
960
|
await Promise.all(toClose.map((store) => store.close()));
|
883
961
|
}
|
884
962
|
async destroy() {
|
885
|
-
const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.
|
963
|
+
const toDestroy = await Promise.all([this.carStore(), this.metaStore(), this.fileStore(), this.WALStore()]);
|
886
964
|
await Promise.all(toDestroy.map((store) => store.destroy()));
|
887
965
|
}
|
888
966
|
// async snapToCar(carCid: AnyLink | string) {
|
@@ -896,6 +974,7 @@ var Loader = class {
|
|
896
974
|
// await this._applyCarHeader(carHeader, true)
|
897
975
|
// }
|
898
976
|
async handleDbMetasFromStore(metas) {
|
977
|
+
this.logger.Debug().Any("metas", metas).Msg("handleDbMetasFromStore");
|
899
978
|
for (const meta of metas) {
|
900
979
|
await this.writeLimit(async () => {
|
901
980
|
await this.mergeDbMetaIntoClock(meta);
|
@@ -908,10 +987,7 @@ var Loader = class {
|
|
908
987
|
}
|
909
988
|
if (this.seenMeta.has(meta.cars.toString())) return;
|
910
989
|
this.seenMeta.add(meta.cars.toString());
|
911
|
-
if (meta.
|
912
|
-
await this.setKey(meta.key);
|
913
|
-
}
|
914
|
-
if (carLogIncludesGroup2(this.carLog, meta.cars)) {
|
990
|
+
if (carLogIncludesGroup(this.carLog, meta.cars)) {
|
915
991
|
return;
|
916
992
|
}
|
917
993
|
const carHeader = await this.loadCarHeaderFromMeta(meta);
|
@@ -920,45 +996,60 @@ var Loader = class {
|
|
920
996
|
this.carLog = [...uniqueCids([meta.cars, ...this.carLog, ...carHeader.cars], this.seenCompacted)];
|
921
997
|
await this.ebOpts.applyMeta?.(carHeader.meta);
|
922
998
|
}
|
923
|
-
async ingestKeyFromMeta(meta) {
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
}
|
999
|
+
// protected async ingestKeyFromMeta(meta: DbMeta): Promise<void> {
|
1000
|
+
// const { key } = meta;
|
1001
|
+
// if (key) {
|
1002
|
+
// await this.setKey(key);
|
1003
|
+
// }
|
1004
|
+
// }
|
929
1005
|
async loadCarHeaderFromMeta({ cars: cids }) {
|
930
1006
|
const reader = await this.loadCar(cids[0]);
|
931
1007
|
return await parseCarFile(reader, this.logger);
|
932
1008
|
}
|
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 }) {
|
1009
|
+
// async _getKey(): Promise<string | undefined> {
|
1010
|
+
// if (this.key) return this.key;
|
1011
|
+
// // generate a random key
|
1012
|
+
// if (!this.ebOpts.public) {
|
1013
|
+
// await this.setKey(toHexString(this.ebOpts.crypto.randomBytes(32)));
|
1014
|
+
// }
|
1015
|
+
// return this.key || undefined;
|
1016
|
+
// }
|
1017
|
+
async commitFiles(t, done) {
|
945
1018
|
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;
|
1019
|
+
const fstore = await this.fileStore();
|
1020
|
+
const wstore = await this.WALStore();
|
1021
|
+
return this.commitQueue.enqueue(() => commitFiles(fstore, wstore, t, done));
|
956
1022
|
}
|
957
|
-
async loadFileCar(cid
|
958
|
-
return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore
|
1023
|
+
async loadFileCar(cid) {
|
1024
|
+
return await this.storesLoadCar(cid, await this.fileStore(), this.remoteFileStore);
|
959
1025
|
}
|
960
1026
|
async commit(t, done, opts = { noLoader: false, compact: false }) {
|
961
|
-
|
1027
|
+
await this.ready();
|
1028
|
+
const fstore = await this.fileStore();
|
1029
|
+
const params = {
|
1030
|
+
encoder: (await fstore.keyedCrypto()).codec(),
|
1031
|
+
carLog: this.carLog,
|
1032
|
+
carStore: fstore,
|
1033
|
+
WALStore: await this.WALStore(),
|
1034
|
+
metaStore: await this.metaStore(),
|
1035
|
+
threshold: this.ebOpts.threshold
|
1036
|
+
};
|
1037
|
+
return this.commitQueue.enqueue(async () => {
|
1038
|
+
await this.cacheTransaction(t);
|
1039
|
+
const ret = await commit(params, t, done, opts);
|
1040
|
+
await this.updateCarLog(ret.cgrp, ret.header, !!opts.compact);
|
1041
|
+
return ret.cgrp;
|
1042
|
+
});
|
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]).catch((e) => e);
|
1050
|
+
} else {
|
1051
|
+
this.carLog.unshift(cids);
|
1052
|
+
}
|
962
1053
|
}
|
963
1054
|
async cacheTransaction(t) {
|
964
1055
|
for await (const block of t.entries()) {
|
@@ -978,79 +1069,6 @@ var Loader = class {
|
|
978
1069
|
}
|
979
1070
|
}
|
980
1071
|
}
|
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
1072
|
async removeCidsForCompact(cid) {
|
1055
1073
|
const carHeader = await this.loadCarHeaderFromMeta({
|
1056
1074
|
cars: [cid]
|
@@ -1069,9 +1087,9 @@ var Loader = class {
|
|
1069
1087
|
// await this.remoteWAL!.enqueue(dbMeta, { public: false })
|
1070
1088
|
// }
|
1071
1089
|
// }
|
1072
|
-
async *entries(
|
1090
|
+
async *entries(cache2 = true) {
|
1073
1091
|
await this.ready();
|
1074
|
-
if (
|
1092
|
+
if (cache2) {
|
1075
1093
|
for (const [, block] of this.getBlockCache) {
|
1076
1094
|
yield block;
|
1077
1095
|
}
|
@@ -1153,10 +1171,6 @@ var Loader = class {
|
|
1153
1171
|
}
|
1154
1172
|
return got;
|
1155
1173
|
}
|
1156
|
-
makeCarHeader(meta, cars, compact = false) {
|
1157
|
-
const coreHeader = compact ? { cars: [], compact: cars } : { cars, compact: [] };
|
1158
|
-
return { ...coreHeader, meta };
|
1159
|
-
}
|
1160
1174
|
async loadCar(cid) {
|
1161
1175
|
if (!this.carStore) {
|
1162
1176
|
throw this.logger.Error().Msg("car store not initialized").AsError();
|
@@ -1164,94 +1178,516 @@ var Loader = class {
|
|
1164
1178
|
const loaded = await this.storesLoadCar(cid, await this.carStore(), this.remoteCarStore);
|
1165
1179
|
return loaded;
|
1166
1180
|
}
|
1181
|
+
async makeDecoderAndCarReader(cid, local, remote) {
|
1182
|
+
const cidsString = cid.toString();
|
1183
|
+
let loadedCar = void 0;
|
1184
|
+
let activeStore = local;
|
1185
|
+
try {
|
1186
|
+
this.logger.Debug().Str("cid", cidsString).Msg("loading car");
|
1187
|
+
loadedCar = await local.load(cid);
|
1188
|
+
this.logger.Debug().Bool("loadedCar", loadedCar).Msg("loaded");
|
1189
|
+
} catch (e) {
|
1190
|
+
if (remote) {
|
1191
|
+
const remoteCar = await remote.load(cid);
|
1192
|
+
if (remoteCar) {
|
1193
|
+
this.logger.Debug().Ref("cid", remoteCar.cid).Msg("saving remote car locally");
|
1194
|
+
await local.save(remoteCar);
|
1195
|
+
loadedCar = remoteCar;
|
1196
|
+
activeStore = remote;
|
1197
|
+
}
|
1198
|
+
} else {
|
1199
|
+
this.logger.Error().Str("cid", cidsString).Err(e).Msg("loading car");
|
1200
|
+
}
|
1201
|
+
}
|
1202
|
+
if (!loadedCar) {
|
1203
|
+
throw this.logger.Error().Url(local.url()).Str("cid", cidsString).Msg("missing car files").AsError();
|
1204
|
+
}
|
1205
|
+
const bytes = await decode({ bytes: loadedCar.bytes, hasher: hasher3, codec: (await activeStore.keyedCrypto()).codec() });
|
1206
|
+
const rawReader = await CarReader.fromBytes(bytes.value);
|
1207
|
+
const readerP = Promise.resolve(rawReader);
|
1208
|
+
const cachedReaderP = readerP.then(async (reader) => {
|
1209
|
+
await this.cacheCarReader(cidsString, reader).catch((e) => {
|
1210
|
+
this.logger.Error().Err(e).Str("cid", cidsString).Msg("error caching car reader");
|
1211
|
+
return;
|
1212
|
+
});
|
1213
|
+
return reader;
|
1214
|
+
});
|
1215
|
+
this.carReaders.set(cidsString, cachedReaderP);
|
1216
|
+
return readerP;
|
1217
|
+
}
|
1167
1218
|
//What if instead it returns an Array of CarHeader
|
1168
|
-
async storesLoadCar(cid, local, remote
|
1219
|
+
async storesLoadCar(cid, local, remote) {
|
1169
1220
|
const cidsString = cid.toString();
|
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
|
-
);
|
1221
|
+
let dacr = this.carReaders.get(cidsString);
|
1222
|
+
if (!dacr) {
|
1223
|
+
dacr = this.makeDecoderAndCarReader(cid, local, remote);
|
1224
|
+
this.carReaders.set(cidsString, dacr);
|
1225
|
+
}
|
1226
|
+
return dacr;
|
1227
|
+
}
|
1228
|
+
async getMoreReaders(cids) {
|
1229
|
+
const limit = pLimit(5);
|
1230
|
+
const missing = cids.filter((cid) => !this.carReaders.has(cid.toString()));
|
1231
|
+
await Promise.all(missing.map((cid) => limit(() => this.loadCar(cid))));
|
1232
|
+
}
|
1233
|
+
};
|
1234
|
+
|
1235
|
+
// src/runtime/keyed-crypto.ts
|
1236
|
+
var keyed_crypto_exports = {};
|
1237
|
+
__export(keyed_crypto_exports, {
|
1238
|
+
BlockIvKeyIdCodec: () => BlockIvKeyIdCodec,
|
1239
|
+
keyedCryptoFactory: () => keyedCryptoFactory
|
1240
|
+
});
|
1241
|
+
import { base58btc as base58btc2 } from "multiformats/bases/base58";
|
1242
|
+
import { sha256 as hasher4 } from "multiformats/hashes/sha2";
|
1243
|
+
import * as CBOR from "cborg";
|
1244
|
+
var generateIV = {
|
1245
|
+
random: {
|
1246
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1247
|
+
calc: async (ko, crypto, data) => {
|
1248
|
+
return crypto.randomBytes(ko.ivLength);
|
1249
|
+
},
|
1250
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1251
|
+
verify: async (ko, crypto, iv, data) => {
|
1252
|
+
return true;
|
1253
|
+
}
|
1254
|
+
},
|
1255
|
+
hash: {
|
1256
|
+
calc: async (ko, crypto, data) => {
|
1257
|
+
const hash = await hasher4.digest(data);
|
1258
|
+
const hashBytes = new Uint8Array(hash.bytes);
|
1259
|
+
const hashArray = new Uint8Array(ko.ivLength);
|
1260
|
+
for (let i = 0; i < hashBytes.length; i++) {
|
1261
|
+
hashArray[i % ko.ivLength] ^= hashBytes[i];
|
1262
|
+
}
|
1263
|
+
return hashArray;
|
1264
|
+
},
|
1265
|
+
verify: async function(ko, crypto, iv, data) {
|
1266
|
+
return ko.url.getParam("ivverify") !== "disable" && UInt8ArrayEqual(iv, await this.calc(ko, crypto, data));
|
1267
|
+
}
|
1268
|
+
}
|
1269
|
+
};
|
1270
|
+
function getGenerateIVFn(url, opts) {
|
1271
|
+
const ivhash = opts.ivCalc || url.getParam("ivhash") || "hash";
|
1272
|
+
return generateIV[ivhash] || generateIV["hash"];
|
1273
|
+
}
|
1274
|
+
var BlockIvKeyIdCodec = class {
|
1275
|
+
constructor(ko, iv, opts) {
|
1276
|
+
this.code = 3147065;
|
1277
|
+
this.name = "Fireproof@encrypted-block:aes-gcm";
|
1278
|
+
this.ko = ko;
|
1279
|
+
this.iv = iv;
|
1280
|
+
this.opts = opts || {};
|
1281
|
+
}
|
1282
|
+
async encode(data) {
|
1283
|
+
const calcIv = this.iv || await getGenerateIVFn(this.ko.url, this.opts).calc(this.ko, this.ko.crypto, data);
|
1284
|
+
const { iv } = this.ko.algo(calcIv);
|
1285
|
+
const fprt = await this.ko.fingerPrint();
|
1286
|
+
const keyId = base58btc2.decode(fprt);
|
1287
|
+
this.ko.logger.Debug().Str("fp", fprt).Msg("encode");
|
1288
|
+
return CBOR.encode({
|
1289
|
+
iv,
|
1290
|
+
keyId,
|
1291
|
+
data: await this.ko._encrypt({ iv, bytes: data })
|
1292
|
+
});
|
1293
|
+
}
|
1294
|
+
async decode(abytes) {
|
1295
|
+
let bytes;
|
1296
|
+
if (abytes instanceof Uint8Array) {
|
1297
|
+
bytes = abytes;
|
1298
|
+
} else {
|
1299
|
+
bytes = new Uint8Array(abytes);
|
1300
|
+
}
|
1301
|
+
const { iv, keyId, data } = CBOR.decode(bytes);
|
1302
|
+
const fprt = await this.ko.fingerPrint();
|
1303
|
+
this.ko.logger.Debug().Str("fp", base58btc2.encode(keyId)).Msg("decode");
|
1304
|
+
if (base58btc2.encode(keyId) !== fprt) {
|
1305
|
+
throw this.ko.logger.Error().Str("fp", fprt).Str("keyId", base58btc2.encode(keyId)).Msg("keyId mismatch").AsError();
|
1306
|
+
}
|
1307
|
+
const result = await this.ko._decrypt({ iv, bytes: data });
|
1308
|
+
if (!this.opts?.noIVVerify && !await getGenerateIVFn(this.ko.url, this.opts).verify(this.ko, this.ko.crypto, iv, result)) {
|
1309
|
+
throw this.ko.logger.Error().Msg("iv missmatch").AsError();
|
1310
|
+
}
|
1311
|
+
return result;
|
1312
|
+
}
|
1313
|
+
};
|
1314
|
+
var keyedCrypto = class {
|
1315
|
+
constructor(url, key, cyopt, sthis) {
|
1316
|
+
this.ivLength = 12;
|
1317
|
+
this.isEncrypting = true;
|
1318
|
+
this.logger = ensureLogger(sthis, "keyedCrypto");
|
1319
|
+
this.crypto = cyopt;
|
1320
|
+
this.key = key;
|
1321
|
+
this.url = url;
|
1322
|
+
}
|
1323
|
+
fingerPrint() {
|
1324
|
+
return Promise.resolve(this.key.fingerPrint);
|
1325
|
+
}
|
1326
|
+
codec(iv, opts) {
|
1327
|
+
return new BlockIvKeyIdCodec(this, iv, opts);
|
1328
|
+
}
|
1329
|
+
algo(iv) {
|
1330
|
+
return {
|
1331
|
+
name: "AES-GCM",
|
1332
|
+
iv: iv || this.crypto.randomBytes(this.ivLength),
|
1333
|
+
tagLength: 128
|
1334
|
+
};
|
1335
|
+
}
|
1336
|
+
async _decrypt(data) {
|
1337
|
+
this.logger.Debug().Len(data.bytes, "bytes").Len(data.iv, "iv").Str("fp", this.key.fingerPrint).Msg("decrypting");
|
1338
|
+
return new Uint8Array(await this.crypto.decrypt(this.algo(data.iv), this.key.key, data.bytes));
|
1339
|
+
}
|
1340
|
+
async _encrypt(data) {
|
1341
|
+
this.logger.Debug().Len(data.bytes).Str("fp", this.key.fingerPrint).Msg("encrypting");
|
1342
|
+
const a = this.algo(data.iv);
|
1343
|
+
return new Uint8Array(await this.crypto.encrypt(a, this.key.key, data.bytes));
|
1344
|
+
}
|
1345
|
+
};
|
1346
|
+
var nullCodec = class {
|
1347
|
+
constructor() {
|
1348
|
+
this.code = 0;
|
1349
|
+
this.name = "Fireproof@unencrypted-block";
|
1350
|
+
}
|
1351
|
+
encode(data) {
|
1352
|
+
return data;
|
1353
|
+
}
|
1354
|
+
decode(data) {
|
1355
|
+
return data;
|
1356
|
+
}
|
1357
|
+
};
|
1358
|
+
var noCrypto = class {
|
1359
|
+
constructor(url, cyrt, sthis) {
|
1360
|
+
this.ivLength = 0;
|
1361
|
+
this.code = 0;
|
1362
|
+
this.name = "Fireproof@unencrypted-block";
|
1363
|
+
this.isEncrypting = false;
|
1364
|
+
this._fingerPrint = "noCrypto:" + Math.random();
|
1365
|
+
this.logger = ensureLogger(sthis, "noCrypto");
|
1366
|
+
this.crypto = cyrt;
|
1367
|
+
this.url = url;
|
1368
|
+
}
|
1369
|
+
fingerPrint() {
|
1370
|
+
return Promise.resolve(this._fingerPrint);
|
1371
|
+
}
|
1372
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1373
|
+
codec(iv) {
|
1374
|
+
return new nullCodec();
|
1375
|
+
}
|
1376
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1377
|
+
algo(iv) {
|
1378
|
+
return {
|
1379
|
+
name: "noCrypto",
|
1380
|
+
iv: new Uint8Array(),
|
1381
|
+
tagLength: 0
|
1382
|
+
};
|
1383
|
+
}
|
1384
|
+
_decrypt() {
|
1385
|
+
throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
|
1386
|
+
}
|
1387
|
+
_encrypt() {
|
1388
|
+
throw this.logger.Error().Msg("noCrypto.decrypt not implemented").AsError();
|
1389
|
+
}
|
1390
|
+
};
|
1391
|
+
async function keyedCryptoFactory(url, kb, sthis) {
|
1392
|
+
const storekey = url.getParam("storekey");
|
1393
|
+
if (storekey && storekey !== "insecure") {
|
1394
|
+
let rkey = await kb.getNamedKey(storekey, true);
|
1395
|
+
if (rkey.isErr()) {
|
1396
|
+
try {
|
1397
|
+
rkey = await kb.toKeyWithFingerPrint(storekey);
|
1398
|
+
} catch (e) {
|
1399
|
+
throw sthis.logger.Error().Err(e).Str("keybag", kb.rt.id()).Str("name", storekey).Msg("getNamedKey failed").AsError();
|
1400
|
+
}
|
1401
|
+
}
|
1402
|
+
return new keyedCrypto(url, rkey.Ok(), kb.rt.crypto, sthis);
|
1403
|
+
}
|
1404
|
+
return new noCrypto(url, kb.rt.crypto, sthis);
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
// src/blockstore/fragment-gateway.ts
|
1408
|
+
import { Result as Result3 } from "@adviser/cement";
|
1409
|
+
import { base58btc as base58btc3 } from "multiformats/bases/base58";
|
1410
|
+
import { encode as encode3, decode as decode3 } from "cborg";
|
1411
|
+
function getFragSize(url) {
|
1412
|
+
const fragSize = url.getParam("fragSize");
|
1413
|
+
let ret = 0;
|
1414
|
+
if (fragSize) {
|
1415
|
+
ret = parseInt(fragSize);
|
1416
|
+
}
|
1417
|
+
if (isNaN(ret) || ret <= 0) {
|
1418
|
+
ret = 0;
|
1419
|
+
}
|
1420
|
+
return ret;
|
1421
|
+
}
|
1422
|
+
async function getFrags(url, innerGW, headerSize, logger) {
|
1423
|
+
const fragSize = getFragSize(url);
|
1424
|
+
if (!fragSize) {
|
1425
|
+
const res = await innerGW.get(url);
|
1426
|
+
if (res.isErr()) {
|
1427
|
+
return [res];
|
1428
|
+
}
|
1429
|
+
const data = res.unwrap();
|
1430
|
+
return [
|
1431
|
+
Result3.Ok({
|
1432
|
+
fid: new Uint8Array(0),
|
1433
|
+
ofs: 0,
|
1434
|
+
len: data.length,
|
1435
|
+
data
|
1436
|
+
})
|
1437
|
+
];
|
1438
|
+
}
|
1439
|
+
const firstRaw = await innerGW.get(url.build().setParam("ofs", "0").URI());
|
1440
|
+
if (firstRaw.isErr()) {
|
1441
|
+
return [firstRaw];
|
1442
|
+
}
|
1443
|
+
const firstFragment = decode3(firstRaw.unwrap());
|
1444
|
+
const blockSize = firstFragment.data.length;
|
1445
|
+
const ops = [Promise.resolve(Result3.Ok(firstFragment))];
|
1446
|
+
const fidStr = base58btc3.encode(firstFragment.fid);
|
1447
|
+
const fragUrl = url.build().setParam("fid", fidStr).setParam("len", firstFragment.len.toString()).setParam("headerSize", headerSize.toString());
|
1448
|
+
for (let ofs = blockSize; ofs < firstFragment.len; ofs += blockSize) {
|
1449
|
+
ops.push(
|
1450
|
+
(async (furl, ofs2) => {
|
1451
|
+
const raw2 = await innerGW.get(furl);
|
1452
|
+
if (raw2.isErr()) {
|
1453
|
+
return raw2;
|
1454
|
+
}
|
1455
|
+
const fragment = decode3(raw2.unwrap());
|
1456
|
+
if (base58btc3.encode(fragment.fid) !== fidStr) {
|
1457
|
+
return Result3.Err(logger.Error().Msg("Fragment fid mismatch").AsError());
|
1458
|
+
}
|
1459
|
+
if (fragment.ofs !== ofs2) {
|
1460
|
+
return Result3.Err(logger.Error().Uint64("ofs", ofs2).Msg("Fragment ofs mismatch").AsError());
|
1461
|
+
}
|
1462
|
+
return Result3.Ok(fragment);
|
1463
|
+
})(fragUrl.setParam("ofs", ofs.toString()).URI(), ofs)
|
1464
|
+
);
|
1465
|
+
}
|
1466
|
+
return Promise.all(ops);
|
1467
|
+
}
|
1468
|
+
var FragmentGateway = class {
|
1469
|
+
constructor(sthis, innerGW) {
|
1470
|
+
this.fidLength = 4;
|
1471
|
+
this.headerSize = 32;
|
1472
|
+
this.sthis = ensureSuperLog(sthis, "FragmentGateway");
|
1473
|
+
this.logger = this.sthis.logger;
|
1474
|
+
this.innerGW = innerGW;
|
1475
|
+
}
|
1476
|
+
slicer(url, body) {
|
1477
|
+
const fragSize = getFragSize(url);
|
1478
|
+
if (!fragSize) {
|
1479
|
+
return [this.innerGW.put(url, body)];
|
1480
|
+
}
|
1481
|
+
const blocksize = fragSize - this.headerSize;
|
1482
|
+
if (blocksize <= 0) {
|
1483
|
+
throw this.logger.Error().Uint64("fragSize", fragSize).Uint64("headerSize", this.headerSize).Msg("Fragment size is too small").AsError();
|
1484
|
+
}
|
1485
|
+
const ops = [];
|
1486
|
+
const fid = this.sthis.nextId(this.fidLength);
|
1487
|
+
const fragUrl = url.build().setParam("fid", fid.str).setParam("len", body.length.toString()).setParam("headerSize", this.headerSize.toString());
|
1488
|
+
for (let ofs = 0; ofs < body.length; ofs += blocksize) {
|
1489
|
+
const block = encode3({
|
1490
|
+
fid: fid.bin,
|
1491
|
+
ofs,
|
1492
|
+
len: body.length,
|
1493
|
+
data: body.slice(ofs, ofs + blocksize)
|
1494
|
+
});
|
1495
|
+
if (block.length > fragSize) {
|
1496
|
+
throw this.logger.Error().Uint64("block", block.length).Uint64("fragSize", fragSize).Msg("Block size to big").AsError();
|
1497
|
+
}
|
1498
|
+
ops.push(this.innerGW.put(fragUrl.setParam("ofs", ofs.toString()).URI(), block));
|
1499
|
+
}
|
1500
|
+
return ops;
|
1501
|
+
}
|
1502
|
+
buildUrl(baseUrl, key) {
|
1503
|
+
return this.innerGW.buildUrl(baseUrl, key);
|
1504
|
+
}
|
1505
|
+
async destroy(iurl) {
|
1506
|
+
return this.innerGW.destroy(iurl);
|
1507
|
+
}
|
1508
|
+
async start(url) {
|
1509
|
+
this.headerSize = encode3({
|
1510
|
+
fid: this.sthis.nextId(this.fidLength).bin,
|
1511
|
+
ofs: 1024 * 1024,
|
1512
|
+
// 32bit
|
1513
|
+
len: 16 * 1024 * 1024,
|
1514
|
+
// 32bit
|
1515
|
+
data: new Uint8Array(1024)
|
1516
|
+
}).length - 1024;
|
1517
|
+
return this.innerGW.start(url);
|
1518
|
+
}
|
1519
|
+
async close(url) {
|
1520
|
+
return this.innerGW.close(url);
|
1521
|
+
}
|
1522
|
+
async put(url, body) {
|
1523
|
+
await Promise.all(this.slicer(url, body));
|
1524
|
+
return Result3.Ok(void 0);
|
1525
|
+
}
|
1526
|
+
async get(url) {
|
1527
|
+
const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
|
1528
|
+
let buffer = void 0;
|
1529
|
+
for (const rfrag of rfrags) {
|
1530
|
+
if (rfrag.isErr()) {
|
1531
|
+
return Result3.Err(rfrag.Err());
|
1532
|
+
}
|
1533
|
+
const frag = rfrag.Ok();
|
1534
|
+
buffer = buffer || new Uint8Array(frag.len);
|
1535
|
+
buffer.set(frag.data, frag.ofs);
|
1536
|
+
}
|
1537
|
+
return Result3.Ok(buffer || new Uint8Array(0));
|
1538
|
+
}
|
1539
|
+
async subscribe(url, callback) {
|
1540
|
+
if (this.innerGW.subscribe) {
|
1541
|
+
return this.innerGW.subscribe(url, callback);
|
1542
|
+
} else {
|
1543
|
+
return Result3.Err(this.logger.Error().Url(url).Msg("subscribe not supported").AsError());
|
1544
|
+
}
|
1545
|
+
}
|
1546
|
+
async delete(url) {
|
1547
|
+
const rfrags = await getFrags(url, this.innerGW, this.headerSize, this.logger);
|
1548
|
+
for (const rfrag of rfrags) {
|
1549
|
+
if (rfrag.isErr()) {
|
1550
|
+
return Result3.Err(rfrag.Err());
|
1551
|
+
}
|
1552
|
+
const frag = rfrag.Ok();
|
1553
|
+
const fidStr = base58btc3.encode(frag.fid);
|
1554
|
+
const fragUrl = url.build().setParam("fid", fidStr).setParam("len", frag.len.toString()).setParam("headerSize", this.headerSize.toString()).URI();
|
1555
|
+
await this.innerGW.delete(fragUrl);
|
1556
|
+
}
|
1557
|
+
return Result3.Ok(void 0);
|
1558
|
+
}
|
1559
|
+
};
|
1560
|
+
|
1561
|
+
// src/blockstore/meta-key-helper.ts
|
1562
|
+
import { format, parse } from "@ipld/dag-json";
|
1563
|
+
import { EventBlock, decodeEventBlock } from "@web3-storage/pail/clock";
|
1564
|
+
import { CID as CID2 } from "multiformats";
|
1565
|
+
import { base64pad } from "multiformats/bases/base64";
|
1566
|
+
import { Result as Result4 } from "@adviser/cement";
|
1567
|
+
async function decodeGatewayMetaBytesToDbMeta(sthis, byteHeads) {
|
1568
|
+
const crdtEntries = JSON.parse(sthis.txt.decode(byteHeads));
|
1569
|
+
if (!crdtEntries.length) {
|
1570
|
+
sthis.logger.Debug().Str("byteHeads", new TextDecoder().decode(byteHeads)).Msg("No CRDT entries found");
|
1571
|
+
return [];
|
1572
|
+
}
|
1573
|
+
if (!crdtEntries.map) {
|
1574
|
+
sthis.logger.Debug().Str("crdtEntries", JSON.stringify(crdtEntries)).Msg("No data in CRDT entries");
|
1575
|
+
return [];
|
1576
|
+
}
|
1577
|
+
return Promise.all(
|
1578
|
+
crdtEntries.map(async (crdtEntry) => {
|
1579
|
+
const eventBlock = await decodeEventBlock(base64pad.decode(crdtEntry.data));
|
1580
|
+
const dbMeta = parse(sthis.txt.decode(eventBlock.value.data.dbMeta));
|
1581
|
+
return {
|
1582
|
+
eventCid: eventBlock.cid,
|
1583
|
+
parents: crdtEntry.parents,
|
1584
|
+
dbMeta
|
1585
|
+
};
|
1586
|
+
})
|
1587
|
+
);
|
1588
|
+
}
|
1589
|
+
async function setCryptoKeyFromGatewayMetaPayload(uri, sthis, data) {
|
1590
|
+
try {
|
1591
|
+
sthis.logger.Debug().Str("uri", uri.toString()).Msg("Setting crypto key from gateway meta payload");
|
1592
|
+
const keyInfo = await decodeGatewayMetaBytesToDbMeta(sthis, data);
|
1593
|
+
if (keyInfo.length) {
|
1594
|
+
const dbMeta = keyInfo[0].dbMeta;
|
1595
|
+
if (dbMeta.key) {
|
1596
|
+
const kb = await getKeyBag(sthis);
|
1597
|
+
const keyName = getStoreKeyName(uri);
|
1598
|
+
const res = await kb.setNamedKey(keyName, dbMeta.key);
|
1599
|
+
if (res.isErr()) {
|
1600
|
+
sthis.logger.Debug().Str("keyName", keyName).Str("dbMeta.key", dbMeta.key).Msg("Failed to set named key");
|
1601
|
+
throw res.Err();
|
1602
|
+
}
|
1603
|
+
}
|
1604
|
+
sthis.logger.Debug().Str("dbMeta.key", dbMeta.key).Str("uri", uri.toString()).Msg("Set crypto key from gateway meta payload");
|
1605
|
+
return Result4.Ok(dbMeta);
|
1209
1606
|
}
|
1210
|
-
|
1607
|
+
sthis.logger.Debug().Str("data", new TextDecoder().decode(data)).Msg("No crypto in gateway meta payload");
|
1608
|
+
return Result4.Ok(void 0);
|
1609
|
+
} catch (error) {
|
1610
|
+
sthis.logger.Debug().Err(error).Msg("Failed to set crypto key from gateway meta payload");
|
1611
|
+
return Result4.Err(error);
|
1211
1612
|
}
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1613
|
+
}
|
1614
|
+
async function addCryptoKeyToGatewayMetaPayload(uri, sthis, body) {
|
1615
|
+
try {
|
1616
|
+
sthis.logger.Debug().Str("uri", uri.toString()).Msg("Adding crypto key to gateway meta payload");
|
1617
|
+
const keyName = getStoreKeyName(uri);
|
1618
|
+
const kb = await getKeyBag(sthis);
|
1619
|
+
const res = await kb.getNamedExtractableKey(keyName, true);
|
1620
|
+
if (res.isErr()) {
|
1621
|
+
sthis.logger.Error().Str("keyName", keyName).Msg("Failed to get named extractable key");
|
1622
|
+
throw res.Err();
|
1216
1623
|
}
|
1217
|
-
const
|
1624
|
+
const keyData = await res.Ok().extract();
|
1625
|
+
const dbMetas = await decodeGatewayMetaBytesToDbMeta(sthis, body);
|
1626
|
+
const { dbMeta, parents } = dbMetas[0];
|
1627
|
+
const parentLinks = parents.map((p) => CID2.parse(p));
|
1628
|
+
dbMeta.key = keyData.keyStr;
|
1629
|
+
const events = await Promise.all([dbMeta].map((dbMeta2) => createDbMetaEventBlock(sthis, dbMeta2, parentLinks)));
|
1630
|
+
const encoded = await encodeEventsWithParents(sthis, events, parentLinks);
|
1631
|
+
sthis.logger.Debug().Str("uri", uri.toString()).Msg("Added crypto key to gateway meta payload");
|
1632
|
+
return Result4.Ok(encoded);
|
1633
|
+
} catch (error) {
|
1634
|
+
sthis.logger.Error().Err(error).Msg("Failed to add crypto key to gateway meta payload");
|
1635
|
+
return Result4.Err(error);
|
1636
|
+
}
|
1637
|
+
}
|
1638
|
+
function getStoreKeyName(url) {
|
1639
|
+
const storeKeyName = [url.getParam("localName") || url.getParam("name")];
|
1640
|
+
const idx = url.getParam("index");
|
1641
|
+
if (idx) {
|
1642
|
+
storeKeyName.push(idx);
|
1643
|
+
}
|
1644
|
+
storeKeyName.push("data");
|
1645
|
+
return `@${storeKeyName.join(":")}@`;
|
1646
|
+
}
|
1647
|
+
async function createDbMetaEventBlock(sthis, dbMeta, parents) {
|
1648
|
+
const event = await EventBlock.create(
|
1649
|
+
{
|
1650
|
+
dbMeta: sthis.txt.encode(format(dbMeta))
|
1651
|
+
},
|
1652
|
+
parents
|
1653
|
+
);
|
1654
|
+
return event;
|
1655
|
+
}
|
1656
|
+
async function encodeEventsWithParents(sthis, events, parents) {
|
1657
|
+
const crdtEntries = events.map((event) => {
|
1658
|
+
const base64String = base64pad.encode(event.bytes);
|
1218
1659
|
return {
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1660
|
+
cid: event.cid.toString(),
|
1661
|
+
data: base64String,
|
1662
|
+
parents: parents.map((p) => p.toString())
|
1222
1663
|
};
|
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
|
-
};
|
1664
|
+
});
|
1665
|
+
return sthis.txt.encode(JSON.stringify(crdtEntries));
|
1666
|
+
}
|
1240
1667
|
|
1241
1668
|
// src/blockstore/store.ts
|
1669
|
+
import pRetry from "p-retry";
|
1670
|
+
import pMap from "p-map";
|
1242
1671
|
function guardVersion(url) {
|
1243
|
-
if (!url.
|
1244
|
-
return
|
1672
|
+
if (!url.hasParam("version")) {
|
1673
|
+
return Result5.Err(`missing version: ${url.toString()}`);
|
1245
1674
|
}
|
1246
|
-
return
|
1675
|
+
return Result5.Ok(url);
|
1247
1676
|
}
|
1248
|
-
var
|
1249
|
-
constructor(name, url, logger) {
|
1677
|
+
var BaseStoreImpl = class {
|
1678
|
+
constructor(name, url, opts, sthis, logger) {
|
1250
1679
|
this._onStarted = [];
|
1251
1680
|
this._onClosed = [];
|
1252
1681
|
this.name = name;
|
1253
|
-
this.
|
1254
|
-
this.
|
1682
|
+
this._url = url;
|
1683
|
+
this.keybag = opts.keybag;
|
1684
|
+
this.sthis = sthis;
|
1685
|
+
this.logger = logger.With().Ref("url", () => this._url.toString()).Str("name", name).Logger();
|
1686
|
+
this.gateway = new FragmentGateway(this.sthis, opts.gateway);
|
1687
|
+
this.loader = opts.loader;
|
1688
|
+
}
|
1689
|
+
url() {
|
1690
|
+
return this._url;
|
1255
1691
|
}
|
1256
1692
|
onStarted(fn) {
|
1257
1693
|
this._onStarted.push(fn);
|
@@ -1259,38 +1695,93 @@ var VersionedStore = class {
|
|
1259
1695
|
onClosed(fn) {
|
1260
1696
|
this._onClosed.push(fn);
|
1261
1697
|
}
|
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;
|
1698
|
+
async ready() {
|
1699
|
+
return;
|
1700
|
+
}
|
1701
|
+
async keyedCrypto() {
|
1702
|
+
return keyedCryptoFactory(this._url, await this.keybag(), this.sthis);
|
1279
1703
|
}
|
1280
1704
|
async start() {
|
1281
|
-
this.logger.Debug().Msg("starting");
|
1282
|
-
|
1705
|
+
this.logger.Debug().Str("storeType", this.storeType).Msg("starting-gateway-pre");
|
1706
|
+
this._url = this._url.build().setParam("store", this.storeType).URI();
|
1707
|
+
const res = await this.gateway.start(this._url);
|
1283
1708
|
if (res.isErr()) {
|
1709
|
+
this.logger.Error().Result("gw-start", res).Msg("started-gateway");
|
1284
1710
|
return res;
|
1285
1711
|
}
|
1712
|
+
this._url = res.Ok();
|
1713
|
+
const kb = await this.keybag();
|
1714
|
+
const skRes = await kb.ensureKeyFromUrl(this._url, () => {
|
1715
|
+
const idx = this._url.getParam("index");
|
1716
|
+
const storeKeyName = [this.name];
|
1717
|
+
if (idx) {
|
1718
|
+
storeKeyName.push(idx);
|
1719
|
+
}
|
1720
|
+
storeKeyName.push(this.storeType);
|
1721
|
+
return storeKeyName.join(":");
|
1722
|
+
});
|
1723
|
+
if (skRes.isErr()) {
|
1724
|
+
return skRes;
|
1725
|
+
}
|
1726
|
+
this._url = skRes.Ok();
|
1727
|
+
const version = guardVersion(this._url);
|
1728
|
+
if (version.isErr()) {
|
1729
|
+
this.logger.Error().Result("version", version).Msg("guardVersion");
|
1730
|
+
await this.close();
|
1731
|
+
return version;
|
1732
|
+
}
|
1733
|
+
if (this.ready) {
|
1734
|
+
const fn = this.ready.bind(this);
|
1735
|
+
const ready = await exception2Result(fn);
|
1736
|
+
if (ready.isErr()) {
|
1737
|
+
await this.close();
|
1738
|
+
return ready;
|
1739
|
+
}
|
1740
|
+
}
|
1286
1741
|
this._onStarted.forEach((fn) => fn());
|
1287
|
-
|
1742
|
+
this.logger.Debug().Msg("started");
|
1743
|
+
return version;
|
1744
|
+
}
|
1745
|
+
};
|
1746
|
+
var MetaStoreImpl = class extends BaseStoreImpl {
|
1747
|
+
// remote: boolean;
|
1748
|
+
constructor(sthis, name, url, opts) {
|
1749
|
+
super(name, url, { ...opts }, sthis, ensureLogger(sthis, "MetaStoreImpl"));
|
1750
|
+
this.storeType = "meta";
|
1751
|
+
this.subscribers = /* @__PURE__ */ new Map();
|
1752
|
+
this.parents = [];
|
1753
|
+
if (
|
1754
|
+
/*this.remote && */
|
1755
|
+
opts.gateway.subscribe
|
1756
|
+
) {
|
1757
|
+
this.onStarted(async () => {
|
1758
|
+
this.logger.Debug().Str("url", this.url().toString()).Msg("Subscribing to the gateway");
|
1759
|
+
opts.gateway.subscribe?.(this.url(), async (message) => {
|
1760
|
+
this.logger.Debug().Msg("Received message from gateway");
|
1761
|
+
const dbMetas = await decodeGatewayMetaBytesToDbMeta(this.sthis, message);
|
1762
|
+
await Promise.all(
|
1763
|
+
dbMetas.map((dbMeta) => this.loader?.taskManager?.handleEvent(dbMeta.eventCid, dbMeta.parents, dbMeta.dbMeta))
|
1764
|
+
);
|
1765
|
+
this.updateParentsFromDbMetas(dbMetas);
|
1766
|
+
});
|
1767
|
+
});
|
1768
|
+
}
|
1769
|
+
}
|
1770
|
+
updateParentsFromDbMetas(dbMetas) {
|
1771
|
+
const cids = dbMetas.map((m) => m.eventCid);
|
1772
|
+
const dbMetaParents = dbMetas.flatMap((m) => m.parents);
|
1773
|
+
const uniqueParentsMap = new Map([...this.parents, ...cids].map((p) => [p.toString(), p]));
|
1774
|
+
const dbMetaParentsSet = new Set(dbMetaParents.map((p) => p.toString()));
|
1775
|
+
this.parents = Array.from(uniqueParentsMap.values()).filter((p) => !dbMetaParentsSet.has(p.toString()));
|
1288
1776
|
}
|
1289
|
-
async
|
1290
|
-
this.
|
1291
|
-
|
1777
|
+
async handleByteHeads(byteHeads) {
|
1778
|
+
return await decodeGatewayMetaBytesToDbMeta(this.sthis, byteHeads);
|
1779
|
+
}
|
1780
|
+
async load() {
|
1781
|
+
const branch = "main";
|
1782
|
+
const url = await this.gateway.buildUrl(this.url(), branch);
|
1292
1783
|
if (url.isErr()) {
|
1293
|
-
throw this.logger.Error().Result("
|
1784
|
+
throw this.logger.Error().Result("buildUrl", url).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
|
1294
1785
|
}
|
1295
1786
|
const bytes = await this.gateway.get(url.Ok());
|
1296
1787
|
if (bytes.isErr()) {
|
@@ -1299,66 +1790,45 @@ var MetaStore = class extends VersionedStore {
|
|
1299
1790
|
}
|
1300
1791
|
throw this.logger.Error().Url(url.Ok()).Result("bytes:", bytes).Msg("gateway get").AsError();
|
1301
1792
|
}
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
}
|
1793
|
+
const dbMetas = await this.handleByteHeads(bytes.Ok());
|
1794
|
+
await this.loader?.handleDbMetasFromStore(dbMetas.map((m) => m.dbMeta));
|
1795
|
+
this.updateParentsFromDbMetas(dbMetas);
|
1796
|
+
return dbMetas.map((m) => m.dbMeta);
|
1307
1797
|
}
|
1308
|
-
async save(meta, branch
|
1798
|
+
async save(meta, branch) {
|
1799
|
+
branch = branch || "main";
|
1309
1800
|
this.logger.Debug().Str("branch", branch).Any("meta", meta).Msg("saving meta");
|
1310
|
-
const
|
1311
|
-
const
|
1801
|
+
const event = await createDbMetaEventBlock(this.sthis, meta, this.parents);
|
1802
|
+
const bytes = await encodeEventsWithParents(this.sthis, [event], this.parents);
|
1803
|
+
const url = await this.gateway.buildUrl(this.url(), branch);
|
1312
1804
|
if (url.isErr()) {
|
1313
|
-
throw this.logger.Error().Err(url.Err()).Str("branch", branch).
|
1805
|
+
throw this.logger.Error().Err(url.Err()).Str("branch", branch).Msg("got error from gateway.buildUrl").AsError();
|
1314
1806
|
}
|
1315
|
-
|
1807
|
+
this.parents = [event.cid];
|
1808
|
+
const res = await this.gateway.put(url.Ok(), bytes);
|
1316
1809
|
if (res.isErr()) {
|
1317
1810
|
throw this.logger.Error().Err(res.Err()).Msg("got error from gateway.put").AsError();
|
1318
1811
|
}
|
1319
|
-
return res
|
1812
|
+
return res;
|
1320
1813
|
}
|
1321
1814
|
async close() {
|
1322
|
-
await this.gateway.close(this.url);
|
1815
|
+
await this.gateway.close(this.url());
|
1323
1816
|
this._onClosed.forEach((fn) => fn());
|
1324
|
-
return
|
1817
|
+
return Result5.Ok(void 0);
|
1325
1818
|
}
|
1326
1819
|
async destroy() {
|
1327
|
-
return this.gateway.destroy(this.url);
|
1820
|
+
return this.gateway.destroy(this.url());
|
1328
1821
|
}
|
1329
1822
|
};
|
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;
|
1823
|
+
var DataStoreImpl = class extends BaseStoreImpl {
|
1824
|
+
// readonly tag: string = "car-base";
|
1825
|
+
constructor(sthis, name, url, opts) {
|
1826
|
+
super(name, url, { ...opts }, sthis, ensureLogger(sthis, "DataStoreImpl"));
|
1827
|
+
this.storeType = "data";
|
1358
1828
|
}
|
1359
1829
|
async load(cid) {
|
1360
1830
|
this.logger.Debug().Any("cid", cid).Msg("loading");
|
1361
|
-
const url = await this.gateway.buildUrl(this.url, cid.toString());
|
1831
|
+
const url = await this.gateway.buildUrl(this.url(), cid.toString());
|
1362
1832
|
if (url.isErr()) {
|
1363
1833
|
throw this.logger.Error().Err(url.Err()).Str("cid", cid.toString()).Msg("got error from gateway.buildUrl").AsError();
|
1364
1834
|
}
|
@@ -1371,7 +1841,7 @@ var DataStore = class extends VersionedStore {
|
|
1371
1841
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
1372
1842
|
async save(car, opts) {
|
1373
1843
|
this.logger.Debug().Any("cid", car.cid.toString()).Msg("saving");
|
1374
|
-
const url = await this.gateway.buildUrl(this.url, car.cid.toString());
|
1844
|
+
const url = await this.gateway.buildUrl(this.url(), car.cid.toString());
|
1375
1845
|
if (url.isErr()) {
|
1376
1846
|
throw this.logger.Error().Err(url.Err()).Ref("cid", car.cid).Msg("got error from gateway.buildUrl").AsError();
|
1377
1847
|
}
|
@@ -1382,31 +1852,30 @@ var DataStore = class extends VersionedStore {
|
|
1382
1852
|
return res.Ok();
|
1383
1853
|
}
|
1384
1854
|
async remove(cid) {
|
1385
|
-
const url = await this.gateway.buildUrl(this.url, cid.toString());
|
1855
|
+
const url = await this.gateway.buildUrl(this.url(), cid.toString());
|
1386
1856
|
if (url.isErr()) {
|
1387
1857
|
return url;
|
1388
1858
|
}
|
1389
1859
|
return this.gateway.delete(url.Ok());
|
1390
1860
|
}
|
1391
1861
|
async close() {
|
1392
|
-
await this.gateway.close(this.url);
|
1862
|
+
await this.gateway.close(this.url());
|
1393
1863
|
this._onClosed.forEach((fn) => fn());
|
1394
|
-
return
|
1864
|
+
return Result5.Ok(void 0);
|
1395
1865
|
}
|
1396
1866
|
destroy() {
|
1397
|
-
return this.gateway.destroy(this.url);
|
1867
|
+
return this.gateway.destroy(this.url());
|
1398
1868
|
}
|
1399
1869
|
};
|
1400
|
-
var
|
1401
|
-
constructor(loader, url,
|
1402
|
-
super(loader.name, url, ensureLogger(
|
1403
|
-
this.
|
1404
|
-
this._ready = new
|
1870
|
+
var WALStoreImpl = class extends BaseStoreImpl {
|
1871
|
+
constructor(loader, url, opts) {
|
1872
|
+
super(loader.name, url, { ...opts }, loader.sthis, ensureLogger(loader.sthis, "WALStoreImpl"));
|
1873
|
+
this.storeType = "wal";
|
1874
|
+
this._ready = new ResolveOnce3();
|
1405
1875
|
this.walState = { operations: [], noLoaderOps: [], fileOperations: [] };
|
1406
1876
|
this.processing = void 0;
|
1407
1877
|
this.processQueue = new CommitQueue();
|
1408
1878
|
this.loader = loader;
|
1409
|
-
this.gateway = gateway;
|
1410
1879
|
}
|
1411
1880
|
async ready() {
|
1412
1881
|
return this._ready.once(async () => {
|
@@ -1425,124 +1894,121 @@ var RemoteWAL = class extends VersionedStore {
|
|
1425
1894
|
}
|
1426
1895
|
async enqueue(dbMeta, opts) {
|
1427
1896
|
await this.ready();
|
1428
|
-
if (opts.
|
1897
|
+
if (opts.compact) {
|
1898
|
+
this.walState.operations = [];
|
1899
|
+
this.walState.noLoaderOps = [dbMeta];
|
1900
|
+
} else if (opts.noLoader) {
|
1429
1901
|
this.walState.noLoaderOps.push(dbMeta);
|
1430
1902
|
} else {
|
1431
1903
|
this.walState.operations.push(dbMeta);
|
1432
1904
|
}
|
1433
1905
|
await this.save(this.walState);
|
1434
|
-
|
1906
|
+
if (!opts.noLoader) {
|
1907
|
+
void this.process();
|
1908
|
+
}
|
1435
1909
|
}
|
1436
1910
|
async enqueueFile(fileCid, publicFile = false) {
|
1437
1911
|
await this.ready();
|
1438
1912
|
this.walState.fileOperations.push({ cid: fileCid, public: publicFile });
|
1439
1913
|
}
|
1440
|
-
async
|
1914
|
+
async process() {
|
1441
1915
|
await this.ready();
|
1442
1916
|
if (!this.loader.remoteCarStore) return;
|
1443
1917
|
await this.processQueue.enqueue(async () => {
|
1444
|
-
|
1918
|
+
try {
|
1919
|
+
await this._doProcess();
|
1920
|
+
} catch (e) {
|
1921
|
+
this.logger.Error().Any("error", e).Msg("error processing wal");
|
1922
|
+
}
|
1445
1923
|
if (this.walState.operations.length || this.walState.fileOperations.length || this.walState.noLoaderOps.length) {
|
1446
|
-
setTimeout(() => void this.
|
1924
|
+
setTimeout(() => void this.process(), 0);
|
1447
1925
|
}
|
1448
1926
|
});
|
1449
1927
|
}
|
1450
1928
|
async _doProcess() {
|
1451
1929
|
if (!this.loader.remoteCarStore) return;
|
1452
|
-
const
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1930
|
+
const operations = [...this.walState.operations];
|
1931
|
+
const noLoaderOps = [...this.walState.noLoaderOps];
|
1932
|
+
const fileOperations = [...this.walState.fileOperations];
|
1933
|
+
if (operations.length + noLoaderOps.length + fileOperations.length === 0) return;
|
1934
|
+
const concurrencyLimit = 3;
|
1935
|
+
const retryableUpload = (fn, description) => pRetry(fn, {
|
1936
|
+
retries: 5,
|
1937
|
+
onFailedAttempt: (error) => {
|
1938
|
+
this.logger.Warn().Msg(`Attempt ${error.attemptNumber} failed for ${description}. There are ${error.retriesLeft} retries left.`);
|
1939
|
+
}
|
1940
|
+
});
|
1941
|
+
try {
|
1942
|
+
await pMap(
|
1943
|
+
noLoaderOps,
|
1944
|
+
async (dbMeta) => {
|
1945
|
+
await retryableUpload(async () => {
|
1946
|
+
for (const cid of dbMeta.cars) {
|
1947
|
+
const car = await (await this.loader.carStore()).load(cid);
|
1948
|
+
if (!car) {
|
1949
|
+
if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
|
1950
|
+
throw this.logger.Error().Ref("cid", cid).Msg("missing local car").AsError();
|
1951
|
+
}
|
1952
|
+
} else {
|
1953
|
+
await throwFalsy(this.loader.remoteCarStore).save(car);
|
1954
|
+
}
|
1468
1955
|
}
|
1469
1956
|
this.walState.noLoaderOps = this.walState.noLoaderOps.filter((op) => op !== dbMeta);
|
1470
|
-
}
|
1471
|
-
}
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1957
|
+
}, `noLoaderOp with dbMeta.cars=${dbMeta.cars.toString()}`);
|
1958
|
+
},
|
1959
|
+
{ concurrency: concurrencyLimit }
|
1960
|
+
);
|
1961
|
+
await pMap(
|
1962
|
+
operations,
|
1963
|
+
async (dbMeta) => {
|
1964
|
+
await retryableUpload(async () => {
|
1965
|
+
for (const cid of dbMeta.cars) {
|
1966
|
+
const car = await (await this.loader.carStore()).load(cid);
|
1967
|
+
if (!car) {
|
1968
|
+
if (carLogIncludesGroup(this.loader.carLog, dbMeta.cars)) {
|
1969
|
+
throw this.logger.Error().Ref("cid", cid).Msg(`missing local car`).AsError();
|
1970
|
+
}
|
1971
|
+
} else {
|
1972
|
+
await throwFalsy(this.loader.remoteCarStore).save(car);
|
1973
|
+
}
|
1483
1974
|
}
|
1484
|
-
|
1485
|
-
|
1486
|
-
}
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
const fileBlock = await (await
|
1494
|
-
|
1975
|
+
this.walState.operations = this.walState.operations.filter((op) => op !== dbMeta);
|
1976
|
+
}, `operation with dbMeta.cars=${dbMeta.cars.toString()}`);
|
1977
|
+
},
|
1978
|
+
{ concurrency: concurrencyLimit }
|
1979
|
+
);
|
1980
|
+
await pMap(
|
1981
|
+
fileOperations,
|
1982
|
+
async ({ cid: fileCid, public: publicFile }) => {
|
1983
|
+
await retryableUpload(async () => {
|
1984
|
+
const fileBlock = await (await this.loader.fileStore()).load(fileCid);
|
1985
|
+
if (!fileBlock) {
|
1986
|
+
throw this.logger.Error().Ref("cid", fileCid).Msg("missing file block").AsError();
|
1987
|
+
}
|
1988
|
+
await this.loader.remoteFileStore?.save(fileBlock, { public: publicFile });
|
1495
1989
|
this.walState.fileOperations = this.walState.fileOperations.filter((op) => op.cid !== fileCid);
|
1496
|
-
});
|
1497
|
-
|
1498
|
-
}
|
1499
|
-
|
1500
|
-
|
1501
|
-
const
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
"errors",
|
1506
|
-
errors.map((e) => e.reason)
|
1507
|
-
).Msg("error uploading").AsError();
|
1508
|
-
errors[0].reason;
|
1509
|
-
}
|
1510
|
-
if (operations.length) {
|
1511
|
-
const lastOp = operations[operations.length - 1];
|
1512
|
-
await this.loader.remoteMetaStore?.save(lastOp).catch((e) => {
|
1513
|
-
this.walState.operations.push(lastOp);
|
1514
|
-
throw this.logger.Error().Any("error", e).Msg("error saving remote meta").AsError();
|
1515
|
-
});
|
1516
|
-
}
|
1517
|
-
} finally {
|
1518
|
-
await this.save(this.walState);
|
1990
|
+
}, `fileOperation with cid=${fileCid.toString()}`);
|
1991
|
+
},
|
1992
|
+
{ concurrency: concurrencyLimit }
|
1993
|
+
);
|
1994
|
+
if (operations.length) {
|
1995
|
+
const lastOp = operations[operations.length - 1];
|
1996
|
+
await retryableUpload(async () => {
|
1997
|
+
await this.loader.remoteMetaStore?.save(lastOp);
|
1998
|
+
}, `remoteMetaStore save with dbMeta.cars=${lastOp.cars.toString()}`);
|
1519
1999
|
}
|
1520
|
-
}
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
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;
|
2000
|
+
} catch (error) {
|
2001
|
+
this.logger.Error().Any("error", error).Msg("Processing failed");
|
2002
|
+
return;
|
2003
|
+
} finally {
|
2004
|
+
await this.save(this.walState);
|
1538
2005
|
}
|
1539
|
-
return ready;
|
1540
2006
|
}
|
1541
2007
|
async load() {
|
1542
2008
|
this.logger.Debug().Msg("loading");
|
1543
|
-
const filepath = await this.gateway.buildUrl(this.url, "main");
|
2009
|
+
const filepath = await this.gateway.buildUrl(this.url(), "main");
|
1544
2010
|
if (filepath.isErr()) {
|
1545
|
-
throw this.logger.Error().Err(filepath.Err()).
|
2011
|
+
throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
|
1546
2012
|
}
|
1547
2013
|
const bytes = await this.gateway.get(filepath.Ok());
|
1548
2014
|
if (bytes.isErr()) {
|
@@ -1552,71 +2018,83 @@ var RemoteWAL = class extends VersionedStore {
|
|
1552
2018
|
throw this.logger.Error().Err(bytes.Err()).Msg("error get").AsError();
|
1553
2019
|
}
|
1554
2020
|
try {
|
1555
|
-
return bytes &&
|
2021
|
+
return bytes && parse2(this.sthis.txt.decode(bytes.Ok()));
|
1556
2022
|
} catch (e) {
|
1557
2023
|
throw this.logger.Error().Err(e).Msg("error parse").AsError();
|
1558
2024
|
}
|
1559
2025
|
}
|
1560
2026
|
async save(state) {
|
1561
|
-
const filepath = await this.gateway.buildUrl(this.url, "main");
|
2027
|
+
const filepath = await this.gateway.buildUrl(this.url(), "main");
|
1562
2028
|
if (filepath.isErr()) {
|
1563
|
-
throw this.logger.Error().Err(filepath.Err()).
|
2029
|
+
throw this.logger.Error().Err(filepath.Err()).Url(this.url()).Msg("error building url").AsError();
|
1564
2030
|
}
|
1565
2031
|
let encoded;
|
1566
2032
|
try {
|
1567
|
-
encoded =
|
2033
|
+
encoded = format2(state);
|
1568
2034
|
} catch (e) {
|
1569
2035
|
throw this.logger.Error().Err(e).Any("state", state).Msg("error format").AsError();
|
1570
2036
|
}
|
1571
|
-
const res = await this.gateway.put(filepath.Ok(),
|
2037
|
+
const res = await this.gateway.put(filepath.Ok(), this.sthis.txt.encode(encoded));
|
1572
2038
|
if (res.isErr()) {
|
1573
2039
|
throw this.logger.Error().Err(res.Err()).Str("filePath", filepath.Ok().toString()).Msg("error saving").AsError();
|
1574
2040
|
}
|
1575
2041
|
}
|
1576
2042
|
async close() {
|
1577
|
-
await this.gateway.close(this.url);
|
2043
|
+
await this.gateway.close(this.url());
|
1578
2044
|
this._onClosed.forEach((fn) => fn());
|
1579
|
-
return
|
2045
|
+
return Result5.Ok(void 0);
|
1580
2046
|
}
|
1581
2047
|
destroy() {
|
1582
|
-
return this.gateway.destroy(this.url);
|
2048
|
+
return this.gateway.destroy(this.url());
|
1583
2049
|
}
|
1584
2050
|
};
|
1585
2051
|
|
1586
2052
|
// src/blockstore/store-factory.ts
|
1587
2053
|
function ensureIsIndex(url, isIndex) {
|
1588
2054
|
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);
|
2055
|
+
return url.build().setParam("index", isIndex).URI();
|
1604
2056
|
}
|
2057
|
+
return url.build().delParam("index").URI();
|
1605
2058
|
}
|
1606
|
-
var storeFactory = /* @__PURE__ */ new Map();
|
1607
2059
|
function ensureName(name, url) {
|
1608
|
-
if (!url.
|
1609
|
-
url.
|
2060
|
+
if (!url.hasParam("name")) {
|
2061
|
+
return url.build().setParam("name", name).URI();
|
1610
2062
|
}
|
2063
|
+
return url;
|
1611
2064
|
}
|
2065
|
+
var storeFactory = /* @__PURE__ */ new Map();
|
1612
2066
|
function buildURL(optURL, loader) {
|
1613
2067
|
const storeOpts = loader.ebOpts.store;
|
1614
2068
|
const obuItem = Array.from(storeFactory.values()).find((items) => items.overrideBaseURL);
|
1615
2069
|
let obuUrl;
|
1616
2070
|
if (obuItem && obuItem.overrideBaseURL) {
|
1617
|
-
obuUrl =
|
2071
|
+
obuUrl = URI5.from(obuItem.overrideBaseURL);
|
1618
2072
|
}
|
1619
|
-
|
2073
|
+
const ret = ensureIsIndex(
|
2074
|
+
URI5.from(optURL || obuUrl || dataDir(loader.sthis, loader.name, storeOpts.stores?.base)),
|
2075
|
+
storeOpts.isIndex
|
2076
|
+
);
|
2077
|
+
return ret;
|
2078
|
+
}
|
2079
|
+
var onceGateway = new KeyedResolvOnce2();
|
2080
|
+
async function getGatewayFromURL(url, sthis) {
|
2081
|
+
return onceGateway.get(url.toString()).once(async () => {
|
2082
|
+
const item = storeFactory.get(url.protocol);
|
2083
|
+
if (item) {
|
2084
|
+
const ret = {
|
2085
|
+
gateway: await item.gateway(sthis),
|
2086
|
+
test: await item.test(sthis)
|
2087
|
+
};
|
2088
|
+
const res = await ret.gateway.start(url);
|
2089
|
+
if (res.isErr()) {
|
2090
|
+
sthis.logger.Error().Result("start", res).Msg("start failed");
|
2091
|
+
return void 0;
|
2092
|
+
}
|
2093
|
+
return ret;
|
2094
|
+
}
|
2095
|
+
sthis.logger.Warn().Url(url).Msg("unsupported protocol");
|
2096
|
+
return void 0;
|
2097
|
+
});
|
1620
2098
|
}
|
1621
2099
|
function registerStoreProtocol(item) {
|
1622
2100
|
let protocol = item.protocol;
|
@@ -1625,8 +2103,7 @@ function registerStoreProtocol(item) {
|
|
1625
2103
|
}
|
1626
2104
|
if (storeFactory.has(protocol)) {
|
1627
2105
|
if (!item.overrideBaseURL && storeFactory.get(protocol) !== item) {
|
1628
|
-
|
1629
|
-
logger.Warn().Msg(`protocol ${protocol} already registered`);
|
2106
|
+
throw new Error(`we need a logger here`);
|
1630
2107
|
return () => {
|
1631
2108
|
};
|
1632
2109
|
}
|
@@ -1641,106 +2118,92 @@ function registerStoreProtocol(item) {
|
|
1641
2118
|
storeFactory.delete(protocol);
|
1642
2119
|
};
|
1643
2120
|
}
|
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();
|
2121
|
+
var onceDataStoreFactory = new KeyedResolvOnce2();
|
1659
2122
|
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");
|
2123
|
+
const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.data, loader)).build().setParam("store", "data").URI();
|
2124
|
+
const sthis = ensureSuperLog(loader.sthis, "dataStoreFactory", { url: url.toString() });
|
1664
2125
|
return onceDataStoreFactory.get(url.toString()).once(async () => {
|
1665
|
-
const gateway = await
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
2126
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2127
|
+
if (!gateway) {
|
2128
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2129
|
+
}
|
2130
|
+
const store = new DataStoreImpl(sthis, loader.name, url, {
|
2131
|
+
gateway: gateway.gateway,
|
2132
|
+
keybag: () => getKeyBag(loader.sthis, {
|
2133
|
+
...loader.ebOpts.keyBag
|
2134
|
+
})
|
2135
|
+
});
|
1672
2136
|
return store;
|
1673
2137
|
});
|
1674
2138
|
}
|
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();
|
2139
|
+
var onceMetaStoreFactory = new KeyedResolvOnce2();
|
1682
2140
|
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");
|
2141
|
+
const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.meta, loader)).build().setParam("store", "meta").URI();
|
2142
|
+
const sthis = ensureSuperLog(loader.sthis, "metaStoreFactory", { url: () => url.toString() });
|
1687
2143
|
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
|
-
|
2144
|
+
sthis.logger.Debug().Str("protocol", url.protocol).Msg("pre-protocol switch");
|
2145
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2146
|
+
if (!gateway) {
|
2147
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2148
|
+
}
|
2149
|
+
const store = new MetaStoreImpl(loader.sthis, loader.name, url, {
|
2150
|
+
gateway: gateway.gateway,
|
2151
|
+
keybag: () => getKeyBag(loader.sthis, {
|
2152
|
+
...loader.ebOpts.keyBag
|
2153
|
+
})
|
2154
|
+
});
|
1695
2155
|
return store;
|
1696
2156
|
});
|
1697
2157
|
}
|
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();
|
2158
|
+
var onceRemoteWalFactory = new KeyedResolvOnce2();
|
1705
2159
|
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");
|
2160
|
+
const url = ensureName(loader.name, buildURL(loader.ebOpts.store.stores?.wal, loader)).build().setParam("store", "wal").URI();
|
2161
|
+
const sthis = ensureSuperLog(loader.sthis, "remoteWalFactory", { url: url.toString() });
|
1710
2162
|
return onceRemoteWalFactory.get(url.toString()).once(async () => {
|
1711
|
-
const gateway = await
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
2163
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2164
|
+
if (!gateway) {
|
2165
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2166
|
+
}
|
2167
|
+
sthis.logger.Debug().Str("prepared", url.toString()).Msg("produced");
|
2168
|
+
const store = new WALStoreImpl(loader, url, {
|
2169
|
+
gateway: gateway.gateway,
|
2170
|
+
keybag: () => getKeyBag(loader.sthis, {
|
2171
|
+
...loader.ebOpts.keyBag
|
2172
|
+
})
|
2173
|
+
});
|
1718
2174
|
return store;
|
1719
2175
|
});
|
1720
2176
|
}
|
1721
|
-
async function testStoreFactory(url,
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
2177
|
+
async function testStoreFactory(url, sthis) {
|
2178
|
+
sthis = ensureSuperLog(sthis, "testStoreFactory");
|
2179
|
+
const gateway = await getGatewayFromURL(url, sthis);
|
2180
|
+
if (!gateway) {
|
2181
|
+
throw sthis.logger.Error().Url(url).Msg("gateway not found").AsError();
|
2182
|
+
}
|
2183
|
+
return gateway.test;
|
2184
|
+
}
|
2185
|
+
async function ensureStart(store, logger) {
|
2186
|
+
const ret = await store.start();
|
2187
|
+
if (ret.isErr()) {
|
2188
|
+
throw logger.Error().Result("start", ret).Msg("start failed").AsError();
|
2189
|
+
}
|
2190
|
+
logger.Debug().Url(ret.Ok(), "prepared").Msg("produced");
|
2191
|
+
return store;
|
1729
2192
|
}
|
1730
|
-
function toStoreRuntime(opts,
|
1731
|
-
const logger = ensureLogger(
|
2193
|
+
function toStoreRuntime(opts, sthis) {
|
2194
|
+
const logger = ensureLogger(sthis, "toStoreRuntime", {});
|
1732
2195
|
return {
|
1733
|
-
makeMetaStore: (loader) => {
|
2196
|
+
makeMetaStore: async (loader) => {
|
1734
2197
|
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeMetaStore).Msg("makeMetaStore");
|
1735
|
-
return (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader);
|
2198
|
+
return ensureStart(await (loader.ebOpts.store.makeMetaStore || metaStoreFactory)(loader), logger);
|
1736
2199
|
},
|
1737
|
-
makeDataStore: (loader) => {
|
2200
|
+
makeDataStore: async (loader) => {
|
1738
2201
|
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeDataStore).Msg("makeDataStore");
|
1739
|
-
return (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader);
|
2202
|
+
return ensureStart(await (loader.ebOpts.store.makeDataStore || dataStoreFactory)(loader), logger);
|
1740
2203
|
},
|
1741
|
-
|
1742
|
-
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.
|
1743
|
-
return (loader.ebOpts.store.
|
2204
|
+
makeWALStore: async (loader) => {
|
2205
|
+
logger.Debug().Str("fromOpts", "" + !!loader.ebOpts.store.makeWALStore).Msg("makeRemoteWAL");
|
2206
|
+
return ensureStart(await (loader.ebOpts.store.makeWALStore || remoteWalFactory)(loader), logger);
|
1744
2207
|
},
|
1745
2208
|
encodeFile: opts.encodeFile || encodeFile,
|
1746
2209
|
decodeFile: opts.decodeFile || decodeFile
|
@@ -1748,43 +2211,131 @@ function toStoreRuntime(opts, ilogger) {
|
|
1748
2211
|
}
|
1749
2212
|
registerStoreProtocol({
|
1750
2213
|
protocol: "file:",
|
1751
|
-
|
1752
|
-
const {
|
1753
|
-
return new
|
2214
|
+
gateway: async (sthis) => {
|
2215
|
+
const { FileGateway } = await import("./gateway-5FCWPX5W.js");
|
2216
|
+
return new FileGateway(sthis);
|
1754
2217
|
},
|
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);
|
2218
|
+
test: async (sthis) => {
|
2219
|
+
const { FileTestStore } = await import("./gateway-5FCWPX5W.js");
|
2220
|
+
return new FileTestStore(sthis);
|
1766
2221
|
}
|
1767
2222
|
});
|
1768
2223
|
registerStoreProtocol({
|
1769
2224
|
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);
|
2225
|
+
gateway: async (sthis) => {
|
2226
|
+
const { IndexDBGateway } = await import("./gateway-H7UD6TNB.js");
|
2227
|
+
return new IndexDBGateway(sthis);
|
1781
2228
|
},
|
1782
|
-
test: async (
|
1783
|
-
const { IndexDBTestStore } = await import("./
|
1784
|
-
return new IndexDBTestStore(
|
2229
|
+
test: async (sthis) => {
|
2230
|
+
const { IndexDBTestStore } = await import("./gateway-H7UD6TNB.js");
|
2231
|
+
return new IndexDBTestStore(sthis);
|
1785
2232
|
}
|
1786
2233
|
});
|
1787
2234
|
|
2235
|
+
// src/blockstore/store-remote.ts
|
2236
|
+
async function RemoteDataStore(sthis, name, url, opts) {
|
2237
|
+
const ds = new DataStoreImpl(sthis, name, url, opts);
|
2238
|
+
await ds.start();
|
2239
|
+
return ds;
|
2240
|
+
}
|
2241
|
+
async function RemoteMetaStore(sthis, name, url, opts) {
|
2242
|
+
const ms = new MetaStoreImpl(
|
2243
|
+
sthis,
|
2244
|
+
name,
|
2245
|
+
url,
|
2246
|
+
opts
|
2247
|
+
/* , true*/
|
2248
|
+
);
|
2249
|
+
await ms.start();
|
2250
|
+
return ms;
|
2251
|
+
}
|
2252
|
+
|
2253
|
+
// src/blockstore/connection-base.ts
|
2254
|
+
var ConnectionBase = class {
|
2255
|
+
constructor(url, logger) {
|
2256
|
+
this.loaded = Promise.resolve();
|
2257
|
+
this.logger = logger;
|
2258
|
+
this.url = url;
|
2259
|
+
}
|
2260
|
+
async refresh() {
|
2261
|
+
await throwFalsy(throwFalsy(this.loader).remoteMetaStore).load();
|
2262
|
+
await (await throwFalsy(this.loader).WALStore()).process();
|
2263
|
+
}
|
2264
|
+
async connect_X({ loader }) {
|
2265
|
+
if (!loader) throw this.logger.Error().Msg("loader is required").AsError();
|
2266
|
+
await this.connectMeta_X({ loader });
|
2267
|
+
await this.connectStorage_X({ loader });
|
2268
|
+
}
|
2269
|
+
async connectMeta_X({ loader }) {
|
2270
|
+
if (!loader) throw this.logger.Error().Msg("connectMeta_X: loader is required").AsError();
|
2271
|
+
this.loader = loader;
|
2272
|
+
await this.onConnect();
|
2273
|
+
const metaUrl = this.url.build().defParam("store", "meta").URI();
|
2274
|
+
const gateway = await getGatewayFromURL(metaUrl, this.loader.sthis);
|
2275
|
+
if (!gateway) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: gateway is required").AsError();
|
2276
|
+
const dbName = metaUrl.getParam("name");
|
2277
|
+
if (!dbName) throw this.logger.Error().Url(metaUrl).Msg("connectMeta_X: name is required").AsError();
|
2278
|
+
const remote = await RemoteMetaStore(loader.sthis, dbName, metaUrl, {
|
2279
|
+
gateway: gateway.gateway,
|
2280
|
+
keybag: () => getKeyBag(loader.sthis, loader.ebOpts.keyBag),
|
2281
|
+
loader
|
2282
|
+
});
|
2283
|
+
this.loader.remoteMetaStore = remote;
|
2284
|
+
this.loaded = this.loader.ready().then(async () => {
|
2285
|
+
remote.load().then(async () => {
|
2286
|
+
(await throwFalsy(this.loader).WALStore()).process();
|
2287
|
+
});
|
2288
|
+
});
|
2289
|
+
}
|
2290
|
+
async connectStorage_X({ loader }) {
|
2291
|
+
if (!loader) throw this.logger.Error().Msg("connectStorage_X: loader is required").AsError();
|
2292
|
+
this.loader = loader;
|
2293
|
+
const dataUrl = this.url.build().defParam("store", "data").URI();
|
2294
|
+
const gateway = await getGatewayFromURL(dataUrl, this.loader.sthis);
|
2295
|
+
if (!gateway) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: gateway is required").AsError();
|
2296
|
+
const name = dataUrl.getParam("name");
|
2297
|
+
if (!name) throw this.logger.Error().Url(dataUrl).Msg("connectStorage_X: name is required").AsError;
|
2298
|
+
loader.remoteCarStore = await RemoteDataStore(loader.sthis, name, this.url, {
|
2299
|
+
gateway: gateway.gateway,
|
2300
|
+
keybag: () => getKeyBag(loader.sthis, this.loader?.ebOpts.keyBag)
|
2301
|
+
});
|
2302
|
+
loader.remoteFileStore = loader.remoteCarStore;
|
2303
|
+
}
|
2304
|
+
// move this stuff to connect
|
2305
|
+
// async getDashboardURL(compact = true) {
|
2306
|
+
// const baseUrl = 'https://dashboard.fireproof.storage/'
|
2307
|
+
// if (!this.loader?.remoteCarStore) return new URL('/howto', baseUrl)
|
2308
|
+
// // if (compact) {
|
2309
|
+
// // await this.compact()
|
2310
|
+
// // }
|
2311
|
+
// const currents = await this.loader?.metaStore?.load()
|
2312
|
+
// if (!currents) throw new Error("Can't sync empty database: save data first")
|
2313
|
+
// if (currents.length > 1)
|
2314
|
+
// throw new Error("Can't sync database with split heads: make an update first")
|
2315
|
+
// const current = currents[0]
|
2316
|
+
// const params = {
|
2317
|
+
// car: current.car.toString()
|
2318
|
+
// }
|
2319
|
+
// if (current.key) {
|
2320
|
+
// // @ts-ignore
|
2321
|
+
// params.key = current.key.toString()
|
2322
|
+
// }
|
2323
|
+
// // @ts-ignore
|
2324
|
+
// if (this.name) {
|
2325
|
+
// // @ts-ignore
|
2326
|
+
// params.name = this.name
|
2327
|
+
// }
|
2328
|
+
// const url = new URL('/import#' + new URLSearchParams(params).toString(), baseUrl)
|
2329
|
+
// console.log('Import to dashboard: ' + url.toString())
|
2330
|
+
// return url
|
2331
|
+
// }
|
2332
|
+
// openDashboard() {
|
2333
|
+
// void this.getDashboardURL().then(url => {
|
2334
|
+
// if (url) window.open(url.toString(), '_blank')
|
2335
|
+
// })
|
2336
|
+
// }
|
2337
|
+
};
|
2338
|
+
|
1788
2339
|
// src/crdt-helpers.ts
|
1789
2340
|
function time(tag) {
|
1790
2341
|
}
|
@@ -1833,7 +2384,7 @@ async function writeDocContent(store, blocks, update, logger) {
|
|
1833
2384
|
await processFiles(store, blocks, update.value, logger);
|
1834
2385
|
value = { doc: update.value };
|
1835
2386
|
}
|
1836
|
-
const block = await
|
2387
|
+
const block = await encode({ value, hasher: hasher5, codec });
|
1837
2388
|
blocks.putSync(block.cid, block.bytes);
|
1838
2389
|
return block.cid;
|
1839
2390
|
}
|
@@ -1842,10 +2393,16 @@ async function processFiles(store, blocks, doc, logger) {
|
|
1842
2393
|
await processFileset(logger, store, blocks, doc._files);
|
1843
2394
|
}
|
1844
2395
|
if (doc._publicFiles) {
|
1845
|
-
await processFileset(
|
2396
|
+
await processFileset(
|
2397
|
+
logger,
|
2398
|
+
store,
|
2399
|
+
blocks,
|
2400
|
+
doc._publicFiles
|
2401
|
+
/*, true*/
|
2402
|
+
);
|
1846
2403
|
}
|
1847
2404
|
}
|
1848
|
-
async function processFileset(logger, store, blocks, files
|
2405
|
+
async function processFileset(logger, store, blocks, files) {
|
1849
2406
|
const dbBlockstore = blocks.parent;
|
1850
2407
|
if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, database name is required").AsError();
|
1851
2408
|
const t = new CarTransaction(dbBlockstore);
|
@@ -1867,9 +2424,10 @@ async function processFileset(logger, store, blocks, files, publicFiles = false)
|
|
1867
2424
|
}
|
1868
2425
|
}
|
1869
2426
|
if (didPut.length) {
|
1870
|
-
const car = await dbBlockstore.loader.commitFiles(
|
1871
|
-
|
1872
|
-
|
2427
|
+
const car = await dbBlockstore.loader.commitFiles(
|
2428
|
+
t,
|
2429
|
+
{ files }
|
2430
|
+
);
|
1873
2431
|
if (car) {
|
1874
2432
|
for (const name of didPut) {
|
1875
2433
|
files[name] = { car, ...files[name] };
|
@@ -1903,7 +2461,7 @@ function readFileset(blocks, files, isPublic = false) {
|
|
1903
2461
|
fileMeta.file = async () => await blocks.ebOpts.storeRuntime.decodeFile(
|
1904
2462
|
{
|
1905
2463
|
get: async (cid) => {
|
1906
|
-
return await blocks.getFile(throwFalsy(fileMeta.car), cid
|
2464
|
+
return await blocks.getFile(throwFalsy(fileMeta.car), cid);
|
1907
2465
|
}
|
1908
2466
|
},
|
1909
2467
|
fileMeta.cid,
|
@@ -1917,7 +2475,7 @@ function readFileset(blocks, files, isPublic = false) {
|
|
1917
2475
|
async function getValueFromLink(blocks, link, logger) {
|
1918
2476
|
const block = await blocks.get(link);
|
1919
2477
|
if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
|
1920
|
-
const { value } = await
|
2478
|
+
const { value } = await decode({ bytes: block.bytes, hasher: hasher5, codec });
|
1921
2479
|
const cvalue = {
|
1922
2480
|
...value,
|
1923
2481
|
cid: link
|
@@ -1926,17 +2484,21 @@ async function getValueFromLink(blocks, link, logger) {
|
|
1926
2484
|
return cvalue;
|
1927
2485
|
}
|
1928
2486
|
var DirtyEventFetcher = class extends EventFetcher {
|
2487
|
+
constructor(logger, blocks) {
|
2488
|
+
super(blocks);
|
2489
|
+
this.logger = logger;
|
2490
|
+
}
|
1929
2491
|
async get(link) {
|
1930
2492
|
try {
|
1931
2493
|
return super.get(link);
|
1932
2494
|
} catch (e) {
|
1933
|
-
|
2495
|
+
this.logger.Error().Ref("link", link.toString()).Err(e).Msg("Missing event");
|
1934
2496
|
return { value: void 0 };
|
1935
2497
|
}
|
1936
2498
|
}
|
1937
2499
|
};
|
1938
2500
|
async function clockChangesSince(blocks, head, since, opts, logger) {
|
1939
|
-
const eventsFetcher = opts.dirty ? new DirtyEventFetcher(blocks) : new EventFetcher(blocks);
|
2501
|
+
const eventsFetcher = opts.dirty ? new DirtyEventFetcher(logger, blocks) : new EventFetcher(blocks);
|
1940
2502
|
const keys = /* @__PURE__ */ new Set();
|
1941
2503
|
const updates = await gatherUpdates(
|
1942
2504
|
blocks,
|
@@ -2032,20 +2594,19 @@ async function doCompact(blockLog, head, logger) {
|
|
2032
2594
|
isCompacting = false;
|
2033
2595
|
}
|
2034
2596
|
async function getBlock(blocks, cidString) {
|
2035
|
-
const block = await blocks.get(
|
2597
|
+
const block = await blocks.get(parse3(cidString));
|
2036
2598
|
if (!block) throw new Error(`Missing block ${cidString}`);
|
2037
|
-
const { cid, value } = await
|
2038
|
-
return new
|
2599
|
+
const { cid, value } = await decode({ bytes: block.bytes, codec, hasher: hasher5 });
|
2600
|
+
return new Block({ cid, value, bytes: block.bytes });
|
2039
2601
|
}
|
2040
2602
|
|
2041
2603
|
// src/indexer-helpers.ts
|
2042
|
-
import {
|
2043
|
-
import
|
2044
|
-
import * as codec3 from "@ipld/dag-cbor";
|
2604
|
+
import { sha256 as hasher6 } from "multiformats/hashes/sha2";
|
2605
|
+
import * as codec2 from "@ipld/dag-cbor";
|
2045
2606
|
import charwise from "charwise";
|
2046
2607
|
import * as DbIndex from "prolly-trees/db-index";
|
2047
|
-
import { bf
|
2048
|
-
import { nocache as
|
2608
|
+
import { bf, simpleCompare } from "prolly-trees/utils";
|
2609
|
+
import { nocache as cache } from "prolly-trees/cache";
|
2049
2610
|
var IndexTree = class {
|
2050
2611
|
};
|
2051
2612
|
function refCompare(aRef, bRef) {
|
@@ -2061,8 +2622,8 @@ function compare(a, b) {
|
|
2061
2622
|
if (comp !== 0) return comp;
|
2062
2623
|
return refCompare(aRef, bRef);
|
2063
2624
|
}
|
2064
|
-
var byKeyOpts = { cache
|
2065
|
-
var byIdOpts = { cache
|
2625
|
+
var byKeyOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare };
|
2626
|
+
var byIdOpts = { cache, chunker: bf(30), codec: codec2, hasher: hasher6, compare: simpleCompare };
|
2066
2627
|
function indexEntriesForChanges(changes, mapFn) {
|
2067
2628
|
const indexEntries = [];
|
2068
2629
|
changes.forEach(({ id: key, value, del }) => {
|
@@ -2090,7 +2651,7 @@ function makeProllyGetBlock(blocks) {
|
|
2090
2651
|
const block = await blocks.get(address);
|
2091
2652
|
if (!block) throw new Error(`Missing block ${address.toString()}`);
|
2092
2653
|
const { cid, bytes } = block;
|
2093
|
-
return
|
2654
|
+
return create({ cid, bytes, hasher: hasher6, codec: codec2 });
|
2094
2655
|
};
|
2095
2656
|
}
|
2096
2657
|
async function bulkIndex(tblocks, inIndex, indexEntries, opts) {
|
@@ -2161,25 +2722,25 @@ function encodeKey(key) {
|
|
2161
2722
|
}
|
2162
2723
|
|
2163
2724
|
// src/indexer.ts
|
2164
|
-
function index({ _crdt }, name, mapFn, meta) {
|
2725
|
+
function index(sthis, { _crdt }, name, mapFn, meta) {
|
2165
2726
|
if (mapFn && meta) throw _crdt.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
|
2166
2727
|
if (mapFn && mapFn.constructor.name !== "Function") throw _crdt.logger.Error().Msg("mapFn must be a function").AsError();
|
2167
2728
|
if (_crdt.indexers.has(name)) {
|
2168
2729
|
const idx = _crdt.indexers.get(name);
|
2169
2730
|
idx.applyMapFn(name, mapFn, meta);
|
2170
2731
|
} else {
|
2171
|
-
const idx = new Index(_crdt, name, mapFn, meta);
|
2732
|
+
const idx = new Index(sthis, _crdt, name, mapFn, meta);
|
2172
2733
|
_crdt.indexers.set(name, idx);
|
2173
2734
|
}
|
2174
2735
|
return _crdt.indexers.get(name);
|
2175
2736
|
}
|
2176
2737
|
var Index = class {
|
2177
|
-
constructor(crdt, name, mapFn, meta) {
|
2738
|
+
constructor(sthis, crdt, name, mapFn, meta) {
|
2178
2739
|
this.mapFnString = "";
|
2179
2740
|
this.byKey = new IndexTree();
|
2180
2741
|
this.byId = new IndexTree();
|
2181
2742
|
this.includeDocsDefault = false;
|
2182
|
-
this.logger = ensureLogger(
|
2743
|
+
this.logger = ensureLogger(sthis, "Index");
|
2183
2744
|
this.blockstore = crdt.indexBlockstore;
|
2184
2745
|
this.crdt = crdt;
|
2185
2746
|
this.applyMapFn(name, mapFn, meta);
|
@@ -2234,7 +2795,7 @@ var Index = class {
|
|
2234
2795
|
}
|
2235
2796
|
if (this.mapFnString) {
|
2236
2797
|
if (this.mapFnString !== mapFn.toString()) {
|
2237
|
-
throw this.logger.Error().Msg("cannot apply different mapFn app").AsError();
|
2798
|
+
throw this.logger.Error().Str("mapFnString", this.mapFnString).Str("mapFn", mapFn.toString()).Msg("cannot apply different mapFn app").AsError();
|
2238
2799
|
}
|
2239
2800
|
} else {
|
2240
2801
|
this.mapFnString = mapFn.toString();
|
@@ -2369,7 +2930,7 @@ var Index = class {
|
|
2369
2930
|
// src/crdt-clock.ts
|
2370
2931
|
import { advance } from "@web3-storage/pail/clock";
|
2371
2932
|
import { root as root2 } from "@web3-storage/pail/crdt";
|
2372
|
-
import { ResolveOnce as
|
2933
|
+
import { ResolveOnce as ResolveOnce4 } from "@adviser/cement";
|
2373
2934
|
|
2374
2935
|
// src/apply-head-queue.ts
|
2375
2936
|
function applyHeadQueue(worker, logger) {
|
@@ -2384,7 +2945,7 @@ function applyHeadQueue(worker, logger) {
|
|
2384
2945
|
queue.sort((a, b) => b.updates ? 1 : -1);
|
2385
2946
|
const task = queue.shift();
|
2386
2947
|
if (!task) continue;
|
2387
|
-
await worker(task.newHead, task.prevHead, task.updates !==
|
2948
|
+
await worker(task.newHead, task.prevHead, task.updates !== void 0).catch((e) => {
|
2388
2949
|
throw logger.Error().Err(e).Msg("int_applyHead worker error").AsError();
|
2389
2950
|
});
|
2390
2951
|
if (task.updates) {
|
@@ -2426,9 +2987,9 @@ var CRDTClock = class {
|
|
2426
2987
|
this.zoomers = /* @__PURE__ */ new Set();
|
2427
2988
|
this.watchers = /* @__PURE__ */ new Set();
|
2428
2989
|
this.emptyWatchers = /* @__PURE__ */ new Set();
|
2429
|
-
this._ready = new
|
2990
|
+
this._ready = new ResolveOnce4();
|
2430
2991
|
this.blockstore = blockstore;
|
2431
|
-
this.logger = ensureLogger(blockstore.
|
2992
|
+
this.logger = ensureLogger(blockstore.sthis, "CRDTClock");
|
2432
2993
|
this.applyHeadQueue = applyHeadQueue(this.int_applyHead.bind(this), this.logger);
|
2433
2994
|
}
|
2434
2995
|
async ready() {
|
@@ -2474,6 +3035,7 @@ var CRDTClock = class {
|
|
2474
3035
|
this.zoomers.add(fn);
|
2475
3036
|
}
|
2476
3037
|
async int_applyHead(newHead, prevHead, localUpdates) {
|
3038
|
+
const noLoader = !localUpdates;
|
2477
3039
|
const ogHead = sortClockHead(this.head);
|
2478
3040
|
newHead = sortClockHead(newHead);
|
2479
3041
|
if (compareClockHeads(ogHead, newHead)) {
|
@@ -2484,7 +3046,6 @@ var CRDTClock = class {
|
|
2484
3046
|
this.setHead(newHead);
|
2485
3047
|
return;
|
2486
3048
|
}
|
2487
|
-
const noLoader = !localUpdates;
|
2488
3049
|
if (!this.blockstore) {
|
2489
3050
|
throw this.logger.Error().Msg("missing blockstore").AsError();
|
2490
3051
|
}
|
@@ -2501,7 +3062,7 @@ var CRDTClock = class {
|
|
2501
3062
|
}
|
2502
3063
|
return { head: advancedHead };
|
2503
3064
|
},
|
2504
|
-
{ noLoader }
|
3065
|
+
{ noLoader, add: false }
|
2505
3066
|
);
|
2506
3067
|
this.setHead(meta.head);
|
2507
3068
|
}
|
@@ -2535,13 +3096,14 @@ async function advanceBlocks(logger, newHead, tblocks, head) {
|
|
2535
3096
|
|
2536
3097
|
// src/crdt.ts
|
2537
3098
|
var CRDT = class {
|
2538
|
-
constructor(name, opts = {}) {
|
2539
|
-
this.onceReady = new ResolveOnce4();
|
3099
|
+
constructor(sthis, name, opts = {}) {
|
2540
3100
|
this.indexers = /* @__PURE__ */ new Map();
|
3101
|
+
this.onceReady = new ResolveOnce5();
|
3102
|
+
this.sthis = sthis;
|
2541
3103
|
this.name = name;
|
2542
|
-
this.logger = ensureLogger(
|
3104
|
+
this.logger = ensureLogger(sthis, "CRDT");
|
2543
3105
|
this.opts = opts;
|
2544
|
-
this.blockstore = blockstoreFactory({
|
3106
|
+
this.blockstore = blockstoreFactory(sthis, {
|
2545
3107
|
name,
|
2546
3108
|
applyMeta: async (meta) => {
|
2547
3109
|
const crdtMeta = meta;
|
@@ -2553,22 +3115,20 @@ var CRDT = class {
|
|
2553
3115
|
return { head: this.clock.head };
|
2554
3116
|
},
|
2555
3117
|
autoCompact: this.opts.autoCompact || 100,
|
2556
|
-
crypto: this.opts.crypto,
|
2557
3118
|
store: { ...this.opts.store, isIndex: void 0 },
|
2558
3119
|
public: this.opts.public,
|
2559
3120
|
meta: this.opts.meta,
|
2560
3121
|
threshold: this.opts.threshold
|
2561
3122
|
});
|
2562
|
-
this.indexBlockstore = blockstoreFactory({
|
3123
|
+
this.indexBlockstore = blockstoreFactory(sthis, {
|
2563
3124
|
name,
|
2564
3125
|
applyMeta: async (meta) => {
|
2565
3126
|
const idxCarMeta = meta;
|
2566
3127
|
if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
|
2567
3128
|
for (const [name2, idx] of Object.entries(idxCarMeta.indexes)) {
|
2568
|
-
index({ _crdt: this }, name2, void 0, idx);
|
3129
|
+
index(this.sthis, { _crdt: this }, name2, void 0, idx);
|
2569
3130
|
}
|
2570
3131
|
},
|
2571
|
-
crypto: this.opts.crypto,
|
2572
3132
|
store: { ...this.opts.store, isIndex: this.opts.store?.isIndex || "idx" },
|
2573
3133
|
public: this.opts.public
|
2574
3134
|
});
|
@@ -2579,17 +3139,6 @@ var CRDT = class {
|
|
2579
3139
|
}
|
2580
3140
|
});
|
2581
3141
|
}
|
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
3142
|
async bulk(updates) {
|
2594
3143
|
await this.ready();
|
2595
3144
|
const prevHead = [...this.clock.head];
|
@@ -2610,6 +3159,21 @@ var CRDT = class {
|
|
2610
3159
|
await this.clock.applyHead(done.meta.head, prevHead, updates);
|
2611
3160
|
return done.meta;
|
2612
3161
|
}
|
3162
|
+
async ready() {
|
3163
|
+
return this.onceReady.once(async () => {
|
3164
|
+
try {
|
3165
|
+
await Promise.all([this.blockstore.ready(), this.indexBlockstore.ready(), this.clock.ready()]);
|
3166
|
+
} catch (e) {
|
3167
|
+
throw this.logger.Error().Err(e).Msg(`CRDT is not ready`).AsError();
|
3168
|
+
}
|
3169
|
+
});
|
3170
|
+
}
|
3171
|
+
async close() {
|
3172
|
+
await Promise.all([this.blockstore.close(), this.indexBlockstore.close(), this.clock.close()]);
|
3173
|
+
}
|
3174
|
+
async destroy() {
|
3175
|
+
await Promise.all([this.blockstore.destroy(), this.indexBlockstore.destroy()]);
|
3176
|
+
}
|
2613
3177
|
// if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
|
2614
3178
|
async allDocs() {
|
2615
3179
|
await this.ready();
|
@@ -2654,11 +3218,12 @@ var Database = class {
|
|
2654
3218
|
this._listening = false;
|
2655
3219
|
this._listeners = /* @__PURE__ */ new Set();
|
2656
3220
|
this._noupdate_listeners = /* @__PURE__ */ new Set();
|
2657
|
-
this._ready = new
|
3221
|
+
this._ready = new ResolveOnce6();
|
2658
3222
|
this.name = name;
|
2659
3223
|
this.opts = opts || this.opts;
|
2660
|
-
this.
|
2661
|
-
this.
|
3224
|
+
this.sthis = ensureSuperThis(this.opts);
|
3225
|
+
this.logger = ensureLogger(this.sthis, "Database");
|
3226
|
+
this._crdt = new CRDT(this.sthis, name, this.opts);
|
2662
3227
|
this.blockstore = this._crdt.blockstore;
|
2663
3228
|
this._writeQueue = writeQueue(async (updates) => {
|
2664
3229
|
return await this._crdt.bulk(updates);
|
@@ -2682,7 +3247,7 @@ var Database = class {
|
|
2682
3247
|
}
|
2683
3248
|
async ready() {
|
2684
3249
|
return this._ready.once(async () => {
|
2685
|
-
await
|
3250
|
+
await this.sthis.start();
|
2686
3251
|
await this._crdt.ready();
|
2687
3252
|
await this.blockstore.ready();
|
2688
3253
|
});
|
@@ -2702,7 +3267,7 @@ var Database = class {
|
|
2702
3267
|
await this.ready();
|
2703
3268
|
this.logger.Debug().Str("id", doc._id).Msg("put");
|
2704
3269
|
const { _id, ...value } = doc;
|
2705
|
-
const docId = _id ||
|
3270
|
+
const docId = _id || this.sthis.timeOrderedNextId().str;
|
2706
3271
|
const result = await this._writeQueue.push({
|
2707
3272
|
id: docId,
|
2708
3273
|
value: {
|
@@ -2767,7 +3332,7 @@ var Database = class {
|
|
2767
3332
|
await this.ready();
|
2768
3333
|
this.logger.Debug().Any("field", field).Any("opts", opts).Msg("query");
|
2769
3334
|
const _crdt = this._crdt;
|
2770
|
-
const idx = typeof field === "string" ? index({ _crdt }, field) : index({ _crdt }, makeName(field.toString()), field);
|
3335
|
+
const idx = typeof field === "string" ? index(this.sthis, { _crdt }, field) : index(this.sthis, { _crdt }, makeName(field.toString()), field);
|
2771
3336
|
return await idx.query(opts);
|
2772
3337
|
}
|
2773
3338
|
async compact() {
|
@@ -2804,12 +3369,7 @@ function fireproof(name, opts) {
|
|
2804
3369
|
const key = JSON.stringify(
|
2805
3370
|
toSortedArray({
|
2806
3371
|
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
|
3372
|
+
stores: toSortedArray(opts?.store?.stores)
|
2813
3373
|
})
|
2814
3374
|
);
|
2815
3375
|
let db = Database.databases.get(key);
|
@@ -2824,7 +3384,10 @@ function makeName(fnString) {
|
|
2824
3384
|
let found = null;
|
2825
3385
|
const matches = Array.from(fnString.matchAll(regex), (match) => match[1].trim());
|
2826
3386
|
if (matches.length === 0) {
|
2827
|
-
found = /=>\s*(
|
3387
|
+
found = /=>\s*{?\s*([^{}]+)\s*}?/.exec(fnString);
|
3388
|
+
if (found && found[1].includes("return")) {
|
3389
|
+
found = null;
|
3390
|
+
}
|
2828
3391
|
}
|
2829
3392
|
if (!found) {
|
2830
3393
|
return fnString;
|
@@ -2833,20 +3396,53 @@ function makeName(fnString) {
|
|
2833
3396
|
}
|
2834
3397
|
}
|
2835
3398
|
|
3399
|
+
// src/runtime/index.ts
|
3400
|
+
var runtime_exports = {};
|
3401
|
+
__export(runtime_exports, {
|
3402
|
+
FILESTORE_VERSION: () => FILESTORE_VERSION,
|
3403
|
+
INDEXDB_VERSION: () => INDEXDB_VERSION,
|
3404
|
+
files: () => files_exports,
|
3405
|
+
getFileName: () => getFileName,
|
3406
|
+
getFileSystem: () => getFileSystem,
|
3407
|
+
getPath: () => getPath,
|
3408
|
+
kb: () => key_bag_exports,
|
3409
|
+
kc: () => keyed_crypto_exports,
|
3410
|
+
mf: () => wait_pr_multiformats_exports,
|
3411
|
+
runtimeFn: () => runtimeFn2,
|
3412
|
+
toArrayBuffer: () => toArrayBuffer
|
3413
|
+
});
|
3414
|
+
|
3415
|
+
// src/runtime/wait-pr-multiformats/index.ts
|
3416
|
+
var wait_pr_multiformats_exports = {};
|
3417
|
+
__export(wait_pr_multiformats_exports, {
|
3418
|
+
block: () => block_exports,
|
3419
|
+
codec: () => codec_interface_exports
|
3420
|
+
});
|
3421
|
+
|
3422
|
+
// src/runtime/wait-pr-multiformats/codec-interface.ts
|
3423
|
+
var codec_interface_exports = {};
|
3424
|
+
|
3425
|
+
// src/runtime/index.ts
|
3426
|
+
import { runtimeFn as runtimeFn2 } from "@adviser/cement";
|
3427
|
+
|
2836
3428
|
// src/version.ts
|
2837
3429
|
var PACKAGE_VERSION = Object.keys({
|
2838
|
-
"0.19.8-dev-
|
3430
|
+
"0.19.8-dev-series-2": "xxxx"
|
2839
3431
|
})[0];
|
2840
3432
|
export {
|
2841
3433
|
CRDT,
|
2842
3434
|
Database,
|
2843
3435
|
Index,
|
3436
|
+
NotFoundError,
|
2844
3437
|
PACKAGE_VERSION,
|
2845
3438
|
Result,
|
3439
|
+
UInt8ArrayEqual,
|
2846
3440
|
blockstore_exports as blockstore,
|
2847
3441
|
blockstore_exports as bs,
|
3442
|
+
dataDir,
|
2848
3443
|
ensureLogger,
|
2849
|
-
|
3444
|
+
ensureSuperLog,
|
3445
|
+
ensureSuperThis,
|
2850
3446
|
exceptionWrapper,
|
2851
3447
|
falsyToUndef,
|
2852
3448
|
fireproof,
|
@@ -2855,6 +3451,7 @@ export {
|
|
2855
3451
|
getStore,
|
2856
3452
|
index,
|
2857
3453
|
isFalsy,
|
3454
|
+
isNotFoundError,
|
2858
3455
|
runtime_exports as rt,
|
2859
3456
|
runtime_exports as runtime,
|
2860
3457
|
throwFalsy
|