@fireproof/core 0.6.2 → 0.6.3-dev
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 +10 -4
- package/dist/src/crypto-poly.js +4 -0
- package/dist/src/database.js +3 -3
- package/dist/src/fireproof.d.ts +26 -2
- package/dist/src/fireproof.js +583 -319
- package/dist/src/fireproof.js.map +1 -1
- package/dist/src/fireproof.mjs +583 -319
- package/dist/src/fireproof.mjs.map +1 -1
- package/dist/src/loader.js +131 -0
- package/dist/src/prolly.js +1 -0
- package/dist/src/valet.js +18 -33
- package/package.json +3 -1
- package/src/database.js +3 -3
- package/src/fireproof.js +4 -3
- package/src/loader.js +168 -0
- package/src/prolly.js +2 -0
- package/src/valet.js +21 -36
- package/src/utils.js +0 -16
@@ -0,0 +1,131 @@
|
|
1
|
+
import { readFileSync
|
2
|
+
// createReadStream
|
3
|
+
} from 'node:fs';
|
4
|
+
import { mkdir, writeFile } from 'fs/promises';
|
5
|
+
import { openDB } from 'idb';
|
6
|
+
import { join, dirname } from 'path';
|
7
|
+
// import { parse } from '@jsonlines/core'
|
8
|
+
// import cargoQueue from 'async/cargoQueue.js'
|
9
|
+
import { homedir } from 'os';
|
10
|
+
const defaultConfig = {
|
11
|
+
dataDir: join(homedir(), '.fireproof'),
|
12
|
+
headerKeyPrefix: 'fp.'
|
13
|
+
};
|
14
|
+
const FORCE_IDB = typeof process !== 'undefined' && !!process.env?.FORCE_IDB;
|
15
|
+
/* global localStorage */
|
16
|
+
export class Loader {
|
17
|
+
constructor(name, keyId, config = defaultConfig) {
|
18
|
+
this.name = name;
|
19
|
+
this.keyId = keyId;
|
20
|
+
this.config = config;
|
21
|
+
this.isBrowser = false;
|
22
|
+
try {
|
23
|
+
this.isBrowser = window.localStorage && true;
|
24
|
+
}
|
25
|
+
catch (e) { }
|
26
|
+
}
|
27
|
+
withDB = async (dbWorkFun) => {
|
28
|
+
if (!this.idb) {
|
29
|
+
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
30
|
+
upgrade(db, oldVersion, newVersion, transaction) {
|
31
|
+
if (oldVersion < 1) {
|
32
|
+
db.createObjectStore('cars');
|
33
|
+
}
|
34
|
+
}
|
35
|
+
});
|
36
|
+
}
|
37
|
+
return await dbWorkFun(this.idb);
|
38
|
+
};
|
39
|
+
async writeCars(cars) {
|
40
|
+
// console.log('writeCars', this.config.dataDir, this.name, cars.map(c => c.cid.toString()))
|
41
|
+
// console.log('writeCars', cars.length)
|
42
|
+
if (FORCE_IDB || this.isBrowser) {
|
43
|
+
await this.writeCarsIDB(cars);
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
const writes = [];
|
47
|
+
for (const { cid, bytes } of cars) {
|
48
|
+
const carFilename = join(this.config.dataDir, this.name, `${cid.toString()}.car`);
|
49
|
+
// console.log('writeCars', carFilename)
|
50
|
+
writes.push(writeSync(carFilename, bytes));
|
51
|
+
}
|
52
|
+
await Promise.all(writes);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
async writeCarsIDB(cars) {
|
56
|
+
return await this.withDB(async (db) => {
|
57
|
+
const tx = db.transaction(['cars'], 'readwrite');
|
58
|
+
for (const { cid, bytes, replaces } of cars) {
|
59
|
+
await tx.objectStore('cars').put(bytes, cid.toString());
|
60
|
+
// todo remove old maps
|
61
|
+
if (replaces) {
|
62
|
+
await tx.objectStore('cars').delete(replaces.toString());
|
63
|
+
}
|
64
|
+
}
|
65
|
+
return await tx.done;
|
66
|
+
});
|
67
|
+
}
|
68
|
+
async readCar(carCid) {
|
69
|
+
if (FORCE_IDB || this.isBrowser) {
|
70
|
+
return await this.readCarIDB(carCid);
|
71
|
+
}
|
72
|
+
else {
|
73
|
+
const carFilename = join(this.config.dataDir, this.name, `${carCid.toString()}.car`);
|
74
|
+
const got = readFileSync(carFilename);
|
75
|
+
// console.log('readCar', carFilename, got.constructor.name)
|
76
|
+
return got;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
async readCarIDB(carCid) {
|
80
|
+
return await this.withDB(async (db) => {
|
81
|
+
const tx = db.transaction(['cars'], 'readonly');
|
82
|
+
// console.log('getCarReader', carCid)
|
83
|
+
return await tx.objectStore('cars').get(carCid);
|
84
|
+
});
|
85
|
+
}
|
86
|
+
getHeader() {
|
87
|
+
if (this.isBrowser) {
|
88
|
+
return localStorage.getItem(this.config.headerKeyPrefix + this.name);
|
89
|
+
}
|
90
|
+
else {
|
91
|
+
return loadSync(this.headerFilename());
|
92
|
+
// return null
|
93
|
+
}
|
94
|
+
}
|
95
|
+
async saveHeader(stringValue) {
|
96
|
+
// console.log('saveHeader', this.isBrowser)
|
97
|
+
if (this.isBrowser) {
|
98
|
+
// console.log('localStorage!', this.config.headerKeyPrefix)
|
99
|
+
return localStorage.setItem(this.config.headerKeyPrefix + this.name, stringValue);
|
100
|
+
}
|
101
|
+
else {
|
102
|
+
// console.log('no localStorage', this.config.dataDir, this.name)
|
103
|
+
// console.log('saving clock to', this.headerFilename(), stringValue)
|
104
|
+
try {
|
105
|
+
await writeSync(this.headerFilename(), stringValue);
|
106
|
+
}
|
107
|
+
catch (error) {
|
108
|
+
console.log('error', error);
|
109
|
+
}
|
110
|
+
// console.log('saved clock to', this.headerFilename())
|
111
|
+
}
|
112
|
+
}
|
113
|
+
headerFilename() {
|
114
|
+
// console.log('headerFilename', this.config.dataDir, this.name)
|
115
|
+
return join(this.config.dataDir, this.name, 'header.json');
|
116
|
+
}
|
117
|
+
}
|
118
|
+
function loadSync(filename) {
|
119
|
+
try {
|
120
|
+
return readFileSync(filename, 'utf8').toString();
|
121
|
+
}
|
122
|
+
catch (error) {
|
123
|
+
// console.log('error', error)
|
124
|
+
return null;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
async function writeSync(fullpath, stringValue) {
|
128
|
+
await mkdir(dirname(fullpath), { recursive: true });
|
129
|
+
// writeFileSync(fullpath, stringValue)
|
130
|
+
await writeFile(fullpath, stringValue);
|
131
|
+
}
|
package/dist/src/prolly.js
CHANGED
@@ -8,6 +8,7 @@ import { nocache as cache } from 'prolly-trees/cache';
|
|
8
8
|
import { CIDCounter, bf, simpleCompare as compare } from 'prolly-trees/utils';
|
9
9
|
import * as codec from '@ipld/dag-cbor';
|
10
10
|
import { sha256 as hasher } from 'multiformats/hashes/sha2';
|
11
|
+
// import { blake2b256 as hasher } from '@multiformats/blake2/blake2b'
|
11
12
|
import { doTransaction } from './blockstore.js';
|
12
13
|
import { create as createBlock } from 'multiformats/block';
|
13
14
|
const blockOpts = { cache, chunker: bf(30), codec, hasher, compare };
|
package/dist/src/valet.js
CHANGED
@@ -6,8 +6,8 @@ import * as CBW from '@ipld/car/buffer-writer';
|
|
6
6
|
import * as raw from 'multiformats/codecs/raw';
|
7
7
|
import * as Block from 'multiformats/block';
|
8
8
|
import * as dagcbor from '@ipld/dag-cbor';
|
9
|
-
import { openDB } from 'idb';
|
10
9
|
import cargoQueue from 'async/cargoQueue.js';
|
10
|
+
import { Loader } from './loader.js';
|
11
11
|
// @ts-ignore
|
12
12
|
// @ts-ignore
|
13
13
|
import { bf, simpleCompare as compare } from 'prolly-trees/utils';
|
@@ -44,6 +44,7 @@ export class Valet {
|
|
44
44
|
constructor(name = 'default', keyMaterial) {
|
45
45
|
this.name = name;
|
46
46
|
this.setKeyMaterial(keyMaterial);
|
47
|
+
this.loader = new Loader(name, this.keyId); // todo send this config.loader, if we ever need it
|
47
48
|
this.uploadQueue = cargoQueue(async (tasks, callback) => {
|
48
49
|
// console.log(
|
49
50
|
// 'queue worker',
|
@@ -76,6 +77,9 @@ export class Valet {
|
|
76
77
|
// })
|
77
78
|
});
|
78
79
|
}
|
80
|
+
saveHeader(header) {
|
81
|
+
return this.loader.saveHeader(header);
|
82
|
+
}
|
79
83
|
getKeyMaterial() {
|
80
84
|
return this.keyMaterial;
|
81
85
|
}
|
@@ -116,18 +120,6 @@ export class Valet {
|
|
116
120
|
throw new Error('missing lastCid for car header');
|
117
121
|
}
|
118
122
|
}
|
119
|
-
withDB = async (dbWorkFun) => {
|
120
|
-
if (!this.idb) {
|
121
|
-
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
122
|
-
upgrade(db, oldVersion, newVersion, transaction) {
|
123
|
-
if (oldVersion < 1) {
|
124
|
-
db.createObjectStore('cars');
|
125
|
-
}
|
126
|
-
}
|
127
|
-
});
|
128
|
-
}
|
129
|
-
return await dbWorkFun(this.idb);
|
130
|
-
};
|
131
123
|
/**
|
132
124
|
* Iterate over all blocks in the store.
|
133
125
|
*
|
@@ -167,6 +159,7 @@ export class Valet {
|
|
167
159
|
blockHasher: blockOpts.hasher,
|
168
160
|
blockCodec: blockOpts.codec
|
169
161
|
});
|
162
|
+
this.valetRoot = indexNode;
|
170
163
|
}
|
171
164
|
const got = await indexNode.get(cid);
|
172
165
|
// console.log('getCarCIDForCID', cid, got)
|
@@ -211,13 +204,14 @@ export class Valet {
|
|
211
204
|
* @param {*} value
|
212
205
|
*/
|
213
206
|
async parkCar(carCid, value, cids) {
|
207
|
+
// const callId = Math.random().toString(36).substring(7)
|
214
208
|
// console.log('parkCar', this.instanceId, this.name, carCid, cids)
|
215
209
|
const combinedReader = await this.getCombinedReader(carCid);
|
216
210
|
const mapNode = await addCidsToCarIndex(combinedReader, this.valetRoot, this.valetRootCid, Array.from(cids).map(cid => ({ key: cid.toString(), value: carCid.toString() })));
|
217
211
|
this.valetRoot = mapNode;
|
218
212
|
this.valetRootCid = mapNode.cid;
|
219
213
|
// make a block set with all the cids of the map
|
220
|
-
const saveValetBlocks = new VMemoryBlockstore();
|
214
|
+
const saveValetBlocks = new VMemoryBlockstore();
|
221
215
|
for await (const cidx of mapNode.cids()) {
|
222
216
|
const bytes = await combinedReader.get(cidx);
|
223
217
|
saveValetBlocks.put(cidx, bytes);
|
@@ -230,7 +224,8 @@ export class Valet {
|
|
230
224
|
newValetCidCar = await blocksToCarBlock(this.valetRootCid, saveValetBlocks);
|
231
225
|
}
|
232
226
|
// console.log('newValetCidCar', this.name, Math.floor(newValetCidCar.bytes.length / 1024))
|
233
|
-
|
227
|
+
// console.log('writeCars', callId, carCid.toString(), newValetCidCar.cid.toString())
|
228
|
+
await this.loader.writeCars([
|
234
229
|
{
|
235
230
|
cid: carCid,
|
236
231
|
bytes: value,
|
@@ -244,6 +239,7 @@ export class Valet {
|
|
244
239
|
}
|
245
240
|
]);
|
246
241
|
this.valetRootCarCid = newValetCidCar.cid; // goes to clock
|
242
|
+
// console.log('wroteCars', callId, carCid.toString(), newValetCidCar.cid.toString())
|
247
243
|
// console.log('parked car', carCid, value.length, Array.from(cids))
|
248
244
|
// upload to web3.storage if we have credentials
|
249
245
|
if (this.uploadFunction) {
|
@@ -260,27 +256,12 @@ export class Valet {
|
|
260
256
|
// console.log('no upload function', carCid, value.length, this.uploadFunction)
|
261
257
|
}
|
262
258
|
}
|
263
|
-
async writeCars(cars) {
|
264
|
-
return await this.withDB(async (db) => {
|
265
|
-
const tx = db.transaction(['cars'], 'readwrite');
|
266
|
-
for (const { cid, bytes, replaces } of cars) {
|
267
|
-
await tx.objectStore('cars').put(bytes, cid.toString());
|
268
|
-
// todo remove old maps
|
269
|
-
if (replaces) {
|
270
|
-
await tx.objectStore('cars').delete(replaces.toString());
|
271
|
-
}
|
272
|
-
}
|
273
|
-
return await tx.done;
|
274
|
-
});
|
275
|
-
}
|
276
259
|
remoteBlockFunction = null;
|
277
260
|
async getCarReader(carCid) {
|
278
261
|
carCid = carCid.toString();
|
279
|
-
const carBytes = await this.
|
280
|
-
|
281
|
-
|
282
|
-
return await tx.objectStore('cars').get(carCid);
|
283
|
-
});
|
262
|
+
const carBytes = await this.loader.readCar(carCid);
|
263
|
+
// const callID = Math.random().toString(36).substring(7)
|
264
|
+
// console.log('getCarReader', callID, carCid)
|
284
265
|
const reader = await CarReader.fromBytes(carBytes);
|
285
266
|
if (this.keyMaterial) {
|
286
267
|
const roots = await reader.getRoots();
|
@@ -303,6 +284,7 @@ export class Valet {
|
|
303
284
|
const { blocks } = await blocksFromEncryptedCarBlock(roots[0], readerGetWithCodec, this.keyMaterial);
|
304
285
|
// last block is the root ??? todo
|
305
286
|
const rootBlock = blocks[blocks.length - 1];
|
287
|
+
// console.log('got reader', callID, carCid)
|
306
288
|
return {
|
307
289
|
root: rootBlock,
|
308
290
|
get: async (dataCID) => {
|
@@ -424,6 +406,8 @@ const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
|
424
406
|
};
|
425
407
|
const addCidsToCarIndex = async (blockstore, valetRoot, valetRootCid, bulkOperations) => {
|
426
408
|
let indexNode;
|
409
|
+
// const callID = Math.random().toString(32).substring(2, 8)
|
410
|
+
// console.log('addCidsToCarIndex', callID, valetRootCid, bulkOperations.length)
|
427
411
|
if (valetRootCid) {
|
428
412
|
if (valetRoot) {
|
429
413
|
indexNode = valetRoot;
|
@@ -445,6 +429,7 @@ const addCidsToCarIndex = async (blockstore, valetRoot, valetRootCid, bulkOperat
|
|
445
429
|
// console.log('adding', key, value)
|
446
430
|
await indexNode.set(key, value);
|
447
431
|
}
|
432
|
+
// console.log('newCidsToCarIndex', callID, indexNode.cid, bulkOperations.length)
|
448
433
|
return indexNode;
|
449
434
|
};
|
450
435
|
export class VMemoryBlockstore {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fireproof/core",
|
3
|
-
"version": "0.6.
|
3
|
+
"version": "0.6.3-dev",
|
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",
|
@@ -40,6 +40,8 @@
|
|
40
40
|
"dependencies": {
|
41
41
|
"@ipld/car": "^5.1.0",
|
42
42
|
"@ipld/dag-cbor": "^9.0.0",
|
43
|
+
"@jsonlines/core": "^1.0.2",
|
44
|
+
"@multiformats/blake2": "^1.0.13",
|
43
45
|
"@rollup/plugin-commonjs": "^24.0.1",
|
44
46
|
"archy": "^1.0.0",
|
45
47
|
"async": "^3.2.4",
|
package/src/database.js
CHANGED
@@ -2,7 +2,6 @@
|
|
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'
|
7
6
|
|
8
7
|
// TypeScript Types
|
@@ -34,7 +33,7 @@ export class Database {
|
|
34
33
|
this.name = name
|
35
34
|
this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`
|
36
35
|
this.blocks = new TransactionBlockstore(name, config.key)
|
37
|
-
this.indexBlocks = new TransactionBlockstore(name + '.indexes', config.key)
|
36
|
+
this.indexBlocks = new TransactionBlockstore(name ? name + '.indexes' : null, config.key)
|
38
37
|
this.clock = clock
|
39
38
|
this.config = config
|
40
39
|
}
|
@@ -78,7 +77,7 @@ export class Database {
|
|
78
77
|
|
79
78
|
maybeSaveClock () {
|
80
79
|
if (this.name && this.blocks.valet) {
|
81
|
-
|
80
|
+
this.blocks.valet.saveHeader(JSON.stringify(this))
|
82
81
|
}
|
83
82
|
}
|
84
83
|
|
@@ -314,6 +313,7 @@ export class Database {
|
|
314
313
|
console.error('failed', event)
|
315
314
|
throw new Error('failed to put at storage layer')
|
316
315
|
}
|
316
|
+
// await new Promise(resolve => setTimeout(resolve, 10)) // makes concurrent tests work
|
317
317
|
this.applyClock(prevClock, result.head)
|
318
318
|
await this.notifyListeners([decodedEvent]) // this type is odd
|
319
319
|
return {
|
package/src/fireproof.js
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
import randomBytes from 'randombytes'
|
2
|
+
// import { randomBytes } from 'crypto'
|
2
3
|
import { Database, parseCID } from './database.js'
|
3
4
|
import { Listener } from './listener.js'
|
4
5
|
import { DbIndex as Index } from './db-index.js'
|
5
6
|
// import { TransactionBlockstore } from './blockstore.js'
|
6
|
-
import {
|
7
|
+
import { Loader } from './loader.js'
|
7
8
|
import { Sync } from './sync.js'
|
8
9
|
|
9
10
|
// todo remove Listener in 0.7.0
|
@@ -21,8 +22,8 @@ export class Fireproof {
|
|
21
22
|
static storage = (name = null, opts = {}) => {
|
22
23
|
if (name) {
|
23
24
|
opts.name = name
|
24
|
-
|
25
|
-
const existing =
|
25
|
+
const loader = new Loader(name, opts.loader)
|
26
|
+
const existing = loader.getHeader()
|
26
27
|
if (existing) {
|
27
28
|
const existingConfig = JSON.parse(existing)
|
28
29
|
return Fireproof.fromConfig(name, existingConfig, opts)
|
package/src/loader.js
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
import {
|
2
|
+
readFileSync
|
3
|
+
// createReadStream
|
4
|
+
} from 'node:fs'
|
5
|
+
import { mkdir, writeFile } from 'fs/promises'
|
6
|
+
import { openDB } from 'idb'
|
7
|
+
import { join, dirname } from 'path'
|
8
|
+
// import { parse } from '@jsonlines/core'
|
9
|
+
// import cargoQueue from 'async/cargoQueue.js'
|
10
|
+
import { homedir } from 'os'
|
11
|
+
|
12
|
+
const defaultConfig = {
|
13
|
+
dataDir: join(homedir(), '.fireproof'),
|
14
|
+
headerKeyPrefix: 'fp.'
|
15
|
+
}
|
16
|
+
|
17
|
+
const FORCE_IDB = typeof process !== 'undefined' && !!process.env?.FORCE_IDB
|
18
|
+
|
19
|
+
/* global localStorage */
|
20
|
+
|
21
|
+
export class Loader {
|
22
|
+
constructor (name, keyId, config = defaultConfig) {
|
23
|
+
this.name = name
|
24
|
+
this.keyId = keyId
|
25
|
+
this.config = config
|
26
|
+
this.isBrowser = false
|
27
|
+
try {
|
28
|
+
this.isBrowser = window.localStorage && true
|
29
|
+
} catch (e) {}
|
30
|
+
}
|
31
|
+
|
32
|
+
withDB = async (dbWorkFun) => {
|
33
|
+
if (!this.idb) {
|
34
|
+
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
35
|
+
upgrade (db, oldVersion, newVersion, transaction) {
|
36
|
+
if (oldVersion < 1) {
|
37
|
+
db.createObjectStore('cars')
|
38
|
+
}
|
39
|
+
}
|
40
|
+
})
|
41
|
+
}
|
42
|
+
return await dbWorkFun(this.idb)
|
43
|
+
}
|
44
|
+
|
45
|
+
async writeCars (cars) {
|
46
|
+
// console.log('writeCars', this.config.dataDir, this.name, cars.map(c => c.cid.toString()))
|
47
|
+
// console.log('writeCars', cars.length)
|
48
|
+
|
49
|
+
if (FORCE_IDB || this.isBrowser) {
|
50
|
+
await this.writeCarsIDB(cars)
|
51
|
+
} else {
|
52
|
+
const writes = []
|
53
|
+
for (const { cid, bytes } of cars) {
|
54
|
+
const carFilename = join(this.config.dataDir, this.name, `${cid.toString()}.car`)
|
55
|
+
// console.log('writeCars', carFilename)
|
56
|
+
writes.push(writeSync(carFilename, bytes))
|
57
|
+
}
|
58
|
+
await Promise.all(writes)
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
async writeCarsIDB (cars) {
|
63
|
+
return await this.withDB(async db => {
|
64
|
+
const tx = db.transaction(['cars'], 'readwrite')
|
65
|
+
for (const { cid, bytes, replaces } of cars) {
|
66
|
+
await tx.objectStore('cars').put(bytes, cid.toString())
|
67
|
+
// todo remove old maps
|
68
|
+
if (replaces) {
|
69
|
+
await tx.objectStore('cars').delete(replaces.toString())
|
70
|
+
}
|
71
|
+
}
|
72
|
+
return await tx.done
|
73
|
+
})
|
74
|
+
}
|
75
|
+
|
76
|
+
async readCar (carCid) {
|
77
|
+
if (FORCE_IDB || this.isBrowser) {
|
78
|
+
return await this.readCarIDB(carCid)
|
79
|
+
} else {
|
80
|
+
const carFilename = join(this.config.dataDir, this.name, `${carCid.toString()}.car`)
|
81
|
+
const got = readFileSync(carFilename)
|
82
|
+
// console.log('readCar', carFilename, got.constructor.name)
|
83
|
+
return got
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
async readCarIDB (carCid) {
|
88
|
+
return await this.withDB(async db => {
|
89
|
+
const tx = db.transaction(['cars'], 'readonly')
|
90
|
+
// console.log('getCarReader', carCid)
|
91
|
+
return await tx.objectStore('cars').get(carCid)
|
92
|
+
})
|
93
|
+
}
|
94
|
+
|
95
|
+
getHeader () {
|
96
|
+
if (this.isBrowser) {
|
97
|
+
return localStorage.getItem(this.config.headerKeyPrefix + this.name)
|
98
|
+
} else {
|
99
|
+
return loadSync(this.headerFilename())
|
100
|
+
// return null
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
async saveHeader (stringValue) {
|
105
|
+
// console.log('saveHeader', this.isBrowser)
|
106
|
+
if (this.isBrowser) {
|
107
|
+
// console.log('localStorage!', this.config.headerKeyPrefix)
|
108
|
+
return localStorage.setItem(this.config.headerKeyPrefix + this.name, stringValue)
|
109
|
+
} else {
|
110
|
+
// console.log('no localStorage', this.config.dataDir, this.name)
|
111
|
+
// console.log('saving clock to', this.headerFilename(), stringValue)
|
112
|
+
|
113
|
+
try {
|
114
|
+
await writeSync(this.headerFilename(), stringValue)
|
115
|
+
} catch (error) {
|
116
|
+
console.log('error', error)
|
117
|
+
}
|
118
|
+
|
119
|
+
// console.log('saved clock to', this.headerFilename())
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
headerFilename () {
|
124
|
+
// console.log('headerFilename', this.config.dataDir, this.name)
|
125
|
+
return join(this.config.dataDir, this.name, 'header.json')
|
126
|
+
}
|
127
|
+
|
128
|
+
// async loadData (database, filename) {
|
129
|
+
// const fullFilePath = join(process.cwd(), filename)
|
130
|
+
// const readableStream = createReadStream(fullFilePath)
|
131
|
+
// const parseStream = parse()
|
132
|
+
// readableStream.pipe(parseStream)
|
133
|
+
|
134
|
+
// const saveQueue = cargoQueue(async (tasks, callback) => {
|
135
|
+
// for (const t of tasks) {
|
136
|
+
// await database.put(t)
|
137
|
+
// }
|
138
|
+
// callback()
|
139
|
+
// })
|
140
|
+
|
141
|
+
// parseStream.on('data', async (data) => {
|
142
|
+
// saveQueue.push(data)
|
143
|
+
// })
|
144
|
+
// let res
|
145
|
+
// const p = new Promise((resolve, reject) => {
|
146
|
+
// res = resolve
|
147
|
+
// })
|
148
|
+
// saveQueue.drain(async (x) => {
|
149
|
+
// res()
|
150
|
+
// })
|
151
|
+
// return p
|
152
|
+
// }
|
153
|
+
}
|
154
|
+
|
155
|
+
function loadSync (filename) {
|
156
|
+
try {
|
157
|
+
return readFileSync(filename, 'utf8').toString()
|
158
|
+
} catch (error) {
|
159
|
+
// console.log('error', error)
|
160
|
+
return null
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
async function writeSync (fullpath, stringValue) {
|
165
|
+
await mkdir(dirname(fullpath), { recursive: true })
|
166
|
+
// writeFileSync(fullpath, stringValue)
|
167
|
+
await writeFile(fullpath, stringValue)
|
168
|
+
}
|
package/src/prolly.js
CHANGED
@@ -15,6 +15,8 @@ import { nocache as cache } from 'prolly-trees/cache'
|
|
15
15
|
import { CIDCounter, bf, simpleCompare as compare } from 'prolly-trees/utils'
|
16
16
|
import * as codec from '@ipld/dag-cbor'
|
17
17
|
import { sha256 as hasher } from 'multiformats/hashes/sha2'
|
18
|
+
// import { blake2b256 as hasher } from '@multiformats/blake2/blake2b'
|
19
|
+
|
18
20
|
import { doTransaction } from './blockstore.js'
|
19
21
|
import { create as createBlock } from 'multiformats/block'
|
20
22
|
const blockOpts = { cache, chunker: bf(30), codec, hasher, compare }
|
package/src/valet.js
CHANGED
@@ -6,8 +6,9 @@ import * as CBW from '@ipld/car/buffer-writer'
|
|
6
6
|
import * as raw from 'multiformats/codecs/raw'
|
7
7
|
import * as Block from 'multiformats/block'
|
8
8
|
import * as dagcbor from '@ipld/dag-cbor'
|
9
|
-
import { openDB } from 'idb'
|
10
9
|
import cargoQueue from 'async/cargoQueue.js'
|
10
|
+
import { Loader } from './loader.js'
|
11
|
+
|
11
12
|
// @ts-ignore
|
12
13
|
|
13
14
|
// @ts-ignore
|
@@ -53,6 +54,7 @@ export class Valet {
|
|
53
54
|
constructor (name = 'default', keyMaterial) {
|
54
55
|
this.name = name
|
55
56
|
this.setKeyMaterial(keyMaterial)
|
57
|
+
this.loader = new Loader(name, this.keyId) // todo send this config.loader, if we ever need it
|
56
58
|
this.uploadQueue = cargoQueue(async (tasks, callback) => {
|
57
59
|
// console.log(
|
58
60
|
// 'queue worker',
|
@@ -87,6 +89,10 @@ export class Valet {
|
|
87
89
|
})
|
88
90
|
}
|
89
91
|
|
92
|
+
saveHeader (header) {
|
93
|
+
return this.loader.saveHeader(header)
|
94
|
+
}
|
95
|
+
|
90
96
|
getKeyMaterial () {
|
91
97
|
return this.keyMaterial
|
92
98
|
}
|
@@ -127,19 +133,6 @@ export class Valet {
|
|
127
133
|
}
|
128
134
|
}
|
129
135
|
|
130
|
-
withDB = async dbWorkFun => {
|
131
|
-
if (!this.idb) {
|
132
|
-
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
133
|
-
upgrade (db, oldVersion, newVersion, transaction) {
|
134
|
-
if (oldVersion < 1) {
|
135
|
-
db.createObjectStore('cars')
|
136
|
-
}
|
137
|
-
}
|
138
|
-
})
|
139
|
-
}
|
140
|
-
return await dbWorkFun(this.idb)
|
141
|
-
}
|
142
|
-
|
143
136
|
/**
|
144
137
|
* Iterate over all blocks in the store.
|
145
138
|
*
|
@@ -180,6 +173,7 @@ export class Valet {
|
|
180
173
|
blockHasher: blockOpts.hasher,
|
181
174
|
blockCodec: blockOpts.codec
|
182
175
|
})
|
176
|
+
this.valetRoot = indexNode
|
183
177
|
}
|
184
178
|
|
185
179
|
const got = await indexNode.get(cid)
|
@@ -226,6 +220,7 @@ export class Valet {
|
|
226
220
|
* @param {*} value
|
227
221
|
*/
|
228
222
|
async parkCar (carCid, value, cids) {
|
223
|
+
// const callId = Math.random().toString(36).substring(7)
|
229
224
|
// console.log('parkCar', this.instanceId, this.name, carCid, cids)
|
230
225
|
const combinedReader = await this.getCombinedReader(carCid)
|
231
226
|
const mapNode = await addCidsToCarIndex(
|
@@ -238,7 +233,7 @@ export class Valet {
|
|
238
233
|
this.valetRoot = mapNode
|
239
234
|
this.valetRootCid = mapNode.cid
|
240
235
|
// make a block set with all the cids of the map
|
241
|
-
const saveValetBlocks = new VMemoryBlockstore()
|
236
|
+
const saveValetBlocks = new VMemoryBlockstore()
|
242
237
|
|
243
238
|
for await (const cidx of mapNode.cids()) {
|
244
239
|
const bytes = await combinedReader.get(cidx)
|
@@ -251,7 +246,8 @@ export class Valet {
|
|
251
246
|
newValetCidCar = await blocksToCarBlock(this.valetRootCid, saveValetBlocks)
|
252
247
|
}
|
253
248
|
// console.log('newValetCidCar', this.name, Math.floor(newValetCidCar.bytes.length / 1024))
|
254
|
-
|
249
|
+
// console.log('writeCars', callId, carCid.toString(), newValetCidCar.cid.toString())
|
250
|
+
await this.loader.writeCars([
|
255
251
|
{
|
256
252
|
cid: carCid,
|
257
253
|
bytes: value,
|
@@ -267,6 +263,8 @@ export class Valet {
|
|
267
263
|
|
268
264
|
this.valetRootCarCid = newValetCidCar.cid // goes to clock
|
269
265
|
|
266
|
+
// console.log('wroteCars', callId, carCid.toString(), newValetCidCar.cid.toString())
|
267
|
+
|
270
268
|
// console.log('parked car', carCid, value.length, Array.from(cids))
|
271
269
|
// upload to web3.storage if we have credentials
|
272
270
|
if (this.uploadFunction) {
|
@@ -283,29 +281,13 @@ export class Valet {
|
|
283
281
|
}
|
284
282
|
}
|
285
283
|
|
286
|
-
async writeCars (cars) {
|
287
|
-
return await this.withDB(async db => {
|
288
|
-
const tx = db.transaction(['cars'], 'readwrite')
|
289
|
-
for (const { cid, bytes, replaces } of cars) {
|
290
|
-
await tx.objectStore('cars').put(bytes, cid.toString())
|
291
|
-
// todo remove old maps
|
292
|
-
if (replaces) {
|
293
|
-
await tx.objectStore('cars').delete(replaces.toString())
|
294
|
-
}
|
295
|
-
}
|
296
|
-
return await tx.done
|
297
|
-
})
|
298
|
-
}
|
299
|
-
|
300
284
|
remoteBlockFunction = null
|
301
285
|
|
302
286
|
async getCarReader (carCid) {
|
303
287
|
carCid = carCid.toString()
|
304
|
-
const carBytes = await this.
|
305
|
-
|
306
|
-
|
307
|
-
return await tx.objectStore('cars').get(carCid)
|
308
|
-
})
|
288
|
+
const carBytes = await this.loader.readCar(carCid)
|
289
|
+
// const callID = Math.random().toString(36).substring(7)
|
290
|
+
// console.log('getCarReader', callID, carCid)
|
309
291
|
const reader = await CarReader.fromBytes(carBytes)
|
310
292
|
if (this.keyMaterial) {
|
311
293
|
const roots = await reader.getRoots()
|
@@ -329,7 +311,7 @@ export class Valet {
|
|
329
311
|
|
330
312
|
// last block is the root ??? todo
|
331
313
|
const rootBlock = blocks[blocks.length - 1]
|
332
|
-
|
314
|
+
// console.log('got reader', callID, carCid)
|
333
315
|
return {
|
334
316
|
root: rootBlock,
|
335
317
|
get: async dataCID => {
|
@@ -456,6 +438,8 @@ const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
|
456
438
|
|
457
439
|
const addCidsToCarIndex = async (blockstore, valetRoot, valetRootCid, bulkOperations) => {
|
458
440
|
let indexNode
|
441
|
+
// const callID = Math.random().toString(32).substring(2, 8)
|
442
|
+
// console.log('addCidsToCarIndex', callID, valetRootCid, bulkOperations.length)
|
459
443
|
if (valetRootCid) {
|
460
444
|
if (valetRoot) {
|
461
445
|
indexNode = valetRoot
|
@@ -475,6 +459,7 @@ const addCidsToCarIndex = async (blockstore, valetRoot, valetRootCid, bulkOperat
|
|
475
459
|
// console.log('adding', key, value)
|
476
460
|
await indexNode.set(key, value)
|
477
461
|
}
|
462
|
+
// console.log('newCidsToCarIndex', callID, indexNode.cid, bulkOperations.length)
|
478
463
|
return indexNode
|
479
464
|
}
|
480
465
|
|