@fireproof/core 0.8.0 → 0.10.1-dev

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.
Files changed (57) hide show
  1. package/README.md +5 -184
  2. package/dist/fireproof.browser.js +18879 -0
  3. package/dist/fireproof.browser.js.map +7 -0
  4. package/dist/fireproof.cjs.js +9305 -0
  5. package/dist/fireproof.cjs.js.map +7 -0
  6. package/dist/fireproof.esm.js +9295 -0
  7. package/dist/fireproof.esm.js.map +7 -0
  8. package/package.json +57 -105
  9. package/dist/blockstore.js +0 -268
  10. package/dist/clock.js +0 -459
  11. package/dist/crypto.js +0 -63
  12. package/dist/database.js +0 -434
  13. package/dist/db-index.js +0 -403
  14. package/dist/encrypted-block.js +0 -48
  15. package/dist/fireproof.js +0 -84
  16. package/dist/import.js +0 -29
  17. package/dist/listener.js +0 -111
  18. package/dist/loader.js +0 -13
  19. package/dist/prolly.js +0 -405
  20. package/dist/remote.js +0 -102
  21. package/dist/sha1.js +0 -74
  22. package/dist/src/fireproof.d.ts +0 -472
  23. package/dist/src/fireproof.js +0 -81191
  24. package/dist/src/fireproof.js.map +0 -1
  25. package/dist/src/fireproof.mjs +0 -81186
  26. package/dist/src/fireproof.mjs.map +0 -1
  27. package/dist/storage/base.js +0 -426
  28. package/dist/storage/blocksToEncryptedCarBlock.js +0 -144
  29. package/dist/storage/browser.js +0 -62
  30. package/dist/storage/filesystem.js +0 -67
  31. package/dist/storage/rest.js +0 -57
  32. package/dist/storage/ucan.js +0 -0
  33. package/dist/storage/utils.js +0 -144
  34. package/dist/sync.js +0 -218
  35. package/dist/utils.js +0 -16
  36. package/dist/valet.js +0 -102
  37. package/src/blockstore.js +0 -283
  38. package/src/clock.js +0 -486
  39. package/src/crypto.js +0 -70
  40. package/src/database.js +0 -469
  41. package/src/db-index.js +0 -426
  42. package/src/encrypted-block.js +0 -57
  43. package/src/fireproof.js +0 -98
  44. package/src/import.js +0 -34
  45. package/src/link.d.ts +0 -3
  46. package/src/loader.js +0 -16
  47. package/src/prolly.js +0 -445
  48. package/src/remote.js +0 -113
  49. package/src/sha1.js +0 -83
  50. package/src/storage/base.js +0 -463
  51. package/src/storage/browser.js +0 -67
  52. package/src/storage/filesystem.js +0 -73
  53. package/src/storage/rest.js +0 -59
  54. package/src/storage/ucan.js +0 -0
  55. package/src/storage/utils.js +0 -152
  56. package/src/sync.js +0 -237
  57. package/src/valet.js +0 -105
