@fireproof/core-base 0.23.1 → 0.23.3

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/ledger.ts DELETED
@@ -1,345 +0,0 @@
1
- import { BuildURI, KeyedResolvOnce, Logger, ResolveOnce, URI, AppContext } from "@adviser/cement";
2
-
3
- import { writeQueue } from "./write-queue.js";
4
- import {
5
- DocUpdate,
6
- ConfigOpts,
7
- defaultWriteQueueOpts,
8
- DocWithId,
9
- ListenerFn,
10
- DocTypes,
11
- SuperThis,
12
- Database,
13
- Ledger,
14
- WriteQueue,
15
- CRDT,
16
- LedgerOpts,
17
- Attachable,
18
- Attached,
19
- LedgerOptsOptionalTracer,
20
- PARAM,
21
- } from "@fireproof/core-types-base";
22
- import { StoreURIRuntime, StoreUrlsOpts } from "@fireproof/core-types-blockstore";
23
- import { decodeFile, encodeFile, ensureLogger, ensureSuperThis, ensureURIDefaults, hashObject } from "@fireproof/core-runtime";
24
-
25
- import { DatabaseImpl } from "./database.js";
26
- import { CRDTImpl } from "./crdt.js";
27
- import { toSortedArray } from "@adviser/cement/utils";
28
- import { getDefaultURI } from "@fireproof/core-blockstore";
29
- import { defaultKeyBagOpts } from "@fireproof/core-keybag";
30
-
31
- const ledgers = new KeyedResolvOnce<Ledger>();
32
-
33
- export function keyConfigOpts(sthis: SuperThis, name: string, opts?: ConfigOpts): string {
34
- return JSON.stringify(
35
- toSortedArray({
36
- name,
37
- stores: toSortedArray(JSON.parse(JSON.stringify(toStoreURIRuntime(sthis, name, opts?.storeUrls)))),
38
- }),
39
- );
40
- }
41
-
42
- export function isLedger(db: unknown): db is Ledger {
43
- return db instanceof LedgerImpl || db instanceof LedgerShell;
44
- }
45
-
46
- export function LedgerFactory(name: string, opts?: ConfigOpts): Ledger {
47
- const sthis = ensureSuperThis(opts);
48
- const key = keyConfigOpts(sthis, name, opts);
49
- const item = ledgers.get(key);
50
- // if (!item.ready) {
51
- // console.log("LedgerFactory", key);
52
- // }
53
- return new LedgerShell(
54
- item.once((key) => {
55
- // console.log("once-LedgerFactory", key);
56
-
57
- const db = new LedgerImpl(sthis, {
58
- name,
59
- meta: opts?.meta,
60
- keyBag: defaultKeyBagOpts(sthis, opts?.keyBag),
61
- storeUrls: toStoreURIRuntime(sthis, name, opts?.storeUrls),
62
- gatewayInterceptor: opts?.gatewayInterceptor,
63
- compactStrategy: opts?.compactStrategy,
64
- writeQueue: defaultWriteQueueOpts(opts?.writeQueue),
65
- ctx: opts?.ctx ?? new AppContext(),
66
- storeEnDe: {
67
- encodeFile,
68
- decodeFile,
69
- ...opts?.storeEnDe,
70
- },
71
- tracer:
72
- opts?.tracer ??
73
- (() => {
74
- /* noop */
75
- }),
76
- });
77
- db.onClosed(() => {
78
- ledgers.unget(key as string);
79
- });
80
- return db;
81
- }),
82
- );
83
- }
84
-
85
- export class LedgerShell implements Ledger {
86
- readonly ref: LedgerImpl;
87
- readonly writeQueue: WriteQueue<DocUpdate<DocTypes>>;
88
- readonly name: string;
89
-
90
- constructor(ref: LedgerImpl) {
91
- this.ref = ref;
92
- this.writeQueue = ref.writeQueue;
93
- this.name = ref.name;
94
- ref.addShell(this);
95
- }
96
-
97
- attach(a: Attachable): Promise<Attached> {
98
- return this.ref.attach(a);
99
- }
100
-
101
- get opts(): LedgerOpts {
102
- return this.ref.opts;
103
- }
104
-
105
- get ctx(): AppContext {
106
- return this.ref.ctx;
107
- }
108
-
109
- refId(): Promise<string> {
110
- return this.ref.refId();
111
- }
112
-
113
- // get id(): string {
114
- // return this.ref.id;
115
- // }
116
- get logger(): Logger {
117
- return this.ref.logger;
118
- }
119
- get sthis(): SuperThis {
120
- return this.ref.sthis;
121
- }
122
- get crdt(): CRDT {
123
- return this.ref.crdt;
124
- }
125
-
126
- onClosed(fn: () => void): () => void {
127
- return this.ref.onClosed(fn);
128
- }
129
- close(): Promise<void> {
130
- return this.ref.shellClose(this);
131
- }
132
- destroy(): Promise<void> {
133
- return this.ref.destroy();
134
- }
135
- ready(): Promise<void> {
136
- return this.ref.ready();
137
- }
138
-
139
- // asDB(): Database {
140
- // return this.ref.asDB();
141
- // }
142
-
143
- subscribe<T extends DocTypes>(listener: ListenerFn<T>, updates?: boolean): () => void {
144
- return this.ref.subscribe(listener, updates);
145
- }
146
- }
147
-
148
- class LedgerImpl implements Ledger {
149
- // readonly name: string;
150
- readonly opts: LedgerOpts;
151
-
152
- _listening = false;
153
- readonly _listeners = new Set<ListenerFn<DocTypes>>();
154
- readonly _noupdate_listeners = new Set<ListenerFn<DocTypes>>();
155
- readonly crdt: CRDT;
156
- readonly writeQueue: WriteQueue<DocUpdate<DocTypes>>;
157
- // readonly blockstore: BaseBlockstore;
158
-
159
- readonly shells: Set<LedgerShell> = new Set<LedgerShell>();
160
-
161
- readonly ctx: AppContext;
162
-
163
- get name(): string {
164
- return this.opts.name;
165
- // this.opts.storeUrls.data.data.getParam(PARAM.NAME) ?? "default";
166
- }
167
-
168
- addShell(shell: LedgerShell) {
169
- this.shells.add(shell);
170
- }
171
-
172
- readonly _refid = new ResolveOnce<string>();
173
- refId(): Promise<string> {
174
- return this._refid.once(() => hashObject(this.opts.storeUrls));
175
- }
176
-
177
- readonly _onClosedFns = new Map<string, () => void>();
178
- onClosed(fn: () => void): () => void {
179
- const id = this.sthis.nextId().str;
180
- this._onClosedFns.set(id, fn);
181
- return () => {
182
- this._onClosedFns.delete(id);
183
- };
184
- }
185
- async close() {
186
- throw this.logger.Error().Str("db", this.name).Msg(`use shellClose`).AsError();
187
- }
188
- async shellClose(db: LedgerShell) {
189
- if (!this.shells.has(db)) {
190
- throw this.logger.Error().Str("db", this.name).Msg(`LedgerShell mismatch`).AsError();
191
- }
192
- this.shells.delete(db);
193
- if (this.shells.size === 0) {
194
- await this.ready();
195
- await this.crdt.close();
196
- await this.writeQueue.close();
197
- this._onClosedFns.forEach((fn) => fn());
198
- }
199
- // await this.blockstore.close();
200
- }
201
-
202
- async destroy() {
203
- await this.ready();
204
- await this.crdt.destroy();
205
- // await this.blockstore.destroy();
206
- }
207
-
208
- readonly _ready: ResolveOnce<void> = new ResolveOnce<void>();
209
- async ready(): Promise<void> {
210
- const ret = await this._ready.once(async () => {
211
- await this.sthis.start();
212
- await this.crdt.ready();
213
- // await this.blockstore.ready();
214
- });
215
- return ret;
216
- }
217
-
218
- readonly logger: Logger;
219
- readonly sthis: SuperThis;
220
- // readonly id: string;
221
-
222
- constructor(sthis: SuperThis, opts: LedgerOptsOptionalTracer) {
223
- this.opts = {
224
- tracer: () => {
225
- /* noop */
226
- },
227
- ...opts,
228
- }; // || this.opts;
229
- // this.name = opts.storeUrls.data.data.getParam(PARAM.NAME) || "default";
230
- this.sthis = sthis;
231
- this.ctx = opts.ctx;
232
- // this.id = sthis.timeOrderedNextId().str;
233
- this.logger = ensureLogger(this.sthis, "Ledger");
234
- this.crdt = new CRDTImpl(this.sthis, this.opts, this);
235
- this.writeQueue = writeQueue(
236
- this.sthis,
237
- async (updates: DocUpdate<DocTypes>[]) => this.crdt.bulk(updates),
238
- this.opts.writeQueue,
239
- );
240
- this.crdt.clock.onTock(() => this._no_update_notify());
241
- }
242
-
243
- async attach(a: Attachable): Promise<Attached> {
244
- await this.ready();
245
- return this.crdt.blockstore.loader.attach(a);
246
- }
247
-
248
- // readonly _asDb = new ResolveOnce<Database>();
249
- // asDB(): Database {
250
- // return this._asDb.once(() => new DatabaseImpl(this));
251
- // }
252
-
253
- subscribe<T extends DocTypes>(listener: ListenerFn<T>, updates?: boolean): () => void {
254
- this.ready();
255
- this.logger.Debug().Bool("updates", updates).Msg("subscribe");
256
- if (updates) {
257
- if (!this._listening) {
258
- this._listening = true;
259
- this.crdt.clock.onTick((updates: DocUpdate<NonNullable<unknown>>[]) => {
260
- void this._notify(updates);
261
- });
262
- }
263
- this._listeners.add(listener as ListenerFn<NonNullable<unknown>>);
264
- return () => {
265
- this._listeners.delete(listener as ListenerFn<NonNullable<unknown>>);
266
- };
267
- } else {
268
- this._noupdate_listeners.add(listener as ListenerFn<NonNullable<unknown>>);
269
- return () => {
270
- this._noupdate_listeners.delete(listener as ListenerFn<NonNullable<unknown>>);
271
- };
272
- }
273
- }
274
-
275
- private async _notify(updates: DocUpdate<DocTypes>[]) {
276
- await this.ready();
277
- if (this._listeners.size) {
278
- const docs: DocWithId<DocTypes>[] = updates.map(({ id, value }) => ({ ...value, _id: id }));
279
- for (const listener of this._listeners) {
280
- await (async () => await listener(docs as DocWithId<DocTypes>[]))().catch((e: Error) => {
281
- this.logger.Error().Err(e).Msg("subscriber error");
282
- });
283
- }
284
- }
285
- }
286
-
287
- private async _no_update_notify() {
288
- await this.ready();
289
- if (this._noupdate_listeners.size) {
290
- for (const listener of this._noupdate_listeners) {
291
- await (async () => await listener([]))().catch((e: Error) => {
292
- this.logger.Error().Err(e).Msg("subscriber error");
293
- });
294
- }
295
- }
296
- }
297
- }
298
-
299
- export function toStoreURIRuntime(sthis: SuperThis, name: string, sopts?: StoreUrlsOpts): StoreURIRuntime {
300
- sopts = sopts || {};
301
- if (!sopts.base) {
302
- const fp_env = sthis.env.get("FP_STORAGE_URL");
303
- if (fp_env) {
304
- sopts = { ...sopts, base: BuildURI.from(fp_env).setParam(PARAM.URL_GEN, "fromEnv") };
305
- } else {
306
- sopts = { ...sopts, base: getDefaultURI(sthis).build().setParam(PARAM.URL_GEN, "default") };
307
- }
308
- }
309
- const base = URI.from(sopts.base);
310
- // bbase.setParam(PARAM.NAME, name);
311
- // const base = bbase.URI();
312
- // readonly public?: boolean;
313
- // readonly meta?: DbMeta;
314
- // readonly persistIndexes?: boolean;
315
- // readonly autoCompact?: number;
316
- // readonly threshold?: number;
317
- return {
318
- idx: {
319
- car: ensureURIDefaults(sthis, { name }, sopts.idx?.car ?? sopts.data?.car, base, "car", { idx: true }),
320
- file: ensureURIDefaults(
321
- sthis,
322
- { name },
323
- sopts.idx?.file ?? sopts.idx?.car ?? sopts.data?.file ?? sopts.data?.car,
324
- base,
325
- "file",
326
- {
327
- file: true,
328
- idx: true,
329
- },
330
- ),
331
- meta: ensureURIDefaults(sthis, { name }, sopts.idx?.meta ?? sopts.data?.meta, base, "meta", { idx: true }),
332
- wal: ensureURIDefaults(sthis, { name }, sopts.idx?.wal ?? sopts.data?.wal, base, "wal", { idx: true }),
333
- },
334
- data: {
335
- car: ensureURIDefaults(sthis, { name }, sopts.data?.car, base, "car"),
336
- file: ensureURIDefaults(sthis, { name }, sopts.data?.file ?? sopts.data?.car, base, "file", { file: true }),
337
- meta: ensureURIDefaults(sthis, { name }, sopts.data?.meta, base, "meta"),
338
- wal: ensureURIDefaults(sthis, { name }, sopts.data?.wal, base, "wal"),
339
- },
340
- };
341
- }
342
-
343
- export function fireproof(name: string, opts?: ConfigOpts): Database {
344
- return new DatabaseImpl(LedgerFactory(name, opts));
345
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "extends": [
3
- "/home/runner/work/fireproof/fireproof/tsconfig.dist.json"
4
- ],
5
- "compilerOptions": {
6
- "noEmit": false,
7
- "outDir": "./"
8
- },
9
- "include": [
10
- "**/*"
11
- ],
12
- "exclude": [
13
- "node_modules",
14
- "dist",
15
- ".git",
16
- ".vscode"
17
- ]
18
- }
package/version.ts DELETED
@@ -1,3 +0,0 @@
1
- export const PACKAGE_VERSION = Object.keys({
2
- __packageVersion__: "xxxx",
3
- })[0] as string;
package/write-queue.ts DELETED
@@ -1,93 +0,0 @@
1
- import { ensureLogger } from "@fireproof/core-runtime";
2
- import {
3
- DocTypes,
4
- MetaType,
5
- DocUpdate,
6
- SuperThis,
7
- WriteQueue,
8
- defaultWriteQueueOpts,
9
- WriteQueueParams,
10
- } from "@fireproof/core-types-base";
11
- import { Future, Logger } from "@adviser/cement";
12
-
13
- type WorkerFunction<T extends DocTypes> = (tasks: DocUpdate<T>[]) => Promise<MetaType>;
14
-
15
- interface WriteQueueItem<T extends DocTypes> {
16
- // readonly task?: DocUpdate<T>;
17
- readonly tasks?: DocUpdate<T>[];
18
- resolve(result: MetaType): void;
19
- reject(error: Error): void;
20
- }
21
-
22
- class WriteQueueImpl<T extends DocUpdate<S>, S extends DocTypes = DocTypes> implements WriteQueue<T> {
23
- private readonly opts: WriteQueueParams;
24
-
25
- private readonly queue: WriteQueueItem<S>[] = [];
26
- private readonly worker: WorkerFunction<S>;
27
- private isProcessing = false;
28
- private readonly logger: Logger;
29
-
30
- constructor(sthis: SuperThis, worker: WorkerFunction<S>, opts: WriteQueueParams) {
31
- this.logger = ensureLogger(sthis, "WriteQueueImpl");
32
- this.worker = worker;
33
- this.opts = defaultWriteQueueOpts(opts);
34
- }
35
-
36
- private waitForEmptyQueue?: Future<void>;
37
- private testEmptyQueue() {
38
- if (this.waitForEmptyQueue && this.queue.length === 0) {
39
- this.waitForEmptyQueue.resolve();
40
- }
41
- }
42
-
43
- private async process() {
44
- if (this.isProcessing || this.queue.length === 0) {
45
- this.testEmptyQueue();
46
- return;
47
- }
48
- this.isProcessing = true;
49
- try {
50
- this.logger.Debug().Any("opts", this.opts).Len(this.queue).Msg("Processing tasks");
51
- const tasksToProcess = this.queue.splice(0, this.opts.chunkSize);
52
- const updates = tasksToProcess.map((item) => item.tasks).filter((item) => item) as DocUpdate<S>[][];
53
- const promises = updates.map(async (update, index) => {
54
- try {
55
- const result = await this.worker(update);
56
- tasksToProcess[index].resolve(result);
57
- } catch (error) {
58
- tasksToProcess[index].reject(this.logger.Error().Err(error).Msg("Error processing task").AsError());
59
- }
60
- });
61
- await Promise.allSettled(promises);
62
- this.logger.Debug().Any("opts", this.opts).Len(this.queue).Msg("Processed tasks");
63
- } catch (error) {
64
- this.logger.Error().Err(error).Msg("Error processing tasks");
65
- } finally {
66
- this.isProcessing = false;
67
- setTimeout(() => this.process(), 0);
68
- }
69
- }
70
-
71
- bulk(tasks: DocUpdate<S>[]): Promise<MetaType> {
72
- return new Promise<MetaType>((resolve, reject) => {
73
- this.queue.push({ tasks, resolve, reject });
74
- this.process();
75
- });
76
- }
77
- push(task: DocUpdate<S>): Promise<MetaType> {
78
- return this.bulk([task]);
79
- }
80
- close(): Promise<void> {
81
- this.waitForEmptyQueue = new Future();
82
- this.testEmptyQueue();
83
- return this.waitForEmptyQueue.asPromise();
84
- }
85
- }
86
-
87
- export function writeQueue<T extends DocUpdate<S>, S extends DocTypes = DocTypes>(
88
- sthis: SuperThis,
89
- worker: WorkerFunction<S>,
90
- opts: WriteQueueParams,
91
- ): WriteQueue<T, S> {
92
- return new WriteQueueImpl<T, S>(sthis, worker, opts);
93
- }