@fireproof/core 0.6.5 → 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} +2 -1
- package/dist/fireproof.js +92 -0
- package/dist/{src/loader.js → loader.js} +2 -3
- package/dist/{src/prolly.js → prolly.js} +1 -0
- package/dist/src/fireproof.d.ts +137 -136
- package/dist/src/fireproof.js +18179 -11484
- package/dist/src/fireproof.js.map +1 -1
- package/dist/src/fireproof.mjs +18180 -11484
- package/dist/src/fireproof.mjs.map +1 -1
- package/dist/{src/storage → storage}/filesystem.js +2 -2
- 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 +2 -1
- 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/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/import.js → import.js} +0 -0
- /package/dist/{src/listener.js → listener.js} +0 -0
- /package/dist/{src/sha1.js → sha1.js} +0 -0
- /package/dist/{src/storage → storage}/base.js +0 -0
- /package/dist/{src/storage → storage}/browser.js +0 -0
- /package/dist/{src/storage → storage}/rest.js +0 -0
- /package/dist/{src/storage → storage}/ucan.js +0 -0
- /package/dist/{src/utils.js → utils.js} +0 -0
@@ -1,5 +1,5 @@
|
|
1
|
-
import { readFileSync } from 'fs';
|
2
|
-
import { mkdir, writeFile } from 'fs/promises';
|
1
|
+
import { readFileSync } from 'node:fs';
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
3
3
|
import { join, dirname } from 'path';
|
4
4
|
import { homedir } from 'os';
|
5
5
|
import { Base } from './base.js';
|
package/dist/valet.js
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
import { sha256 } from 'multiformats/hashes/sha2';
|
2
|
+
import * as CBW from '@ipld/car/buffer-writer';
|
3
|
+
import * as raw from 'multiformats/codecs/raw';
|
4
|
+
import * as Block from 'multiformats/block';
|
5
|
+
import { Loader } from './loader.js';
|
6
|
+
// @ts-ignore
|
7
|
+
import { bf } from 'prolly-trees/utils';
|
8
|
+
// @ts-ignore
|
9
|
+
import { nocache as cache } from 'prolly-trees/cache';
|
10
|
+
import { encrypt, decrypt } from './crypto.js';
|
11
|
+
import { Buffer } from 'buffer';
|
12
|
+
const chunker = bf(30);
|
13
|
+
export class Valet {
|
14
|
+
idb = null;
|
15
|
+
name = null;
|
16
|
+
uploadQueue = null;
|
17
|
+
alreadyEnqueued = new Set();
|
18
|
+
instanceId = Math.random().toString(36).slice(2);
|
19
|
+
constructor(name = 'default', config = {}) {
|
20
|
+
this.name = name;
|
21
|
+
// console.log('new Valet', name, config.primary)
|
22
|
+
this.primary = Loader.appropriate(name, config.primary);
|
23
|
+
this.secondary = config.secondary ? Loader.appropriate(name, config.secondary) : null;
|
24
|
+
// set up a promise listener that applies all the headers to the clock
|
25
|
+
// when they resolve
|
26
|
+
const readyP = [this.primary.ready];
|
27
|
+
if (this.secondary)
|
28
|
+
readyP.push(this.secondary.ready);
|
29
|
+
this.ready = Promise.all(readyP).then((blocksReady) => {
|
30
|
+
// console.log('blocksReady valet', this.name, blocksReady)
|
31
|
+
return blocksReady;
|
32
|
+
});
|
33
|
+
}
|
34
|
+
async saveHeader(header) {
|
35
|
+
// each storage needs to add its own carCidMapCarCid to the header
|
36
|
+
if (this.secondary) {
|
37
|
+
this.secondary.saveHeader(header);
|
38
|
+
} // todo: await?
|
39
|
+
return await this.primary.saveHeader(header);
|
40
|
+
}
|
41
|
+
/**
|
42
|
+
* Group the blocks into a car and write it to the valet.
|
43
|
+
* @param {import('./blockstore.js').InnerBlockstore} innerBlockstore
|
44
|
+
* @param {Set<string>} cids
|
45
|
+
* @returns {Promise<void>}
|
46
|
+
* @memberof Valet
|
47
|
+
*/
|
48
|
+
async writeTransaction(innerBlockstore, cids) {
|
49
|
+
if (innerBlockstore.lastCid) {
|
50
|
+
await parkCar(this.primary, innerBlockstore, cids);
|
51
|
+
if (this.secondary)
|
52
|
+
await parkCar(this.secondary, innerBlockstore, cids);
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
throw new Error('missing lastCid for car header');
|
56
|
+
}
|
57
|
+
}
|
58
|
+
/**
|
59
|
+
* Iterate over all blocks in the store.
|
60
|
+
*
|
61
|
+
* @yields {{cid: string, value: Uint8Array}}
|
62
|
+
* @returns {AsyncGenerator<any, any, any>}
|
63
|
+
*/
|
64
|
+
async *cids() {
|
65
|
+
// console.log('valet cids')
|
66
|
+
// todo use cidMap
|
67
|
+
// while (cursor) {
|
68
|
+
// yield { cid: cursor.key, car: cursor.value.car }
|
69
|
+
// cursor = await cursor.continue()
|
70
|
+
// }
|
71
|
+
}
|
72
|
+
remoteBlockFunction = null;
|
73
|
+
async getValetBlock(dataCID) {
|
74
|
+
// console.log('getValetBlock primary', dataCID)
|
75
|
+
try {
|
76
|
+
const { block } = await this.primary.getLoaderBlock(dataCID);
|
77
|
+
return block;
|
78
|
+
}
|
79
|
+
catch (e) {
|
80
|
+
// console.log('getValetBlock error', e)
|
81
|
+
if (this.secondary) {
|
82
|
+
// console.log('getValetBlock secondary', dataCID)
|
83
|
+
try {
|
84
|
+
const { block, reader } = await this.secondary.getLoaderBlock(dataCID);
|
85
|
+
const cids = new Set();
|
86
|
+
for await (const { cid } of reader.entries()) {
|
87
|
+
// console.log(cid, bytes)
|
88
|
+
cids.add(cid.toString());
|
89
|
+
}
|
90
|
+
reader.get = reader.gat; // some consumers prefer get
|
91
|
+
// console.log('replicating', reader.root)
|
92
|
+
reader.lastCid = reader.root.cid;
|
93
|
+
await parkCar(this.primary, reader, [...cids]);
|
94
|
+
return block;
|
95
|
+
}
|
96
|
+
catch (e) {
|
97
|
+
// console.log('getValetBlock secondary error', e)
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
async function parkCar(storage, innerBlockstore, cids) {
|
104
|
+
// console.log('parkCar', this.instanceId, this.name, carCid, cids)
|
105
|
+
let newCar;
|
106
|
+
if (storage.keyMaterial) {
|
107
|
+
// console.log('encrypting car', innerBlockstore.label)
|
108
|
+
newCar = await blocksToEncryptedCarBlock(innerBlockstore.lastCid, innerBlockstore, storage.keyMaterial);
|
109
|
+
}
|
110
|
+
else {
|
111
|
+
// todo should we pass cids in instead of iterating innerBlockstore?
|
112
|
+
newCar = await blocksToCarBlock(innerBlockstore.lastCid, innerBlockstore);
|
113
|
+
}
|
114
|
+
// console.log('new car', newCar.cid.toString())
|
115
|
+
return await storage.saveCar(newCar.cid.toString(), newCar.bytes, cids);
|
116
|
+
}
|
117
|
+
export const blocksToCarBlock = async (rootCids, blocks) => {
|
118
|
+
// console.log('blocksToCarBlock', rootCids, blocks.constructor.name)
|
119
|
+
let size = 0;
|
120
|
+
if (!Array.isArray(rootCids)) {
|
121
|
+
rootCids = [rootCids];
|
122
|
+
}
|
123
|
+
const headerSize = CBW.headerLength({ roots: rootCids });
|
124
|
+
size += headerSize;
|
125
|
+
if (!Array.isArray(blocks)) {
|
126
|
+
blocks = Array.from(blocks.entries());
|
127
|
+
}
|
128
|
+
for (const { cid, bytes } of blocks) {
|
129
|
+
// console.log(cid, bytes)
|
130
|
+
size += CBW.blockLength({ cid, bytes });
|
131
|
+
}
|
132
|
+
const buffer = new Uint8Array(size);
|
133
|
+
const writer = await CBW.createWriter(buffer, { headerSize });
|
134
|
+
for (const cid of rootCids) {
|
135
|
+
writer.addRoot(cid);
|
136
|
+
}
|
137
|
+
for (const { cid, bytes } of blocks) {
|
138
|
+
writer.write({ cid, bytes });
|
139
|
+
}
|
140
|
+
await writer.close();
|
141
|
+
return await Block.encode({ value: writer.bytes, hasher: sha256, codec: raw });
|
142
|
+
};
|
143
|
+
export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blocks, keyMaterial) => {
|
144
|
+
const encryptionKey = Buffer.from(keyMaterial, 'hex');
|
145
|
+
const encryptedBlocks = [];
|
146
|
+
const theCids = [];
|
147
|
+
// console.trace('blocksToEncryptedCarBlock', blocks)
|
148
|
+
for (const { cid } of blocks.entries()) {
|
149
|
+
theCids.push(cid.toString());
|
150
|
+
}
|
151
|
+
// console.log('encrypting', theCids.length, 'blocks', theCids.includes(innerBlockStoreClockRootCid.toString()), keyMaterial)
|
152
|
+
// console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
|
153
|
+
let last;
|
154
|
+
for await (const block of encrypt({
|
155
|
+
cids: theCids,
|
156
|
+
get: async (cid) => blocks.get(cid),
|
157
|
+
key: encryptionKey,
|
158
|
+
hasher: sha256,
|
159
|
+
chunker,
|
160
|
+
cache,
|
161
|
+
// codec: dagcbor, // should be crypto?
|
162
|
+
root: innerBlockStoreClockRootCid
|
163
|
+
})) {
|
164
|
+
encryptedBlocks.push(block);
|
165
|
+
last = block;
|
166
|
+
}
|
167
|
+
// console.log('last', last.cid.toString(), 'for clock', innerBlockStoreClockRootCid.toString())
|
168
|
+
const encryptedCar = await blocksToCarBlock(last.cid, encryptedBlocks);
|
169
|
+
return encryptedCar;
|
170
|
+
};
|
171
|
+
// { root, get, key, cache, chunker, hasher }
|
172
|
+
const memoizeDecryptedCarBlocks = new Map();
|
173
|
+
export const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
174
|
+
if (memoizeDecryptedCarBlocks.has(cid.toString())) {
|
175
|
+
return memoizeDecryptedCarBlocks.get(cid.toString());
|
176
|
+
}
|
177
|
+
else {
|
178
|
+
const blocksPromise = (async () => {
|
179
|
+
const decryptionKey = Buffer.from(keyMaterial, 'hex');
|
180
|
+
// console.log('decrypting', keyMaterial, cid.toString())
|
181
|
+
const cids = new Set();
|
182
|
+
const decryptedBlocks = [];
|
183
|
+
for await (const block of decrypt({
|
184
|
+
root: cid,
|
185
|
+
get,
|
186
|
+
key: decryptionKey,
|
187
|
+
chunker,
|
188
|
+
hasher: sha256,
|
189
|
+
cache
|
190
|
+
// codec: dagcbor
|
191
|
+
})) {
|
192
|
+
decryptedBlocks.push(block);
|
193
|
+
cids.add(block.cid.toString());
|
194
|
+
}
|
195
|
+
return { blocks: decryptedBlocks, cids };
|
196
|
+
})();
|
197
|
+
memoizeDecryptedCarBlocks.set(cid.toString(), blocksPromise);
|
198
|
+
return blocksPromise;
|
199
|
+
}
|
200
|
+
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fireproof/core",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.7.0-alpha.0",
|
4
4
|
"description": "Live data for React, accelerated by proofs, powered by IPFS",
|
5
5
|
"main": "dist/src/fireproof.js",
|
6
6
|
"module": "dist/src/fireproof.mjs",
|
@@ -9,7 +9,7 @@
|
|
9
9
|
"type": "module",
|
10
10
|
"scripts": {
|
11
11
|
"keygen": "node scripts/keygen.js",
|
12
|
-
"test": "standard && npm run test:unencrypted && npm run test:mocha",
|
12
|
+
"test": "standard && tsc && npm run test:unencrypted && npm run test:mocha",
|
13
13
|
"test:unencrypted": "set NO_ENCRYPT=true && npm run test:mocha",
|
14
14
|
"test:mocha": "mocha test/*.test.js",
|
15
15
|
"test:watch": "npm run test:mocha -- -w --parallel",
|
@@ -40,6 +40,7 @@
|
|
40
40
|
"dependencies": {
|
41
41
|
"@ipld/car": "^5.1.0",
|
42
42
|
"@ipld/dag-cbor": "^9.0.0",
|
43
|
+
"@jsonlines/core": "^1.0.2",
|
43
44
|
"@rollup/plugin-commonjs": "^24.0.1",
|
44
45
|
"archy": "^1.0.0",
|
45
46
|
"async": "^3.2.4",
|
@@ -51,6 +52,7 @@
|
|
51
52
|
"idb": "^7.1.1",
|
52
53
|
"ipld-hashmap": "^2.1.18",
|
53
54
|
"multiformats": "^11.0.1",
|
55
|
+
"node-fetch": "^3.3.1",
|
54
56
|
"node-polyfill-webpack-plugin": "^2.0.1",
|
55
57
|
"prolly-trees": "1.0.4",
|
56
58
|
"randombytes": "^2.1.0",
|
@@ -110,9 +112,6 @@
|
|
110
112
|
"default": "./dist/src/fireproof.js",
|
111
113
|
"require": "./dist/src/fireproof.js"
|
112
114
|
},
|
113
|
-
"./hooks/use-fireproof": {
|
114
|
-
"import": "./hooks/use-fireproof.js"
|
115
|
-
},
|
116
115
|
"./package.json": "./package.json"
|
117
116
|
},
|
118
117
|
"files": [
|
package/src/blockstore.js
CHANGED
@@ -41,9 +41,12 @@ export class TransactionBlockstore {
|
|
41
41
|
inflightTransactions = new Set()
|
42
42
|
syncs = new Set()
|
43
43
|
|
44
|
-
constructor (name,
|
44
|
+
constructor (name, config) {
|
45
45
|
if (name) {
|
46
|
-
this.valet = new Valet(name,
|
46
|
+
this.valet = new Valet(name, config)
|
47
|
+
this.ready = this.valet.ready
|
48
|
+
} else {
|
49
|
+
this.ready = Promise.resolve()
|
47
50
|
}
|
48
51
|
this.remoteBlockFunction = null
|
49
52
|
}
|
@@ -58,7 +61,7 @@ export class TransactionBlockstore {
|
|
58
61
|
const key = cid.toString()
|
59
62
|
// it is safe to read from the in-flight transactions becauase they are immutable
|
60
63
|
const bytes = await Promise.any([this.transactionsGet(key), this.committedGet(key)]).catch(e => {
|
61
|
-
|
64
|
+
console.log('get error', cid.toString(), e)
|
62
65
|
return this.networkGet(key)
|
63
66
|
})
|
64
67
|
if (!bytes) throw new Error('Missing block: ' + key)
|
package/src/database.js
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
import { visMerkleClock, visMerkleTree, vis, put, get, getAll, eventsSince } from './prolly.js'
|
3
3
|
import { doTransaction, TransactionBlockstore } from './blockstore.js'
|
4
4
|
import charwise from 'charwise'
|
5
|
-
import { localSet } from './utils.js'
|
6
5
|
import { CID } from 'multiformats'
|
6
|
+
import { DbIndex as Index } from './db-index.js'
|
7
7
|
|
8
8
|
// TypeScript Types
|
9
9
|
// eslint-disable-next-line no-unused-vars
|
@@ -30,13 +30,53 @@ export class Database {
|
|
30
30
|
rootCache = null
|
31
31
|
eventsCache = new Map()
|
32
32
|
|
33
|
-
constructor (name,
|
33
|
+
constructor (name, config = {}) {
|
34
34
|
this.name = name
|
35
|
+
this.clock = []
|
35
36
|
this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`
|
36
|
-
this.blocks = new TransactionBlockstore(name, config
|
37
|
-
this.indexBlocks = new TransactionBlockstore(name + '.indexes', config.
|
38
|
-
|
37
|
+
this.blocks = new TransactionBlockstore(name, config)
|
38
|
+
this.indexBlocks = new TransactionBlockstore(name ? name + '.indexes' : null, { primary: config.index })
|
39
|
+
|
39
40
|
this.config = config
|
41
|
+
// todo we can wait for index blocks elsewhere
|
42
|
+
this.ready = Promise.all([this.blocks.ready, this.indexBlocks.ready]).then(([blocksReady, indexReady]) => {
|
43
|
+
const clock = new Set()
|
44
|
+
// console.log('blocksReady', blocksReady)
|
45
|
+
if (!blocksReady) {
|
46
|
+
return
|
47
|
+
}
|
48
|
+
for (const headers of blocksReady) {
|
49
|
+
for (const [, header] of Object.entries(headers)) {
|
50
|
+
if (!header) continue
|
51
|
+
for (const cid of header.clock) {
|
52
|
+
clock.add(cid)
|
53
|
+
}
|
54
|
+
if (header.index) {
|
55
|
+
this.indexBlocks.valet.primary.setCarCidMapCarCid(header.index.car)
|
56
|
+
this.indexBlocks.valet.primary.setKeyMaterial(header.index.key)
|
57
|
+
}
|
58
|
+
if (header.indexes) {
|
59
|
+
for (const {
|
60
|
+
name,
|
61
|
+
code,
|
62
|
+
clock: { byId, byKey, db }
|
63
|
+
} of header.indexes) {
|
64
|
+
// console.log('index', name, code, { byId, byKey }, db, header.indexes)
|
65
|
+
Index.fromJSON(this, {
|
66
|
+
clock: {
|
67
|
+
byId: byId ? parseCID(byId) : null,
|
68
|
+
byKey: byKey ? parseCID(byKey) : null,
|
69
|
+
db: (db && db.length > 0) ? db.map(c => parseCID(c)) : null
|
70
|
+
},
|
71
|
+
code,
|
72
|
+
name
|
73
|
+
})
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
this.clock = [...clock]
|
79
|
+
})
|
40
80
|
}
|
41
81
|
|
42
82
|
/**
|
@@ -46,12 +86,17 @@ export class Database {
|
|
46
86
|
* @instance
|
47
87
|
*/
|
48
88
|
toJSON () {
|
89
|
+
return this.blocks.valet ? this.blocks.valet.primary.prepareHeader(this.toHeader(), false) : this.toHeader() // omg
|
90
|
+
}
|
91
|
+
|
92
|
+
toHeader () {
|
49
93
|
return {
|
50
94
|
clock: this.clockToJSON(),
|
51
95
|
name: this.name,
|
52
|
-
|
53
|
-
|
54
|
-
|
96
|
+
index: {
|
97
|
+
key: this.indexBlocks.valet?.primary.keyMaterial,
|
98
|
+
car: this.indexBlocks.valet?.primary.valetRootCarCid?.toString()
|
99
|
+
},
|
55
100
|
indexes: [...this.indexes.values()].map(index => index.toJSON())
|
56
101
|
}
|
57
102
|
}
|
@@ -66,27 +111,13 @@ export class Database {
|
|
66
111
|
return (clock || this.clock).map(cid => cid.toString())
|
67
112
|
}
|
68
113
|
|
69
|
-
hydrate ({ clock, name, key, car, indexCar }) {
|
70
|
-
this.name = name
|
71
|
-
this.clock = clock
|
72
|
-
this.blocks.valet?.setKeyMaterial(key)
|
73
|
-
this.blocks.valet?.setRootCarCid(car) // maybe
|
74
|
-
this.indexBlocks.valet?.setKeyMaterial(key)
|
75
|
-
this.indexBlocks.valet?.setRootCarCid(indexCar) // maybe
|
76
|
-
// this.indexBlocks = null
|
77
|
-
}
|
78
|
-
|
79
114
|
maybeSaveClock () {
|
80
115
|
if (this.name && this.blocks.valet) {
|
81
|
-
|
116
|
+
this.blocks.valet.saveHeader(this.toHeader())
|
82
117
|
}
|
83
118
|
}
|
84
119
|
|
85
120
|
index (name) {
|
86
|
-
// iterate over the indexes and gather any with the same name
|
87
|
-
// if there are more than one, throw an error
|
88
|
-
// if there is one, return it
|
89
|
-
// if there are none, return null
|
90
121
|
const indexes = [...this.indexes.values()].filter(index => index.name === name)
|
91
122
|
if (indexes.length > 1) {
|
92
123
|
throw new Error(`Multiple indexes found with name ${name}`)
|
@@ -102,14 +133,10 @@ export class Database {
|
|
102
133
|
* @instance
|
103
134
|
*/
|
104
135
|
async notifyReset () {
|
136
|
+
await this.ready
|
105
137
|
await this.notifyListeners({ _reset: true, _clock: this.clockToJSON() })
|
106
138
|
}
|
107
139
|
|
108
|
-
// used be indexes etc to notify database listeners of new availability
|
109
|
-
// async notifyExternal (source = 'unknown') {
|
110
|
-
// // await this.notifyListeners({ _external: source, _clock: this.clockToJSON() })
|
111
|
-
// }
|
112
|
-
|
113
140
|
/**
|
114
141
|
* Returns the changes made to the Fireproof instance since the specified event.
|
115
142
|
* @function changesSince
|
@@ -119,6 +146,7 @@ export class Database {
|
|
119
146
|
* @instance
|
120
147
|
*/
|
121
148
|
async changesSince (aClock) {
|
149
|
+
await this.ready
|
122
150
|
// console.log('events for', this.instanceId, aClock?.constructor.name)
|
123
151
|
// console.log('changesSince', this.instanceId, this.clockToJSON(aClock), this.clockToJSON())
|
124
152
|
let rows, dataCIDs, clockCIDs
|
@@ -162,6 +190,7 @@ export class Database {
|
|
162
190
|
}
|
163
191
|
|
164
192
|
async allDocuments () {
|
193
|
+
await this.ready
|
165
194
|
const allResp = await getAll(this.blocks, this.clock, this.rootCache)
|
166
195
|
this.rootCache = { root: allResp.root, clockCIDs: allResp.clockCIDs }
|
167
196
|
|
@@ -176,6 +205,7 @@ export class Database {
|
|
176
205
|
}
|
177
206
|
|
178
207
|
async allCIDs () {
|
208
|
+
await this.ready
|
179
209
|
const allResp = await getAll(this.blocks, this.clock, this.rootCache, true)
|
180
210
|
this.rootCache = { root: allResp.root, clockCIDs: allResp.clockCIDs }
|
181
211
|
// console.log('allcids', allResp.cids, allResp.clockCIDs)
|
@@ -186,6 +216,7 @@ export class Database {
|
|
186
216
|
}
|
187
217
|
|
188
218
|
async allStoredCIDs () {
|
219
|
+
await this.ready
|
189
220
|
const allCIDs = []
|
190
221
|
for await (const { cid } of this.blocks.entries()) {
|
191
222
|
allCIDs.push(cid)
|
@@ -193,24 +224,6 @@ export class Database {
|
|
193
224
|
return allCIDs
|
194
225
|
}
|
195
226
|
|
196
|
-
/**
|
197
|
-
* Runs validation on the specified document using the Fireproof instance's configuration. Throws an error if the document is invalid.
|
198
|
-
*
|
199
|
-
* @param {Object} doc - The document to validate.
|
200
|
-
* @returns {Promise<void>}
|
201
|
-
* @throws {Error} - Throws an error if the document is invalid.
|
202
|
-
* @memberof Fireproof
|
203
|
-
* @instance
|
204
|
-
*/
|
205
|
-
async runValidation (doc) {
|
206
|
-
if (this.config && this.config.validateChange) {
|
207
|
-
const oldDoc = await this.get(doc._id)
|
208
|
-
.then(doc => doc)
|
209
|
-
.catch(() => ({}))
|
210
|
-
this.config.validateChange(doc, oldDoc, this.authCtx)
|
211
|
-
}
|
212
|
-
}
|
213
|
-
|
214
227
|
/**
|
215
228
|
* Retrieves the document with the specified ID from the database
|
216
229
|
*
|
@@ -221,6 +234,7 @@ export class Database {
|
|
221
234
|
* @instance
|
222
235
|
*/
|
223
236
|
async get (key, opts = {}) {
|
237
|
+
await this.ready
|
224
238
|
const clock = opts.clock || this.clock
|
225
239
|
const resp = await get(this.blocks, clock, charwise.encode(key), this.rootCache)
|
226
240
|
this.rootCache = { root: resp.root, clockCIDs: resp.clockCIDs }
|
@@ -256,6 +270,7 @@ export class Database {
|
|
256
270
|
* @instance
|
257
271
|
*/
|
258
272
|
async put ({ _id, _proof, ...doc }) {
|
273
|
+
await this.ready
|
259
274
|
const id = _id || 'f' + Math.random().toString(36).slice(2)
|
260
275
|
await this.runValidation({ _id: id, ...doc })
|
261
276
|
return await this.putToProllyTree({ key: id, value: doc }, doc._clock)
|
@@ -269,6 +284,7 @@ export class Database {
|
|
269
284
|
* @instance
|
270
285
|
*/
|
271
286
|
async del (docOrId) {
|
287
|
+
await this.ready
|
272
288
|
let id
|
273
289
|
let clock = null
|
274
290
|
if (docOrId._id) {
|
@@ -283,6 +299,24 @@ export class Database {
|
|
283
299
|
// return await this.putToProllyTree({ key: id, value: null }, clock)
|
284
300
|
}
|
285
301
|
|
302
|
+
/**
|
303
|
+
* Runs validation on the specified document using the Fireproof instance's configuration. Throws an error if the document is invalid.
|
304
|
+
*
|
305
|
+
* @param {Object} doc - The document to validate.
|
306
|
+
* @returns {Promise<void>}
|
307
|
+
* @throws {Error} - Throws an error if the document is invalid.
|
308
|
+
* @memberof Fireproof
|
309
|
+
* @instance
|
310
|
+
*/
|
311
|
+
async runValidation (doc) {
|
312
|
+
if (this.config && this.config.validateChange) {
|
313
|
+
const oldDoc = await this.get(doc._id)
|
314
|
+
.then(doc => doc)
|
315
|
+
.catch(() => ({}))
|
316
|
+
this.config.validateChange(doc, oldDoc, this.authCtx)
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
286
320
|
/**
|
287
321
|
* Updates the underlying storage with the specified event.
|
288
322
|
* @private
|
@@ -395,12 +429,6 @@ export class Database {
|
|
395
429
|
}
|
396
430
|
}
|
397
431
|
|
398
|
-
setCarUploader (carUploaderFn) {
|
399
|
-
// console.log('registering car uploader')
|
400
|
-
// https://en.wikipedia.org/wiki/Law_of_Demeter - this is a violation of the law of demeter
|
401
|
-
this.blocks.valet.uploadFunction = carUploaderFn
|
402
|
-
}
|
403
|
-
|
404
432
|
setRemoteBlockReader (remoteBlockReaderFn) {
|
405
433
|
this.blocks.remoteBlockFunction = remoteBlockReaderFn
|
406
434
|
}
|
package/src/db-index.js
CHANGED
@@ -247,6 +247,7 @@ export class DbIndex {
|
|
247
247
|
* @returns
|
248
248
|
*/
|
249
249
|
async applyQuery (resp, query) {
|
250
|
+
// console.log('applyQuery', resp, query)
|
250
251
|
if (query.descending) {
|
251
252
|
resp.result = resp.result.reverse()
|
252
253
|
}
|
@@ -280,7 +281,7 @@ export class DbIndex {
|
|
280
281
|
return await this.applyQuery(await this.indexByKey.root.range(...encodedRange), query)
|
281
282
|
} else if (query.key) {
|
282
283
|
const encodedKey = charwise.encode(query.key)
|
283
|
-
return await this.applyQuery(this.indexByKey.root.get(encodedKey), query)
|
284
|
+
return await this.applyQuery(await this.indexByKey.root.get(encodedKey), query)
|
284
285
|
} else {
|
285
286
|
const { result, ...all } = await this.indexByKey.root.getAllEntries()
|
286
287
|
return await this.applyQuery(
|
package/src/fireproof.js
CHANGED
@@ -1,13 +1,8 @@
|
|
1
|
-
import randomBytes from 'randombytes'
|
2
1
|
import { Database, parseCID } from './database.js'
|
3
|
-
import { Listener } from './listener.js'
|
4
2
|
import { DbIndex as Index } from './db-index.js'
|
5
|
-
// import { TransactionBlockstore } from './blockstore.js'
|
6
|
-
import { localGet } from './utils.js'
|
7
3
|
import { Sync } from './sync.js'
|
8
4
|
|
9
|
-
|
10
|
-
export { Index, Listener, Database, Sync }
|
5
|
+
export { Index, Database, Sync }
|
11
6
|
|
12
7
|
export class Fireproof {
|
13
8
|
/**
|
@@ -16,34 +11,47 @@ export class Fireproof {
|
|
16
11
|
* Creates a new Fireproof instance with default storage settings
|
17
12
|
* Most apps should use this and not worry about the details.
|
18
13
|
* @static
|
19
|
-
* @returns {Database} - a new Fireproof instance
|
14
|
+
* @returns {Database|Promise<Database>} - a new Fireproof instance or a promise for remote loaders
|
20
15
|
*/
|
21
16
|
static storage = (name = null, opts = {}) => {
|
22
|
-
if (name) {
|
23
|
-
|
24
|
-
// todo this can come from a registry also eg remote database / config, etc
|
25
|
-
const existing = localGet('fp.' + name)
|
26
|
-
if (existing) {
|
27
|
-
const existingConfig = JSON.parse(existing)
|
28
|
-
return Fireproof.fromConfig(name, existingConfig, opts)
|
29
|
-
} else {
|
30
|
-
const instanceKey = randomBytes(32).toString('hex') // pass null to disable encryption
|
31
|
-
opts.key = instanceKey
|
32
|
-
return new Database(name, [], opts)
|
33
|
-
}
|
17
|
+
if (!name) {
|
18
|
+
return new Database(null, opts)
|
34
19
|
} else {
|
35
|
-
|
20
|
+
// const primaryLoader = Loader.appropriate(name, opts.primary, { key: null })
|
21
|
+
// const secondaryLoader = opts.secondary ? Loader.appropriate(name, opts.secondary, { key: null }) : null
|
22
|
+
const db = new Database(name, opts)
|
23
|
+
return db
|
24
|
+
// const loaders = [pr]
|
25
|
+
|
26
|
+
// todo we need branch names here
|
27
|
+
|
28
|
+
// console.log('storage', name, opts, primaryLoader, secondaryLoader)
|
36
29
|
}
|
37
30
|
}
|
38
31
|
|
39
|
-
static fromConfig (name,
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
// static fromConfig (name, primary, secondary, opts = {}) {
|
33
|
+
// console.log('fromConfig', name, primary, secondary, opts)
|
34
|
+
// let clock = []
|
35
|
+
// if (primary && primary.clock) {
|
36
|
+
// clock = clock.concat(primary.clock)
|
37
|
+
// }
|
38
|
+
// if (secondary && secondary.clock) {
|
39
|
+
// clock = clock.concat(secondary.clock)
|
40
|
+
// }
|
41
|
+
|
42
|
+
// const mergedClock = [...new Set(clock)].map(c => parseCID(c))
|
43
|
+
|
44
|
+
// opts.primaryHeader = primary
|
45
|
+
// opts.secondaryHeader = secondary
|
46
|
+
|
47
|
+
// opts.index = primary ? primary.index : {}
|
44
48
|
|
45
|
-
|
46
|
-
|
49
|
+
// const fp = new Database(name, mergedClock, opts)
|
50
|
+
// return Fireproof.fromJSON(primary, secondary, fp)
|
51
|
+
// }
|
52
|
+
|
53
|
+
static fromJSON (primary, secondary, database) {
|
54
|
+
const json = primary && primary.indexes ? primary : secondary
|
47
55
|
if (json.indexes) {
|
48
56
|
for (const {
|
49
57
|
name,
|
@@ -66,8 +74,6 @@ export class Fireproof {
|
|
66
74
|
|
67
75
|
static snapshot (database, clock) {
|
68
76
|
const definition = database.toJSON()
|
69
|
-
const withBlocks = new Database(database.name)
|
70
|
-
withBlocks.blocks = database.blocks
|
71
77
|
if (clock) {
|
72
78
|
definition.clock = clock.map(c => parseCID(c))
|
73
79
|
definition.indexes.forEach(index => {
|
@@ -76,7 +82,12 @@ export class Fireproof {
|
|
76
82
|
index.clock.db = null
|
77
83
|
})
|
78
84
|
}
|
79
|
-
|
85
|
+
|
86
|
+
const withBlocks = new Database(database.name)
|
87
|
+
withBlocks.blocks = database.blocks
|
88
|
+
withBlocks.clock = definition.clock
|
89
|
+
|
90
|
+
const snappedDb = Fireproof.fromJSON(definition, null, withBlocks)
|
80
91
|
;[...database.indexes.values()].forEach(index => {
|
81
92
|
snappedDb.indexes.get(index.mapFnString).mapFn = index.mapFn
|
82
93
|
})
|