@fireproof/core 0.8.0 → 0.10.1-dev

Sign up to get free protection for your applications and to get access to all the features.
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
- }