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