@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
@@ -1,152 +0,0 @@
1
- import * as CBW from '@ipld/car/buffer-writer'
2
- import * as raw from 'multiformats/codecs/raw'
3
- import { encrypt, decrypt } from '../crypto.js'
4
- import { parse } from 'multiformats/link'
5
- import { sha256 } from 'multiformats/hashes/sha2'
6
- import * as Block from 'multiformats/block'
7
- import { Buffer } from 'buffer'
8
- // @ts-ignore
9
- import { bf } from 'prolly-trees/utils'
10
- // @ts-ignore
11
- import { nocache as cache } from 'prolly-trees/cache'
12
- const chunker = bf(30)
13
-
14
- export async function getEmptyLoader () { // unused?
15
- const theseWriteableBlocks = new VMemoryBlockstore()
16
- return {
17
- blocks: theseWriteableBlocks,
18
- put: async (cid, bytes) => {
19
- return await theseWriteableBlocks.put(cid, bytes)
20
- },
21
- get: async (cid) => {
22
- const got = await theseWriteableBlocks.get(cid)
23
- return got.bytes
24
- }
25
- }
26
- }
27
-
28
- export class VMemoryBlockstore {
29
- /** @type {Map<string, Uint8Array>} */
30
- blocks = new Map()
31
- instanceId = Math.random().toString(36).slice(2)
32
-
33
- async get (cid) {
34
- const bytes = this.blocks.get(cid.toString())
35
- if (!bytes) throw new Error('block not found ' + cid.toString())
36
- return { cid, bytes }
37
- }
38
-
39
- /**
40
- * @param {any} cid
41
- * @param {Uint8Array} bytes
42
- */
43
- async put (cid, bytes) {
44
- this.blocks.set(cid.toString(), bytes)
45
- }
46
-
47
- * entries () {
48
- for (const [str, bytes] of this.blocks) {
49
- yield { cid: parse(str), bytes }
50
- }
51
- }
52
- }
53
-
54
- export const blocksToCarBlock = async (rootCids, blocks) => {
55
- // console.log('blocksToCarBlock', rootCids, blocks.constructor.name)
56
- let size = 0
57
- if (!Array.isArray(rootCids)) {
58
- rootCids = [rootCids]
59
- }
60
- const headerSize = CBW.headerLength({ roots: rootCids })
61
- size += headerSize
62
- if (!Array.isArray(blocks)) {
63
- blocks = Array.from(blocks.entries())
64
- }
65
- for (const { cid, bytes } of blocks) {
66
- // console.log(cid, bytes)
67
- size += CBW.blockLength({ cid, bytes })
68
- }
69
- const buffer = new Uint8Array(size)
70
- const writer = await CBW.createWriter(buffer, { headerSize })
71
-
72
- for (const cid of rootCids) {
73
- writer.addRoot(cid)
74
- }
75
-
76
- for (const { cid, bytes } of blocks) {
77
- writer.write({ cid, bytes })
78
- }
79
- await writer.close()
80
- return await Block.encode({ value: writer.bytes, hasher: sha256, codec: raw })
81
- }
82
-
83
- export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blocks, keyMaterial, cids) => {
84
- const encryptionKey = Buffer.from(keyMaterial, 'hex')
85
- const encryptedBlocks = []
86
- const theCids = cids
87
- // console.trace('blocksToEncryptedCarBlock', blocks)
88
- // for (const { cid } of blocks.entries()) {
89
- // theCids.push(cid.toString())
90
- // }
91
- // console.log(
92
- // 'encrypting',
93
- // theCids.length,
94
- // 'blocks',
95
- // theCids.includes(innerBlockStoreClockRootCid.toString()),
96
- // keyMaterial
97
- // )
98
- // console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
99
- let last
100
- for await (const block of encrypt({
101
- cids: theCids,
102
- get: async (cid) => {
103
- // console.log('getencrypt', cid)
104
- const got = blocks.get(cid)
105
- // console.log('got', got)
106
- return got.block ? ({ cid, bytes: got.block }) : got
107
- },
108
- key: encryptionKey,
109
- hasher: sha256,
110
- chunker,
111
- cache,
112
- // codec: dagcbor, // should be crypto?
113
- root: innerBlockStoreClockRootCid
114
- })) {
115
- encryptedBlocks.push(block)
116
- last = block
117
- }
118
- // console.log('last', last.cid.toString(), 'for clock', innerBlockStoreClockRootCid.toString())
119
- const encryptedCar = await blocksToCarBlock(last.cid, encryptedBlocks)
120
- return encryptedCar
121
- }
122
-
123
- // { root, get, key, cache, chunker, hasher }
124
- const memoizeDecryptedCarBlocks = new Map()
125
- export const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
126
- if (memoizeDecryptedCarBlocks.has(cid.toString())) {
127
- return memoizeDecryptedCarBlocks.get(cid.toString())
128
- } else {
129
- const blocksPromise = (async () => {
130
- const decryptionKey = Buffer.from(keyMaterial, 'hex')
131
- // console.log('decrypting', keyMaterial, cid.toString())
132
- const cids = new Set()
133
- const decryptedBlocks = []
134
- for await (const block of decrypt({
135
- root: cid,
136
- get,
137
- key: decryptionKey,
138
- chunker,
139
- hasher: sha256,
140
- cache
141
- // codec: dagcbor
142
- })) {
143
- // console.log('decrypted', block.cid.toString())
144
- decryptedBlocks.push(block)
145
- cids.add(block.cid.toString())
146
- }
147
- return { blocks: decryptedBlocks, cids }
148
- })()
149
- memoizeDecryptedCarBlocks.set(cid.toString(), blocksPromise)
150
- return blocksPromise
151
- }
152
- }
package/src/sync.js DELETED
@@ -1,237 +0,0 @@
1
- import SimplePeer from 'simple-peer'
2
- import { parseCID } from './database.js'
3
- import { decodeEventBlock } from './clock.js'
4
- import { blocksToCarBlock, blocksToEncryptedCarBlock } from './storage/utils.js'
5
- import { CarReader } from '@ipld/car'
6
-
7
- /**
8
- * @typedef {import('./database.js').Database} Database
9
- */
10
- export class Sync {
11
- /**
12
- * @param {Database} database
13
- * @param {typeof SimplePeer} [PeerClass]
14
- * @memberof Sync
15
- * @static
16
- */
17
- status = 'new'
18
- constructor (database, PeerClass = SimplePeer) {
19
- this.database = database
20
- this.database.blocks.syncs.add(this) // should this happen during setup?
21
- this.PeerClass = PeerClass
22
- this.pushBacklog = new Promise((resolve, reject) => {
23
- this.pushBacklogResolve = resolve
24
- this.pushBacklogReject = reject
25
- })
26
- this.isReady = false
27
- // this.pushBacklog.then(() => {
28
- // // console.log('sync backlog resolved')
29
- // this.database.notifyReset()
30
- // })
31
- // this.connected = new Promise((resolve, reject) => {
32
- // this.readyResolve = resolve
33
- // this.readyReject = reject
34
- // })
35
- }
36
-
37
- async offer () {
38
- this.status = 'offering'
39
- return this.setupPeer(true)
40
- }
41
-
42
- async accept (base64offer) {
43
- const offer = JSON.parse(atob(base64offer))
44
- const p = this.setupPeer(false)
45
- this.peer.signal(offer)
46
- this.status = 'accepting'
47
- return p
48
- }
49
-
50
- connect (base64accept) {
51
- const accept = JSON.parse(atob(base64accept))
52
- this.status = 'connecting'
53
- this.peer.signal(accept)
54
- }
55
-
56
- async setupPeer (initiator = false) {
57
- this.peer = new this.PeerClass({
58
- initiator,
59
- trickle: false
60
- })
61
- this.peer.on('connect', () => this.startSync())
62
- this.peer.on('data', data => this.gotData(data))
63
- const p = new Promise((resolve, reject) => {
64
- this.peer.on('signal', resolve)
65
- this.peer.on('error', reject)
66
- })
67
- return p.then(signal => btoa(JSON.stringify(signal)))
68
- }
69
-
70
- async backlog () {
71
- return this.pushBacklog
72
- }
73
-
74
- async gotData (data) {
75
- // console.log('got data', data.toString())
76
- let reader = null
77
- try {
78
- reader = await CarReader.fromBytes(data)
79
- } catch (e) {
80
- // console.log('not a car', data.toString())
81
- }
82
- if (reader) {
83
- // console.log('got car')
84
- this.status = 'parking car'
85
- const blz = new Set()
86
- for await (const block of reader.blocks()) {
87
- blz.add(block)
88
- }
89
- const roots = await reader.getRoots()
90
- // console.log(
91
- // 'got car',
92
- // roots.map(c => c.toString()),
93
- // this.database.clock.map(c => c.toString())
94
- // )
95
- // console.log(
96
- // 'got blocks!',
97
- // [...blz].map(({ cid }) => cid.toString())
98
- // )
99
- // @ts-ignore
100
- reader.entries = reader.blocks
101
- await this.database.blocks.commit(
102
- {
103
- label: 'sync',
104
- entries: () => [...blz],
105
- get: async cid => await reader.get(cid),
106
- lastCid: [...blz][0].cid // doesn't matter
107
- },
108
- false
109
- )
110
- // first arg could be the roots parents?
111
- // get the roots parents
112
- const parents = await Promise.all(
113
- roots.map(async cid => {
114
- const rbl = await reader.get(cid)
115
- if (!rbl) {
116
- console.log('missing root block', cid.toString(), reader)
117
- throw new Error('missing root block')
118
- }
119
- const block = await decodeEventBlock(rbl.bytes)
120
- return block.value.parents
121
- })
122
- )
123
- this.database.applyClock(parents.flat(), roots)
124
- this.database.notifyReset()
125
- // console.log('after', this.database.clockToJSON())
126
- this.pushBacklogResolve({ ok: true })
127
- } else {
128
- // data is a json string, parse it
129
- const message = JSON.parse(data.toString())
130
- // console.log('got message', message)
131
- if (message.ok) {
132
- this.status = 'ok'
133
- this.pushBacklogResolve({ ok: true })
134
- } else if (message.clock) {
135
- const reqCidDiff = message
136
- // this might be a CID diff
137
- // console.log('got diff', reqCidDiff)
138
- const carBlock = await Sync.makeCar(this.database, null, reqCidDiff.cids)
139
- if (!carBlock) {
140
- // we are full synced
141
- // console.log('we are full synced')
142
- this.status = 'full synced'
143
- this.peer.send(JSON.stringify({ ok: true }))
144
- // this.pushBacklogResolve({ ok: true })
145
- } else {
146
- // console.log('do send diff', carBlock.bytes.length)
147
- this.status = 'sending diff car'
148
- this.peer.send(carBlock.bytes)
149
- // console.log('sent diff car')
150
- // this.pushBacklogResolve({ ok: true })
151
- }
152
- }
153
- }
154
- }
155
-
156
- destroy () {
157
- this.database.blocks.syncs.delete(this)
158
- this.status = 'destroyed'
159
- // this.peer.destroy() todo
160
- }
161
-
162
- async sendUpdate (blockstore) {
163
- if (!this.peer || !this.isReady) return
164
- // console.log('send update from', this.database.instanceId)
165
- // todo should send updates since last sync (currently sends each transaction)
166
- const newCar = await blocksToCarBlock(blockstore.lastCid, blockstore)
167
- this.status = 'sending update car'
168
- this.peer.send(newCar.bytes)
169
- }
170
-
171
- async startSync () {
172
- // console.log('start sync', this.peer.initiator)
173
- this.isReady = true
174
- const allCIDs = await this.database.allStoredCIDs()
175
- // console.log('allCIDs', allCIDs)
176
- const reqCidDiff = {
177
- clock: this.database.clockToJSON(),
178
- cids: allCIDs.map(cid => cid.toString())
179
- }
180
- // console.log('send diff', reqCidDiff)
181
- this.status = 'sending cid diff'
182
- this.peer.send(JSON.stringify(reqCidDiff))
183
- }
184
-
185
- // get all the cids
186
- // tell valet to make a file
187
- /**
188
- * @param {import("./database.js").Database} database
189
- * @param {string} key
190
- */
191
- static async makeCar (database, key, skip = []) {
192
- const allCIDs = await database.allCIDs()
193
- const blocks = database.blocks
194
- const rootCIDs = database.clock
195
-
196
- const newCIDs = [...new Set([...rootCIDs, ...allCIDs])].filter(cid => !skip.includes(cid.toString()))
197
- const syncCIDs = [...new Set([...rootCIDs, ...allCIDs.filter(cid => !skip.includes(cid.toString()))])]
198
- // console.log(
199
- // 'makeCar',
200
- // rootCIDs.map(c => c.toString()),
201
- // syncCIDs.map(c => c.toString()),
202
- // allCIDs.map(c => c.toString())
203
- // )
204
- if (newCIDs.length === 0) {
205
- return null
206
- }
207
-
208
- if (typeof key === 'undefined') {
209
- key = blocks.valet?.primary.keyMaterial
210
- }
211
- if (key) {
212
- return blocksToEncryptedCarBlock(
213
- rootCIDs,
214
- {
215
- entries: () => syncCIDs.map(cid => ({ cid })),
216
- get: async cid => await blocks.get(cid)
217
- },
218
- key,
219
- syncCIDs.map(c => c.toString())
220
- )
221
- } else {
222
- const carBlocks = await Promise.all(
223
- syncCIDs.map(async c => {
224
- const b = await blocks.get(c)
225
- if (typeof b.cid === 'string') {
226
- b.cid = parseCID(b.cid)
227
- }
228
- return b
229
- })
230
- )
231
- // console.log('carblock')
232
- return blocksToCarBlock(rootCIDs, {
233
- entries: () => carBlocks
234
- })
235
- }
236
- }
237
- }
package/src/valet.js DELETED
@@ -1,105 +0,0 @@
1
-
2
- import { Loader } from './loader.js'
3
-
4
- export class Valet {
5
- idb = null
6
- name = null
7
- uploadQueue = null
8
- alreadyEnqueued = new Set()
9
-
10
- instanceId = Math.random().toString(36).slice(2)
11
-
12
- constructor (name = 'default', config = {}) {
13
- this.name = name
14
- // console.log('new Valet', name, config.primary)
15
- this.primary = Loader.appropriate(name, config.primary)
16
- this.secondary = config.secondary ? Loader.appropriate(name, config.secondary) : null
17
- // set up a promise listener that applies all the headers to the clock
18
- // when they resolve
19
- const readyP = [this.primary.ready]
20
- if (this.secondary) readyP.push(this.secondary.ready)
21
-
22
- this.ready = Promise.all(readyP).then((blocksReady) => {
23
- // console.log('blocksReady valet', this.name, blocksReady)
24
- return blocksReady
25
- })
26
- }
27
-
28
- async saveHeader (header) {
29
- // each storage needs to add its own carCidMapCarCid to the header
30
- if (this.secondary) { this.secondary.saveHeader(header) } // todo: await?
31
- return await this.primary.saveHeader(header)
32
- }
33
-
34
- async compact (clock) {
35
- await this.primary.compact(clock)
36
- if (this.secondary) await this.secondary.compact(clock)
37
- }
38
-
39
- /**
40
- * Group the blocks into a car and write it to the valet.
41
- * @param {import('./blockstore.js').InnerBlockstore} innerBlockstore
42
- * @param {Set<string>} cids
43
- * @returns {Promise<void>}
44
- * @memberof Valet
45
- */
46
- async writeTransaction (innerBlockstore, cids) {
47
- if (innerBlockstore.lastCid) {
48
- await this.primary.parkCar(innerBlockstore, cids)
49
- if (this.secondary) await this.secondary.parkCar(innerBlockstore, cids)
50
- } else {
51
- throw new Error('missing lastCid for car header')
52
- }
53
- }
54
-
55
- /**
56
- * Iterate over all blocks in the store.
57
- *
58
- * @yields {{cid: string, value: Uint8Array}}
59
- * @returns {AsyncGenerator<any, any, any>}
60
- */
61
- async * cids () {
62
- // console.log('valet cids')
63
- // todo use cidMap
64
- // while (cursor) {
65
- // yield { cid: cursor.key, car: cursor.value.car }
66
- // cursor = await cursor.continue()
67
- // }
68
- }
69
-
70
- remoteBlockFunction = null
71
-
72
- async getValetBlock (dataCID) {
73
- // console.log('getValetBlock primary', dataCID)
74
- try {
75
- const { block } = await this.primary.getLoaderBlock(dataCID)
76
- return block
77
- } catch (e) {
78
- // console.log('getValetBlock error', e)
79
- if (this.secondary) {
80
- // console.log('getValetBlock secondary', dataCID)
81
- try {
82
- // eslint-disable-next-line
83
- const { block, reader } = await this.secondary.getLoaderBlock(dataCID)
84
- const writeableCarReader = await this.primary.getWriteableCarReader(reader)
85
- // console.log('getValetBlock secondary', dataCID, block.length)
86
- // eslint-disable-next-line
87
- const cids = new Set()
88
- for await (const { cid } of reader.entries()) {
89
- // console.log(cid, bytes)
90
- cids.add(cid.toString())
91
- }
92
- // reader.get = reader.gat // some consumers prefer get
93
- // console.log('replicating', reader.root)
94
- writeableCarReader.lastCid = reader.root.cid
95
- writeableCarReader.head = []
96
- await this.primary.parkCar(writeableCarReader, cids).catch(e => console.error('parkCar error', e))
97
- // console.log('FIX THIS', did)
98
- return block
99
- } catch (e) {
100
- // console.log('getValetBlock secondary error', e)
101
- }
102
- }
103
- }
104
- }
105
- }