package/dist/database.js DELETED
@@ -1,434 +0,0 @@
1
- // @ts-nocheck
2
- import { visMerkleClock, visMerkleTree, vis, put, get, getAll, eventsSince } from './prolly.js';
3
- import { doTransaction, TransactionBlockstore } from './blockstore.js';
4
- import charwise from 'charwise';
5
- import { CID } from 'multiformats';
6
- import { DbIndex as Index } from './db-index.js';
7
- import { Remote } from './remote.js';
8
- // TypeScript Types
9
- // eslint-disable-next-line no-unused-vars
10
- // import { CID } from 'multiformats/dist/types/src/cid.js'
11
- // eslint-disable-next-line no-unused-vars
12
- class Proof {
13
- }
14
- export const parseCID = cid => (typeof cid === 'string' ? CID.parse(cid) : cid);
15
- /**
16
- * @class Fireproof
17
- * @classdesc Fireproof stores data in IndexedDB and provides a Merkle clock.
18
- * This is the main class for saving and loading JSON and other documents with the database. You can find additional examples and
19
- * usage guides in the repository README.
20
- *
21
- * @param {CID[]} clock - The Merkle clock head to use for the Fireproof instance.
22
- * @param {object} [config] - Optional configuration options for the Fireproof instance.
23
- * @param {object} [authCtx] - Optional authorization context object to use for any authentication checks.
24
- *
25
- */
26
- export class Database {
27
- listeners = new Set();
28
- indexes = new Map();
29
- rootCache = null;
30
- eventsCache = new Map();
31
- remote = null;
32
- name = '';
33
- constructor(name, config = {}) {
34
- this.name = name;
35
- this.clock = [];
36
- this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`;
37
- this.blocks = new TransactionBlockstore(name, config);
38
- this.indexBlocks = new TransactionBlockstore(name ? name + '.indexes' : null, { primary: config.index });
39
- this.remote = new Remote(this, name, config);
40
- this.config = config;
41
- // todo we can wait for index blocks elsewhere
42
- this.ready = Promise.all([this.blocks.ready, this.indexBlocks.ready]).then(([blocksReady, indexReady]) => {
43
- const clock = new Set();
44
- // console.log('blocksReady', blocksReady)
45
- if (!blocksReady) {
46
- return;
47
- }
48
- for (const headers of blocksReady) {
49
- for (const [, header] of Object.entries(headers)) {
50
- if (!header)
51
- continue;
52
- for (const cid of header.clock) {
53
- clock.add(cid);
54
- }
55
- if (header.index) {
56
- this.indexBlocks.valet.primary.setLastCar(header.index.car);
57
- this.indexBlocks.valet.primary.setKeyMaterial(header.index.key);
58
- }
59
- if (header.indexes) {
60
- for (const { name, code, clock: { byId, byKey, db } } of header.indexes) {
61
- // console.log('index', name, code, { byId, byKey }, db, header.indexes)
62
- Index.fromJSON(this, {
63
- clock: {
64
- byId: byId ? parseCID(byId) : null,
65
- byKey: byKey ? parseCID(byKey) : null,
66
- db: db && db.length > 0 ? db.map(c => parseCID(c)) : null
67
- },
68
- code,
69
- name
70
- });
71
- }
72
- }
73
- }
74
- }
75
- this.clock = [...clock];
76
- });
77
- }
78
- /**
79
- * Renders the Fireproof instance as a JSON object.
80
- * @returns {Object} - The JSON representation of the Fireproof instance. Includes clock heads for the database and its indexes.
81
- * @memberof Fireproof
82
- * @instance
83
- */
84
- toJSON() {
85
- // todo this prepareHeader ignores secondary storage, need both
86
- return this.blocks.valet ? this.blocks.valet.primary.prepareHeader(this.toHeader(), false) : this.toHeader(); // omg
87
- }
88
- toHeader() {
89
- return {
90
- // clock: this.clockToJSON(),
91
- name: this.name,
92
- index: {
93
- key: this.indexBlocks.valet?.primary.keyMaterial,
94
- car: this.indexBlocks.valet?.primary.lastCar?.toString()
95
- },
96
- indexes: [...this.indexes.values()].map(index => index.toJSON())
97
- };
98
- }
99
- /**
100
- * Returns the Merkle clock heads for the Fireproof instance.
101
- * @returns {string[]} - The Merkle clock heads for the Fireproof instance.
102
- * @memberof Fireproof
103
- * @instance
104
- */
105
- clockToJSON(clock = null) {
106
- return (clock || this.clock).map(cid => cid.toString());
107
- }
108
- async maybeSaveClock() {
109
- if (this.name && this.blocks.valet) {
110
- await this.blocks.valet.saveHeader(this.toHeader());
111
- }
112
- }
113
- index(name) {
114
- const indexes = [...this.indexes.values()].filter(index => index.name === name);
115
- if (indexes.length > 1) {
116
- throw new Error(`Multiple indexes found with name ${name}`);
117
- }
118
- return indexes[0] || null;
119
- }
120
- /**
121
- * Triggers a notification to all listeners
122
- * of the Fireproof instance so they can repaint UI, etc.
123
- * @returns {Promise<void>}
124
- * @memberof Fireproof
125
- * @instance
126
- */
127
- async notifyReset() {
128
- await this.ready;
129
- await this.notifyListeners({ _reset: true, _clock: this.clockToJSON() });
130
- }
131
- async compact() {
132
- if (this.name && this.blocks.valet) {
133
- await this.blocks.valet.compact(this.clock);
134
- await this.blocks.valet.saveHeader(this.toHeader());
135
- }
136
- }
137
- /**
138
- * Returns the changes made to the Fireproof instance since the specified event.
139
- * @function changesSince
140
- * @param {CID[]} [event] - The clock head to retrieve changes since. If null or undefined, retrieves all changes.
141
- * @returns {Promise<{rows : Object[], clock: CID[], proof: {}}>} An object containing the rows and the head of the instance's clock.
142
- * @memberof Fireproof
143
- * @instance
144
- */
145
- async changesSince(aClock) {
146
- await this.ready;
147
- // console.log('events for', this.instanceId, aClock?.constructor.name)
148
- // console.log('changesSince', this.instanceId, this.clockToJSON(aClock), this.clockToJSON())
149
- let rows, dataCIDs, clockCIDs;
150
- // if (!aClock) aClock = []
151
- if (aClock && aClock.length > 0) {
152
- aClock = aClock.map(cid => cid.toString());
153
- const eventKey = JSON.stringify([...this.clockToJSON(aClock), ...this.clockToJSON()]);
154
- let resp;
155
- if (this.eventsCache.has(eventKey)) {
156
- // console.log('events from cache')
157
- resp = this.eventsCache.get(eventKey);
158
- }
159
- else {
160
- resp = await eventsSince(this.blocks, this.clock, aClock);
161
- this.eventsCache.set(eventKey, resp);
162
- }
163
- const docsMap = new Map();
164
- for (const { key, type, value } of resp.result.map(decodeEvent)) {
165
- if (type === 'del') {
166
- docsMap.set(key, { key, del: true });
167
- }
168
- else {
169
- docsMap.set(key, { key, value });
170
- }
171
- }
172
- rows = Array.from(docsMap.values());
173
- clockCIDs = resp.clockCIDs;
174
- // console.log('change rows', this.instanceId, rows)
175
- }
176
- else {
177
- const allResp = await getAll(this.blocks, this.clock, this.rootCache);
178
- this.rootCache = { root: allResp.root, clockCIDs: allResp.clockCIDs };
179
- rows = allResp.result.map(({ key, value }) => decodeEvent({ key, value }));
180
- dataCIDs = allResp.cids;
181
- // console.log('dbdoc rows', this.instanceId, rows)
182
- }
183
- return {
184
- rows,
185
- clock: this.clockToJSON(),
186
- proof: { data: await cidsToProof(dataCIDs), clock: await cidsToProof(clockCIDs) }
187
- };
188
- }
189
- async allDocuments() {
190
- await this.ready;
191
- const allResp = await getAll(this.blocks, this.clock, this.rootCache);
192
- this.rootCache = { root: allResp.root, clockCIDs: allResp.clockCIDs };
193
- const rows = allResp.result
194
- .map(({ key, value }) => decodeEvent({ key, value }))
195
- .map(({ key, value }) => ({ key, value: { _id: key, ...value } }));
196
- return {
197
- rows,
198
- clock: this.clockToJSON(),
199
- proof: await cidsToProof(allResp.cids)
200
- };
201
- }
202
- async allCIDs() {
203
- await this.ready;
204
- const allResp = await getAll(this.blocks, this.clock, this.rootCache, true);
205
- this.rootCache = { root: allResp.root, clockCIDs: allResp.clockCIDs };
206
- // console.log('allcids', allResp.cids, allResp.clockCIDs)
207
- const cids = await cidsToProof(allResp.cids);
208
- const clockCids = await cidsToProof(allResp.clockCIDs);
209
- // console.log('allcids', cids, clockCids)
210
- return [...cids, ...clockCids]; // clock CID last -- need to handle multiple entry clocks
211
- }
212
- async allStoredCIDs() {
213
- await this.ready;
214
- const allCIDs = [];
215
- for await (const { cid } of this.blocks.entries()) {
216
- allCIDs.push(cid);
217
- }
218
- return allCIDs;
219
- }
220
- /**
221
- * Retrieves the document with the specified ID from the database
222
- *
223
- * @param {string} key - the ID of the document to retrieve
224
- * @param {Object} [opts] - options
225
- * @returns {Promise<{_id: string}>} - the document with the specified ID
226
- * @memberof Fireproof
227
- * @instance
228
- */
229
- async get(key, opts = {}) {
230
- await this.ready;
231
- const clock = opts.clock || this.clock;
232
- const resp = await get(this.blocks, clock, charwise.encode(key), this.rootCache);
233
- this.rootCache = { root: resp.root, clockCIDs: resp.clockCIDs };
234
- // ? this tombstone is temporary until we can get the prolly tree to delete
235
- if (!resp || resp.result === null) {
236
- throw new Error('Not found');
237
- }
238
- const doc = { ...resp.result };
239
- if (opts.mvcc === true) {
240
- doc._clock = this.clockToJSON();
241
- }
242
- doc._proof = {
243
- data: await cidsToProof(resp.cids),
244
- clock: this.clockToJSON()
245
- };
246
- doc._id = key;
247
- return doc;
248
- }
249
- /**
250
- * @typedef {any} Document
251
- * @property {string} _id - The ID of the document (required)
252
- * @property {string} [_proof] - The proof of the document (optional)
253
- * @property {string} [_clock] - The clock of the document (optional)
254
- * @property {Object.<string, any>} [unknown: string] - Any other unknown properties (optional)
255
- */
256
- /**
257
- * Adds a new document to the database, or updates an existing document. Returns the ID of the document and the new clock head.
258
- *
259
- * @param {Document} doc - the document to be added
260
- * @returns {Promise<{ id: string, clock: CID[] }>} - The result of adding the document to the database
261
- * @memberof Fireproof
262
- * @instance
263
- */
264
- async put({ _id, _proof, _clock, ...doc }) {
265
- await this.ready;
266
- const id = _id || 'f' + Math.random().toString(36).slice(2);
267
- doc = JSON.parse(JSON.stringify(doc));
268
- if (_clock)
269
- doc._clock = _clock;
270
- await this.runValidation({ _id: id, ...doc });
271
- return await this.putToProllyTree({ key: id, value: doc }, _clock);
272
- }
273
- /**
274
- * Deletes a document from the database
275
- * @param {string | any} docOrId - the document ID
276
- * @returns {Promise<{ id: string, clock: CID[] }>} - The result of deleting the document from the database
277
- * @memberof Fireproof
278
- * @instance
279
- */
280
- async del(docOrId) {
281
- await this.ready;
282
- let id;
283
- let clock = null;
284
- if (docOrId._id) {
285
- id = docOrId._id;
286
- clock = docOrId._clock;
287
- }
288
- else {
289
- id = docOrId;
290
- }
291
- await this.runValidation({ _id: id, _deleted: true });
292
- return await this.putToProllyTree({ key: id, del: true }, clock); // not working at prolly tree layer?
293
- // this tombstone is temporary until we can get the prolly tree to delete
294
- // return await this.putToProllyTree({ key: id, value: null }, clock)
295
- }
296
- /**
297
- * Runs validation on the specified document using the Fireproof instance's configuration. Throws an error if the document is invalid.
298
- *
299
- * @param {Object} doc - The document to validate.
300
- * @returns {Promise<void>}
301
- * @throws {Error} - Throws an error if the document is invalid.
302
- * @memberof Fireproof
303
- * @instance
304
- */
305
- async runValidation(doc) {
306
- if (this.config && this.config.validateChange) {
307
- const oldDoc = await this.get(doc._id)
308
- .then(doc => doc)
309
- .catch(() => ({}));
310
- this.config.validateChange(doc, oldDoc, this.authCtx);
311
- }
312
- }
313
- /**
314
- * Updates the underlying storage with the specified event.
315
- * @private
316
- * @param {{del?: true, key : string, value?: any}} decodedEvent - the event to add
317
- * @returns {Promise<{ proof:{}, id: string, clock: CID[] }>} - The result of adding the event to storage
318
- */
319
- async putToProllyTree(decodedEvent, clock = null) {
320
- // console.log('putToProllyTree', decodedEvent)
321
- const event = encodeEvent(decodedEvent);
322
- if (clock && JSON.stringify(this.clockToJSON(clock)) !== JSON.stringify(this.clockToJSON())) {
323
- // console.log('this.clock', this.clockToJSON())
324
- // console.log('that.clock', this.clockToJSON(clock))
325
- // we need to check and see what version of the document exists at the clock specified
326
- // if it is the same as the one we are trying to put, then we can proceed
327
- const resp = await eventsSince(this.blocks, this.clock, event.value._clock);
328
- const missedChange = resp.result.find(({ key }) => key === event.key);
329
- if (missedChange) {
330
- throw new Error('MVCC conflict, document is changed, please reload the document and try again.');
331
- }
332
- }
333
- const prevClock = [...this.clock];
334
- // console.log('putToProllyTree', this.clockToJSON(), decodedEvent)
335
- const result = await doTransaction('putToProllyTree', this.blocks, async (blocks) => await put(blocks, this.clock, event));
336
- if (!result) {
337
- console.error('failed', event);
338
- throw new Error('failed to put at storage layer');
339
- }
340
- this.applyClock(prevClock, result.head);
341
- await this.notifyListeners([decodedEvent]); // this type is odd
342
- return {
343
- id: decodedEvent.key,
344
- clock: this.clockToJSON(),
345
- proof: { data: await cidsToProof(result.cids), clock: await cidsToProof(result.clockCIDs) }
346
- };
347
- // todo should include additions (or split clock)
348
- }
349
- applyClock(prevClock, newClock) {
350
- // console.log('prevClock', prevClock.length, prevClock.map((cid) => cid.toString()))
351
- // console.log('newClock', newClock.length, newClock.map((cid) => cid.toString()))
352
- // console.log('this.clock', this.clock.length, this.clockToJSON())
353
- const stPrev = prevClock.map(cid => cid.toString());
354
- const keptPrevClock = this.clock.filter(cid => stPrev.indexOf(cid.toString()) === -1);
355
- const merged = keptPrevClock.concat(newClock);
356
- const uniquebyCid = new Map();
357
- for (const cid of merged) {
358
- uniquebyCid.set(cid.toString(), cid);
359
- }
360
- this.clock = Array.from(uniquebyCid.values()).sort((a, b) => a.toString().localeCompare(b.toString()));
361
- this.rootCache = null;
362
- this.eventsCache.clear();
363
- // console.log('afterClock', this.clock.length, this.clockToJSON())
364
- }
365
- // /**
366
- // * Advances the clock to the specified event and updates the root CID
367
- // * Will be used by replication
368
- // */
369
- // async advance (event) {
370
- // this.clock = await advance(this.blocks, this.clock, event)
371
- // this.rootCid = await root(this.blocks, this.clock)
372
- // return this.clock
373
- // }
374
- async *vis() {
375
- return yield* vis(this.blocks, this.clock);
376
- }
377
- async visTree() {
378
- return await visMerkleTree(this.blocks, this.clock);
379
- }
380
- async visClock() {
381
- return await visMerkleClock(this.blocks, this.clock);
382
- }
383
- /**
384
- * Registers a Listener to be called when the Fireproof instance's clock is updated.
385
- * Recieves live changes from the database after they are committed.
386
- * @param {Function} listener - The listener to be called when the clock is updated.
387
- * @returns {Function} - A function that can be called to unregister the listener.
388
- * @memberof Fireproof
389
- */
390
- subscribe(listener) {
391
- this.listeners.add(listener);
392
- return () => {
393
- this.listeners.delete(listener);
394
- };
395
- }
396
- /**
397
- * @deprecated 0.7.0 - renamed subscribe(listener)
398
- * @param {Function} listener - The listener to be called when the clock is updated.
399
- * @returns {Function} - A function that can be called to unregister the listener.
400
- * @memberof Fireproof
401
- */
402
- registerListener(listener) {
403
- return this.subscribe(listener);
404
- }
405
- async notifyListeners(changes) {
406
- // await sleep(10)
407
- await this.maybeSaveClock();
408
- for (const listener of this.listeners) {
409
- await listener(changes);
410
- }
411
- }
412
- setRemoteBlockReader(remoteBlockReaderFn) {
413
- this.blocks.remoteBlockFunction = remoteBlockReaderFn;
414
- }
415
- }
416
- export async function cidsToProof(cids) {
417
- if (!cids)
418
- return [];
419
- if (!cids.all) {
420
- return [...cids];
421
- }
422
- const all = await cids.all();
423
- return [...all].map(cid => cid.toString());
424
- }
425
- function decodeEvent(event) {
426
- const decodedKey = charwise.decode(event.key);
427
- return { ...event, key: decodedKey };
428
- }
429
- function encodeEvent(event) {
430
- if (!(event && event.key))
431
- return;
432
- const encodedKey = charwise.encode(event.key);
433
- return { ...event, key: encodedKey };
434
- }