@fireproof/core-base 0.0.0-smoke-1b31059-1752074105
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/LICENSE.md +232 -0
- package/apply-head-queue.d.ts +17 -0
- package/apply-head-queue.js +47 -0
- package/apply-head-queue.js.map +1 -0
- package/apply-head-queue.ts +72 -0
- package/bundle-not-impl.d.ts +1 -0
- package/bundle-not-impl.js +4 -0
- package/bundle-not-impl.js.map +1 -0
- package/bundle-not-impl.ts +4 -0
- package/crdt-clock.d.ts +25 -0
- package/crdt-clock.js +138 -0
- package/crdt-clock.js.map +1 -0
- package/crdt-clock.ts +192 -0
- package/crdt-helpers.d.ts +18 -0
- package/crdt-helpers.js +331 -0
- package/crdt-helpers.js.map +1 -0
- package/crdt-helpers.ts +484 -0
- package/crdt.d.ts +40 -0
- package/crdt.js +172 -0
- package/crdt.js.map +1 -0
- package/crdt.ts +268 -0
- package/database.d.ts +32 -0
- package/database.js +136 -0
- package/database.js.map +1 -0
- package/database.ts +200 -0
- package/index.d.ts +6 -0
- package/index.js +7 -0
- package/index.js.map +1 -0
- package/index.ts +9 -0
- package/indexer-helpers.d.ts +25 -0
- package/indexer-helpers.js +155 -0
- package/indexer-helpers.js.map +1 -0
- package/indexer-helpers.ts +263 -0
- package/indexer.d.ts +22 -0
- package/indexer.js +246 -0
- package/indexer.js.map +1 -0
- package/indexer.ts +360 -0
- package/ledger.d.ts +55 -0
- package/ledger.js +245 -0
- package/ledger.js.map +1 -0
- package/ledger.ts +344 -0
- package/package.json +54 -0
- package/tsconfig.json +18 -0
- package/version.d.ts +1 -0
- package/version.js +4 -0
- package/version.js.map +1 -0
- package/version.ts +3 -0
- package/write-queue.d.ts +4 -0
- package/write-queue.js +69 -0
- package/write-queue.js.map +1 -0
- package/write-queue.ts +93 -0
package/indexer.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { throwFalsy, } from "@fireproof/core-types-base";
|
|
2
|
+
import { bulkIndex, indexEntriesForChanges, byIdOpts, byKeyOpts, applyQuery, encodeRange, encodeKey, loadIndex, } from "./indexer-helpers.js";
|
|
3
|
+
import { ensureLogger } from "@fireproof/core-runtime";
|
|
4
|
+
function refLedger(u) {
|
|
5
|
+
return !!u.ledger;
|
|
6
|
+
}
|
|
7
|
+
export function index(refDb, name, mapFn, meta) {
|
|
8
|
+
const crdt = refLedger(refDb) ? refDb.ledger.crdt : refDb.crdt;
|
|
9
|
+
if (mapFn && meta)
|
|
10
|
+
throw refDb.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
|
|
11
|
+
if (mapFn && mapFn.constructor.name !== "Function")
|
|
12
|
+
throw refDb.logger.Error().Msg("mapFn must be a function").AsError();
|
|
13
|
+
if (crdt.indexers.has(name)) {
|
|
14
|
+
const idx = crdt.indexers.get(name);
|
|
15
|
+
idx.applyMapFn(name, mapFn, meta);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const idx = new Index(refDb.sthis, crdt, name, mapFn, meta);
|
|
19
|
+
crdt.indexers.set(name, idx);
|
|
20
|
+
}
|
|
21
|
+
return crdt.indexers.get(name);
|
|
22
|
+
}
|
|
23
|
+
export class Index {
|
|
24
|
+
blockstore;
|
|
25
|
+
crdt;
|
|
26
|
+
name;
|
|
27
|
+
mapFn;
|
|
28
|
+
mapFnString = "";
|
|
29
|
+
byKey = {};
|
|
30
|
+
byId = {};
|
|
31
|
+
indexHead;
|
|
32
|
+
initError;
|
|
33
|
+
ready() {
|
|
34
|
+
return Promise.all([this.blockstore.ready(), this.crdt.ready()]).then(() => {
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
logger;
|
|
38
|
+
constructor(sthis, crdt, name, mapFn, meta) {
|
|
39
|
+
this.logger = ensureLogger(sthis, "Index");
|
|
40
|
+
if (!crdt.indexBlockstore) {
|
|
41
|
+
throw sthis.logger.Error().Msg("indexBlockstore not set").AsError();
|
|
42
|
+
}
|
|
43
|
+
this.blockstore = crdt.indexBlockstore;
|
|
44
|
+
this.crdt = crdt;
|
|
45
|
+
this.applyMapFn(name, mapFn, meta);
|
|
46
|
+
this.name = name;
|
|
47
|
+
if (!(this.mapFnString || this.initError))
|
|
48
|
+
throw this.logger.Error().Msg("missing mapFnString").AsError();
|
|
49
|
+
}
|
|
50
|
+
applyMapFn(name, mapFn, meta) {
|
|
51
|
+
if (mapFn && meta)
|
|
52
|
+
throw this.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
|
|
53
|
+
if (this.name && this.name !== name)
|
|
54
|
+
throw this.logger.Error().Msg("cannot change name").AsError();
|
|
55
|
+
try {
|
|
56
|
+
let mapFnChanged = false;
|
|
57
|
+
if (meta) {
|
|
58
|
+
if (this.indexHead && this.indexHead.map((c) => c.toString()).join() !== meta.head.map((c) => c.toString()).join()) {
|
|
59
|
+
throw this.logger.Error().Msg("cannot apply different head meta").AsError();
|
|
60
|
+
}
|
|
61
|
+
if (this.mapFnString) {
|
|
62
|
+
if (this.mapFnString !== meta.map) {
|
|
63
|
+
this.mapFnString = meta.map;
|
|
64
|
+
mapFnChanged = true;
|
|
65
|
+
}
|
|
66
|
+
this.byId.cid = meta.byId;
|
|
67
|
+
this.byKey.cid = meta.byKey;
|
|
68
|
+
this.indexHead = meta.head;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this.mapFnString = meta.map;
|
|
72
|
+
this.byId.cid = meta.byId;
|
|
73
|
+
this.byKey.cid = meta.byKey;
|
|
74
|
+
this.indexHead = meta.head;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
if (this.mapFn) {
|
|
79
|
+
if (mapFn) {
|
|
80
|
+
if (this.mapFn.toString() !== mapFn.toString()) {
|
|
81
|
+
this.mapFn = mapFn;
|
|
82
|
+
this.mapFnString = mapFn.toString();
|
|
83
|
+
mapFnChanged = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
if (!mapFn) {
|
|
89
|
+
mapFn = ((doc) => doc[name] ?? undefined);
|
|
90
|
+
}
|
|
91
|
+
if (this.mapFnString) {
|
|
92
|
+
if (this.mapFnString !== mapFn.toString()) {
|
|
93
|
+
mapFnChanged = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.mapFnString = mapFn.toString();
|
|
98
|
+
}
|
|
99
|
+
this.mapFn = mapFn;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (mapFnChanged) {
|
|
103
|
+
this._resetIndex();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
this.initError = e;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async query(opts = {}) {
|
|
111
|
+
this.logger.Debug().Msg("enter query");
|
|
112
|
+
await this.ready();
|
|
113
|
+
this.logger.Debug().Msg("post ready query");
|
|
114
|
+
await this._updateIndex();
|
|
115
|
+
this.logger.Debug().Msg("post _updateIndex query");
|
|
116
|
+
await this._hydrateIndex();
|
|
117
|
+
this.logger.Debug().Msg("post _hydrateIndex query");
|
|
118
|
+
if (!this.byKey.root) {
|
|
119
|
+
return await applyQuery(this.crdt, { result: [] }, opts);
|
|
120
|
+
}
|
|
121
|
+
if (opts.includeDocs === undefined)
|
|
122
|
+
opts.includeDocs = true;
|
|
123
|
+
if (opts.range) {
|
|
124
|
+
const eRange = encodeRange(opts.range);
|
|
125
|
+
return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).range(eRange[0], eRange[1]), opts);
|
|
126
|
+
}
|
|
127
|
+
if (typeof opts.key === "boolean" || opts.key) {
|
|
128
|
+
const encodedKey = encodeKey(opts.key);
|
|
129
|
+
return await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts);
|
|
130
|
+
}
|
|
131
|
+
if (Array.isArray(opts.keys)) {
|
|
132
|
+
const optsWithoutLimit = { ...opts };
|
|
133
|
+
delete optsWithoutLimit.limit;
|
|
134
|
+
const results = await Promise.all(opts.keys.map(async (key) => {
|
|
135
|
+
const encodedKey = encodeKey(key);
|
|
136
|
+
return (await applyQuery(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), optsWithoutLimit)).rows;
|
|
137
|
+
}));
|
|
138
|
+
let flattenedRows = results.flat();
|
|
139
|
+
if (opts) {
|
|
140
|
+
flattenedRows = flattenedRows.slice(0, opts.limit);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
rows: flattenedRows,
|
|
144
|
+
docs: flattenedRows.map((r) => r.doc).filter((r) => !!r),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (opts.prefix) {
|
|
148
|
+
if (!Array.isArray(opts.prefix))
|
|
149
|
+
opts.prefix = [opts.prefix];
|
|
150
|
+
const start = [...opts.prefix, NaN];
|
|
151
|
+
const end = [...opts.prefix, Infinity];
|
|
152
|
+
const encodedR = encodeRange([start, end]);
|
|
153
|
+
return await applyQuery(this.crdt, await this.byKey.root.range(...encodedR), opts);
|
|
154
|
+
}
|
|
155
|
+
const all = await this.byKey.root.getAllEntries();
|
|
156
|
+
return await applyQuery(this.crdt, {
|
|
157
|
+
result: all.result.map(({ key: [k, id], value }) => ({
|
|
158
|
+
key: k,
|
|
159
|
+
id,
|
|
160
|
+
value,
|
|
161
|
+
})),
|
|
162
|
+
}, opts);
|
|
163
|
+
}
|
|
164
|
+
_resetIndex() {
|
|
165
|
+
this.byId = {};
|
|
166
|
+
this.byKey = {};
|
|
167
|
+
this.indexHead = undefined;
|
|
168
|
+
}
|
|
169
|
+
async _hydrateIndex() {
|
|
170
|
+
if (this.byId.root && this.byKey.root)
|
|
171
|
+
return;
|
|
172
|
+
if (!this.byId.cid || !this.byKey.cid)
|
|
173
|
+
return;
|
|
174
|
+
this.byId.root = await loadIndex(this.blockstore, this.byId.cid, byIdOpts);
|
|
175
|
+
this.byKey.root = await loadIndex(this.blockstore, this.byKey.cid, byKeyOpts);
|
|
176
|
+
}
|
|
177
|
+
async _updateIndex() {
|
|
178
|
+
await this.ready();
|
|
179
|
+
this.logger.Debug().Msg("enter _updateIndex");
|
|
180
|
+
if (this.initError)
|
|
181
|
+
throw this.initError;
|
|
182
|
+
if (!this.mapFn)
|
|
183
|
+
throw this.logger.Error().Msg("No map function defined").AsError();
|
|
184
|
+
let result, head;
|
|
185
|
+
if (!this.indexHead || this.indexHead.length === 0) {
|
|
186
|
+
({ result, head } = await this.crdt.allDocs());
|
|
187
|
+
this.logger.Debug().Msg("enter crdt.allDocs");
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
({ result, head } = await this.crdt.changes(this.indexHead));
|
|
191
|
+
this.logger.Debug().Msg("enter crdt.changes");
|
|
192
|
+
}
|
|
193
|
+
if (result.length === 0) {
|
|
194
|
+
this.indexHead = head;
|
|
195
|
+
}
|
|
196
|
+
let staleKeyIndexEntries = [];
|
|
197
|
+
let removeIdIndexEntries = [];
|
|
198
|
+
if (this.byId.root) {
|
|
199
|
+
const removeIds = result.map(({ id: key }) => key);
|
|
200
|
+
const { result: oldChangeEntries } = await this.byId.root.getMany(removeIds);
|
|
201
|
+
staleKeyIndexEntries = oldChangeEntries.map((key) => ({ key, del: true }));
|
|
202
|
+
removeIdIndexEntries = oldChangeEntries.map((key) => ({ key: key[1], del: true }));
|
|
203
|
+
}
|
|
204
|
+
const indexEntries = indexEntriesForChanges(result, this.mapFn);
|
|
205
|
+
const byIdIndexEntries = indexEntries.map(({ key }) => ({
|
|
206
|
+
key: key[1],
|
|
207
|
+
value: key,
|
|
208
|
+
}));
|
|
209
|
+
const indexerMeta = { indexes: new Map() };
|
|
210
|
+
for (const [name, indexer] of this.crdt.indexers) {
|
|
211
|
+
if (indexer.indexHead) {
|
|
212
|
+
indexerMeta.indexes?.set(name, {
|
|
213
|
+
byId: indexer.byId.cid,
|
|
214
|
+
byKey: indexer.byKey.cid,
|
|
215
|
+
head: indexer.indexHead,
|
|
216
|
+
map: indexer.mapFnString,
|
|
217
|
+
name: indexer.name,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (result.length === 0) {
|
|
222
|
+
return indexerMeta;
|
|
223
|
+
}
|
|
224
|
+
this.logger.Debug().Msg("pre this.blockstore.transaction");
|
|
225
|
+
const { meta } = await this.blockstore.transaction(async (tblocks) => {
|
|
226
|
+
this.byId = await bulkIndex(this.logger, tblocks, this.byId, removeIdIndexEntries.concat(byIdIndexEntries), byIdOpts);
|
|
227
|
+
this.byKey = await bulkIndex(this.logger, tblocks, this.byKey, staleKeyIndexEntries.concat(indexEntries), byKeyOpts);
|
|
228
|
+
this.indexHead = head;
|
|
229
|
+
if (this.byId.cid && this.byKey.cid) {
|
|
230
|
+
const idxMeta = {
|
|
231
|
+
byId: this.byId.cid,
|
|
232
|
+
byKey: this.byKey.cid,
|
|
233
|
+
head,
|
|
234
|
+
map: this.mapFnString,
|
|
235
|
+
name: this.name,
|
|
236
|
+
};
|
|
237
|
+
indexerMeta.indexes?.set(this.name, idxMeta);
|
|
238
|
+
}
|
|
239
|
+
this.logger.Debug().Any("indexerMeta", new Array(indexerMeta.indexes?.entries())).Msg("exit this.blockstore.transaction fn");
|
|
240
|
+
return indexerMeta;
|
|
241
|
+
});
|
|
242
|
+
this.logger.Debug().Msg("post this.blockstore.transaction");
|
|
243
|
+
return meta;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=indexer.js.map
|
package/indexer.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["indexer.ts"],"names":[],"mappings":"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"}
|
package/indexer.ts
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/// <reference types="@fireproof/core-types-base/prolly-trees.d.ts" />
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type ClockHead,
|
|
5
|
+
type DocUpdate,
|
|
6
|
+
type MapFn,
|
|
7
|
+
type IndexUpdate,
|
|
8
|
+
type QueryOpts,
|
|
9
|
+
type IdxMeta,
|
|
10
|
+
type DocFragment,
|
|
11
|
+
type IdxMetaMap,
|
|
12
|
+
type IndexKeyType,
|
|
13
|
+
type IndexRows,
|
|
14
|
+
type DocTypes,
|
|
15
|
+
type IndexUpdateString,
|
|
16
|
+
throwFalsy,
|
|
17
|
+
type IndexTransactionMeta,
|
|
18
|
+
type SuperThis,
|
|
19
|
+
type BaseBlockstore,
|
|
20
|
+
type CRDT,
|
|
21
|
+
type HasCRDT,
|
|
22
|
+
type HasLogger,
|
|
23
|
+
type HasSuperThis,
|
|
24
|
+
type RefLedger,
|
|
25
|
+
type DocWithId,
|
|
26
|
+
IndexIf,
|
|
27
|
+
IndexTree,
|
|
28
|
+
} from "@fireproof/core-types-base";
|
|
29
|
+
// import { BaseBlockstore } from "./blockstore/index.js";
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
bulkIndex,
|
|
33
|
+
indexEntriesForChanges,
|
|
34
|
+
byIdOpts,
|
|
35
|
+
byKeyOpts,
|
|
36
|
+
applyQuery,
|
|
37
|
+
encodeRange,
|
|
38
|
+
encodeKey,
|
|
39
|
+
loadIndex,
|
|
40
|
+
IndexDocString,
|
|
41
|
+
CompareKey,
|
|
42
|
+
} from "./indexer-helpers.js";
|
|
43
|
+
import { ensureLogger } from "@fireproof/core-runtime";
|
|
44
|
+
import { Logger } from "@adviser/cement";
|
|
45
|
+
|
|
46
|
+
// import { ProllyNode } from "prolly-trees/base";
|
|
47
|
+
|
|
48
|
+
function refLedger(u: HasCRDT | RefLedger): u is RefLedger {
|
|
49
|
+
return !!(u as RefLedger).ledger;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function index<T extends DocTypes = DocTypes, K extends IndexKeyType = string, R extends DocFragment = T>(
|
|
53
|
+
refDb: HasLogger & HasSuperThis & (HasCRDT | RefLedger),
|
|
54
|
+
name: string,
|
|
55
|
+
mapFn?: MapFn<T>,
|
|
56
|
+
meta?: IdxMeta,
|
|
57
|
+
): Index<T, K, R> {
|
|
58
|
+
const crdt = refLedger(refDb) ? refDb.ledger.crdt : refDb.crdt;
|
|
59
|
+
|
|
60
|
+
if (mapFn && meta) throw refDb.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
|
|
61
|
+
if (mapFn && mapFn.constructor.name !== "Function") throw refDb.logger.Error().Msg("mapFn must be a function").AsError();
|
|
62
|
+
if (crdt.indexers.has(name)) {
|
|
63
|
+
const idx = crdt.indexers.get(name) as unknown as Index<T, K>;
|
|
64
|
+
idx.applyMapFn(name, mapFn, meta);
|
|
65
|
+
} else {
|
|
66
|
+
const idx = new Index<T, K>(refDb.sthis, crdt, name, mapFn, meta);
|
|
67
|
+
crdt.indexers.set(name, idx as unknown as Index<DocTypes, K, DocTypes>);
|
|
68
|
+
}
|
|
69
|
+
return crdt.indexers.get(name) as unknown as Index<T, K, R>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// interface ByIdIndexIten<K extends IndexKeyType> {
|
|
73
|
+
// readonly key: K;
|
|
74
|
+
// readonly value: [K, K];
|
|
75
|
+
// }
|
|
76
|
+
|
|
77
|
+
export class Index<T extends DocTypes, K extends IndexKeyType = string, R extends DocFragment = T> implements IndexIf<T, K, R> {
|
|
78
|
+
readonly blockstore: BaseBlockstore;
|
|
79
|
+
readonly crdt: CRDT;
|
|
80
|
+
readonly name: string;
|
|
81
|
+
mapFn?: MapFn<T>;
|
|
82
|
+
mapFnString = "";
|
|
83
|
+
byKey: IndexTree<K, R> = {};
|
|
84
|
+
byId: IndexTree<K, R> = {};
|
|
85
|
+
indexHead?: ClockHead;
|
|
86
|
+
|
|
87
|
+
initError?: Error;
|
|
88
|
+
|
|
89
|
+
ready(): Promise<void> {
|
|
90
|
+
return Promise.all([this.blockstore.ready(), this.crdt.ready()]).then(() => {
|
|
91
|
+
/* noop */
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// close(): Promise<void> {
|
|
96
|
+
// return Promise.all([this.blockstore.close(), this.crdt.close()]).then(() => {
|
|
97
|
+
// /* noop */
|
|
98
|
+
// });
|
|
99
|
+
// }
|
|
100
|
+
// destroy(): Promise<void> {
|
|
101
|
+
// return Promise.all([this.blockstore.destroy(), this.crdt.destroy()]).then(() => {
|
|
102
|
+
// /* noop */
|
|
103
|
+
// });
|
|
104
|
+
// }
|
|
105
|
+
|
|
106
|
+
readonly logger: Logger;
|
|
107
|
+
|
|
108
|
+
constructor(sthis: SuperThis, crdt: CRDT, name: string, mapFn?: MapFn<T>, meta?: IdxMeta) {
|
|
109
|
+
this.logger = ensureLogger(sthis, "Index");
|
|
110
|
+
if (!crdt.indexBlockstore) {
|
|
111
|
+
throw sthis.logger.Error().Msg("indexBlockstore not set").AsError();
|
|
112
|
+
}
|
|
113
|
+
this.blockstore = crdt.indexBlockstore;
|
|
114
|
+
this.crdt = crdt as CRDT;
|
|
115
|
+
this.applyMapFn(name, mapFn, meta);
|
|
116
|
+
this.name = name;
|
|
117
|
+
if (!(this.mapFnString || this.initError)) throw this.logger.Error().Msg("missing mapFnString").AsError();
|
|
118
|
+
// this.ready = this.blockstore.ready.then(() => {
|
|
119
|
+
// return;
|
|
120
|
+
// });
|
|
121
|
+
// .then((header: IdxCarHeader) => {
|
|
122
|
+
// // @ts-ignore
|
|
123
|
+
// if (header.head) throw new Error('cannot have head in idx header')
|
|
124
|
+
// if (header.indexes === undefined) throw new Error('missing indexes in idx header')
|
|
125
|
+
// // for (const [name, idx] of Object.entries(header.indexes)) {
|
|
126
|
+
// // index({ _crdt: crdt }, name, undefined, idx as IdxMeta)
|
|
127
|
+
// // }
|
|
128
|
+
// })
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
applyMapFn(name: string, mapFn?: MapFn<T>, meta?: IdxMeta) {
|
|
132
|
+
if (mapFn && meta) throw this.logger.Error().Msg("cannot provide both mapFn and meta").AsError();
|
|
133
|
+
if (this.name && this.name !== name) throw this.logger.Error().Msg("cannot change name").AsError();
|
|
134
|
+
// this.name = name;
|
|
135
|
+
try {
|
|
136
|
+
let mapFnChanged = false;
|
|
137
|
+
|
|
138
|
+
if (meta) {
|
|
139
|
+
// hydrating from header
|
|
140
|
+
if (this.indexHead && this.indexHead.map((c) => c.toString()).join() !== meta.head.map((c) => c.toString()).join()) {
|
|
141
|
+
throw this.logger.Error().Msg("cannot apply different head meta").AsError();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.mapFnString) {
|
|
145
|
+
// we already initialized from application code
|
|
146
|
+
if (this.mapFnString !== meta.map) {
|
|
147
|
+
this.mapFnString = meta.map;
|
|
148
|
+
mapFnChanged = true;
|
|
149
|
+
}
|
|
150
|
+
// Always apply the metadata
|
|
151
|
+
this.byId.cid = meta.byId;
|
|
152
|
+
this.byKey.cid = meta.byKey;
|
|
153
|
+
this.indexHead = meta.head;
|
|
154
|
+
} else {
|
|
155
|
+
// we are first
|
|
156
|
+
this.mapFnString = meta.map;
|
|
157
|
+
this.byId.cid = meta.byId;
|
|
158
|
+
this.byKey.cid = meta.byKey;
|
|
159
|
+
this.indexHead = meta.head;
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
if (this.mapFn) {
|
|
163
|
+
// we already initialized from application code
|
|
164
|
+
if (mapFn) {
|
|
165
|
+
if (this.mapFn.toString() !== mapFn.toString()) {
|
|
166
|
+
this.mapFn = mapFn;
|
|
167
|
+
this.mapFnString = mapFn.toString();
|
|
168
|
+
mapFnChanged = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
// application code is creating an index
|
|
173
|
+
if (!mapFn) {
|
|
174
|
+
mapFn = ((doc) => (doc as unknown as Record<string, unknown>)[name] ?? undefined) as MapFn<T>;
|
|
175
|
+
}
|
|
176
|
+
if (this.mapFnString) {
|
|
177
|
+
// we already loaded from a header
|
|
178
|
+
if (this.mapFnString !== mapFn.toString()) {
|
|
179
|
+
mapFnChanged = true;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
// we are first
|
|
183
|
+
this.mapFnString = mapFn.toString();
|
|
184
|
+
}
|
|
185
|
+
this.mapFn = mapFn;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// If the map function changed, reset the index for correctness
|
|
190
|
+
if (mapFnChanged) {
|
|
191
|
+
this._resetIndex();
|
|
192
|
+
}
|
|
193
|
+
} catch (e) {
|
|
194
|
+
this.initError = e as Error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async query(opts: QueryOpts<K> = {}): Promise<IndexRows<T, K, R>> {
|
|
199
|
+
this.logger.Debug().Msg("enter query");
|
|
200
|
+
await this.ready();
|
|
201
|
+
// this._resetIndex();
|
|
202
|
+
this.logger.Debug().Msg("post ready query");
|
|
203
|
+
await this._updateIndex();
|
|
204
|
+
this.logger.Debug().Msg("post _updateIndex query");
|
|
205
|
+
await this._hydrateIndex();
|
|
206
|
+
this.logger.Debug().Msg("post _hydrateIndex query");
|
|
207
|
+
if (!this.byKey.root) {
|
|
208
|
+
return await applyQuery<T, K, R>(this.crdt, { result: [] }, opts);
|
|
209
|
+
}
|
|
210
|
+
if (opts.includeDocs === undefined) opts.includeDocs = true;
|
|
211
|
+
if (opts.range) {
|
|
212
|
+
const eRange = encodeRange(opts.range);
|
|
213
|
+
return await applyQuery<T, K, R>(this.crdt, await throwFalsy(this.byKey.root).range(eRange[0], eRange[1]), opts);
|
|
214
|
+
}
|
|
215
|
+
if (typeof opts.key === "boolean" || opts.key) {
|
|
216
|
+
const encodedKey = encodeKey(opts.key);
|
|
217
|
+
return await applyQuery<T, K, R>(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), opts);
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(opts.keys)) {
|
|
220
|
+
// Create a new options object without the limit to avoid limiting individual key results
|
|
221
|
+
const optsWithoutLimit = { ...opts };
|
|
222
|
+
delete optsWithoutLimit.limit;
|
|
223
|
+
|
|
224
|
+
// Process each key separately but don't apply limit yet
|
|
225
|
+
const results = await Promise.all(
|
|
226
|
+
opts.keys.map(async (key: DocFragment) => {
|
|
227
|
+
const encodedKey = encodeKey(key);
|
|
228
|
+
return (await applyQuery<T, K, R>(this.crdt, await throwFalsy(this.byKey.root).get(encodedKey), optsWithoutLimit)).rows;
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Flatten all results into a single array
|
|
233
|
+
let flattenedRows = results.flat();
|
|
234
|
+
|
|
235
|
+
// Apply the original limit to the combined results if it was specified
|
|
236
|
+
if (opts) {
|
|
237
|
+
flattenedRows = flattenedRows.slice(0, opts.limit);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
rows: flattenedRows,
|
|
242
|
+
docs: flattenedRows.map((r) => r.doc).filter((r): r is DocWithId<T> => !!r),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (opts.prefix) {
|
|
246
|
+
if (!Array.isArray(opts.prefix)) opts.prefix = [opts.prefix];
|
|
247
|
+
// prefix should be always an array
|
|
248
|
+
const start = [...opts.prefix, NaN];
|
|
249
|
+
const end = [...opts.prefix, Infinity];
|
|
250
|
+
const encodedR = encodeRange([start, end]);
|
|
251
|
+
return await applyQuery<T, K, R>(this.crdt, await this.byKey.root.range(...encodedR), opts);
|
|
252
|
+
}
|
|
253
|
+
const all = await this.byKey.root.getAllEntries(); // funky return type
|
|
254
|
+
return await applyQuery<T, K, R>(
|
|
255
|
+
this.crdt,
|
|
256
|
+
{
|
|
257
|
+
// getAllEntries returns a different type than range
|
|
258
|
+
result: all.result.map(({ key: [k, id], value }) => ({
|
|
259
|
+
key: k as [K, string],
|
|
260
|
+
id,
|
|
261
|
+
value,
|
|
262
|
+
})),
|
|
263
|
+
},
|
|
264
|
+
opts,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
_resetIndex() {
|
|
269
|
+
this.byId = {};
|
|
270
|
+
this.byKey = {};
|
|
271
|
+
this.indexHead = undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async _hydrateIndex() {
|
|
275
|
+
if (this.byId.root && this.byKey.root) return;
|
|
276
|
+
if (!this.byId.cid || !this.byKey.cid) return;
|
|
277
|
+
this.byId.root = await loadIndex<K, R, string | number>(this.blockstore, this.byId.cid, byIdOpts);
|
|
278
|
+
this.byKey.root = await loadIndex<K, R, CompareKey>(this.blockstore, this.byKey.cid, byKeyOpts);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async _updateIndex(): Promise<IndexTransactionMeta> {
|
|
282
|
+
await this.ready();
|
|
283
|
+
this.logger.Debug().Msg("enter _updateIndex");
|
|
284
|
+
if (this.initError) throw this.initError;
|
|
285
|
+
if (!this.mapFn) throw this.logger.Error().Msg("No map function defined").AsError();
|
|
286
|
+
let result: DocUpdate<T>[], head: ClockHead;
|
|
287
|
+
if (!this.indexHead || this.indexHead.length === 0) {
|
|
288
|
+
({ result, head } = await this.crdt.allDocs<T>());
|
|
289
|
+
this.logger.Debug().Msg("enter crdt.allDocs");
|
|
290
|
+
} else {
|
|
291
|
+
({ result, head } = await this.crdt.changes<T>(this.indexHead));
|
|
292
|
+
this.logger.Debug().Msg("enter crdt.changes");
|
|
293
|
+
}
|
|
294
|
+
if (result.length === 0) {
|
|
295
|
+
this.indexHead = head;
|
|
296
|
+
// return { byId: this.byId, byKey: this.byKey } as IndexTransactionMeta;
|
|
297
|
+
}
|
|
298
|
+
let staleKeyIndexEntries: IndexUpdate<K>[] = [];
|
|
299
|
+
let removeIdIndexEntries: IndexUpdateString[] = [];
|
|
300
|
+
if (this.byId.root) {
|
|
301
|
+
const removeIds = result.map(({ id: key }) => key);
|
|
302
|
+
const { result: oldChangeEntries } = await this.byId.root.getMany(removeIds);
|
|
303
|
+
staleKeyIndexEntries = oldChangeEntries.map((key) => ({ key, del: true }));
|
|
304
|
+
removeIdIndexEntries = oldChangeEntries.map((key) => ({ key: key[1], del: true }));
|
|
305
|
+
}
|
|
306
|
+
const indexEntries = indexEntriesForChanges<T, K>(result, this.mapFn); // use a getter to translate from string
|
|
307
|
+
const byIdIndexEntries: IndexDocString[] = indexEntries.map(({ key }) => ({
|
|
308
|
+
key: key[1],
|
|
309
|
+
value: key,
|
|
310
|
+
}));
|
|
311
|
+
const indexerMeta: IdxMetaMap = { indexes: new Map() };
|
|
312
|
+
|
|
313
|
+
for (const [name, indexer] of this.crdt.indexers) {
|
|
314
|
+
if (indexer.indexHead) {
|
|
315
|
+
indexerMeta.indexes?.set(name, {
|
|
316
|
+
byId: indexer.byId.cid,
|
|
317
|
+
byKey: indexer.byKey.cid,
|
|
318
|
+
head: indexer.indexHead,
|
|
319
|
+
map: indexer.mapFnString,
|
|
320
|
+
name: indexer.name,
|
|
321
|
+
} as IdxMeta);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (result.length === 0) {
|
|
325
|
+
return indexerMeta as unknown as IndexTransactionMeta;
|
|
326
|
+
}
|
|
327
|
+
this.logger.Debug().Msg("pre this.blockstore.transaction");
|
|
328
|
+
const { meta } = await this.blockstore.transaction<IndexTransactionMeta>(async (tblocks): Promise<IndexTransactionMeta> => {
|
|
329
|
+
this.byId = await bulkIndex<K, R, string | number>(
|
|
330
|
+
this.logger,
|
|
331
|
+
tblocks,
|
|
332
|
+
this.byId,
|
|
333
|
+
removeIdIndexEntries.concat(byIdIndexEntries),
|
|
334
|
+
byIdOpts,
|
|
335
|
+
);
|
|
336
|
+
this.byKey = await bulkIndex<K, R, CompareKey>(
|
|
337
|
+
this.logger,
|
|
338
|
+
tblocks,
|
|
339
|
+
this.byKey,
|
|
340
|
+
staleKeyIndexEntries.concat(indexEntries),
|
|
341
|
+
byKeyOpts,
|
|
342
|
+
);
|
|
343
|
+
this.indexHead = head;
|
|
344
|
+
if (this.byId.cid && this.byKey.cid) {
|
|
345
|
+
const idxMeta = {
|
|
346
|
+
byId: this.byId.cid,
|
|
347
|
+
byKey: this.byKey.cid,
|
|
348
|
+
head,
|
|
349
|
+
map: this.mapFnString,
|
|
350
|
+
name: this.name,
|
|
351
|
+
} as IdxMeta;
|
|
352
|
+
indexerMeta.indexes?.set(this.name, idxMeta);
|
|
353
|
+
}
|
|
354
|
+
this.logger.Debug().Any("indexerMeta", new Array(indexerMeta.indexes?.entries())).Msg("exit this.blockstore.transaction fn");
|
|
355
|
+
return indexerMeta as unknown as IndexTransactionMeta;
|
|
356
|
+
});
|
|
357
|
+
this.logger.Debug().Msg("post this.blockstore.transaction");
|
|
358
|
+
return meta;
|
|
359
|
+
}
|
|
360
|
+
}
|
package/ledger.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Logger, ResolveOnce, AppContext } from "@adviser/cement";
|
|
2
|
+
import { DocUpdate, ConfigOpts, ListenerFn, DocTypes, SuperThis, Database, Ledger, WriteQueue, CRDT, LedgerOpts, Attachable, Attached, LedgerOptsOptionalTracer } from "@fireproof/core-types-base";
|
|
3
|
+
import { StoreURIRuntime, StoreUrlsOpts } from "@fireproof/core-types-blockstore";
|
|
4
|
+
export declare function keyConfigOpts(sthis: SuperThis, name: string, opts?: ConfigOpts): string;
|
|
5
|
+
export declare function isLedger(db: unknown): db is Ledger;
|
|
6
|
+
export declare function LedgerFactory(name: string, opts?: ConfigOpts): Ledger;
|
|
7
|
+
export declare class LedgerShell implements Ledger {
|
|
8
|
+
readonly ref: LedgerImpl;
|
|
9
|
+
readonly writeQueue: WriteQueue<DocUpdate<DocTypes>>;
|
|
10
|
+
readonly name: string;
|
|
11
|
+
constructor(ref: LedgerImpl);
|
|
12
|
+
attach(a: Attachable): Promise<Attached>;
|
|
13
|
+
get opts(): LedgerOpts;
|
|
14
|
+
get ctx(): AppContext;
|
|
15
|
+
refId(): Promise<string>;
|
|
16
|
+
get logger(): Logger;
|
|
17
|
+
get sthis(): SuperThis;
|
|
18
|
+
get crdt(): CRDT;
|
|
19
|
+
onClosed(fn: () => void): () => void;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
destroy(): Promise<void>;
|
|
22
|
+
ready(): Promise<void>;
|
|
23
|
+
subscribe<T extends DocTypes>(listener: ListenerFn<T>, updates?: boolean): () => void;
|
|
24
|
+
}
|
|
25
|
+
declare class LedgerImpl implements Ledger {
|
|
26
|
+
readonly opts: LedgerOpts;
|
|
27
|
+
_listening: boolean;
|
|
28
|
+
readonly _listeners: Set<ListenerFn<{}>>;
|
|
29
|
+
readonly _noupdate_listeners: Set<ListenerFn<{}>>;
|
|
30
|
+
readonly crdt: CRDT;
|
|
31
|
+
readonly writeQueue: WriteQueue<DocUpdate<DocTypes>>;
|
|
32
|
+
readonly shells: Set<LedgerShell>;
|
|
33
|
+
readonly ctx: AppContext;
|
|
34
|
+
get name(): string;
|
|
35
|
+
addShell(shell: LedgerShell): void;
|
|
36
|
+
readonly _refid: ResolveOnce<string, void>;
|
|
37
|
+
refId(): Promise<string>;
|
|
38
|
+
readonly _onClosedFns: Map<string, () => void>;
|
|
39
|
+
onClosed(fn: () => void): () => void;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
shellClose(db: LedgerShell): Promise<void>;
|
|
42
|
+
destroy(): Promise<void>;
|
|
43
|
+
readonly _ready: ResolveOnce<void>;
|
|
44
|
+
ready(): Promise<void>;
|
|
45
|
+
readonly logger: Logger;
|
|
46
|
+
readonly sthis: SuperThis;
|
|
47
|
+
constructor(sthis: SuperThis, opts: LedgerOptsOptionalTracer);
|
|
48
|
+
attach(a: Attachable): Promise<Attached>;
|
|
49
|
+
subscribe<T extends DocTypes>(listener: ListenerFn<T>, updates?: boolean): () => void;
|
|
50
|
+
private _notify;
|
|
51
|
+
private _no_update_notify;
|
|
52
|
+
}
|
|
53
|
+
export declare function toStoreURIRuntime(sthis: SuperThis, name: string, sopts?: StoreUrlsOpts): StoreURIRuntime;
|
|
54
|
+
export declare function fireproof(name: string, opts?: ConfigOpts): Database;
|
|
55
|
+
export {};
|