@fireproof/core-base 0.22.0-keybag → 0.23.1-dev-issue-1057

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/crdt-helpers.ts DELETED
@@ -1,408 +0,0 @@
1
- import { asyncBlockDecode } from "@fireproof/core-runtime";
2
- import { parse } from "multiformats/link";
3
- import { Block } from "multiformats/block";
4
- import { sha256 as hasher } from "multiformats/hashes/sha2";
5
- import * as codec from "@ipld/dag-cbor";
6
- import { put, get, entries } from "@web3-storage/pail/crdt";
7
- import {
8
- EventBlockView,
9
- EventLink,
10
- Operation,
11
- PutOperation,
12
- Result,
13
- BlockFetcher as PailBlockFetcher,
14
- } from "@web3-storage/pail/crdt/api";
15
- import { EventFetcher, vis } from "@web3-storage/pail/clock";
16
- import * as Batch from "@web3-storage/pail/crdt/batch";
17
- import { BlockFetcher, TransactionMeta, AnyLink, StoreRuntime } from "@fireproof/core-types-blockstore";
18
- import {
19
- type EncryptedBlockstore,
20
- CarTransactionImpl,
21
- anyBlock2FPBlock,
22
- doc2FPBlock,
23
- fileBlock2FPBlock,
24
- } from "@fireproof/core-blockstore";
25
- import {
26
- type IndexKeyType,
27
- type DocUpdate,
28
- type ClockHead,
29
- type DocValue,
30
- type CRDTMeta,
31
- type ChangesOptions,
32
- type DocFileMeta,
33
- type DocFiles,
34
- type DocSet,
35
- type DocWithId,
36
- type DocTypes,
37
- throwFalsy,
38
- CarTransaction,
39
- BaseBlockstore,
40
- PARAM,
41
- NotFoundError,
42
- } from "@fireproof/core-types-base";
43
- import { Logger } from "@adviser/cement";
44
- import { Link, Version } from "multiformats";
45
-
46
- function toString<K extends IndexKeyType>(key: K, logger: Logger): string {
47
- switch (typeof key) {
48
- case "string":
49
- case "number":
50
- return key.toString();
51
- default:
52
- throw logger.Error().Msg("Invalid key type").AsError();
53
- }
54
- }
55
-
56
- export function toPailFetcher(tblocks: BlockFetcher): PailBlockFetcher {
57
- return {
58
- get: async <T = unknown, C extends number = number, A extends number = number, V extends Version = 1>(
59
- link: Link<T, C, A, V>,
60
- ) => {
61
- const block = await tblocks.get(link);
62
- return block
63
- ? ({
64
- cid: block.cid,
65
- bytes: block.bytes,
66
- } as Block<T, C, A, V>)
67
- : undefined;
68
- },
69
- };
70
- }
71
-
72
- export function sanitizeDocumentFields<T>(obj: T): T {
73
- if (Array.isArray(obj)) {
74
- return obj.map((item: unknown) => {
75
- if (typeof item === "object" && item !== null) {
76
- return sanitizeDocumentFields(item);
77
- }
78
- return item;
79
- }) as T;
80
- } else if (typeof obj === "object" && obj !== null) {
81
- // Special case for Date objects - convert to ISO string
82
- if (obj instanceof Date) {
83
- return obj.toISOString() as unknown as T;
84
- }
85
-
86
- const typedObj = obj as Record<string, unknown>;
87
- const result: Record<string, unknown> = {};
88
- for (const key in typedObj) {
89
- if (Object.hasOwnProperty.call(typedObj, key)) {
90
- const value = typedObj[key];
91
- if (value === null || (!Number.isNaN(value) && value !== undefined)) {
92
- if (typeof value === "object" && !key.startsWith("_")) {
93
- // Handle Date objects in properties
94
- if (value instanceof Date) {
95
- result[key] = (value as Date).toISOString();
96
- } else {
97
- const sanitized = sanitizeDocumentFields(value);
98
- result[key] = sanitized;
99
- }
100
- } else {
101
- result[key] = value;
102
- }
103
- }
104
- }
105
- }
106
- return result as T;
107
- }
108
- return obj;
109
- }
110
-
111
- export async function applyBulkUpdateToCrdt<T extends DocTypes>(
112
- store: StoreRuntime,
113
- tblocks: CarTransaction,
114
- head: ClockHead,
115
- updates: DocUpdate<T>[],
116
- logger: Logger,
117
- ): Promise<CRDTMeta> {
118
- let result: Result | null = null;
119
- if (updates.length > 1) {
120
- const batch = await Batch.create(toPailFetcher(tblocks), head);
121
- for (const update of updates) {
122
- const link = await writeDocContent(store, tblocks, update, logger);
123
- await batch.put(toString(update.id, logger), link);
124
- }
125
- result = await batch.commit();
126
- } else if (updates.length === 1) {
127
- const link = await writeDocContent(store, tblocks, updates[0], logger);
128
- result = await put(toPailFetcher(tblocks), head, toString(updates[0].id, logger), link);
129
- }
130
- if (!result) throw logger.Error().Uint64("updates.len", updates.length).Msg("Missing result").AsError();
131
-
132
- if (result.event) {
133
- for (const block of [
134
- ...result.additions,
135
- // ...result.removals,
136
- result.event,
137
- ]) {
138
- tblocks.putSync(await anyBlock2FPBlock(block));
139
- }
140
- }
141
- return { head: result.head }; // satisfies CRDTMeta;
142
- }
143
-
144
- // this whole thing can get pulled outside of the write queue
145
- async function writeDocContent<T extends DocTypes>(
146
- store: StoreRuntime,
147
- blocks: CarTransaction,
148
- update: DocUpdate<T>,
149
- logger: Logger,
150
- ): Promise<AnyLink> {
151
- let value: Partial<DocValue<T>>;
152
- if (update.del) {
153
- value = { del: true };
154
- } else {
155
- if (!update.value) throw logger.Error().Msg("Missing value").AsError();
156
- await processFiles(store, blocks, update.value, logger);
157
- value = { doc: update.value as DocWithId<T> };
158
- }
159
- // const ref = await encode({ value, hasher, codec });
160
- const block = await doc2FPBlock(value);
161
- // if (ref.cid.toString() !== block.cid.toString()) {
162
- // debugger
163
- // }
164
- blocks.putSync(block);
165
- return block.cid;
166
- }
167
-
168
- async function processFiles<T extends DocTypes>(store: StoreRuntime, blocks: CarTransaction, doc: DocSet<T>, logger: Logger) {
169
- if (doc._files) {
170
- await processFileset(logger, store, blocks, doc._files);
171
- }
172
- if (doc._publicFiles) {
173
- await processFileset(logger, store, blocks, doc._publicFiles /*, true*/);
174
- }
175
- }
176
-
177
- async function processFileset(
178
- logger: Logger,
179
- store: StoreRuntime,
180
- blocks: CarTransaction,
181
- files: DocFiles /*, publicFiles = false */,
182
- ) {
183
- const dbBlockstore = blocks.parent as unknown as EncryptedBlockstore;
184
- if (!dbBlockstore.loader) throw logger.Error().Msg("Missing loader, ledger name is required").AsError();
185
- const t = new CarTransactionImpl(dbBlockstore); // maybe this should move to encrypted-blockstore
186
- const didPut = [];
187
- // let totalSize = 0
188
- for (const filename in files) {
189
- if (File === files[filename].constructor) {
190
- const file = files[filename] as File;
191
-
192
- // totalSize += file.size
193
- const { cid, blocks: fileBlocks } = await store.encodeFile(file);
194
- didPut.push(filename);
195
- for (const block of fileBlocks) {
196
- // console.log("processFileset", block.cid.toString())
197
- t.putSync(await fileBlock2FPBlock(block));
198
- }
199
- files[filename] = { cid, type: file.type, size: file.size, lastModified: file.lastModified } as DocFileMeta;
200
- } else {
201
- const { cid, type, size, car, lastModified } = files[filename] as DocFileMeta;
202
- if (cid && type && size && car) {
203
- files[filename] = { cid, type, size, car, lastModified };
204
- }
205
- }
206
- }
207
-
208
- if (didPut.length) {
209
- const car = await dbBlockstore.loader.commitFiles(
210
- t,
211
- { files } as unknown as TransactionMeta /* {
212
- public: publicFiles,
213
- } */,
214
- );
215
- if (car) {
216
- for (const name of didPut) {
217
- files[name] = { car, ...files[name] } as DocFileMeta;
218
- }
219
- }
220
- }
221
- }
222
-
223
- export async function getValueFromCrdt<T extends DocTypes>(
224
- blocks: BaseBlockstore,
225
- head: ClockHead,
226
- key: string,
227
- logger: Logger,
228
- ): Promise<DocValue<T>> {
229
- if (!head.length) throw logger.Debug().Msg("Getting from an empty ledger").AsError();
230
- // console.log("getValueFromCrdt-1", head, key)
231
- const link = await get(toPailFetcher(blocks), head, key);
232
- // console.log("getValueFromCrdt-2", key)
233
- if (!link) {
234
- // Use NotFoundError instead of logging an error
235
- throw new NotFoundError(`Not found: ${key}`);
236
- }
237
- const ret = await getValueFromLink<T>(blocks, link, logger);
238
- // console.log("getValueFromCrdt-3", key)
239
- return ret;
240
- }
241
-
242
- export function readFiles<T extends DocTypes>(blocks: BaseBlockstore, { doc }: Partial<DocValue<T>>) {
243
- if (!doc) return;
244
- if (doc._files) {
245
- readFileset(blocks as EncryptedBlockstore, doc._files);
246
- }
247
- if (doc._publicFiles) {
248
- readFileset(blocks as EncryptedBlockstore, doc._publicFiles, true);
249
- }
250
- }
251
-
252
- function readFileset(blocks: EncryptedBlockstore, files: DocFiles, isPublic = false) {
253
- for (const filename in files) {
254
- const fileMeta = files[filename] as DocFileMeta;
255
- if (fileMeta.cid) {
256
- if (isPublic) {
257
- fileMeta.url = `https://${fileMeta.cid.toString()}.ipfs.w3s.link/`;
258
- }
259
- if (fileMeta.car) {
260
- fileMeta.file = async () => {
261
- const result = await blocks.ebOpts.storeRuntime.decodeFile(
262
- {
263
- get: async (cid: AnyLink) => {
264
- return await blocks.getFile(throwFalsy(fileMeta.car), cid);
265
- },
266
- },
267
- fileMeta.cid,
268
- fileMeta,
269
- );
270
- if (result.isErr()) {
271
- throw blocks.logger.Error().Any("error", result.Err()).Any("cid", fileMeta.cid).Msg("Error decoding file").AsError();
272
- }
273
-
274
- return result.unwrap();
275
- };
276
- }
277
- }
278
- files[filename] = fileMeta;
279
- }
280
- }
281
-
282
- async function getValueFromLink<T extends DocTypes>(blocks: BlockFetcher, link: AnyLink, logger: Logger): Promise<DocValue<T>> {
283
- const block = await blocks.get(link);
284
- if (!block) throw logger.Error().Str("link", link.toString()).Msg(`Missing linked block`).AsError();
285
- const { value } = (await asyncBlockDecode({ bytes: block.bytes, hasher, codec })) as { value: DocValue<T> };
286
- const cvalue = {
287
- ...value,
288
- cid: link,
289
- };
290
- readFiles(blocks as EncryptedBlockstore, cvalue);
291
- return cvalue;
292
- }
293
-
294
- class DirtyEventFetcher<T> extends EventFetcher<T> {
295
- readonly logger: Logger;
296
- constructor(logger: Logger, blocks: BlockFetcher) {
297
- super(toPailFetcher(blocks));
298
- this.logger = logger;
299
- }
300
- async get(link: EventLink<T>): Promise<EventBlockView<T>> {
301
- try {
302
- return await super.get(link);
303
- } catch (e) {
304
- this.logger.Error().Ref("link", link.toString()).Err(e).Msg("Missing event");
305
- return { value: undefined } as unknown as EventBlockView<T>;
306
- }
307
- }
308
- }
309
-
310
- export async function clockChangesSince<T extends DocTypes>(
311
- blocks: BlockFetcher,
312
- head: ClockHead,
313
- since: ClockHead,
314
- opts: ChangesOptions,
315
- logger: Logger,
316
- ): Promise<{ result: DocUpdate<T>[]; head: ClockHead }> {
317
- const eventsFetcher = (
318
- opts.dirty ? new DirtyEventFetcher<Operation>(logger, blocks) : new EventFetcher<Operation>(toPailFetcher(blocks))
319
- ) as EventFetcher<Operation>;
320
- const keys = new Set<string>();
321
- const updates = await gatherUpdates<T>(
322
- blocks,
323
- eventsFetcher,
324
- head,
325
- since,
326
- [],
327
- keys,
328
- new Set<string>(),
329
- opts.limit || Infinity,
330
- logger,
331
- );
332
- return { result: updates.reverse(), head };
333
- }
334
-
335
- async function gatherUpdates<T extends DocTypes>(
336
- blocks: BlockFetcher,
337
- eventsFetcher: EventFetcher<Operation>,
338
- head: ClockHead,
339
- since: ClockHead,
340
- updates: DocUpdate<T>[] = [],
341
- keys: Set<string>,
342
- didLinks: Set<string>,
343
- limit: number,
344
- logger: Logger,
345
- ): Promise<DocUpdate<T>[]> {
346
- if (limit <= 0) return updates;
347
- // if (Math.random() < 0.001) console.log('gatherUpdates', head.length, since.length, updates.length)
348
- const sHead = head.map((l) => l.toString());
349
- for (const link of since) {
350
- if (sHead.includes(link.toString())) {
351
- return updates;
352
- }
353
- }
354
- for (const link of head) {
355
- if (didLinks.has(link.toString())) continue;
356
- didLinks.add(link.toString());
357
- const { value: event } = await eventsFetcher.get(link);
358
- if (!event) continue;
359
- const { type } = event.data;
360
- let ops = [] as PutOperation[];
361
- if (type === "batch") {
362
- ops = event.data.ops as PutOperation[];
363
- } else if (type === "put") {
364
- ops = [event.data] as PutOperation[];
365
- }
366
- for (let i = ops.length - 1; i >= 0; i--) {
367
- const { key, value } = ops[i];
368
- if (!keys.has(key)) {
369
- // todo option to see all updates
370
- const docValue = await getValueFromLink<T>(blocks, value, logger);
371
- if (key === PARAM.GENESIS_CID) {
372
- continue;
373
- }
374
- updates.push({ id: key, value: docValue.doc, del: docValue.del, clock: link });
375
- limit--;
376
- keys.add(key);
377
- }
378
- }
379
- if (event.parents) {
380
- updates = await gatherUpdates(blocks, eventsFetcher, event.parents, since, updates, keys, didLinks, limit, logger);
381
- }
382
- }
383
- return updates;
384
- }
385
-
386
- export async function* getAllEntries<T extends DocTypes>(blocks: BlockFetcher, head: ClockHead, logger: Logger) {
387
- // return entries(blocks, head)
388
- for await (const [key, link] of entries(toPailFetcher(blocks), head)) {
389
- // console.log("getAllEntries", key, link);
390
- if (key !== PARAM.GENESIS_CID) {
391
- const docValue = await getValueFromLink(blocks, link, logger);
392
- yield { id: key, value: docValue.doc, del: docValue.del } as DocUpdate<T>;
393
- }
394
- }
395
- }
396
-
397
- export async function* clockVis(blocks: BlockFetcher, head: ClockHead) {
398
- for await (const line of vis(toPailFetcher(blocks), head)) {
399
- yield line;
400
- }
401
- }
402
-
403
- export async function getBlock(blocks: BlockFetcher, cidString: string) {
404
- const block = await blocks.get(parse(cidString));
405
- if (!block) throw new Error(`Missing block ${cidString}`);
406
- const { cid, value } = await asyncBlockDecode({ bytes: block.bytes, codec, hasher });
407
- return new Block({ cid, value, bytes: block.bytes });
408
- }
package/crdt.ts DELETED
@@ -1,275 +0,0 @@
1
- import type { Block } from "multiformats";
2
- import { Logger, ResolveOnce } from "@adviser/cement";
3
- import { EncryptedBlockstore, toStoreRuntime } from "@fireproof/core-blockstore";
4
- import {
5
- clockChangesSince,
6
- applyBulkUpdateToCrdt,
7
- getValueFromCrdt,
8
- readFiles,
9
- getAllEntries,
10
- clockVis,
11
- getBlock,
12
- sanitizeDocumentFields,
13
- } from "./crdt-helpers.js";
14
- import {
15
- type DocUpdate,
16
- type CRDTMeta,
17
- type ClockHead,
18
- type ChangesOptions,
19
- type DocValue,
20
- type IndexKeyType,
21
- type DocWithId,
22
- type Falsy,
23
- type SuperThis,
24
- type IndexTransactionMeta,
25
- type LedgerOpts,
26
- type BaseBlockstore,
27
- type CRDT,
28
- type CRDTClock,
29
- type CarTransaction,
30
- type DocTypes,
31
- PARAM,
32
- Ledger,
33
- TraceEvent,
34
- } from "@fireproof/core-types-base";
35
- import { index, type Index } from "./indexer.js";
36
- // import { blockstoreFactory } from "./blockstore/transaction.js";
37
- import { ensureLogger, getCompactStrategy } from "@fireproof/core-runtime";
38
- import { CRDTClockImpl } from "./crdt-clock.js";
39
- import { TransactionMeta, BlockstoreOpts } from "@fireproof/core-types-blockstore";
40
-
41
- export type CRDTOpts = Omit<LedgerOpts, "storeUrls"> & {
42
- readonly storeUrls: {
43
- readonly data: LedgerOpts["storeUrls"]["data"];
44
- readonly idx?: LedgerOpts["storeUrls"]["idx"];
45
- };
46
- };
47
-
48
- function tracerAction(opts: CRDTOpts, parent?: Ledger) {
49
- return (event: TraceEvent) => {
50
- switch (event.event) {
51
- case "idleFromCommitQueue":
52
- opts.tracer({
53
- event: "idleFromBlockstore",
54
- blockstore: "data",
55
- ledger: parent,
56
- });
57
- break;
58
- case "busyFromCommitQueue":
59
- opts.tracer({
60
- event: "busyFromBlockstore",
61
- blockstore: "data",
62
- ledger: parent,
63
- queueLen: event.queueLen,
64
- });
65
- break;
66
- default:
67
- return opts.tracer(event);
68
- }
69
- };
70
- }
71
-
72
- export class CRDTImpl implements CRDT {
73
- readonly opts: CRDTOpts;
74
-
75
- readonly blockstore: BaseBlockstore;
76
- // we can run without an index instance
77
- readonly indexBlockstore?: BaseBlockstore;
78
- readonly indexers = new Map<string, Index<DocTypes, IndexKeyType>>();
79
- readonly clock: CRDTClock;
80
-
81
- readonly logger: Logger;
82
- readonly sthis: SuperThis;
83
- // self reference to fullfill HasCRDT
84
- readonly crdt: CRDT;
85
-
86
- readonly ledgerParent?: Ledger;
87
-
88
- constructor(sthis: SuperThis, opts: CRDTOpts, parent?: Ledger) {
89
- this.sthis = sthis;
90
- this.ledgerParent = parent;
91
- this.crdt = this;
92
- this.logger = ensureLogger(sthis, "CRDTImpl");
93
- this.opts = opts;
94
- const rCompactStrategy = getCompactStrategy(this.opts.compactStrategy);
95
- if (rCompactStrategy.isErr()) {
96
- throw this.logger
97
- .Error()
98
- .Err(rCompactStrategy.Err())
99
- .Str("compactorName", this.opts.compactStrategy)
100
- .Msg("compactor not found")
101
- .AsError();
102
- }
103
- const blockstoreOpts = {
104
- tracer: tracerAction(opts, parent),
105
- applyMeta: async (meta: TransactionMeta) => {
106
- const crdtMeta = meta as CRDTMeta;
107
- if (!crdtMeta.head) throw this.logger.Error().Msg("missing head").AsError();
108
- // console.log("applyMeta-pre", crdtMeta.head, this.clock.head);
109
- await this.clock.applyHead(crdtMeta.head, []);
110
- // console.log("applyMeta-post", crdtMeta.head, this.clock.head);
111
- },
112
- compactStrategy: rCompactStrategy.Ok(),
113
- gatewayInterceptor: opts.gatewayInterceptor,
114
- // autoCompact: this.opts.autoCompact || 100,
115
- storeRuntime: toStoreRuntime(this.sthis, this.opts.storeEnDe),
116
- storeUrls: this.opts.storeUrls.data,
117
- keyBag: this.opts.keyBag,
118
- // public: this.opts.public,
119
- meta: this.opts.meta,
120
- // threshold: this.opts.threshold,
121
- } satisfies BlockstoreOpts;
122
-
123
- this.blockstore = new EncryptedBlockstore(sthis, blockstoreOpts, this);
124
- if (this.opts.storeUrls.idx) {
125
- this.indexBlockstore = new EncryptedBlockstore(
126
- sthis,
127
- {
128
- tracer: opts.tracer,
129
- // name: opts.name,
130
- applyMeta: async (meta: TransactionMeta) => {
131
- const idxCarMeta = meta as IndexTransactionMeta;
132
- if (!idxCarMeta.indexes) throw this.logger.Error().Msg("missing indexes").AsError();
133
- for (const [name, idx] of Object.entries(idxCarMeta.indexes)) {
134
- index(this, name, undefined, idx);
135
- }
136
- },
137
- gatewayInterceptor: opts.gatewayInterceptor,
138
- storeRuntime: toStoreRuntime(this.sthis, this.opts.storeEnDe),
139
- storeUrls: this.opts.storeUrls.idx,
140
- keyBag: this.opts.keyBag,
141
- // public: this.opts.public,
142
- },
143
- this,
144
- );
145
- }
146
- this.clock = new CRDTClockImpl(this.blockstore);
147
- this.clock.onZoom(() => {
148
- for (const idx of this.indexers.values()) {
149
- idx._resetIndex();
150
- }
151
- });
152
- }
153
-
154
- async bulk<T extends DocTypes>(updates: DocUpdate<T>[]): Promise<CRDTMeta> {
155
- await this.ready();
156
- updates = updates.map((dupdate: DocUpdate<T>) => ({
157
- ...dupdate,
158
- value: sanitizeDocumentFields(dupdate.value),
159
- }));
160
-
161
- if (this.clock.head.length === 0) {
162
- // INJECT GENESIS Block
163
- const value = { id: PARAM.GENESIS_CID, value: { _id: PARAM.GENESIS_CID } }; // satisfies DocUpdate<DocSet<DocTypes>>;
164
- // const block = await encode({ value, hasher: sha256, codec: dagCodec });
165
- await this._bulk([value]);
166
- }
167
- return await this._bulk(updates);
168
- }
169
-
170
- async _bulk<T extends DocTypes>(updates: DocUpdate<T>[]): Promise<CRDTMeta> {
171
- const prevHead = [...this.clock.head];
172
- const done = await this.blockstore.transaction<CRDTMeta>(async (blocks: CarTransaction): Promise<CRDTMeta> => {
173
- const { head } = await applyBulkUpdateToCrdt<DocTypes>(
174
- this.blockstore.ebOpts.storeRuntime,
175
- blocks,
176
- this.clock.head,
177
- updates,
178
- this.logger,
179
- );
180
- updates = updates.map((dupdate: DocUpdate<T>) => {
181
- // if (!dupdate.value) throw new Error("missing value");
182
- readFiles(this.blockstore, { doc: dupdate.value as DocWithId<DocTypes> });
183
- return dupdate;
184
- });
185
- return { head };
186
- });
187
- await this.clock.applyHead(done.meta.head, prevHead, updates);
188
- return done.meta;
189
- }
190
-
191
- readonly onceReady: ResolveOnce<void> = new ResolveOnce<void>();
192
- async ready(): Promise<void> {
193
- return this.onceReady.once(async () => {
194
- try {
195
- // console.log("bs-ready-pre")
196
- // await this.blockstore.ready();
197
- // console.log("bs-ready-post-1")
198
- // await this.indexBlockstore?.ready();
199
- // console.log("bs-ready-post-2")
200
- // await this.clock.ready();
201
- // console.log("bs-ready-post-3")
202
- await Promise.all([
203
- this.blockstore.ready(),
204
- this.indexBlockstore ? this.indexBlockstore.ready() : Promise.resolve(),
205
- this.clock.ready(),
206
- ]);
207
- } catch (e) {
208
- throw this.logger.Error().Err(e).Msg(`CRDT is not ready`).AsError();
209
- }
210
- });
211
- }
212
-
213
- async close(): Promise<void> {
214
- // await this.blockstore.close();
215
- // await this.indexBlockstore.close();
216
- // await this.clock.close();
217
- await Promise.all([
218
- this.blockstore.close(),
219
- this.indexBlockstore ? this.indexBlockstore.close() : Promise.resolve(),
220
- this.clock.close(),
221
- ]);
222
- }
223
-
224
- async destroy(): Promise<void> {
225
- await Promise.all([this.blockstore.destroy(), this.indexBlockstore ? this.indexBlockstore.destroy() : Promise.resolve()]);
226
- }
227
-
228
- // if (snap) await this.clock.applyHead(crdtMeta.head, this.clock.head)
229
-
230
- async allDocs<T extends DocTypes>(): Promise<{ result: DocUpdate<T>[]; head: ClockHead }> {
231
- await this.ready();
232
- const result: DocUpdate<T>[] = [];
233
- for await (const entry of getAllEntries<DocTypes>(this.blockstore, this.clock.head, this.logger)) {
234
- result.push(entry as DocUpdate<T>);
235
- }
236
- return { result, head: this.clock.head };
237
- }
238
-
239
- async vis(): Promise<string> {
240
- await this.ready();
241
- const txt: string[] = [];
242
- for await (const line of clockVis(this.blockstore, this.clock.head)) {
243
- txt.push(line);
244
- }
245
- return txt.join("\n");
246
- }
247
-
248
- async getBlock(cidString: string): Promise<Block> {
249
- await this.ready();
250
- return await getBlock(this.blockstore, cidString);
251
- }
252
-
253
- async get(key: string): Promise<DocValue<DocTypes> | Falsy> {
254
- await this.ready();
255
- const result = await getValueFromCrdt<DocTypes>(this.blockstore, this.clock.head, key, this.logger);
256
- if (result.del) return undefined;
257
- return result;
258
- }
259
-
260
- async changes<T extends DocTypes>(
261
- since: ClockHead = [],
262
- opts: ChangesOptions = {},
263
- ): Promise<{
264
- result: DocUpdate<T>[];
265
- head: ClockHead;
266
- }> {
267
- await this.ready();
268
- return await clockChangesSince<T>(this.blockstore, this.clock.head, since, opts, this.logger);
269
- }
270
-
271
- async compact(): Promise<void> {
272
- const blocks = this.blockstore as EncryptedBlockstore;
273
- return await blocks.compact();
274
- }
275
- }