@fireproof/core-base 0.23.0 → 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.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
- }
package/database.ts DELETED
@@ -1,200 +0,0 @@
1
- import { Logger } from "@adviser/cement";
2
- import { index } from "./indexer.js";
3
- import {
4
- ClockHead,
5
- MapFn,
6
- QueryOpts,
7
- ChangesOptions,
8
- DocSet,
9
- DocWithId,
10
- IndexKeyType,
11
- ListenerFn,
12
- DocResponse,
13
- BulkResponse,
14
- ChangesResponse,
15
- DocTypes,
16
- IndexRows,
17
- DocFragment,
18
- ChangesResponseRow,
19
- CRDTMeta,
20
- AllDocsQueryOpts,
21
- AllDocsResponse,
22
- SuperThis,
23
- Database,
24
- Ledger,
25
- Attachable,
26
- Attached,
27
- NotFoundError,
28
- } from "@fireproof/core-types-base";
29
- import { ensureLogger, makeName } from "@fireproof/core-runtime";
30
-
31
- export function isDatabase(db: unknown): db is Database {
32
- return db instanceof DatabaseImpl;
33
- }
34
-
35
- export class DatabaseImpl implements Database {
36
- onClosed(fn: () => void) {
37
- this.ledger.onClosed(fn);
38
- }
39
- close() {
40
- return this.ledger.close();
41
- }
42
-
43
- destroy() {
44
- return this.ledger.destroy();
45
- }
46
-
47
- ready(): Promise<void> {
48
- return this.ledger.ready();
49
- }
50
-
51
- readonly ledger: Ledger;
52
- readonly logger: Logger;
53
- readonly sthis: SuperThis;
54
- // readonly id: string;
55
-
56
- constructor(ledger: Ledger) {
57
- this.sthis = ledger.sthis;
58
- this.ledger = ledger;
59
- // this.id = ledger.id;
60
- this.logger = ensureLogger(this.sthis, "Database");
61
- }
62
-
63
- attach(a: Attachable): Promise<Attached> {
64
- return this.ledger.attach(a);
65
- }
66
-
67
- get name() {
68
- return this.ledger.name;
69
- }
70
- async get<T extends DocTypes>(id: string): Promise<DocWithId<T>> {
71
- if (!id) throw this.logger.Error().Str("db", this.name).Msg(`Doc id is required`).AsError();
72
-
73
- await this.ready();
74
- this.logger.Debug().Str("id", id).Msg("get");
75
- try {
76
- const got = await this.ledger.crdt.get(id);
77
- if (!got) throw new NotFoundError(`Not found: ${id}`);
78
- const { doc } = got;
79
- return { ...(doc as unknown as DocWithId<T>), _id: id };
80
- } catch (e) {
81
- throw new NotFoundError(`Not found: ${id} - ${e instanceof Error ? e.message : String(e)}`);
82
- }
83
- }
84
-
85
- async put<T extends DocTypes>(doc: DocSet<T>): Promise<DocResponse> {
86
- await this.ready();
87
- this.logger.Debug().Str("id", doc._id).Msg("put");
88
- const { _id, ...value } = doc;
89
- const docId = _id || this.sthis.timeOrderedNextId().str;
90
- const result = (await this.ledger.writeQueue.push({
91
- id: docId,
92
- value: {
93
- ...(value as unknown as DocSet<T>),
94
- _id: docId,
95
- },
96
- })) as CRDTMeta;
97
- return { id: docId, clock: result?.head, name: this.name } as DocResponse;
98
- }
99
-
100
- async bulk<T extends DocTypes>(docs: DocSet<T>[]): Promise<BulkResponse> {
101
- await this.ready();
102
-
103
- const updates = docs.map((doc) => {
104
- const id = doc._id || this.sthis.timeOrderedNextId().str;
105
- return {
106
- id,
107
- value: {
108
- ...(doc as unknown as DocSet<T>),
109
- _id: id,
110
- },
111
- };
112
- });
113
- const result = (await this.ledger.writeQueue.bulk(updates)) as CRDTMeta;
114
- return { ids: updates.map((u) => u.id), clock: result.head, name: this.name } as BulkResponse;
115
- }
116
-
117
- async del(id: string): Promise<DocResponse> {
118
- await this.ready();
119
- this.logger.Debug().Str("id", id).Msg("del");
120
- const result = (await this.ledger.writeQueue.push({ id: id, del: true })) as CRDTMeta;
121
- return { id, clock: result?.head, name: this.name } as DocResponse;
122
- }
123
-
124
- async remove(id: string): Promise<DocResponse> {
125
- return this.del(id);
126
- }
127
-
128
- async changes<T extends DocTypes>(since: ClockHead = [], opts: ChangesOptions = {}): Promise<ChangesResponse<T>> {
129
- await this.ready();
130
- this.logger.Debug().Any("since", since).Any("opts", opts).Msg("changes");
131
- const { result, head } = await this.ledger.crdt.changes(since, opts);
132
- const rows: ChangesResponseRow<T>[] = result.map(({ id: key, value, del, clock }) => ({
133
- key,
134
- value: (del ? { _id: key, _deleted: true } : { _id: key, ...value }) as DocWithId<T>,
135
- clock,
136
- }));
137
- return { rows, clock: head, name: this.name };
138
- }
139
-
140
- async allDocs<T extends DocTypes>(opts: Partial<AllDocsQueryOpts> = {}): Promise<AllDocsResponse<T>> {
141
- await this.ready();
142
- this.logger.Debug().Msg("allDocs");
143
- const { result, head } = await this.ledger.crdt.allDocs();
144
-
145
- // Map all docs to the expected format
146
- let rows = result.map(({ id: key, value, del }) => ({
147
- key,
148
- value: (del ? { _id: key, _deleted: true } : { _id: key, ...value }) as DocWithId<T>,
149
- }));
150
-
151
- // Filter out deleted documents unless includeDeleted is true
152
- if (!opts.includeDeleted) {
153
- rows = rows.filter((row) => !(row.value as DocWithId<T> & { _deleted?: boolean })._deleted);
154
- }
155
-
156
- // Apply limit if specified
157
- if (typeof opts.limit === "number" && opts.limit >= 0) {
158
- rows = rows.slice(0, opts.limit);
159
- }
160
-
161
- return { rows, clock: head, name: this.name };
162
- }
163
-
164
- async allDocuments<T extends DocTypes>(): Promise<{
165
- rows: {
166
- key: string;
167
- value: DocWithId<T>;
168
- }[];
169
- clock: ClockHead;
170
- }> {
171
- return this.allDocs<T>();
172
- }
173
-
174
- subscribe<T extends DocTypes>(listener: ListenerFn<T>, updates?: boolean): () => void {
175
- return this.ledger.subscribe(listener, updates);
176
- }
177
-
178
- // todo if we add this onto dbs in fireproof.ts then we can make index.ts a separate package
179
- async query<T extends DocTypes, K extends IndexKeyType = string, R extends DocFragment = T>(
180
- field: string | MapFn<T>,
181
- opts: QueryOpts<K> = {},
182
- ): Promise<IndexRows<T, K, R>> {
183
- await this.ready();
184
- this.logger.Debug().Any("field", field).Any("opts", opts).Msg("query");
185
- // const _crdt = this.ledger.crdt as unknown as CRDT<T>;
186
- const idx = typeof field === "string" ? index<T, K, R>(this, field) : index<T, K, R>(this, makeName(field.toString()), field);
187
- const result = await idx.query(opts);
188
-
189
- // Add docs property to match useLiveQuery behavior
190
- return {
191
- rows: result.rows,
192
- docs: result.rows.map((r) => r.doc).filter((r): r is DocWithId<T> => !!r),
193
- };
194
- }
195
-
196
- async compact() {
197
- await this.ready();
198
- await this.ledger.crdt.compact();
199
- }
200
- }
package/index.ts DELETED
@@ -1,15 +0,0 @@
1
- export * from "./ledger.js";
2
- export * from "./database.js";
3
-
4
- export * from "./crdt.js";
5
-
6
- export * from "./indexer.js";
7
- export * from "./indexer-helpers.js";
8
-
9
- export * from "./version.js";
10
-
11
- import "./compact-strategies.js";
12
-
13
- export { getCompactStrategy, registerCompactStrategy, getCompactStrategyThrow } from "@fireproof/core-runtime";
14
-
15
- export { registerStoreProtocol } from "@fireproof/core-blockstore";