@fireproof/core 0.0.1
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/index.js +5 -0
- package/package.json +70 -0
- package/scripts/propernames/gen.sh +3 -0
- package/scripts/randomcid.js +12 -0
- package/scripts/words/gen.js +55 -0
- package/src/block.js +75 -0
- package/src/blockstore.js +246 -0
- package/src/clock.js +352 -0
- package/src/db-index.js +196 -0
- package/src/fireproof.js +209 -0
- package/src/link.d.ts +3 -0
- package/src/listener.js +111 -0
- package/src/prolly.js +235 -0
- package/src/valet.js +142 -0
- package/test/clock.test.js +725 -0
- package/test/db-index.test.js +214 -0
- package/test/fireproof.test.js +287 -0
- package/test/helpers.js +45 -0
- package/test/listener.test.js +102 -0
- package/test/prolly.test.js +203 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
import { describe, it } from 'mocha'
|
2
|
+
import assert from 'node:assert'
|
3
|
+
import { advance } from '../src/clock.js'
|
4
|
+
|
5
|
+
import { put, get, getAll, root, eventsSince } from '../src/prolly.js'
|
6
|
+
import { Blockstore, seqEventData, setSeq } from './helpers.js'
|
7
|
+
|
8
|
+
describe('Prolly', () => {
|
9
|
+
it('put a value to a new clock', async () => {
|
10
|
+
const blocks = new Blockstore()
|
11
|
+
const alice = new TestPail(blocks, [])
|
12
|
+
const key = 'key'
|
13
|
+
const value = seqEventData()
|
14
|
+
const { event, head } = await alice.put(key, value)
|
15
|
+
|
16
|
+
assert.equal(event.value.data.type, 'put')
|
17
|
+
assert.equal(event.value.data.key, key)
|
18
|
+
assert.equal(event.value.data.value.toString(), value.toString())
|
19
|
+
assert.equal(head.length, 1)
|
20
|
+
assert.equal(head[0].toString(), event.cid.toString())
|
21
|
+
|
22
|
+
const avalue = await alice.get('key')
|
23
|
+
assert(avalue)
|
24
|
+
assert.equal(JSON.stringify(avalue), JSON.stringify(value))
|
25
|
+
})
|
26
|
+
|
27
|
+
it('linear put multiple values', async () => {
|
28
|
+
setSeq(-1)
|
29
|
+
const blocks = new Blockstore()
|
30
|
+
const alice = new TestPail(blocks, [])
|
31
|
+
|
32
|
+
const key0 = 'key0'
|
33
|
+
const value0 = seqEventData()
|
34
|
+
const { head: oldHead } = await alice.put(key0, value0)
|
35
|
+
|
36
|
+
const key1 = 'key1'
|
37
|
+
const value1 = seqEventData()
|
38
|
+
const result = await alice.put(key1, value1)
|
39
|
+
|
40
|
+
assert.equal(result.event.value.data.type, 'put')
|
41
|
+
assert.equal(result.event.value.data.key, key1)
|
42
|
+
assert.equal(result.event.value.data.value.toString(), value1.toString())
|
43
|
+
assert.equal(result.head.length, 1)
|
44
|
+
assert.equal(result.head[0].toString(), result.event.cid.toString())
|
45
|
+
|
46
|
+
const allResp = await alice.getAll()
|
47
|
+
assert(allResp)
|
48
|
+
assert.equal(allResp.length, 2)
|
49
|
+
assert.equal(allResp[0].key, key0)
|
50
|
+
|
51
|
+
// add a third value
|
52
|
+
// try getSince
|
53
|
+
const sinceResp = await alice.getSince(oldHead)
|
54
|
+
assert.equal(sinceResp.length, 1)
|
55
|
+
assert.equal(sinceResp[0].value.value, 'event0')
|
56
|
+
})
|
57
|
+
|
58
|
+
it('get missing', async () => {
|
59
|
+
const blocks = new Blockstore()
|
60
|
+
const alice = new TestPail(blocks, [])
|
61
|
+
const key = 'key'
|
62
|
+
const value = seqEventData('test-missing-root')
|
63
|
+
await alice.put(key, value)
|
64
|
+
|
65
|
+
await alice.get('missing').then((value) => {
|
66
|
+
assert('false', 'should not get here')
|
67
|
+
}).catch((err) => {
|
68
|
+
assert.equal(err.message, 'Not found')
|
69
|
+
})
|
70
|
+
})
|
71
|
+
|
72
|
+
it('simple parallel put multiple values', async () => {
|
73
|
+
const blocks = new Blockstore()
|
74
|
+
const alice = new TestPail(blocks, [])
|
75
|
+
await alice.put('key0', seqEventData())
|
76
|
+
const bob = new TestPail(blocks, alice.head)
|
77
|
+
|
78
|
+
/** @type {Array<[string, import('../src/link').AnyLink]>} */
|
79
|
+
const data = [
|
80
|
+
['key1', seqEventData()],
|
81
|
+
['key2', seqEventData()],
|
82
|
+
['key3', seqEventData()],
|
83
|
+
['key4', seqEventData()]
|
84
|
+
]
|
85
|
+
|
86
|
+
const { event: aevent0 } = await alice.put(data[0][0], data[0][1])
|
87
|
+
const { event: bevent0 } = await bob.put(data[1][0], data[1][1])
|
88
|
+
const { event: bevent1 } = await bob.put(data[2][0], data[2][1])
|
89
|
+
|
90
|
+
await alice.advance(bevent0.cid)
|
91
|
+
await alice.advance(bevent1.cid)
|
92
|
+
await bob.advance(aevent0.cid)
|
93
|
+
|
94
|
+
const { event: aevent1 } = await alice.put(data[3][0], data[3][1])
|
95
|
+
|
96
|
+
await bob.advance(aevent1.cid)
|
97
|
+
|
98
|
+
assert(alice.root)
|
99
|
+
assert(bob.root)
|
100
|
+
assert.equal(alice.root.toString(), bob.root.toString())
|
101
|
+
|
102
|
+
// get item put to bob
|
103
|
+
const avalue = await alice.get(data[1][0])
|
104
|
+
assert(avalue)
|
105
|
+
assert.equal(avalue.toString(), data[1][1].toString())
|
106
|
+
|
107
|
+
// get item put to alice
|
108
|
+
const bvalue = await bob.get(data[0][0])
|
109
|
+
assert(bvalue)
|
110
|
+
assert.equal(bvalue.toString(), data[0][1].toString())
|
111
|
+
})
|
112
|
+
|
113
|
+
it.skip('linear put hundreds of values', async () => {
|
114
|
+
const blocks = new Blockstore()
|
115
|
+
const alice = new TestPail(blocks, [])
|
116
|
+
|
117
|
+
for (let i = 0; i < 100; i++) {
|
118
|
+
await alice.put('key' + i, seqEventData())
|
119
|
+
}
|
120
|
+
|
121
|
+
for (let i = 0; i < 100; i++) {
|
122
|
+
const vx = await alice.get('key' + i)
|
123
|
+
assert(vx)
|
124
|
+
// console.log('vx', vx)
|
125
|
+
// assert.equal(vx.toString(), value.toString())
|
126
|
+
}
|
127
|
+
console.log('blocks', Array.from(blocks.entries()).length)
|
128
|
+
}).timeout(10000)
|
129
|
+
})
|
130
|
+
|
131
|
+
class TestPail {
|
132
|
+
/**
|
133
|
+
* @param {Blockstore} blocks
|
134
|
+
* @param {import('../src/clock').EventLink<import('../src/crdt').EventData>[]} head
|
135
|
+
*/
|
136
|
+
constructor (blocks, head) {
|
137
|
+
this.blocks = blocks
|
138
|
+
this.head = head
|
139
|
+
/** @type {import('../src/shard.js').ShardLink?} */
|
140
|
+
this.root = null
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* @param {string} key
|
145
|
+
* @param {import('../src/link').AnyLink} value
|
146
|
+
*/
|
147
|
+
async put (key, value) {
|
148
|
+
const result = await put(this.blocks, this.head, { key, value })
|
149
|
+
if (!result) { console.log('failed', key, value) }
|
150
|
+
this.blocks.putSync(result.event.cid, result.event.bytes)
|
151
|
+
result.additions.forEach(a => this.blocks.putSync(a.cid, a.bytes))
|
152
|
+
this.head = result.head
|
153
|
+
this.root = result.root.cid
|
154
|
+
// this difference probably matters, but we need to test it
|
155
|
+
// this.root = await root(this.blocks, this.head)
|
156
|
+
// console.log('prolly PUT', key, value, { head: result.head, additions: result.additions.map(a => a.cid), event: result.event.cid })
|
157
|
+
return result
|
158
|
+
}
|
159
|
+
|
160
|
+
// todo make bulk ops which should be easy at the prolly layer by passing a list of events instead of one
|
161
|
+
// async bulk() {}
|
162
|
+
|
163
|
+
/** @param {import('../src/clock').EventLink<import('../src/crdt').EventData>} event */
|
164
|
+
async advance (event) {
|
165
|
+
this.head = await advance(this.blocks, this.head, event)
|
166
|
+
this.root = (await root(this.blocks, this.head)).block.cid
|
167
|
+
return this.head
|
168
|
+
}
|
169
|
+
|
170
|
+
// /**
|
171
|
+
// * @param {string} key
|
172
|
+
// * @param {import('../src/link.js').AnyLink} value
|
173
|
+
// */
|
174
|
+
// async putAndVis (key, value) {
|
175
|
+
// const result = await this.put(key, value)
|
176
|
+
// /** @param {import('../src/link').AnyLink} l */
|
177
|
+
// const shortLink = l => `${String(l).slice(0, 4)}..${String(l).slice(-4)}`
|
178
|
+
// /** @type {(e: import('../src/clock').EventBlockView<import('../src/crdt').EventData>) => string} */
|
179
|
+
// const renderNodeLabel = event => {
|
180
|
+
// return event.value.data.type === 'put'
|
181
|
+
// ? `${shortLink(event.cid)}\\nput(${event.value.data.key}, ${shortLink(event.value.data.value)})`
|
182
|
+
// : `${shortLink(event.cid)}\\ndel(${event.value.data.key})`
|
183
|
+
// }
|
184
|
+
// for await (const line of vis(this.blocks, result.head, { renderNodeLabel })) {
|
185
|
+
// console.log(line)
|
186
|
+
// }
|
187
|
+
// return result
|
188
|
+
// }
|
189
|
+
|
190
|
+
/** @param {string} key */
|
191
|
+
async get (key) {
|
192
|
+
return get(this.blocks, this.head, key)
|
193
|
+
}
|
194
|
+
|
195
|
+
/** @param {string} key */
|
196
|
+
async getAll () {
|
197
|
+
return getAll(this.blocks, this.head)
|
198
|
+
}
|
199
|
+
|
200
|
+
async getSince (since) {
|
201
|
+
return eventsSince(this.blocks, this.head, since)
|
202
|
+
}
|
203
|
+
}
|