@fireproof/core 0.3.21 → 0.4.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/dist/blockstore.js +242 -0
- package/dist/clock.js +355 -0
- package/dist/crypto.js +59 -0
- package/dist/database.js +308 -0
- package/dist/db-index.js +314 -0
- package/dist/fireproof.js +83 -0
- package/dist/hooks/use-fireproof.js +100 -0
- package/dist/listener.js +110 -0
- package/dist/prolly.js +316 -0
- package/dist/sha1.js +74 -0
- package/dist/src/blockstore.js +242 -0
- package/dist/src/clock.js +355 -0
- package/dist/src/crypto.js +59 -0
- package/dist/src/database.js +312 -0
- package/dist/src/db-index.js +314 -0
- package/dist/src/fireproof.d.ts +319 -0
- package/dist/src/fireproof.js +38976 -0
- package/dist/src/fireproof.js.map +1 -0
- package/dist/src/fireproof.mjs +38972 -0
- package/dist/src/fireproof.mjs.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +21 -16
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.mjs +21 -16
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/listener.js +108 -0
- package/dist/src/prolly.js +319 -0
- package/dist/src/sha1.js +74 -0
- package/dist/src/utils.js +16 -0
- package/dist/src/valet.js +262 -0
- package/dist/test/block.js +57 -0
- package/dist/test/clock.test.js +556 -0
- package/dist/test/db-index.test.js +231 -0
- package/dist/test/fireproof.test.js +444 -0
- package/dist/test/fulltext.test.js +61 -0
- package/dist/test/helpers.js +39 -0
- package/dist/test/hydrator.test.js +142 -0
- package/dist/test/listener.test.js +103 -0
- package/dist/test/prolly.test.js +162 -0
- package/dist/test/proofs.test.js +45 -0
- package/dist/test/reproduce-fixture-bug.test.js +57 -0
- package/dist/test/valet.test.js +56 -0
- package/dist/utils.js +16 -0
- package/dist/valet.js +262 -0
- package/hooks/use-fireproof.js +38 -63
- package/package.json +13 -14
- package/src/blockstore.js +8 -4
- package/src/database.js +338 -0
- package/src/db-index.js +3 -3
- package/src/fireproof.js +65 -322
- package/src/listener.js +10 -8
- package/src/prolly.js +10 -6
- package/src/utils.js +16 -0
- package/src/valet.js +2 -2
- package/src/hydrator.js +0 -54
- package/src/index.js +0 -6
@@ -0,0 +1,61 @@
|
|
1
|
+
import { describe, it, beforeEach } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { Fireproof } from '../src/fireproof.js';
|
4
|
+
import flexsearch from 'flexsearch';
|
5
|
+
const { Index } = flexsearch;
|
6
|
+
// this is an illustration of how to use the flexsearch library
|
7
|
+
let database, flexed;
|
8
|
+
describe('Fulltext with flexsearch', () => {
|
9
|
+
beforeEach(async () => {
|
10
|
+
database = Fireproof.storage();
|
11
|
+
flexed = withFlexsearch(database); // this is a function that adds the flexsearch library to the database object
|
12
|
+
const messages = [
|
13
|
+
'Hello World, this is a test',
|
14
|
+
'We are testing the flexsearch library',
|
15
|
+
'When we test we test',
|
16
|
+
'Apples are red',
|
17
|
+
'Bananas are yellow',
|
18
|
+
'Oranges are orange',
|
19
|
+
'Pears are green',
|
20
|
+
'Grapes are purple',
|
21
|
+
'Strawberries are red',
|
22
|
+
'Blueberries are blue',
|
23
|
+
'Raspberries are red',
|
24
|
+
'Watermelons are green',
|
25
|
+
'Pineapples are yellow'
|
26
|
+
];
|
27
|
+
for (let i = 0, len = messages.length; i < len; i++) {
|
28
|
+
await database.put({
|
29
|
+
_id: `message-${i}`,
|
30
|
+
message: messages[i]
|
31
|
+
});
|
32
|
+
}
|
33
|
+
});
|
34
|
+
it('search the index', async () => {
|
35
|
+
const changes = await database.changesSince();
|
36
|
+
assert.equal(changes.rows.length, 13);
|
37
|
+
const results = await flexed.search('red');
|
38
|
+
assert.equal(results.length, 3);
|
39
|
+
for (let i = 0, len = results.length; i < len; i++) {
|
40
|
+
const doc = await database.get(results[i]);
|
41
|
+
assert.match(doc.message, /red/);
|
42
|
+
}
|
43
|
+
});
|
44
|
+
// it('add more docs and search again', async () => {})
|
45
|
+
// it('delete some docs and search again', async () => {})
|
46
|
+
// it('update some docs and search again', async () => {})
|
47
|
+
});
|
48
|
+
function withFlexsearch(database, flexsearchOptions = {}) {
|
49
|
+
const index = new Index(flexsearchOptions);
|
50
|
+
let clock = null;
|
51
|
+
const search = async (query, options) => {
|
52
|
+
const changes = await database.changesSince(clock);
|
53
|
+
clock = changes.clock;
|
54
|
+
for (let i = 0; i < changes.rows.length; i++) {
|
55
|
+
const { key, value } = changes.rows[i];
|
56
|
+
await index.add(key, value.message);
|
57
|
+
}
|
58
|
+
return index.search(query, options);
|
59
|
+
};
|
60
|
+
return { search };
|
61
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import crypto from 'node:crypto';
|
2
|
+
// import assert from 'node:assert'
|
3
|
+
import * as Link from 'multiformats/link';
|
4
|
+
import * as raw from 'multiformats/codecs/raw';
|
5
|
+
import { sha256 } from 'multiformats/hashes/sha2';
|
6
|
+
import { MemoryBlockstore } from './block.js';
|
7
|
+
// console.x = console.log
|
8
|
+
// console.log = function (...args) {
|
9
|
+
// // window.mutedLog = window.mutedLog || []
|
10
|
+
// // window.mutedLog.push(args)
|
11
|
+
// }
|
12
|
+
/** @param {number} size */
|
13
|
+
export async function randomCID(size) {
|
14
|
+
const hash = await sha256.digest(await randomBytes(size));
|
15
|
+
return Link.create(raw.code, hash);
|
16
|
+
}
|
17
|
+
/** @param {number} size */
|
18
|
+
export async function randomBytes(size) {
|
19
|
+
const bytes = new Uint8Array(size);
|
20
|
+
while (size) {
|
21
|
+
const chunk = new Uint8Array(Math.min(size, 65_536));
|
22
|
+
crypto.getRandomValues(chunk);
|
23
|
+
size -= bytes.length;
|
24
|
+
bytes.set(chunk, size);
|
25
|
+
}
|
26
|
+
return bytes;
|
27
|
+
}
|
28
|
+
export class Blockstore extends MemoryBlockstore {
|
29
|
+
}
|
30
|
+
let seq = 0;
|
31
|
+
export function seqEventData(tag = '') {
|
32
|
+
return {
|
33
|
+
type: 'put',
|
34
|
+
value: `event${seq++}${tag}`
|
35
|
+
};
|
36
|
+
}
|
37
|
+
export function setSeq(n) {
|
38
|
+
seq = n;
|
39
|
+
}
|
@@ -0,0 +1,142 @@
|
|
1
|
+
import { describe, it, beforeEach } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { Fireproof } from '../src/fireproof.js';
|
4
|
+
import { DbIndex } from '../src/db-index.js';
|
5
|
+
describe('DbIndex query without name', () => {
|
6
|
+
let database, index;
|
7
|
+
beforeEach(async () => {
|
8
|
+
database = Fireproof.storage();
|
9
|
+
const docs = [
|
10
|
+
{ _id: 'a1s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'alice', age: 40 },
|
11
|
+
{ _id: 'b2s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'bob', age: 40 },
|
12
|
+
{ _id: 'c3s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'carol', age: 43 },
|
13
|
+
{ _id: 'd4s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'dave', age: 48 },
|
14
|
+
{ _id: 'e4s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'emily', age: 4 },
|
15
|
+
{ _id: 'f4s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'frank', age: 7 }
|
16
|
+
];
|
17
|
+
for (const doc of docs) {
|
18
|
+
const id = doc._id;
|
19
|
+
const response = await database.put(doc);
|
20
|
+
assert(response);
|
21
|
+
assert(response.id, 'should have id');
|
22
|
+
assert.equal(response.id, id);
|
23
|
+
}
|
24
|
+
index = new DbIndex(database, function (doc, map) {
|
25
|
+
map(doc.age, doc.name);
|
26
|
+
}, null, { name: 'names_by_age' });
|
27
|
+
});
|
28
|
+
it('serialize database with index', async () => {
|
29
|
+
await database.put({ _id: 'rehy', name: 'drate', age: 1 });
|
30
|
+
assert.equal((await database.changesSince()).rows.length, 7);
|
31
|
+
const result = await index.query({ range: [0, 54] });
|
32
|
+
assert.equal(result.rows[0].value, 'drate');
|
33
|
+
const serialized = database.toJSON();
|
34
|
+
// console.log('serialized', serialized)
|
35
|
+
assert.equal(serialized.name, undefined);
|
36
|
+
assert.equal(serialized.key, null);
|
37
|
+
assert.equal(serialized.clock.length, 1);
|
38
|
+
assert.equal(serialized.clock[0].constructor.name, 'String');
|
39
|
+
assert.equal(serialized.indexes.length, 1);
|
40
|
+
assert.equal(serialized.indexes[0].code, `function (doc, map) {
|
41
|
+
map(doc.age, doc.name)
|
42
|
+
}`);
|
43
|
+
assert.equal(serialized.indexes[0].name, 'names_by_age');
|
44
|
+
assert.equal(serialized.indexes[0].clock.byId.constructor.name, 'String');
|
45
|
+
assert.equal(serialized.indexes[0].clock.byKey.constructor.name, 'String');
|
46
|
+
assert.equal(serialized.indexes[0].clock.db[0].constructor.name, 'String');
|
47
|
+
});
|
48
|
+
it('rehydrate database', async () => {
|
49
|
+
await database.put({ _id: 'rehy', name: 'drate', age: 1 });
|
50
|
+
assert.equal((await database.changesSince()).rows.length, 7);
|
51
|
+
const result = await index.query({ range: [0, 54] });
|
52
|
+
assert.equal(result.rows[0].value, 'drate');
|
53
|
+
const serialized = JSON.parse(JSON.stringify(database));
|
54
|
+
// console.log('serialized', JSON.stringify(serialized))
|
55
|
+
// connect it to the same blockstore for testing
|
56
|
+
const newDb = Fireproof.fromJSON(serialized, database);
|
57
|
+
assert.equal(newDb.name, undefined);
|
58
|
+
assert.equal(newDb.clock.length, 1);
|
59
|
+
assert.equal((await newDb.changesSince()).rows.length, 7);
|
60
|
+
const newIndex = [...newDb.indexes.values()][0];
|
61
|
+
assert.equal(newIndex.mapFn, `function (doc, map) {
|
62
|
+
map(doc.age, doc.name)
|
63
|
+
}`);
|
64
|
+
assert.equal(newIndex.indexById.cid, 'bafyreifuz54ugnq77fur47vwv3dwab7p3gpnf5to6hlnbhv5p4kwo7auoi');
|
65
|
+
// assert.equal(newIndex.indexById.root, null)
|
66
|
+
assert.equal(newIndex.indexByKey.cid, 'bafyreicr5rpvsxnqchcwk5rxlmdvd3fah2vexmbsp2dvr4cfdxd2q2ycgu');
|
67
|
+
// assert.equal(newIndex.indexByKey.root, null)
|
68
|
+
assert.equal(newIndex.name, 'names_by_age');
|
69
|
+
const newResult = await newIndex.query({ range: [0, 54] });
|
70
|
+
assert.equal(newResult.rows[0].value, 'drate');
|
71
|
+
});
|
72
|
+
});
|
73
|
+
describe('DbIndex query with dbname', () => {
|
74
|
+
let database, index;
|
75
|
+
beforeEach(async () => {
|
76
|
+
database = Fireproof.storage('index-test');
|
77
|
+
const docs = [
|
78
|
+
{ _id: 'a1s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'alice', age: 40 },
|
79
|
+
{ _id: 'b2s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'bob', age: 40 },
|
80
|
+
{ _id: 'c3s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'carol', age: 43 },
|
81
|
+
{ _id: 'd4s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'dave', age: 48 },
|
82
|
+
{ _id: 'e4s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'emily', age: 4 },
|
83
|
+
{ _id: 'f4s3b32a-3c3a-4b5e-9c1c-8c5c0c5c0c5c', name: 'frank', age: 7 }
|
84
|
+
];
|
85
|
+
for (const doc of docs) {
|
86
|
+
const id = doc._id;
|
87
|
+
const response = await database.put(doc);
|
88
|
+
assert(response);
|
89
|
+
assert(response.id, 'should have id');
|
90
|
+
assert.equal(response.id, id);
|
91
|
+
}
|
92
|
+
index = new DbIndex(database, function (doc, map) {
|
93
|
+
map(doc.age, doc.name);
|
94
|
+
}, null, { name: 'names_by_age' });
|
95
|
+
});
|
96
|
+
it('serialize database with index', async () => {
|
97
|
+
await database.put({ _id: 'rehy', name: 'drate', age: 1 });
|
98
|
+
assert.equal((await database.changesSince()).rows.length, 7);
|
99
|
+
const result = await index.query({ range: [0, 54] });
|
100
|
+
assert.equal(result.rows[0].value, 'drate');
|
101
|
+
const serialized = database.toJSON();
|
102
|
+
// console.log('serialized', serialized)
|
103
|
+
assert.equal(serialized.name, 'index-test');
|
104
|
+
if (database.blocks.valet.keyId !== 'null') {
|
105
|
+
assert.equal(serialized.key.length, 64);
|
106
|
+
}
|
107
|
+
assert.equal(serialized.clock.length, 1);
|
108
|
+
assert.equal(serialized.clock[0].constructor.name, 'String');
|
109
|
+
assert.equal(serialized.indexes.length, 1);
|
110
|
+
assert.equal(serialized.indexes[0].code, `function (doc, map) {
|
111
|
+
map(doc.age, doc.name)
|
112
|
+
}`);
|
113
|
+
assert.equal(serialized.indexes[0].name, 'names_by_age');
|
114
|
+
assert.equal(serialized.indexes[0].clock.byId.constructor.name, 'String');
|
115
|
+
assert.equal(serialized.indexes[0].clock.byKey.constructor.name, 'String');
|
116
|
+
assert.equal(serialized.indexes[0].clock.db[0].constructor.name, 'String');
|
117
|
+
});
|
118
|
+
it('rehydrate database', async () => {
|
119
|
+
await database.put({ _id: 'rehy', name: 'drate', age: 1 });
|
120
|
+
assert.equal((await database.changesSince()).rows.length, 7);
|
121
|
+
const result = await index.query({ range: [0, 54] });
|
122
|
+
assert.equal(result.rows[0].value, 'drate');
|
123
|
+
const serialized = JSON.parse(JSON.stringify(database));
|
124
|
+
// console.log('serialized', JSON.stringify(serialized))
|
125
|
+
// connect it to the same blockstore for testing
|
126
|
+
const newDb = Fireproof.fromJSON(serialized, database);
|
127
|
+
assert.equal(newDb.name, 'index-test');
|
128
|
+
assert.equal(newDb.clock.length, 1);
|
129
|
+
assert.equal((await newDb.changesSince()).rows.length, 7);
|
130
|
+
const newIndex = [...newDb.indexes.values()][0];
|
131
|
+
assert.equal(newIndex.mapFn, `function (doc, map) {
|
132
|
+
map(doc.age, doc.name)
|
133
|
+
}`);
|
134
|
+
assert.equal(newIndex.indexById.cid, 'bafyreifuz54ugnq77fur47vwv3dwab7p3gpnf5to6hlnbhv5p4kwo7auoi');
|
135
|
+
// assert.equal(newIndex.indexById.root, null)
|
136
|
+
assert.equal(newIndex.indexByKey.cid, 'bafyreicr5rpvsxnqchcwk5rxlmdvd3fah2vexmbsp2dvr4cfdxd2q2ycgu');
|
137
|
+
// assert.equal(newIndex.indexByKey.root, null)
|
138
|
+
assert.equal(newIndex.name, 'names_by_age');
|
139
|
+
const newResult = await newIndex.query({ range: [0, 54] });
|
140
|
+
assert.equal(newResult.rows[0].value, 'drate');
|
141
|
+
});
|
142
|
+
});
|
@@ -0,0 +1,103 @@
|
|
1
|
+
import { describe, it, beforeEach } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { TransactionBlockstore as Blockstore } from '../src/blockstore.js';
|
4
|
+
import { Fireproof } from '../src/fireproof.js';
|
5
|
+
import { Database } from '../src/database.js';
|
6
|
+
import { Listener } from '../src/listener.js';
|
7
|
+
let database, listener, star;
|
8
|
+
describe('Listener', () => {
|
9
|
+
beforeEach(async () => {
|
10
|
+
database = new Database(new Blockstore(), []); // todo: these need a cloud name aka w3name, add this after we have cloud storage of blocks
|
11
|
+
const docs = [
|
12
|
+
{ _id: 'd4s3b32a-3c3a', name: 'dave', age: 48 },
|
13
|
+
{ _id: 'a1s3b32a-3c3a', name: 'alice', age: 40 },
|
14
|
+
{ _id: 'b2s3b32a-3c3a', name: 'bob', age: 40 },
|
15
|
+
{ _id: 'c3s3b32a-3c3a', name: 'carol', age: 43 },
|
16
|
+
{ _id: 'e4s3b32a-3c3a', name: 'emily', age: 4 },
|
17
|
+
{ _id: 'f4s3b32a-3c3a', name: 'frank', age: 7 }
|
18
|
+
];
|
19
|
+
for (const doc of docs) {
|
20
|
+
const id = doc._id;
|
21
|
+
const response = await database.put(doc);
|
22
|
+
assert(response);
|
23
|
+
assert(response.id, 'should have id');
|
24
|
+
assert.equal(response.id, id);
|
25
|
+
}
|
26
|
+
listener = new Listener(database, function (doc, emit) {
|
27
|
+
if (doc.name) {
|
28
|
+
emit('person');
|
29
|
+
}
|
30
|
+
});
|
31
|
+
star = new Listener(database);
|
32
|
+
});
|
33
|
+
it('all listeners get the reset event', (done) => {
|
34
|
+
let count = 0;
|
35
|
+
const check = () => {
|
36
|
+
count++;
|
37
|
+
if (count === 3)
|
38
|
+
done();
|
39
|
+
};
|
40
|
+
const startClock = database.clock;
|
41
|
+
database.put({ _id: 'k645-87tk', name: 'karl' }).then((ok) => {
|
42
|
+
listener.on('person', check);
|
43
|
+
listener.on('not-found', check);
|
44
|
+
star.on('*', check);
|
45
|
+
database.put({ _id: 'k645-87tk', name: 'karl2' }).then((ok) => {
|
46
|
+
assert(ok.id);
|
47
|
+
assert.notEqual(database.clock, startClock);
|
48
|
+
Fireproof.zoom(database, startClock);
|
49
|
+
}).catch(done);
|
50
|
+
});
|
51
|
+
});
|
52
|
+
it('can listen to all events', (done) => {
|
53
|
+
star.on('*', (key) => {
|
54
|
+
assert.equal(key, 'i645-87ti');
|
55
|
+
done();
|
56
|
+
});
|
57
|
+
database.put({ _id: 'i645-87ti', name: 'isaac' }).then((ok) => {
|
58
|
+
assert(ok.id);
|
59
|
+
}).catch(done);
|
60
|
+
});
|
61
|
+
it('shares only new events by default', (done) => {
|
62
|
+
listener.on('person', (key) => {
|
63
|
+
assert.equal(key, 'g645-87tg');
|
64
|
+
done();
|
65
|
+
});
|
66
|
+
database.put({ _id: 'g645-87tg', name: 'gwen' }).then((ok) => {
|
67
|
+
assert(ok.id);
|
68
|
+
}).catch(done);
|
69
|
+
}).timeout(200);
|
70
|
+
it('shares all events if asked', async () => {
|
71
|
+
let people = 0;
|
72
|
+
listener.on('person', (key) => {
|
73
|
+
people++;
|
74
|
+
}, null);
|
75
|
+
// this has to take longer than the database save operation
|
76
|
+
// it's safe to make this number longer if it start failing
|
77
|
+
await sleep(50);
|
78
|
+
assert.equal(people, 6);
|
79
|
+
}).timeout(2000);
|
80
|
+
it('shares events since db.clock', (done) => {
|
81
|
+
const clock = database.clock;
|
82
|
+
const afterEvent = () => {
|
83
|
+
database.put({ _id: 'j645-87tj', name: 'jimmy' });
|
84
|
+
};
|
85
|
+
let people = 0;
|
86
|
+
database.put({ _id: 'h645-87th', name: 'harold' }).then(newPerson => {
|
87
|
+
assert(newPerson.id);
|
88
|
+
listener.on('person', (key) => {
|
89
|
+
people++;
|
90
|
+
if (people === 1) {
|
91
|
+
assert.equal(key, 'h645-87th');
|
92
|
+
}
|
93
|
+
else {
|
94
|
+
assert.equal(key, 'j645-87tj');
|
95
|
+
done();
|
96
|
+
}
|
97
|
+
if (people === 1)
|
98
|
+
afterEvent();
|
99
|
+
}, clock);
|
100
|
+
});
|
101
|
+
}).timeout(2000);
|
102
|
+
});
|
103
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
@@ -0,0 +1,162 @@
|
|
1
|
+
import { describe, it } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { advance } from '../src/clock.js';
|
4
|
+
import { put, get, getAll, root, eventsSince } from '../src/prolly.js';
|
5
|
+
import { Blockstore, seqEventData, setSeq } from './helpers.js';
|
6
|
+
/**
|
7
|
+
* @typedef {import('../src/blockstore.js').TransactionBlockstore} TransactionBlockstore
|
8
|
+
*/
|
9
|
+
describe('Prolly', () => {
|
10
|
+
it('put a value to a new clock', async () => {
|
11
|
+
const blocks = new Blockstore();
|
12
|
+
const alice = new TestPail(blocks, []);
|
13
|
+
const key = 'key';
|
14
|
+
const value = seqEventData();
|
15
|
+
const { event, head } = await alice.put(key, value);
|
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
|
+
const avalue = await alice.get('key');
|
22
|
+
assert(avalue);
|
23
|
+
assert.equal(JSON.stringify(avalue), JSON.stringify(value));
|
24
|
+
});
|
25
|
+
it('linear put multiple values', async () => {
|
26
|
+
setSeq(-1);
|
27
|
+
const blocks = new Blockstore();
|
28
|
+
const alice = new TestPail(blocks, []);
|
29
|
+
const key0 = 'key0';
|
30
|
+
const value0 = seqEventData();
|
31
|
+
const { head: oldHead } = await alice.put(key0, value0);
|
32
|
+
const key1 = 'key1';
|
33
|
+
const value1 = seqEventData();
|
34
|
+
const result = await alice.put(key1, value1);
|
35
|
+
assert.equal(result.event.value.data.type, 'put');
|
36
|
+
assert.equal(result.event.value.data.key, key1);
|
37
|
+
assert.equal(result.event.value.data.value.toString(), value1.toString());
|
38
|
+
assert.equal(result.head.length, 1);
|
39
|
+
assert.equal(result.head[0].toString(), result.event.cid.toString());
|
40
|
+
const allResp = await alice.getAll();
|
41
|
+
assert(allResp);
|
42
|
+
assert.equal(allResp.length, 2);
|
43
|
+
assert.equal(allResp[0].key, key0);
|
44
|
+
// add a third value
|
45
|
+
// try getSince
|
46
|
+
const sinceResp = await alice.getSince(oldHead);
|
47
|
+
assert.equal(sinceResp.length, 1);
|
48
|
+
assert.equal(sinceResp[0].value.value, 'event0');
|
49
|
+
});
|
50
|
+
it('get missing', async () => {
|
51
|
+
const blocks = new Blockstore();
|
52
|
+
const alice = new TestPail(blocks, []);
|
53
|
+
const key = 'key';
|
54
|
+
const value = seqEventData('test-missing-root');
|
55
|
+
await alice.put(key, value);
|
56
|
+
await alice.get('missing').then((value) => {
|
57
|
+
assert('false', 'should not get here');
|
58
|
+
}).catch((err) => {
|
59
|
+
assert.equal(err.message, 'Not found');
|
60
|
+
});
|
61
|
+
});
|
62
|
+
it('simple parallel put multiple values', async () => {
|
63
|
+
const blocks = new Blockstore();
|
64
|
+
const alice = new TestPail(blocks, []);
|
65
|
+
await alice.put('key0', seqEventData());
|
66
|
+
const bob = new TestPail(blocks, alice.head);
|
67
|
+
/** @type {Array<[string, import('../src/link').AnyLink]>} */
|
68
|
+
const data = [
|
69
|
+
['key1', seqEventData()],
|
70
|
+
['key2', seqEventData()],
|
71
|
+
['key3', seqEventData()],
|
72
|
+
['key4', seqEventData()]
|
73
|
+
];
|
74
|
+
const { event: aevent0 } = await alice.put(data[0][0], data[0][1]);
|
75
|
+
const { event: bevent0 } = await bob.put(data[1][0], data[1][1]);
|
76
|
+
const { event: bevent1 } = await bob.put(data[2][0], data[2][1]);
|
77
|
+
await alice.advance(bevent0.cid);
|
78
|
+
await alice.advance(bevent1.cid);
|
79
|
+
await bob.advance(aevent0.cid);
|
80
|
+
const { event: aevent1 } = await alice.put(data[3][0], data[3][1]);
|
81
|
+
await bob.advance(aevent1.cid);
|
82
|
+
assert(alice.root);
|
83
|
+
assert(bob.root);
|
84
|
+
assert.equal(alice.root.toString(), bob.root.toString());
|
85
|
+
// get item put to bob
|
86
|
+
const avalue = await alice.get(data[1][0]);
|
87
|
+
assert(avalue);
|
88
|
+
assert.equal(avalue.toString(), data[1][1].toString());
|
89
|
+
// get item put to alice
|
90
|
+
const bvalue = await bob.get(data[0][0]);
|
91
|
+
assert(bvalue);
|
92
|
+
assert.equal(bvalue.toString(), data[0][1].toString());
|
93
|
+
});
|
94
|
+
it.skip('passing, slow: linear put hundreds of values', async () => {
|
95
|
+
const blocks = new Blockstore();
|
96
|
+
const alice = new TestPail(blocks, []);
|
97
|
+
for (let i = 0; i < 100; i++) {
|
98
|
+
await alice.put('key' + i, seqEventData());
|
99
|
+
}
|
100
|
+
for (let i = 0; i < 100; i++) {
|
101
|
+
const vx = await alice.get('key' + i);
|
102
|
+
assert(vx);
|
103
|
+
// console.log('vx', vx)
|
104
|
+
// assert.equal(vx.toString(), value.toString())
|
105
|
+
}
|
106
|
+
console.log('blocks', Array.from(blocks.entries()).length);
|
107
|
+
}).timeout(10000);
|
108
|
+
});
|
109
|
+
class TestPail {
|
110
|
+
/**
|
111
|
+
* @param {Blockstore} blocks
|
112
|
+
* @param {import('../src/clock').EventLink<import('../src/clock').EventData>[]} head
|
113
|
+
*/
|
114
|
+
constructor(blocks, head) {
|
115
|
+
this.blocks = blocks;
|
116
|
+
this.head = head;
|
117
|
+
/** @type {import('../src/clock.js').ShardLink?} */
|
118
|
+
this.root = null;
|
119
|
+
}
|
120
|
+
/**
|
121
|
+
* @param {string} key
|
122
|
+
* @param {import('../src/link').AnyLink} value
|
123
|
+
*/
|
124
|
+
async put(key, value) {
|
125
|
+
const result = await put(this.blocks, this.head, { key, value });
|
126
|
+
if (!result) {
|
127
|
+
console.log('failed', key, value);
|
128
|
+
}
|
129
|
+
this.blocks.putSync(result.event.cid, result.event.bytes);
|
130
|
+
result.additions.forEach((a) => this.blocks.putSync(a.cid, a.bytes));
|
131
|
+
this.head = result.head;
|
132
|
+
this.root = result.root.cid;
|
133
|
+
// this difference probably matters, but we need to test it
|
134
|
+
// this.root = await root(this.blocks, this.head)
|
135
|
+
// console.log('prolly PUT', key, value, { head: result.head, additions: result.additions.map(a => a.cid), event: result.event.cid })
|
136
|
+
return result;
|
137
|
+
}
|
138
|
+
// todo make bulk ops which should be easy at the prolly layer by passing a list of events instead of one
|
139
|
+
// async bulk() {}
|
140
|
+
/** @param {import('../src/clock').EventLink<import('../src/clock.js').EventData>} event */
|
141
|
+
async advance(event) {
|
142
|
+
const { head } = await advance(this.blocks, this.head, event);
|
143
|
+
this.head = head;
|
144
|
+
// needs a transaction
|
145
|
+
this.root = (await root(this.blocks, this.head)).node.block.cid;
|
146
|
+
return this.head;
|
147
|
+
}
|
148
|
+
/** @param {string} key */
|
149
|
+
async get(key) {
|
150
|
+
const resp = await get(this.blocks, this.head, key);
|
151
|
+
// console.log('prolly GET', key, resp)
|
152
|
+
return resp.result;
|
153
|
+
}
|
154
|
+
async getAll() {
|
155
|
+
const resp = await getAll(this.blocks, this.head);
|
156
|
+
return resp.result;
|
157
|
+
}
|
158
|
+
async getSince(since) {
|
159
|
+
const resp = await eventsSince(this.blocks, this.head, since);
|
160
|
+
return resp.result;
|
161
|
+
}
|
162
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { describe, it, beforeEach } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { Fireproof } from '../src/fireproof.js';
|
4
|
+
let database, ok, doc;
|
5
|
+
describe('Proofs', () => {
|
6
|
+
beforeEach(async () => {
|
7
|
+
database = Fireproof.storage();
|
8
|
+
ok = await database.put({
|
9
|
+
_id: 'test1',
|
10
|
+
score: 75
|
11
|
+
});
|
12
|
+
doc = await database.get(ok.id, { mvcc: true });
|
13
|
+
});
|
14
|
+
it('first put result shoud not include proof', async () => {
|
15
|
+
assert(ok.proof);
|
16
|
+
assert(ok.proof.data);
|
17
|
+
assert(ok.proof.clock);
|
18
|
+
// console.log('ok', ok)
|
19
|
+
assert.equal(ok.proof.data.length, 0);
|
20
|
+
assert.equal(ok.proof.clock.length, 0);
|
21
|
+
// assert.equal(ok.proof.data[0], 'bafyreibsbxxd4ueujryihk6xza2ekwhzsh6pzuu5fysft5ilz7cbw6bjju')
|
22
|
+
// assert.equal(ok.proof.clock[0].toString(), 'bafyreiactx5vku7zueq27i5zdrgcjnczxvepceo5yszjqb2exufwrwxg44')
|
23
|
+
});
|
24
|
+
it.skip('second put result shoud include proof', async () => {
|
25
|
+
const ok2 = await database.put({ ...doc, winner: true });
|
26
|
+
assert(ok2.proof);
|
27
|
+
assert(ok2.proof.data);
|
28
|
+
assert(ok2.proof.clock);
|
29
|
+
// console.log('ok2', ok2)
|
30
|
+
assert.equal(ok2.proof.data.length, 1);
|
31
|
+
assert.equal(ok2.proof.clock.length, 1);
|
32
|
+
// assert.equal(ok.proof.data[0], 'bafyreibsbxxd4ueujryihk6xza2ekwhzsh6pzuu5fysft5ilz7cbw6bjju')
|
33
|
+
// assert.equal(ok.proof.clock[0].toString(), 'bafyreiactx5vku7zueq27i5zdrgcjnczxvepceo5yszjqb2exufwrwxg44')
|
34
|
+
});
|
35
|
+
it('get result shoud include proof', async () => {
|
36
|
+
assert(doc._clock);
|
37
|
+
assert(doc._proof);
|
38
|
+
assert(doc._proof.data);
|
39
|
+
assert(doc._proof.clock);
|
40
|
+
assert.equal(doc._proof.data.length, 1);
|
41
|
+
assert.equal(doc._proof.clock.length, 1);
|
42
|
+
assert.equal(doc._proof.data[0], 'bafyreieilmvxq6wudu46i2ssmuyrmaszr4onzlqxzlvngrczbn7ppyvloq');
|
43
|
+
assert.equal(doc._proof.clock[0].toString(), 'bafyreict4aip45uwnm4xcsn4oikh73t5n7nzdmc2u36rdbguroun2yaf2y');
|
44
|
+
});
|
45
|
+
});
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import { describe, it, beforeEach } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { Fireproof } from '../src/fireproof.js';
|
4
|
+
import { DbIndex } from '../src/db-index.js';
|
5
|
+
let database;
|
6
|
+
describe('IPLD encode error', () => {
|
7
|
+
beforeEach(async () => {
|
8
|
+
database = Fireproof.storage();
|
9
|
+
defineIndexes(database);
|
10
|
+
});
|
11
|
+
it('reproduce', async () => {
|
12
|
+
await loadFixtures(database);
|
13
|
+
assert(true);
|
14
|
+
});
|
15
|
+
});
|
16
|
+
const defineIndexes = (database) => {
|
17
|
+
database.allLists = new DbIndex(database, function (doc, map) {
|
18
|
+
if (doc.type === 'list')
|
19
|
+
map(doc.type, doc);
|
20
|
+
});
|
21
|
+
database.todosByList = new DbIndex(database, function (doc, map) {
|
22
|
+
if (doc.type === 'todo' && doc.listId) {
|
23
|
+
map([doc.listId, doc.createdAt], doc);
|
24
|
+
}
|
25
|
+
});
|
26
|
+
return database;
|
27
|
+
};
|
28
|
+
function mulberry32(a) {
|
29
|
+
return function () {
|
30
|
+
let t = (a += 0x6d2b79f5);
|
31
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
32
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
33
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
34
|
+
};
|
35
|
+
}
|
36
|
+
const rand = mulberry32(1); // determinstic fixtures
|
37
|
+
async function loadFixtures(database) {
|
38
|
+
const nextId = (prefix = '') => prefix + rand().toString(32).slice(2);
|
39
|
+
const ok = await database.put({ title: 'Building Apps', type: 'list', _id: nextId() });
|
40
|
+
await database.put({
|
41
|
+
_id: nextId(),
|
42
|
+
title: 'In the browser',
|
43
|
+
listId: ok.id,
|
44
|
+
completed: rand() > 0.75,
|
45
|
+
type: 'todo',
|
46
|
+
createdAt: '2'
|
47
|
+
});
|
48
|
+
await reproduceBug(database);
|
49
|
+
}
|
50
|
+
const reproduceBug = async (database) => {
|
51
|
+
const id = '02pkji8';
|
52
|
+
const doc = await database.get(id);
|
53
|
+
// (await database.put({ completed: !completed, ...doc }))
|
54
|
+
await database.put(doc);
|
55
|
+
await database.todosByList.query({ range: [0, 1] });
|
56
|
+
// console.log('ok', ok)
|
57
|
+
};
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import { before, describe, it } from 'mocha';
|
2
|
+
import assert from 'node:assert';
|
3
|
+
import { Valet } from '../src/valet.js';
|
4
|
+
describe('new Valet', () => {
|
5
|
+
let val;
|
6
|
+
const calls = [];
|
7
|
+
before(async () => {
|
8
|
+
val = new Valet();
|
9
|
+
val.uploadFunction = async (carCid, value) => {
|
10
|
+
calls.push({ carCid, value });
|
11
|
+
};
|
12
|
+
});
|
13
|
+
it('has default attributes', async () => {
|
14
|
+
assert(val.getBlock);
|
15
|
+
assert(val.parkCar);
|
16
|
+
});
|
17
|
+
it('can park a car and serve the blocks', async () => {
|
18
|
+
await val
|
19
|
+
.parkCar('carCid', carBytes, [
|
20
|
+
'bafyreifwghknmzabvgearl72url3v5leqqhtafvybcsrceelc3psqkocoi',
|
21
|
+
'bafyreieth2ckopwivda5mf6vu76xwqvox3q5wsaxgbmxy2dgrd4hfuzmma'
|
22
|
+
])
|
23
|
+
.then((car) => {
|
24
|
+
assert('car parked');
|
25
|
+
val.getBlock('bafyreieth2ckopwivda5mf6vu76xwqvox3q5wsaxgbmxy2dgrd4hfuzmma').then((block) => {
|
26
|
+
assert.equal(block.length, carBytes.length);
|
27
|
+
});
|
28
|
+
});
|
29
|
+
});
|
30
|
+
it('calls the upload function', () => {
|
31
|
+
assert.equal(calls.length, 1);
|
32
|
+
assert.equal(calls[0].carCid, 'carCid');
|
33
|
+
});
|
34
|
+
});
|
35
|
+
const carBytes = new Uint8Array([
|
36
|
+
58, 162, 101, 114, 111, 111, 116, 115, 129, 216, 42, 88, 37, 0, 1, 113, 18, 32, 147, 62, 132, 167, 62, 200, 168, 193,
|
37
|
+
214, 23, 213, 167, 253, 123, 66, 174, 190, 225, 219, 72, 23, 48, 89, 124, 104, 102, 136, 248, 114, 211, 44, 96, 103,
|
38
|
+
118, 101, 114, 115, 105, 111, 110, 1, 108, 1, 113, 18, 32, 182, 49, 212, 214, 100, 1, 169, 136, 8, 175, 250, 164, 87,
|
39
|
+
186, 245, 100, 132, 15, 48, 22, 184, 8, 165, 17, 16, 139, 22, 223, 40, 41, 194, 114, 162, 100, 108, 101, 97, 102, 129,
|
40
|
+
130, 120, 36, 49, 101, 102, 51, 98, 51, 50, 97, 45, 51, 99, 51, 97, 45, 52, 98, 53, 101, 45, 57, 99, 49, 99, 45, 56,
|
41
|
+
99, 53, 99, 48, 99, 53, 99, 48, 99, 53, 99, 162, 99, 97, 103, 101, 24, 42, 100, 110, 97, 109, 101, 101, 97, 108, 105,
|
42
|
+
99, 101, 102, 99, 108, 111, 115, 101, 100, 244, 208, 2, 1, 113, 18, 32, 147, 62, 132, 167, 62, 200, 168, 193, 214, 23,
|
43
|
+
213, 167, 253, 123, 66, 174, 190, 225, 219, 72, 23, 48, 89, 124, 104, 102, 136, 248, 114, 211, 44, 96, 162, 100, 100,
|
44
|
+
97, 116, 97, 164, 99, 107, 101, 121, 120, 36, 49, 101, 102, 51, 98, 51, 50, 97, 45, 51, 99, 51, 97, 45, 52, 98, 53,
|
45
|
+
101, 45, 57, 99, 49, 99, 45, 56, 99, 53, 99, 48, 99, 53, 99, 48, 99, 53, 99, 100, 114, 111, 111, 116, 163, 99, 99,
|
46
|
+
105, 100, 216, 42, 88, 37, 0, 1, 113, 18, 32, 182, 49, 212, 214, 100, 1, 169, 136, 8, 175, 250, 164, 87, 186, 245,
|
47
|
+
100, 132, 15, 48, 22, 184, 8, 165, 17, 16, 139, 22, 223, 40, 41, 194, 114, 101, 98, 121, 116, 101, 115, 88, 72, 162,
|
48
|
+
100, 108, 101, 97, 102, 129, 130, 120, 36, 49, 101, 102, 51, 98, 51, 50, 97, 45, 51, 99, 51, 97, 45, 52, 98, 53, 101,
|
49
|
+
45, 57, 99, 49, 99, 45, 56, 99, 53, 99, 48, 99, 53, 99, 48, 99, 53, 99, 162, 99, 97, 103, 101, 24, 42, 100, 110, 97,
|
50
|
+
109, 101, 101, 97, 108, 105, 99, 101, 102, 99, 108, 111, 115, 101, 100, 244, 101, 118, 97, 108, 117, 101, 162, 100,
|
51
|
+
108, 101, 97, 102, 129, 130, 120, 36, 49, 101, 102, 51, 98, 51, 50, 97, 45, 51, 99, 51, 97, 45, 52, 98, 53, 101, 45,
|
52
|
+
57, 99, 49, 99, 45, 56, 99, 53, 99, 48, 99, 53, 99, 48, 99, 53, 99, 162, 99, 97, 103, 101, 24, 42, 100, 110, 97, 109,
|
53
|
+
101, 101, 97, 108, 105, 99, 101, 102, 99, 108, 111, 115, 101, 100, 244, 100, 116, 121, 112, 101, 99, 112, 117, 116,
|
54
|
+
101, 118, 97, 108, 117, 101, 162, 99, 97, 103, 101, 24, 42, 100, 110, 97, 109, 101, 101, 97, 108, 105, 99, 101, 103,
|
55
|
+
112, 97, 114, 101, 110, 116, 115, 128
|
56
|
+
]);
|