@fireproof/core 0.6.4 → 0.7.0-alpha.0
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/README.md +2 -2
- package/dist/{src/blockstore.js → blockstore.js} +7 -3
- package/dist/{src/database.js → database.js} +74 -48
- package/dist/{src/db-index.js → db-index.js} +13 -2
- package/dist/fireproof.js +92 -0
- package/dist/import.js +29 -0
- package/dist/loader.js +23 -0
- package/dist/{src/prolly.js → prolly.js} +1 -0
- package/dist/src/fireproof.d.ts +138 -137
- package/dist/src/fireproof.js +18183 -11479
- package/dist/src/fireproof.js.map +1 -1
- package/dist/src/fireproof.mjs +18184 -11479
- package/dist/src/fireproof.mjs.map +1 -1
- package/dist/storage/base.js +348 -0
- package/dist/storage/browser.js +61 -0
- package/dist/storage/filesystem.js +65 -0
- package/dist/storage/rest.js +58 -0
- package/dist/storage/ucan.js +0 -0
- package/dist/{src/sync.js → sync.js} +1 -1
- package/dist/valet.js +200 -0
- package/package.json +4 -5
- package/src/blockstore.js +6 -3
- package/src/database.js +80 -52
- package/src/db-index.js +12 -2
- package/src/fireproof.js +41 -30
- package/src/import.js +34 -0
- package/src/loader.js +26 -0
- package/src/prolly.js +2 -0
- package/src/storage/base.js +371 -0
- package/src/storage/browser.js +67 -0
- package/src/storage/filesystem.js +70 -0
- package/src/storage/rest.js +60 -0
- package/src/storage/ucan.js +0 -0
- package/src/sync.js +1 -1
- package/src/valet.js +57 -359
- package/dist/hooks/use-fireproof.js +0 -150
- package/dist/src/crypto-poly.js +0 -4
- package/dist/src/link.js +0 -1
- package/dist/src/loader.js +0 -131
- package/dist/src/valet.js +0 -476
- package/hooks/use-fireproof.js +0 -173
- package/src/listener.js +0 -119
- package/src/utils.js +0 -16
- /package/dist/{src/clock.js → clock.js} +0 -0
- /package/dist/{src/crypto.js → crypto.js} +0 -0
- /package/dist/{src/listener.js → listener.js} +0 -0
- /package/dist/{src/sha1.js → sha1.js} +0 -0
- /package/dist/{src/utils.js → utils.js} +0 -0
package/src/valet.js
CHANGED
@@ -1,107 +1,45 @@
|
|
1
|
-
import { CarReader } from '@ipld/car'
|
2
|
-
import { CID } from 'multiformats/cid'
|
3
1
|
import { sha256 } from 'multiformats/hashes/sha2'
|
4
|
-
import { parse } from 'multiformats/link'
|
5
2
|
import * as CBW from '@ipld/car/buffer-writer'
|
6
3
|
import * as raw from 'multiformats/codecs/raw'
|
7
4
|
import * as Block from 'multiformats/block'
|
8
|
-
import
|
9
|
-
import { openDB } from 'idb'
|
10
|
-
import cargoQueue from 'async/cargoQueue.js'
|
5
|
+
import { Loader } from './loader.js'
|
11
6
|
// @ts-ignore
|
12
|
-
|
13
|
-
// @ts-ignore
|
14
|
-
import { bf, simpleCompare as compare } from 'prolly-trees/utils'
|
7
|
+
import { bf } from 'prolly-trees/utils'
|
15
8
|
// @ts-ignore
|
16
9
|
import { nocache as cache } from 'prolly-trees/cache'
|
17
|
-
// import { makeGetBlock } from './prolly.js'
|
18
10
|
import { encrypt, decrypt } from './crypto.js'
|
19
11
|
import { Buffer } from 'buffer'
|
20
|
-
// @ts-ignore
|
21
|
-
import * as codec from 'encrypted-block'
|
22
|
-
|
23
|
-
import { create, load } from 'ipld-hashmap'
|
24
12
|
|
25
|
-
import { rawSha1 as sha1sync } from './sha1.js'
|
26
13
|
const chunker = bf(30)
|
27
14
|
|
28
|
-
const blockOpts = { cache, chunker, codec: dagcbor, hasher: sha256, compare }
|
29
|
-
|
30
|
-
const NO_ENCRYPT = typeof process !== 'undefined' && !!process.env?.NO_ENCRYPT
|
31
|
-
// ? process.env.NO_ENCRYPT : import.meta && import.meta.env.VITE_NO_ENCRYPT
|
32
|
-
|
33
15
|
export class Valet {
|
34
16
|
idb = null
|
35
17
|
name = null
|
36
18
|
uploadQueue = null
|
37
19
|
alreadyEnqueued = new Set()
|
38
|
-
keyMaterial = null
|
39
|
-
keyId = 'null'
|
40
|
-
valetRoot = null
|
41
|
-
valetRootCid = null // set by hydrate
|
42
|
-
valetRootCarCid = null // most recent diff
|
43
20
|
|
44
|
-
valetCidBlocks = new VMemoryBlockstore()
|
45
21
|
instanceId = Math.random().toString(36).slice(2)
|
46
22
|
|
47
|
-
|
48
|
-
* Function installed by the database to upload car files
|
49
|
-
* @type {null|function(string, Uint8Array):Promise<void>}
|
50
|
-
*/
|
51
|
-
uploadFunction = null
|
52
|
-
|
53
|
-
constructor (name = 'default', keyMaterial) {
|
23
|
+
constructor (name = 'default', config = {}) {
|
54
24
|
this.name = name
|
55
|
-
|
56
|
-
this.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
await this.uploadFunction(task.carCid, task.value)
|
67
|
-
// todo update syncCidMap to say this has been synced
|
68
|
-
// const carMeta = await db.get('cidToCar', task.carCid)
|
69
|
-
// delete carMeta.pending
|
70
|
-
// await db.put('cidToCar', carMeta)
|
71
|
-
}
|
72
|
-
}
|
73
|
-
callback()
|
74
|
-
})
|
75
|
-
|
76
|
-
this.uploadQueue.drain(async () => {
|
77
|
-
// todo read syncCidMap and sync any that are still unsynced
|
78
|
-
// return await this.withDB(async db => {
|
79
|
-
// const carKeys = (await db.getAllFromIndex('cidToCar', 'pending')).map(c => c.car)
|
80
|
-
// for (const carKey of carKeys) {
|
81
|
-
// await this.uploadFunction(carKey, await db.get('cars', carKey))
|
82
|
-
// const carMeta = await db.get('cidToCar', carKey)
|
83
|
-
// delete carMeta.pending
|
84
|
-
// await db.put('cidToCar', carMeta)
|
85
|
-
// }
|
86
|
-
// })
|
25
|
+
// console.log('new Valet', name, config.primary)
|
26
|
+
this.primary = Loader.appropriate(name, config.primary)
|
27
|
+
this.secondary = config.secondary ? Loader.appropriate(name, config.secondary) : null
|
28
|
+
// set up a promise listener that applies all the headers to the clock
|
29
|
+
// when they resolve
|
30
|
+
const readyP = [this.primary.ready]
|
31
|
+
if (this.secondary) readyP.push(this.secondary.ready)
|
32
|
+
|
33
|
+
this.ready = Promise.all(readyP).then((blocksReady) => {
|
34
|
+
// console.log('blocksReady valet', this.name, blocksReady)
|
35
|
+
return blocksReady
|
87
36
|
})
|
88
37
|
}
|
89
38
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
setKeyMaterial (km) {
|
95
|
-
if (km && !NO_ENCRYPT) {
|
96
|
-
const hex = Uint8Array.from(Buffer.from(km, 'hex'))
|
97
|
-
this.keyMaterial = km
|
98
|
-
const hash = sha1sync(hex)
|
99
|
-
this.keyId = Buffer.from(hash).toString('hex')
|
100
|
-
} else {
|
101
|
-
this.keyMaterial = null
|
102
|
-
this.keyId = 'null'
|
103
|
-
}
|
104
|
-
// console.trace('keyId', this.name, this.keyId)
|
39
|
+
async saveHeader (header) {
|
40
|
+
// each storage needs to add its own carCidMapCarCid to the header
|
41
|
+
if (this.secondary) { this.secondary.saveHeader(header) } // todo: await?
|
42
|
+
return await this.primary.saveHeader(header)
|
105
43
|
}
|
106
44
|
|
107
45
|
/**
|
@@ -113,33 +51,13 @@ export class Valet {
|
|
113
51
|
*/
|
114
52
|
async writeTransaction (innerBlockstore, cids) {
|
115
53
|
if (innerBlockstore.lastCid) {
|
116
|
-
|
117
|
-
|
118
|
-
// should we pass cids in instead of iterating frin innerBlockstore?
|
119
|
-
const newCar = await blocksToEncryptedCarBlock(innerBlockstore.lastCid, innerBlockstore, this.keyMaterial)
|
120
|
-
await this.parkCar(newCar.cid.toString(), newCar.bytes, cids)
|
121
|
-
} else {
|
122
|
-
const newCar = await blocksToCarBlock(innerBlockstore.lastCid, innerBlockstore)
|
123
|
-
await this.parkCar(newCar.cid.toString(), newCar.bytes, cids)
|
124
|
-
}
|
54
|
+
await parkCar(this.primary, innerBlockstore, cids)
|
55
|
+
if (this.secondary) await parkCar(this.secondary, innerBlockstore, cids)
|
125
56
|
} else {
|
126
57
|
throw new Error('missing lastCid for car header')
|
127
58
|
}
|
128
59
|
}
|
129
60
|
|
130
|
-
withDB = async dbWorkFun => {
|
131
|
-
if (!this.idb) {
|
132
|
-
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
133
|
-
upgrade (db, oldVersion, newVersion, transaction) {
|
134
|
-
if (oldVersion < 1) {
|
135
|
-
db.createObjectStore('cars')
|
136
|
-
}
|
137
|
-
}
|
138
|
-
})
|
139
|
-
}
|
140
|
-
return await dbWorkFun(this.idb)
|
141
|
-
}
|
142
|
-
|
143
61
|
/**
|
144
62
|
* Iterate over all blocks in the store.
|
145
63
|
*
|
@@ -155,216 +73,49 @@ export class Valet {
|
|
155
73
|
// }
|
156
74
|
}
|
157
75
|
|
158
|
-
setRootCarCid (cid) {
|
159
|
-
this.valetRootCarCid = cid
|
160
|
-
this.valetRoot = null
|
161
|
-
this.valetRootCid = null
|
162
|
-
}
|
163
|
-
|
164
|
-
// todo memoize this
|
165
|
-
async getCarCIDForCID (cid) {
|
166
|
-
// make a car reader for this.valetRootCarCid
|
167
|
-
if (!this.valetRootCarCid) return { result: null }
|
168
|
-
|
169
|
-
let indexNode
|
170
|
-
if (this.valetRoot) {
|
171
|
-
indexNode = this.valetRoot
|
172
|
-
} else {
|
173
|
-
const combinedReader = await this.getCombinedReader(this.valetRootCarCid)
|
174
|
-
if (!this.valetRootCid) {
|
175
|
-
const root = combinedReader.root.cid
|
176
|
-
// console.log('roots', this.instanceId, this.name, root, this.valetRootCarCid, this.valetRootCid)
|
177
|
-
this.valetRootCid = root
|
178
|
-
}
|
179
|
-
indexNode = await load(combinedReader, this.valetRootCid, {
|
180
|
-
blockHasher: blockOpts.hasher,
|
181
|
-
blockCodec: blockOpts.codec
|
182
|
-
})
|
183
|
-
}
|
184
|
-
|
185
|
-
const got = await indexNode.get(cid)
|
186
|
-
// console.log('getCarCIDForCID', cid, got)
|
187
|
-
return { result: got }
|
188
|
-
}
|
189
|
-
|
190
|
-
async getCombinedReader (carCid) {
|
191
|
-
let carMapReader
|
192
|
-
if (this.valetRootCarCid) {
|
193
|
-
// todo only need this if we are cold starting
|
194
|
-
carMapReader = await this.getCarReader(this.valetRootCarCid)
|
195
|
-
}
|
196
|
-
|
197
|
-
const theseValetCidBlocks = this.valetCidBlocks
|
198
|
-
// console.log('theseValetCidBlocks', theseValetCidBlocks)
|
199
|
-
const combinedReader = {
|
200
|
-
root: carMapReader?.root,
|
201
|
-
put: async (cid, bytes) => {
|
202
|
-
// console.log('mapPut', cid, bytes.length)
|
203
|
-
return await theseValetCidBlocks.put(cid, bytes)
|
204
|
-
},
|
205
|
-
get: async cid => {
|
206
|
-
// console.log('mapGet', cid)
|
207
|
-
try {
|
208
|
-
const got = await theseValetCidBlocks.get(cid)
|
209
|
-
return got.bytes
|
210
|
-
} catch (e) {
|
211
|
-
// console.log('get from car', cid, carMapReader)
|
212
|
-
if (!carMapReader) throw e
|
213
|
-
const bytes = await carMapReader.get(cid)
|
214
|
-
await theseValetCidBlocks.put(cid, bytes)
|
215
|
-
// console.log('mapGet', cid, bytes.length, bytes.constructor.name)
|
216
|
-
return bytes
|
217
|
-
}
|
218
|
-
}
|
219
|
-
}
|
220
|
-
return combinedReader
|
221
|
-
}
|
222
|
-
|
223
|
-
/**
|
224
|
-
*
|
225
|
-
* @param {string} carCid
|
226
|
-
* @param {*} value
|
227
|
-
*/
|
228
|
-
async parkCar (carCid, value, cids) {
|
229
|
-
// console.log('parkCar', this.instanceId, this.name, carCid, cids)
|
230
|
-
const combinedReader = await this.getCombinedReader(carCid)
|
231
|
-
const mapNode = await addCidsToCarIndex(
|
232
|
-
combinedReader,
|
233
|
-
this.valetRoot,
|
234
|
-
this.valetRootCid,
|
235
|
-
Array.from(cids).map(cid => ({ key: cid.toString(), value: carCid.toString() }))
|
236
|
-
)
|
237
|
-
|
238
|
-
this.valetRoot = mapNode
|
239
|
-
this.valetRootCid = mapNode.cid
|
240
|
-
// make a block set with all the cids of the map
|
241
|
-
const saveValetBlocks = new VMemoryBlockstore() // todo this blockstore should read from the last valetCid car also
|
242
|
-
|
243
|
-
for await (const cidx of mapNode.cids()) {
|
244
|
-
const bytes = await combinedReader.get(cidx)
|
245
|
-
saveValetBlocks.put(cidx, bytes)
|
246
|
-
}
|
247
|
-
let newValetCidCar
|
248
|
-
if (this.keyMaterial) {
|
249
|
-
newValetCidCar = await blocksToEncryptedCarBlock(this.valetRootCid, saveValetBlocks, this.keyMaterial)
|
250
|
-
} else {
|
251
|
-
newValetCidCar = await blocksToCarBlock(this.valetRootCid, saveValetBlocks)
|
252
|
-
}
|
253
|
-
// console.log('newValetCidCar', this.name, Math.floor(newValetCidCar.bytes.length / 1024))
|
254
|
-
await this.writeCars([
|
255
|
-
{
|
256
|
-
cid: carCid,
|
257
|
-
bytes: value,
|
258
|
-
replaces: null
|
259
|
-
},
|
260
|
-
{
|
261
|
-
cid: newValetCidCar.cid,
|
262
|
-
bytes: newValetCidCar.bytes,
|
263
|
-
replaces: null
|
264
|
-
// replaces: this.valetRootCarCid // todo
|
265
|
-
}
|
266
|
-
])
|
267
|
-
|
268
|
-
this.valetRootCarCid = newValetCidCar.cid // goes to clock
|
269
|
-
|
270
|
-
// console.log('parked car', carCid, value.length, Array.from(cids))
|
271
|
-
// upload to web3.storage if we have credentials
|
272
|
-
if (this.uploadFunction) {
|
273
|
-
if (this.alreadyEnqueued.has(carCid)) {
|
274
|
-
// console.log('already enqueued', carCid)
|
275
|
-
return
|
276
|
-
}
|
277
|
-
// don't await this, it will be done in the queue
|
278
|
-
// console.log('add to queue', carCid, value.length)
|
279
|
-
this.uploadQueue.push({ carCid, value })
|
280
|
-
this.alreadyEnqueued.add(carCid)
|
281
|
-
} else {
|
282
|
-
// console.log('no upload function', carCid, value.length, this.uploadFunction)
|
283
|
-
}
|
284
|
-
}
|
285
|
-
|
286
|
-
async writeCars (cars) {
|
287
|
-
return await this.withDB(async db => {
|
288
|
-
const tx = db.transaction(['cars'], 'readwrite')
|
289
|
-
for (const { cid, bytes, replaces } of cars) {
|
290
|
-
await tx.objectStore('cars').put(bytes, cid.toString())
|
291
|
-
// todo remove old maps
|
292
|
-
if (replaces) {
|
293
|
-
await tx.objectStore('cars').delete(replaces.toString())
|
294
|
-
}
|
295
|
-
}
|
296
|
-
return await tx.done
|
297
|
-
})
|
298
|
-
}
|
299
|
-
|
300
76
|
remoteBlockFunction = null
|
301
77
|
|
302
|
-
async
|
303
|
-
|
304
|
-
|
305
|
-
const
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
// todo cleanup types
|
318
|
-
useCodec = dagcbor
|
319
|
-
}
|
320
|
-
const decoded = await Block.decode({
|
321
|
-
...got,
|
322
|
-
codec: useCodec,
|
323
|
-
hasher: sha256
|
324
|
-
})
|
325
|
-
// console.log('decoded', decoded.value)
|
326
|
-
return decoded
|
327
|
-
}
|
328
|
-
const { blocks } = await blocksFromEncryptedCarBlock(roots[0], readerGetWithCodec, this.keyMaterial)
|
329
|
-
|
330
|
-
// last block is the root ??? todo
|
331
|
-
const rootBlock = blocks[blocks.length - 1]
|
332
|
-
|
333
|
-
return {
|
334
|
-
root: rootBlock,
|
335
|
-
get: async dataCID => {
|
336
|
-
// console.log('getCarReader dataCID', dataCID)
|
337
|
-
dataCID = dataCID.toString()
|
338
|
-
const block = blocks.find(b => b.cid.toString() === dataCID)
|
339
|
-
// console.log('getCarReader block', block)
|
340
|
-
if (block) {
|
341
|
-
return block.bytes
|
342
|
-
}
|
343
|
-
}
|
344
|
-
}
|
345
|
-
} else {
|
346
|
-
return {
|
347
|
-
root: reader.getRoots()[0],
|
348
|
-
get: async dataCID => {
|
349
|
-
const gotBlock = await reader.get(CID.parse(dataCID))
|
350
|
-
if (gotBlock) {
|
351
|
-
return gotBlock.bytes
|
78
|
+
async getValetBlock (dataCID) {
|
79
|
+
// console.log('getValetBlock primary', dataCID)
|
80
|
+
try {
|
81
|
+
const { block } = await this.primary.getLoaderBlock(dataCID)
|
82
|
+
return block
|
83
|
+
} catch (e) {
|
84
|
+
// console.log('getValetBlock error', e)
|
85
|
+
if (this.secondary) {
|
86
|
+
// console.log('getValetBlock secondary', dataCID)
|
87
|
+
try {
|
88
|
+
const { block, reader } = await this.secondary.getLoaderBlock(dataCID)
|
89
|
+
const cids = new Set()
|
90
|
+
for await (const { cid } of reader.entries()) {
|
91
|
+
// console.log(cid, bytes)
|
92
|
+
cids.add(cid.toString())
|
352
93
|
}
|
94
|
+
reader.get = reader.gat // some consumers prefer get
|
95
|
+
// console.log('replicating', reader.root)
|
96
|
+
reader.lastCid = reader.root.cid
|
97
|
+
await parkCar(this.primary, reader, [...cids])
|
98
|
+
return block
|
99
|
+
} catch (e) {
|
100
|
+
// console.log('getValetBlock secondary error', e)
|
353
101
|
}
|
354
102
|
}
|
355
103
|
}
|
356
104
|
}
|
105
|
+
}
|
357
106
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
107
|
+
async function parkCar (storage, innerBlockstore, cids) {
|
108
|
+
// console.log('parkCar', this.instanceId, this.name, carCid, cids)
|
109
|
+
let newCar
|
110
|
+
if (storage.keyMaterial) {
|
111
|
+
// console.log('encrypting car', innerBlockstore.label)
|
112
|
+
newCar = await blocksToEncryptedCarBlock(innerBlockstore.lastCid, innerBlockstore, storage.keyMaterial)
|
113
|
+
} else {
|
114
|
+
// todo should we pass cids in instead of iterating innerBlockstore?
|
115
|
+
newCar = await blocksToCarBlock(innerBlockstore.lastCid, innerBlockstore)
|
367
116
|
}
|
117
|
+
// console.log('new car', newCar.cid.toString())
|
118
|
+
return await storage.saveCar(newCar.cid.toString(), newCar.bytes, cids)
|
368
119
|
}
|
369
120
|
|
370
121
|
export const blocksToCarBlock = async (rootCids, blocks) => {
|
@@ -400,10 +151,11 @@ export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blo
|
|
400
151
|
const encryptionKey = Buffer.from(keyMaterial, 'hex')
|
401
152
|
const encryptedBlocks = []
|
402
153
|
const theCids = []
|
154
|
+
// console.trace('blocksToEncryptedCarBlock', blocks)
|
403
155
|
for (const { cid } of blocks.entries()) {
|
404
156
|
theCids.push(cid.toString())
|
405
157
|
}
|
406
|
-
// console.log('encrypting', theCids.length, 'blocks', theCids.includes(innerBlockStoreClockRootCid.toString()))
|
158
|
+
// console.log('encrypting', theCids.length, 'blocks', theCids.includes(innerBlockStoreClockRootCid.toString()), keyMaterial)
|
407
159
|
// console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
|
408
160
|
let last
|
409
161
|
for await (const block of encrypt({
|
@@ -426,7 +178,7 @@ export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blo
|
|
426
178
|
// { root, get, key, cache, chunker, hasher }
|
427
179
|
|
428
180
|
const memoizeDecryptedCarBlocks = new Map()
|
429
|
-
const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
181
|
+
export const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
430
182
|
if (memoizeDecryptedCarBlocks.has(cid.toString())) {
|
431
183
|
return memoizeDecryptedCarBlocks.get(cid.toString())
|
432
184
|
} else {
|
@@ -453,57 +205,3 @@ const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
|
453
205
|
return blocksPromise
|
454
206
|
}
|
455
207
|
}
|
456
|
-
|
457
|
-
const addCidsToCarIndex = async (blockstore, valetRoot, valetRootCid, bulkOperations) => {
|
458
|
-
let indexNode
|
459
|
-
if (valetRootCid) {
|
460
|
-
if (valetRoot) {
|
461
|
-
indexNode = valetRoot
|
462
|
-
} else {
|
463
|
-
indexNode = await load(blockstore, valetRootCid, { blockHasher: blockOpts.hasher, blockCodec: blockOpts.codec })
|
464
|
-
}
|
465
|
-
} else {
|
466
|
-
indexNode = await create(blockstore, {
|
467
|
-
bitWidth: 4,
|
468
|
-
bucketSize: 2,
|
469
|
-
blockHasher: blockOpts.hasher,
|
470
|
-
blockCodec: blockOpts.codec
|
471
|
-
})
|
472
|
-
}
|
473
|
-
// console.log('adding', bulkOperations.length, 'cids to index')
|
474
|
-
for (const { key, value } of bulkOperations) {
|
475
|
-
// console.log('adding', key, value)
|
476
|
-
await indexNode.set(key, value)
|
477
|
-
}
|
478
|
-
return indexNode
|
479
|
-
}
|
480
|
-
|
481
|
-
export class VMemoryBlockstore {
|
482
|
-
/** @type {Map<string, Uint8Array>} */
|
483
|
-
blocks = new Map()
|
484
|
-
instanceId = Math.random().toString(36).slice(2)
|
485
|
-
|
486
|
-
async get (cid) {
|
487
|
-
const bytes = this.blocks.get(cid.toString())
|
488
|
-
// console.log('getvm', bytes.constructor.name, this.instanceId, cid, bytes && bytes.length)
|
489
|
-
if (bytes.length === 253) {
|
490
|
-
// console.log('getvm', bytes.())
|
491
|
-
}
|
492
|
-
if (!bytes) throw new Error('block not found ' + cid.toString())
|
493
|
-
return { cid, bytes }
|
494
|
-
}
|
495
|
-
|
496
|
-
/**
|
497
|
-
* @param {import('../src/link').AnyLink} cid
|
498
|
-
* @param {Uint8Array} bytes
|
499
|
-
*/
|
500
|
-
async put (cid, bytes) {
|
501
|
-
this.blocks.set(cid.toString(), bytes)
|
502
|
-
}
|
503
|
-
|
504
|
-
* entries () {
|
505
|
-
for (const [str, bytes] of this.blocks) {
|
506
|
-
yield { cid: parse(str), bytes }
|
507
|
-
}
|
508
|
-
}
|
509
|
-
}
|
@@ -1,150 +0,0 @@
|
|
1
|
-
// @ts-ignore
|
2
|
-
import { useEffect, useState, useCallback, createContext } from 'react';
|
3
|
-
import { Fireproof, Index } from '@fireproof/core';
|
4
|
-
/**
|
5
|
-
@typedef {Object} FireproofCtxValue
|
6
|
-
@property {Function} addSubscriber - A function to add a subscriber with a label and function.
|
7
|
-
@property {Fireproof} database - An instance of the Fireproof class.
|
8
|
-
@property {Function} useLiveQuery - A hook to return a query result
|
9
|
-
@property {Function} useLiveDocument - A hook to return a live document
|
10
|
-
@property {boolean} ready - A boolean indicating whether the database is ready.
|
11
|
-
@param {string} label - A label for the subscriber.
|
12
|
-
@param {Function} fn - A function to be added as a subscriber.
|
13
|
-
@returns {void}
|
14
|
-
*/
|
15
|
-
export const FireproofCtx = createContext({
|
16
|
-
addSubscriber: () => { },
|
17
|
-
database: null,
|
18
|
-
ready: false
|
19
|
-
});
|
20
|
-
// const inboundSubscriberQueue = new Map()
|
21
|
-
let startedSetup = false;
|
22
|
-
let database;
|
23
|
-
const initializeDatabase = name => {
|
24
|
-
if (database)
|
25
|
-
return;
|
26
|
-
database = Fireproof.storage(name);
|
27
|
-
};
|
28
|
-
/**
|
29
|
-
@function useFireproof
|
30
|
-
React hook to initialize a Fireproof database, automatically saving and loading the clock.
|
31
|
-
You might need to import { nodePolyfills } from 'vite-plugin-node-polyfills' in your vite.config.ts
|
32
|
-
@deprecated - npm install @fireproof/react instead
|
33
|
-
@param {string} name - The path to the database file
|
34
|
-
@param {function(database): void} [defineDatabaseFn] - Synchronous function that defines the database, run this before any async calls
|
35
|
-
@param {function(database): Promise<void>} [setupDatabaseFn] - Asynchronous function that sets up the database, run this to load fixture data etc
|
36
|
-
@returns {FireproofCtxValue} { useLiveQuery, useLiveDocument, database, ready }
|
37
|
-
*/
|
38
|
-
export function useFireproof(name = 'useFireproof', defineDatabaseFn = () => { }, setupDatabaseFn = async () => { }) {
|
39
|
-
const [ready, setReady] = useState(false);
|
40
|
-
initializeDatabase(name);
|
41
|
-
/**
|
42
|
-
* @deprecated - use database.subscribe instead
|
43
|
-
*/
|
44
|
-
const addSubscriber = (label, fn) => {
|
45
|
-
// todo test that the label is not needed
|
46
|
-
return database.registerListener(fn);
|
47
|
-
// inboundSubscriberQueue.set(label, fn)
|
48
|
-
};
|
49
|
-
useEffect(() => {
|
50
|
-
const doSetup = async () => {
|
51
|
-
if (ready)
|
52
|
-
return;
|
53
|
-
if (startedSetup)
|
54
|
-
return;
|
55
|
-
startedSetup = true;
|
56
|
-
defineDatabaseFn(database); // define indexes before querying them
|
57
|
-
if (database.clock.length === 0) {
|
58
|
-
await setupDatabaseFn(database);
|
59
|
-
}
|
60
|
-
setReady(true);
|
61
|
-
};
|
62
|
-
doSetup();
|
63
|
-
}, [ready]);
|
64
|
-
function useLiveDocument(initialDoc) {
|
65
|
-
const id = initialDoc._id;
|
66
|
-
const [doc, setDoc] = useState(initialDoc);
|
67
|
-
const saveDoc = async (newDoc) => {
|
68
|
-
await database.put({ _id: id, ...newDoc });
|
69
|
-
};
|
70
|
-
const refreshDoc = useCallback(async () => {
|
71
|
-
// todo add option for mvcc checks
|
72
|
-
const got = await database.get(id).catch(() => initialDoc);
|
73
|
-
setDoc(got);
|
74
|
-
}, [id, initialDoc]);
|
75
|
-
useEffect(() => database.subscribe(change => {
|
76
|
-
if (change.find(c => c.key === id)) {
|
77
|
-
refreshDoc(); // todo use change.value
|
78
|
-
}
|
79
|
-
}), [id, refreshDoc]);
|
80
|
-
useEffect(() => {
|
81
|
-
refreshDoc();
|
82
|
-
}, []);
|
83
|
-
return [doc, saveDoc];
|
84
|
-
}
|
85
|
-
function useLiveQuery(mapFn, query = null, initialRows = []) {
|
86
|
-
const [rows, setRows] = useState({ rows: initialRows, proof: {} });
|
87
|
-
const [index, setIndex] = useState(null);
|
88
|
-
const refreshRows = useCallback(async () => {
|
89
|
-
if (!index)
|
90
|
-
return;
|
91
|
-
const got = await index.query(query || {});
|
92
|
-
setRows(got);
|
93
|
-
}, [index, JSON.stringify(query)]);
|
94
|
-
useEffect(() => {
|
95
|
-
// todo listen to index changes
|
96
|
-
return database.subscribe(() => {
|
97
|
-
refreshRows();
|
98
|
-
});
|
99
|
-
}, [refreshRows]);
|
100
|
-
useEffect(() => {
|
101
|
-
refreshRows();
|
102
|
-
}, [index]);
|
103
|
-
useEffect(() => {
|
104
|
-
const index = new Index(database, null, mapFn); // this should only be created once
|
105
|
-
setIndex(index);
|
106
|
-
}, [mapFn.toString()]);
|
107
|
-
return rows;
|
108
|
-
}
|
109
|
-
return {
|
110
|
-
addSubscriber,
|
111
|
-
useLiveQuery,
|
112
|
-
useLiveDocument,
|
113
|
-
database,
|
114
|
-
ready
|
115
|
-
};
|
116
|
-
}
|
117
|
-
// const husherMap = new Map()
|
118
|
-
// const husher = (id, workFn, ms) => {
|
119
|
-
// if (!husherMap.has(id)) {
|
120
|
-
// const start = Date.now()
|
121
|
-
// husherMap.set(
|
122
|
-
// id,
|
123
|
-
// workFn().finally(() => setTimeout(() => husherMap.delete(id), ms - (Date.now() - start)))
|
124
|
-
// )
|
125
|
-
// }
|
126
|
-
// return husherMap.get(id)
|
127
|
-
// }
|
128
|
-
// const hushed =
|
129
|
-
// (id, workFn, ms) =>
|
130
|
-
// (...args) =>
|
131
|
-
// husher(id, () => workFn(...args), ms)
|
132
|
-
// let storageSupported = false
|
133
|
-
// try {
|
134
|
-
// storageSupported = window.localStorage && true
|
135
|
-
// } catch (e) {}
|
136
|
-
// export function localGet (key) {
|
137
|
-
// if (storageSupported) {
|
138
|
-
// return localStorage && localStorage.getItem(key)
|
139
|
-
// }
|
140
|
-
// }
|
141
|
-
// function localSet (key, value) {
|
142
|
-
// if (storageSupported) {
|
143
|
-
// return localStorage && localStorage.setItem(key, value)
|
144
|
-
// }
|
145
|
-
// }
|
146
|
-
// function localRemove(key) {
|
147
|
-
// if (storageSupported) {
|
148
|
-
// return localStorage && localStorage.removeItem(key)
|
149
|
-
// }
|
150
|
-
// }
|
package/dist/src/crypto-poly.js
DELETED
package/dist/src/link.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export {};
|