@fireproof/core 0.0.5 → 0.0.7
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/hooks/use-fireproof.ts +0 -2
- package/package.json +6 -5
- package/src/blockstore.js +14 -10
- package/src/clock.js +29 -50
- package/src/db-index.js +109 -69
- package/src/fireproof.js +64 -27
- package/src/listener.js +0 -6
- package/src/prolly.js +68 -52
- package/src/valet.js +6 -62
- package/{src → test}/block.js +6 -6
- package/test/clock.test.js +91 -170
- package/test/db-index.test.js +10 -8
- package/test/fireproof.test.js +84 -16
- package/test/fulltext.test.js +66 -0
- package/test/helpers.js +1 -1
- package/test/prolly.test.js +15 -28
- package/test/proofs.test.js +53 -0
- package/test/reproduce-fixture-bug.test.js +65 -0
- package/hooks/use-fireproof.md +0 -149
- package/scripts/propernames/gen.sh +0 -3
- package/scripts/randomcid.js +0 -12
- package/scripts/words/gen.js +0 -55
package/src/fireproof.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
import { put, get, getAll, eventsSince } from './prolly.js'
|
2
|
-
import
|
1
|
+
import { vis, put, get, getAll, eventsSince } from './prolly.js'
|
2
|
+
import TransactionBlockstore, { doTransaction } from './blockstore.js'
|
3
|
+
import charwise from 'charwise'
|
3
4
|
|
4
5
|
// const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
5
6
|
|
@@ -26,16 +27,17 @@ export default class Fireproof {
|
|
26
27
|
* @static
|
27
28
|
* @returns {Fireproof} - a new Fireproof instance
|
28
29
|
*/
|
29
|
-
static storage = () => {
|
30
|
-
return new Fireproof(new
|
30
|
+
static storage = (name) => {
|
31
|
+
return new Fireproof(new TransactionBlockstore(name), [], { name })
|
31
32
|
}
|
32
33
|
|
33
|
-
constructor (blocks, clock, config
|
34
|
+
constructor (blocks, clock, config, authCtx = {}) {
|
35
|
+
this.name = config?.name || 'global'
|
36
|
+
this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`
|
34
37
|
this.blocks = blocks
|
35
38
|
this.clock = clock
|
36
39
|
this.config = config
|
37
40
|
this.authCtx = authCtx
|
38
|
-
this.instanceId = 'fp.' + Math.random().toString(36).substring(2, 7)
|
39
41
|
}
|
40
42
|
|
41
43
|
/**
|
@@ -49,7 +51,7 @@ export default class Fireproof {
|
|
49
51
|
*/
|
50
52
|
snapshot (clock) {
|
51
53
|
// how to handle listeners, views, and config?
|
52
|
-
// todo needs a test for
|
54
|
+
// todo needs a test for listeners, views, and config
|
53
55
|
return new Fireproof(this.blocks, clock || this.clock)
|
54
56
|
}
|
55
57
|
|
@@ -89,11 +91,11 @@ export default class Fireproof {
|
|
89
91
|
*/
|
90
92
|
async changesSince (event) {
|
91
93
|
// console.log('changesSince', this.instanceId, event, this.clock)
|
92
|
-
let rows
|
94
|
+
let rows, dataCIDs, clockCIDs
|
93
95
|
if (event) {
|
94
96
|
const resp = await eventsSince(this.blocks, this.clock, event)
|
95
97
|
const docsMap = new Map()
|
96
|
-
for (const { key, type, value } of resp) {
|
98
|
+
for (const { key, type, value } of resp.result.map(decodeEvent)) {
|
97
99
|
if (type === 'del') {
|
98
100
|
docsMap.set(key, { key, del: true })
|
99
101
|
} else {
|
@@ -101,12 +103,19 @@ export default class Fireproof {
|
|
101
103
|
}
|
102
104
|
}
|
103
105
|
rows = Array.from(docsMap.values())
|
106
|
+
clockCIDs = resp.cids
|
104
107
|
// console.log('change rows', this.instanceId, rows)
|
105
108
|
} else {
|
106
|
-
|
109
|
+
const allResp = await getAll(this.blocks, this.clock)
|
110
|
+
rows = allResp.result.map(({ key, value }) => (decodeEvent({ key, value })))
|
111
|
+
dataCIDs = allResp.cids
|
107
112
|
// console.log('dbdoc rows', this.instanceId, rows)
|
108
113
|
}
|
109
|
-
return {
|
114
|
+
return {
|
115
|
+
rows,
|
116
|
+
clock: this.clock,
|
117
|
+
proof: { data: await cidsToProof(dataCIDs), clock: await cidsToProof(clockCIDs) }
|
118
|
+
}
|
110
119
|
}
|
111
120
|
|
112
121
|
/**
|
@@ -158,7 +167,7 @@ export default class Fireproof {
|
|
158
167
|
* @memberof Fireproof
|
159
168
|
* @instance
|
160
169
|
*/
|
161
|
-
async put ({ _id, ...doc }) {
|
170
|
+
async put ({ _id, _proof, ...doc }) {
|
162
171
|
const id = _id || 'f' + Math.random().toString(36).slice(2)
|
163
172
|
await this.#runValidation({ _id: id, ...doc })
|
164
173
|
return await this.#putToProllyTree({ key: id, value: doc }, doc._clock)
|
@@ -192,12 +201,13 @@ export default class Fireproof {
|
|
192
201
|
* @param {Object<{key : string, value: any}>} event - the event to add
|
193
202
|
* @returns {Object<{ id: string, clock: CID[] }>} - The result of adding the event to storage
|
194
203
|
*/
|
195
|
-
async #putToProllyTree (
|
204
|
+
async #putToProllyTree (decodedEvent, clock = null) {
|
205
|
+
const event = encodeEvent(decodedEvent)
|
196
206
|
if (clock && JSON.stringify(clock) !== JSON.stringify(this.clock)) {
|
197
207
|
// we need to check and see what version of the document exists at the clock specified
|
198
208
|
// if it is the same as the one we are trying to put, then we can proceed
|
199
209
|
const resp = await eventsSince(this.blocks, this.clock, event.value._clock)
|
200
|
-
const missedChange = resp.find(({ key }) => key === event.key)
|
210
|
+
const missedChange = resp.result.find(({ key }) => key === event.key)
|
201
211
|
if (missedChange) {
|
202
212
|
throw new Error('MVCC conflict, document is changed, please reload the document and try again.')
|
203
213
|
}
|
@@ -212,9 +222,13 @@ export default class Fireproof {
|
|
212
222
|
throw new Error('failed to put at storage layer')
|
213
223
|
}
|
214
224
|
this.clock = result.head // do we want to do this as a finally block
|
215
|
-
|
216
|
-
|
217
|
-
|
225
|
+
await this.#notifyListeners([decodedEvent]) // this type is odd
|
226
|
+
return {
|
227
|
+
id: decodedEvent.key,
|
228
|
+
clock: this.clock,
|
229
|
+
proof: { data: await cidsToProof(result.cids), clock: await cidsToProof(result.clockCIDs) }
|
230
|
+
}
|
231
|
+
// todo should include additions (or split clock)
|
218
232
|
}
|
219
233
|
|
220
234
|
// /**
|
@@ -251,21 +265,27 @@ export default class Fireproof {
|
|
251
265
|
* @instance
|
252
266
|
*/
|
253
267
|
async get (key, opts = {}) {
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
} else {
|
258
|
-
got = await get(this.blocks, this.clock, key)
|
259
|
-
}
|
268
|
+
const clock = opts.clock || this.clock
|
269
|
+
const resp = await get(this.blocks, clock, charwise.encode(key))
|
270
|
+
|
260
271
|
// this tombstone is temporary until we can get the prolly tree to delete
|
261
|
-
if (
|
272
|
+
if (!resp || resp.result === null) {
|
262
273
|
throw new Error('Not found')
|
263
274
|
}
|
275
|
+
const doc = resp.result
|
264
276
|
if (opts.mvcc === true) {
|
265
|
-
|
277
|
+
doc._clock = this.clock
|
278
|
+
}
|
279
|
+
doc._proof = {
|
280
|
+
data: await cidsToProof(resp.cids),
|
281
|
+
clock: this.clock
|
266
282
|
}
|
267
|
-
|
268
|
-
return
|
283
|
+
doc._id = key
|
284
|
+
return doc
|
285
|
+
}
|
286
|
+
|
287
|
+
async * vis () {
|
288
|
+
return yield * vis(this.blocks, this.clock)
|
269
289
|
}
|
270
290
|
|
271
291
|
setCarUploader (carUploaderFn) {
|
@@ -279,3 +299,20 @@ export default class Fireproof {
|
|
279
299
|
this.blocks.valet.remoteBlockFunction = remoteBlockReaderFn
|
280
300
|
}
|
281
301
|
}
|
302
|
+
|
303
|
+
export async function cidsToProof (cids) {
|
304
|
+
if (!cids || !cids.all) return []
|
305
|
+
const all = await cids.all()
|
306
|
+
return [...all].map((cid) => cid.toString())
|
307
|
+
}
|
308
|
+
|
309
|
+
function decodeEvent (event) {
|
310
|
+
const decodedKey = charwise.decode(event.key)
|
311
|
+
return { ...event, key: decodedKey }
|
312
|
+
}
|
313
|
+
|
314
|
+
function encodeEvent (event) {
|
315
|
+
if (!(event && event.key)) return
|
316
|
+
const encodedKey = charwise.encode(event.key)
|
317
|
+
return { ...event, key: encodedKey }
|
318
|
+
}
|
package/src/listener.js
CHANGED
@@ -11,12 +11,6 @@
|
|
11
11
|
|
12
12
|
export default class Listener {
|
13
13
|
#subcribers = new Map()
|
14
|
-
|
15
|
-
// todo code review if there is a better way that doesn't create a circular reference
|
16
|
-
// because otherwise we need to document that the user must call stopListening
|
17
|
-
// or else the listener will never be garbage collected
|
18
|
-
// maybe we can use WeakRef on the db side
|
19
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef
|
20
14
|
#doStopListening = null
|
21
15
|
|
22
16
|
constructor (database, routingFn) {
|
package/src/prolly.js
CHANGED
@@ -3,16 +3,15 @@ import {
|
|
3
3
|
EventFetcher,
|
4
4
|
EventBlock,
|
5
5
|
findCommonAncestorWithSortedEvents,
|
6
|
-
|
6
|
+
findEventsToSync
|
7
7
|
} from './clock.js'
|
8
8
|
import { create, load } from 'prolly-trees/map'
|
9
|
+
// import { create, load } from '../../../../prolly-trees/src/map.js'
|
10
|
+
import { nocache as cache } from 'prolly-trees/cache'
|
11
|
+
import { CIDCounter, bf, simpleCompare as compare } from 'prolly-trees/utils'
|
9
12
|
import * as codec from '@ipld/dag-cbor'
|
10
13
|
import { sha256 as hasher } from 'multiformats/hashes/sha2'
|
11
|
-
import { MemoryBlockstore, MultiBlockFetcher } from './block.js'
|
12
14
|
import { doTransaction } from './blockstore.js'
|
13
|
-
|
14
|
-
import { nocache as cache } from 'prolly-trees/cache'
|
15
|
-
import { bf, simpleCompare as compare } from 'prolly-trees/utils'
|
16
15
|
import { create as createBlock } from 'multiformats/block'
|
17
16
|
const opts = { cache, chunker: bf(3), codec, hasher, compare }
|
18
17
|
|
@@ -22,9 +21,18 @@ const withLog = async (label, fn) => {
|
|
22
21
|
return resp
|
23
22
|
}
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
// should also return a CIDCounter
|
25
|
+
export const makeGetBlock = (blocks) => {
|
26
|
+
// const cids = new CIDCounter() // this could be used for proofs of mutations
|
27
|
+
const getBlockFn = async (address) => {
|
28
|
+
const { cid, bytes } = await withLog(address, () => blocks.get(address))
|
29
|
+
// cids.add({ address: cid })
|
30
|
+
return createBlock({ cid, bytes, hasher, codec })
|
31
|
+
}
|
32
|
+
return {
|
33
|
+
// cids,
|
34
|
+
getBlock: getBlockFn
|
35
|
+
}
|
28
36
|
}
|
29
37
|
|
30
38
|
/**
|
@@ -46,17 +54,17 @@ const makeGetBlock = (blocks) => async (address) => {
|
|
46
54
|
* event: CID[]
|
47
55
|
* }>}
|
48
56
|
*/
|
49
|
-
async function createAndSaveNewEvent (
|
57
|
+
async function createAndSaveNewEvent ({
|
50
58
|
inBlocks,
|
51
|
-
mblocks,
|
52
|
-
getBlock,
|
53
59
|
bigPut,
|
54
60
|
root,
|
55
|
-
|
61
|
+
event: inEvent,
|
56
62
|
head,
|
57
63
|
additions,
|
58
64
|
removals = []
|
59
|
-
) {
|
65
|
+
}) {
|
66
|
+
let cids
|
67
|
+
const { key, value, del } = inEvent
|
60
68
|
const data = {
|
61
69
|
type: 'put',
|
62
70
|
root: {
|
@@ -77,32 +85,33 @@ async function createAndSaveNewEvent (
|
|
77
85
|
|
78
86
|
const event = await EventBlock.create(data, head)
|
79
87
|
bigPut(event)
|
80
|
-
head = await advance(inBlocks, head, event.cid)
|
88
|
+
;({ head, cids } = await advance(inBlocks, head, event.cid))
|
81
89
|
|
82
90
|
return {
|
83
91
|
root,
|
84
92
|
additions,
|
85
93
|
removals,
|
86
94
|
head,
|
95
|
+
clockCIDs: cids,
|
87
96
|
event
|
88
97
|
}
|
89
98
|
}
|
90
99
|
|
91
100
|
const makeGetAndPutBlock = (inBlocks) => {
|
92
|
-
const mblocks = new MemoryBlockstore()
|
93
|
-
const blocks = new MultiBlockFetcher(mblocks, inBlocks)
|
94
|
-
const getBlock = makeGetBlock(
|
101
|
+
// const mblocks = new MemoryBlockstore()
|
102
|
+
// const blocks = new MultiBlockFetcher(mblocks, inBlocks)
|
103
|
+
const { getBlock, cids } = makeGetBlock(inBlocks)
|
95
104
|
const put = inBlocks.put.bind(inBlocks)
|
96
105
|
const bigPut = async (block, additions) => {
|
97
106
|
// console.log('bigPut', block.cid.toString())
|
98
107
|
const { cid, bytes } = block
|
99
108
|
put(cid, bytes)
|
100
|
-
mblocks.putSync(cid, bytes)
|
109
|
+
// mblocks.putSync(cid, bytes)
|
101
110
|
if (additions) {
|
102
111
|
additions.set(cid.toString(), block)
|
103
112
|
}
|
104
113
|
}
|
105
|
-
return { getBlock, bigPut,
|
114
|
+
return { getBlock, bigPut, blocks: inBlocks, cids }
|
106
115
|
}
|
107
116
|
|
108
117
|
const bulkFromEvents = (sorted) =>
|
@@ -132,7 +141,7 @@ const prollyRootFromAncestor = async (events, ancestor, getBlock) => {
|
|
132
141
|
/**
|
133
142
|
* Put a value (a CID) for the given key. If the key exists it's value is overwritten.
|
134
143
|
*
|
135
|
-
* @param {import('
|
144
|
+
* @param {import('../test/block.js').BlockFetcher} blocks Bucket block storage.
|
136
145
|
* @param {import('./clock').EventLink<EventData>[]} head Merkle clock head.
|
137
146
|
* @param {string} key The key of the value to put.
|
138
147
|
* @param {CID} value The value to put.
|
@@ -140,7 +149,7 @@ const prollyRootFromAncestor = async (events, ancestor, getBlock) => {
|
|
140
149
|
* @returns {Promise<Result>}
|
141
150
|
*/
|
142
151
|
export async function put (inBlocks, head, event, options) {
|
143
|
-
const { getBlock, bigPut,
|
152
|
+
const { getBlock, bigPut, blocks } = makeGetAndPutBlock(inBlocks)
|
144
153
|
|
145
154
|
// If the head is empty, we create a new event and return the root and addition blocks
|
146
155
|
if (!head.length) {
|
@@ -150,12 +159,14 @@ export async function put (inBlocks, head, event, options) {
|
|
150
159
|
root = await node.block
|
151
160
|
bigPut(root, additions)
|
152
161
|
}
|
153
|
-
return createAndSaveNewEvent(inBlocks,
|
162
|
+
return createAndSaveNewEvent({ inBlocks, bigPut, root, event, head, additions: Array.from(additions.values()) })
|
154
163
|
}
|
155
164
|
|
156
165
|
// Otherwise, we find the common ancestor and update the root and other blocks
|
157
166
|
const events = new EventFetcher(blocks)
|
167
|
+
// this is returning more events than necessary
|
158
168
|
const { ancestor, sorted } = await findCommonAncestorWithSortedEvents(events, head)
|
169
|
+
// console.log('sorted', JSON.stringify(sorted.map(({ value: { data: { key, value } } }) => ({ key, value }))))
|
159
170
|
const prollyRootNode = await prollyRootFromAncestor(events, ancestor, getBlock)
|
160
171
|
|
161
172
|
const bulkOperations = bulkFromEvents(sorted)
|
@@ -166,23 +177,21 @@ export async function put (inBlocks, head, event, options) {
|
|
166
177
|
for (const nb of newBlocks) {
|
167
178
|
bigPut(nb, additions)
|
168
179
|
}
|
169
|
-
|
170
|
-
return createAndSaveNewEvent(
|
180
|
+
// additions are new blocks
|
181
|
+
return createAndSaveNewEvent({
|
171
182
|
inBlocks,
|
172
|
-
mblocks,
|
173
|
-
getBlock,
|
174
183
|
bigPut,
|
175
|
-
prollyRootBlock,
|
184
|
+
root: prollyRootBlock,
|
176
185
|
event,
|
177
186
|
head,
|
178
|
-
Array.from(additions.values()) /*, Array.from(removals.values()) */
|
179
|
-
)
|
187
|
+
additions: Array.from(additions.values()) /*, todo? Array.from(removals.values()) */
|
188
|
+
})
|
180
189
|
}
|
181
190
|
|
182
191
|
/**
|
183
192
|
* Determine the effective prolly root given the current merkle clock head.
|
184
193
|
*
|
185
|
-
* @param {import('
|
194
|
+
* @param {import('../test/block.js').BlockFetcher} blocks Bucket block storage.
|
186
195
|
* @param {import('./clock').EventLink<EventData>[]} head Merkle clock head.
|
187
196
|
*/
|
188
197
|
export async function root (inBlocks, head) {
|
@@ -197,23 +206,22 @@ export async function root (inBlocks, head) {
|
|
197
206
|
// Perform bulk operations (put or delete) for each event in the sorted array
|
198
207
|
const bulkOperations = bulkFromEvents(sorted)
|
199
208
|
const { root: newProllyRootNode, blocks: newBlocks } = await prollyRootNode.bulk(bulkOperations)
|
200
|
-
const prollyRootBlock = await newProllyRootNode.block
|
201
|
-
// console.log('
|
209
|
+
// const prollyRootBlock = await newProllyRootNode.block
|
210
|
+
// console.log('newBlocks', newBlocks.map((nb) => nb.cid.toString()))
|
202
211
|
// todo maybe these should go to a temp blockstore?
|
203
212
|
await doTransaction('root', inBlocks, async (transactionBlockstore) => {
|
204
213
|
const { bigPut } = makeGetAndPutBlock(transactionBlockstore)
|
205
214
|
for (const nb of newBlocks) {
|
206
215
|
bigPut(nb)
|
207
216
|
}
|
208
|
-
bigPut(prollyRootBlock)
|
217
|
+
// bigPut(prollyRootBlock)
|
209
218
|
})
|
210
|
-
|
211
|
-
return newProllyRootNode // .block).cid // todo return live object not cid
|
219
|
+
return { cids: events.cids, node: newProllyRootNode }
|
212
220
|
}
|
213
221
|
|
214
222
|
/**
|
215
223
|
* Get the list of events not known by the `since` event
|
216
|
-
* @param {import('
|
224
|
+
* @param {import('../test/block.js').BlockFetcher} blocks Bucket block storage.
|
217
225
|
* @param {import('./clock').EventLink<EventData>[]} head Merkle clock head.
|
218
226
|
* @param {import('./clock').EventLink<EventData>} since Event to compare against.
|
219
227
|
* @returns {Promise<import('./clock').EventLink<EventData>[]>}
|
@@ -223,17 +231,13 @@ export async function eventsSince (blocks, head, since) {
|
|
223
231
|
throw new Error('no head')
|
224
232
|
}
|
225
233
|
const sinceHead = [...since, ...head]
|
226
|
-
const unknownSorted3 = await
|
227
|
-
|
228
|
-
sinceHead,
|
229
|
-
await findCommonAncestorWithSortedEvents(blocks, sinceHead)
|
230
|
-
)
|
231
|
-
return unknownSorted3.map(({ value: { data } }) => data)
|
234
|
+
const { cids, events: unknownSorted3 } = await findEventsToSync(blocks, sinceHead)
|
235
|
+
return { clockCIDs: cids, result: unknownSorted3.map(({ value: { data } }) => data) }
|
232
236
|
}
|
233
237
|
|
234
238
|
/**
|
235
239
|
*
|
236
|
-
* @param {import('
|
240
|
+
* @param {import('../test/block.js').BlockFetcher} blocks Bucket block storage.
|
237
241
|
* @param {import('./clock').EventLink<EventData>[]} head Merkle clock head.
|
238
242
|
*
|
239
243
|
* @returns {Promise<import('./prolly').Entry[]>}
|
@@ -243,24 +247,36 @@ export async function getAll (blocks, head) {
|
|
243
247
|
// todo use the root node left around from put, etc
|
244
248
|
// move load to a central place
|
245
249
|
if (!head.length) {
|
246
|
-
return []
|
250
|
+
return { clockCIDs: new CIDCounter(), cids: new CIDCounter(), result: [] }
|
247
251
|
}
|
248
|
-
const prollyRootNode = await root(blocks, head)
|
249
|
-
const { result } = await prollyRootNode.getAllEntries()
|
250
|
-
return result.map(({ key, value }) => ({ key, value }))
|
252
|
+
const { node: prollyRootNode, cids: clockCIDs } = await root(blocks, head)
|
253
|
+
const { result, cids } = await prollyRootNode.getAllEntries() // todo params
|
254
|
+
return { clockCIDs, cids, result: result.map(({ key, value }) => ({ key, value })) }
|
251
255
|
}
|
252
256
|
|
253
257
|
/**
|
254
|
-
* @param {import('
|
258
|
+
* @param {import('../test/block.js').BlockFetcher} blocks Bucket block storage.
|
255
259
|
* @param {import('./clock').EventLink<EventData>[]} head Merkle clock head.
|
256
260
|
* @param {string} key The key of the value to retrieve.
|
257
261
|
*/
|
258
262
|
export async function get (blocks, head, key) {
|
259
263
|
// instead pass root from db? and always update on change
|
260
264
|
if (!head.length) {
|
261
|
-
return null
|
265
|
+
return { cids: new CIDCounter(), result: null }
|
266
|
+
}
|
267
|
+
const { node: prollyRootNode, cids: clockCIDs } = await root(blocks, head)
|
268
|
+
const { result, cids } = await prollyRootNode.get(key)
|
269
|
+
return { result, cids, clockCIDs }
|
270
|
+
}
|
271
|
+
|
272
|
+
export async function * vis (blocks, head) {
|
273
|
+
if (!head.length) {
|
274
|
+
return { cids: new CIDCounter(), result: null }
|
275
|
+
}
|
276
|
+
const { node: prollyRootNode, cids } = await root(blocks, head)
|
277
|
+
const lines = []
|
278
|
+
for await (const line of prollyRootNode.vis()) {
|
279
|
+
yield line
|
262
280
|
}
|
263
|
-
|
264
|
-
const { result } = await prollyRootNode.get(key)
|
265
|
-
return result
|
281
|
+
return { vis: lines.join('\n'), cids }
|
266
282
|
}
|
package/src/valet.js
CHANGED
@@ -3,17 +3,8 @@ import { CID } from 'multiformats/cid'
|
|
3
3
|
import { openDB } from 'idb'
|
4
4
|
import cargoQueue from 'async/cargoQueue.js'
|
5
5
|
|
6
|
-
// const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
7
|
-
// let storageSupported = false
|
8
|
-
// try {
|
9
|
-
// storageSupported = window.localStorage && true
|
10
|
-
// } catch (e) {}
|
11
|
-
// const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
12
|
-
|
13
6
|
export default class Valet {
|
14
|
-
|
15
|
-
#cidToCar = new Map() // cid to car
|
16
|
-
#db = null
|
7
|
+
idb = null
|
17
8
|
#uploadQueue = null
|
18
9
|
#alreadyEnqueued = new Set()
|
19
10
|
|
@@ -23,7 +14,8 @@ export default class Valet {
|
|
23
14
|
*/
|
24
15
|
uploadFunction = null
|
25
16
|
|
26
|
-
constructor () {
|
17
|
+
constructor (name = 'default') {
|
18
|
+
this.name = name
|
27
19
|
this.#uploadQueue = cargoQueue(async (tasks, callback) => {
|
28
20
|
console.log(
|
29
21
|
'queue worker',
|
@@ -59,9 +51,8 @@ export default class Valet {
|
|
59
51
|
}
|
60
52
|
|
61
53
|
withDB = async (dbWorkFun) => {
|
62
|
-
|
63
|
-
|
64
|
-
this.#db = await openDB('valet', 2, {
|
54
|
+
if (!this.idb) {
|
55
|
+
this.idb = await openDB(`fp.${this.name}.valet`, 2, {
|
65
56
|
upgrade (db, oldVersion, newVersion, transaction) {
|
66
57
|
if (oldVersion < 1) {
|
67
58
|
db.createObjectStore('cars') // todo use database name
|
@@ -75,7 +66,7 @@ export default class Valet {
|
|
75
66
|
}
|
76
67
|
})
|
77
68
|
}
|
78
|
-
return await dbWorkFun(this
|
69
|
+
return await dbWorkFun(this.idb)
|
79
70
|
}
|
80
71
|
|
81
72
|
/**
|
@@ -84,11 +75,6 @@ export default class Valet {
|
|
84
75
|
* @param {*} value
|
85
76
|
*/
|
86
77
|
async parkCar (carCid, value, cids) {
|
87
|
-
// this.#cars.set(carCid, value)
|
88
|
-
// for (const cid of cids) {
|
89
|
-
// this.#cidToCar.set(cid, carCid)
|
90
|
-
// }
|
91
|
-
|
92
78
|
await this.withDB(async (db) => {
|
93
79
|
const tx = db.transaction(['cars', 'cidToCar'], 'readwrite')
|
94
80
|
await tx.objectStore('cars').put(value, carCid)
|
@@ -130,45 +116,3 @@ export default class Valet {
|
|
130
116
|
})
|
131
117
|
}
|
132
118
|
}
|
133
|
-
|
134
|
-
// export class MemoryValet {
|
135
|
-
// #cars = new Map() // cars by cid
|
136
|
-
// #cidToCar = new Map() // cid to car
|
137
|
-
|
138
|
-
// /**
|
139
|
-
// *
|
140
|
-
// * @param {string} carCid
|
141
|
-
// * @param {*} value
|
142
|
-
// */
|
143
|
-
// async parkCar (carCid, value, cids) {
|
144
|
-
// this.#cars.set(carCid, value)
|
145
|
-
// for (const cid of cids) {
|
146
|
-
// this.#cidToCar.set(cid, carCid)
|
147
|
-
// }
|
148
|
-
// }
|
149
|
-
|
150
|
-
// async getBlock (dataCID) {
|
151
|
-
// return await this.#valetGet(dataCID)
|
152
|
-
// }
|
153
|
-
|
154
|
-
// /**
|
155
|
-
// * Internal function to load blocks from persistent storage.
|
156
|
-
// * Currently it just searches all the cars for the block, but in the future
|
157
|
-
// * we need to index the block CIDs to the cars, and reference that to find the block.
|
158
|
-
// * This index will also allow us to use accelerator links for the gateway when needed.
|
159
|
-
// * It can itself be a prolly tree...
|
160
|
-
// * @param {string} cid
|
161
|
-
// * @returns {Promise<Uint8Array|undefined>}
|
162
|
-
// */
|
163
|
-
// #valetGet = async (cid) => {
|
164
|
-
// const carCid = this.#cidToCar.get(cid)
|
165
|
-
// if (carCid) {
|
166
|
-
// const carBytes = this.#cars.get(carCid)
|
167
|
-
// const reader = await CarReader.fromBytes(carBytes)
|
168
|
-
// const gotBlock = await reader.get(CID.parse(cid))
|
169
|
-
// if (gotBlock) {
|
170
|
-
// return gotBlock.bytes
|
171
|
-
// }
|
172
|
-
// }
|
173
|
-
// }
|
174
|
-
// }
|
package/{src → test}/block.js
RENAMED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { parse } from 'multiformats/link'
|
2
2
|
|
3
3
|
/**
|
4
|
-
* @typedef {{ cid: import('
|
5
|
-
* @typedef {{ get: (link: import('
|
4
|
+
* @typedef {{ cid: import('../src/link').AnyLink, bytes: Uint8Array }} AnyBlock
|
5
|
+
* @typedef {{ get: (link: import('../src/link').AnyLink) => Promise<AnyBlock | undefined> }} BlockFetcher
|
6
6
|
*/
|
7
7
|
|
8
8
|
/** @implements {BlockFetcher} */
|
@@ -11,7 +11,7 @@ export class MemoryBlockstore {
|
|
11
11
|
#blocks = new Map()
|
12
12
|
|
13
13
|
/**
|
14
|
-
* @param {import('
|
14
|
+
* @param {import('../src/link').AnyLink} cid
|
15
15
|
* @returns {Promise<AnyBlock | undefined>}
|
16
16
|
*/
|
17
17
|
async get (cid) {
|
@@ -21,7 +21,7 @@ export class MemoryBlockstore {
|
|
21
21
|
}
|
22
22
|
|
23
23
|
/**
|
24
|
-
* @param {import('
|
24
|
+
* @param {import('../src/link').AnyLink} cid
|
25
25
|
* @param {Uint8Array} bytes
|
26
26
|
*/
|
27
27
|
async put (cid, bytes) {
|
@@ -30,7 +30,7 @@ export class MemoryBlockstore {
|
|
30
30
|
}
|
31
31
|
|
32
32
|
/**
|
33
|
-
* @param {import('
|
33
|
+
* @param {import('../src/link').AnyLink} cid
|
34
34
|
* @param {Uint8Array} bytes
|
35
35
|
*/
|
36
36
|
putSync (cid, bytes) {
|
@@ -53,7 +53,7 @@ export class MultiBlockFetcher {
|
|
53
53
|
this.#fetchers = fetchers
|
54
54
|
}
|
55
55
|
|
56
|
-
/** @param {import('
|
56
|
+
/** @param {import('../src/link').AnyLink} link */
|
57
57
|
async get (link) {
|
58
58
|
for (const f of this.#fetchers) {
|
59
59
|
const v = await f.get(link)
|