@fireproof/core 0.5.16 → 0.5.18
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 +1 -1
- package/dist/src/blockstore.js +1 -1
- package/dist/src/clock.js +4 -2
- package/dist/src/database.js +14 -8
- package/dist/src/db-index.js +15 -9
- package/dist/src/fireproof.d.ts +198 -6
- package/dist/src/fireproof.js +3102 -425
- package/dist/src/fireproof.js.map +1 -1
- package/dist/src/fireproof.mjs +3102 -425
- package/dist/src/fireproof.mjs.map +1 -1
- package/dist/src/sync.js +1 -1
- package/dist/src/valet.js +222 -41
- package/package.json +2 -1
- package/src/blockstore.js +1 -1
- package/src/clock.js +3 -2
- package/src/database.js +17 -9
- package/src/db-index.js +13 -12
- package/src/fireproof.js +15 -7
- package/src/sync.js +1 -1
- package/src/valet.js +239 -38
package/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
Fireproof is a realtime database for today's interactive applications. It uses immutable data and distributed protocols
|
4
4
|
to offer a new kind of database that:
|
5
5
|
- can be embedded in any page or app, with a flexible data ownership model
|
6
|
-
-
|
6
|
+
- can be hosted on any cloud
|
7
7
|
- uses cryptographically verifiable protocols (what plants crave)
|
8
8
|
|
9
9
|
Learn more about the [concepts and architecture behind Fireproof](https://fireproof.storage/documentation/how-the-database-engine-works/), or jump to the [quick start](#quick-start) for React and server-side examples.
|
package/dist/src/blockstore.js
CHANGED
@@ -71,7 +71,7 @@ export class TransactionBlockstore {
|
|
71
71
|
return old;
|
72
72
|
if (!this.valet)
|
73
73
|
throw new Error('Missing block: ' + key);
|
74
|
-
const got = await this.valet.
|
74
|
+
const got = await this.valet.getValetBlock(key);
|
75
75
|
this.committedBlocks.set(key, got);
|
76
76
|
return got;
|
77
77
|
}
|
package/dist/src/clock.js
CHANGED
@@ -223,11 +223,13 @@ export async function findEventsToSync(blocks, head) {
|
|
223
223
|
// console.time(callTag + '.findCommonAncestorWithSortedEvents')
|
224
224
|
const { ancestor, sorted } = await findCommonAncestorWithSortedEvents(events, head);
|
225
225
|
// console.timeEnd(callTag + '.findCommonAncestorWithSortedEvents')
|
226
|
-
// console.log('sorted', !!ancestor, sorted
|
226
|
+
// console.log('sorted', !!ancestor, sorted)
|
227
227
|
// console.time(callTag + '.contains')
|
228
228
|
const toSync = ancestor ? await asyncFilter(sorted, async (uks) => !(await contains(events, ancestor, uks.cid))) : sorted;
|
229
229
|
// console.timeEnd(callTag + '.contains')
|
230
|
-
|
230
|
+
const sortDifference = sorted.length - toSync.length;
|
231
|
+
if (sortDifference / sorted.length > 0.6)
|
232
|
+
console.log('optimize sorted', !!ancestor, sortDifference);
|
231
233
|
return { cids: events, events: toSync };
|
232
234
|
}
|
233
235
|
const asyncFilter = async (arr, predicate) => Promise.all(arr.map(predicate)).then(results => arr.filter((_v, index) => results[index]));
|
package/dist/src/database.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
// @ts-nocheck
|
2
2
|
import { visMerkleClock, visMerkleTree, vis, put, get, getAll, eventsSince } from './prolly.js';
|
3
|
-
import { doTransaction } from './blockstore.js';
|
3
|
+
import { doTransaction, TransactionBlockstore } from './blockstore.js';
|
4
4
|
import charwise from 'charwise';
|
5
5
|
import { localSet } from './utils.js';
|
6
6
|
import { CID } from 'multiformats';
|
@@ -17,7 +17,6 @@ export const parseCID = cid => (typeof cid === 'string' ? CID.parse(cid) : cid);
|
|
17
17
|
* This is the main class for saving and loading JSON and other documents with the database. You can find additional examples and
|
18
18
|
* usage guides in the repository README.
|
19
19
|
*
|
20
|
-
* @param {import('./blockstore.js').TransactionBlockstore} blocks - The block storage instance to use documents and indexes
|
21
20
|
* @param {CID[]} clock - The Merkle clock head to use for the Fireproof instance.
|
22
21
|
* @param {object} [config] - Optional configuration options for the Fireproof instance.
|
23
22
|
* @param {object} [authCtx] - Optional authorization context object to use for any authentication checks.
|
@@ -28,10 +27,11 @@ export class Database {
|
|
28
27
|
indexes = new Map();
|
29
28
|
rootCache = null;
|
30
29
|
eventsCache = new Map();
|
31
|
-
constructor(
|
32
|
-
this.name =
|
30
|
+
constructor(name, clock, config = {}) {
|
31
|
+
this.name = name;
|
33
32
|
this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`;
|
34
|
-
this.blocks =
|
33
|
+
this.blocks = new TransactionBlockstore(name, config.key);
|
34
|
+
this.indexBlocks = new TransactionBlockstore(name + '.indexes', config.key);
|
35
35
|
this.clock = clock;
|
36
36
|
this.config = config;
|
37
37
|
}
|
@@ -47,6 +47,8 @@ export class Database {
|
|
47
47
|
clock: this.clockToJSON(),
|
48
48
|
name: this.name,
|
49
49
|
key: this.blocks.valet?.getKeyMaterial(),
|
50
|
+
car: this.blocks.valet?.valetRootCarCid.toString(),
|
51
|
+
indexCar: this.indexBlocks.valet?.valetRootCarCid?.toString(),
|
50
52
|
indexes: [...this.indexes.values()].map(index => index.toJSON())
|
51
53
|
};
|
52
54
|
}
|
@@ -59,11 +61,14 @@ export class Database {
|
|
59
61
|
clockToJSON(clock = null) {
|
60
62
|
return (clock || this.clock).map(cid => cid.toString());
|
61
63
|
}
|
62
|
-
hydrate({ clock, name, key }) {
|
64
|
+
hydrate({ clock, name, key, car, indexCar }) {
|
63
65
|
this.name = name;
|
64
66
|
this.clock = clock;
|
65
67
|
this.blocks.valet?.setKeyMaterial(key);
|
66
|
-
this.
|
68
|
+
this.blocks.valet?.setRootCarCid(car); // maybe
|
69
|
+
this.indexBlocks.valet?.setKeyMaterial(key);
|
70
|
+
this.indexBlocks.valet?.setRootCarCid(indexCar); // maybe
|
71
|
+
// this.indexBlocks = null
|
67
72
|
}
|
68
73
|
maybeSaveClock() {
|
69
74
|
if (this.name && this.blocks.valet) {
|
@@ -98,7 +103,7 @@ export class Database {
|
|
98
103
|
let rows, dataCIDs, clockCIDs;
|
99
104
|
// if (!aClock) aClock = []
|
100
105
|
if (aClock && aClock.length > 0) {
|
101
|
-
aClock = aClock.map(
|
106
|
+
aClock = aClock.map(cid => cid.toString());
|
102
107
|
const eventKey = JSON.stringify([...this.clockToJSON(aClock), ...this.clockToJSON()]);
|
103
108
|
let resp;
|
104
109
|
if (this.eventsCache.has(eventKey)) {
|
@@ -259,6 +264,7 @@ export class Database {
|
|
259
264
|
* @returns {Promise<{ proof:{}, id: string, clock: CID[] }>} - The result of adding the event to storage
|
260
265
|
*/
|
261
266
|
async putToProllyTree(decodedEvent, clock = null) {
|
267
|
+
// console.log('putToProllyTree', decodedEvent)
|
262
268
|
const event = encodeEvent(decodedEvent);
|
263
269
|
if (clock && JSON.stringify(this.clockToJSON(clock)) !== JSON.stringify(this.clockToJSON())) {
|
264
270
|
// console.log('this.clock', this.clockToJSON())
|
package/dist/src/db-index.js
CHANGED
@@ -11,7 +11,7 @@ import { makeGetBlock, visMerkleTree } from './prolly.js';
|
|
11
11
|
import { Database, cidsToProof } from './database.js';
|
12
12
|
import * as codec from '@ipld/dag-cbor';
|
13
13
|
// import { create as createBlock } from 'multiformats/block'
|
14
|
-
import {
|
14
|
+
import { doTransaction } from './blockstore.js';
|
15
15
|
// @ts-ignore
|
16
16
|
import charwise from 'charwise';
|
17
17
|
const ALWAYS_REBUILD = false; // todo: remove
|
@@ -63,23 +63,23 @@ const makeDoc = ({ key, value }) => ({ _id: key, ...value });
|
|
63
63
|
*/
|
64
64
|
const indexEntriesForChanges = (changes, mapFn) => {
|
65
65
|
const indexEntries = [];
|
66
|
-
changes.forEach(({ key, value, del }) => {
|
66
|
+
changes.forEach(({ key: _id, value, del }) => {
|
67
67
|
// key is _id, value is the document
|
68
68
|
if (del || !value)
|
69
69
|
return;
|
70
70
|
let mapCalled = false;
|
71
|
-
const mapReturn = mapFn(makeDoc({ key, value }), (k, v) => {
|
71
|
+
const mapReturn = mapFn(makeDoc({ key: _id, value }), (k, v) => {
|
72
72
|
mapCalled = true;
|
73
73
|
if (typeof k === 'undefined')
|
74
74
|
return;
|
75
75
|
indexEntries.push({
|
76
|
-
key: [charwise.encode(k),
|
76
|
+
key: [charwise.encode(k), _id],
|
77
77
|
value: v || null
|
78
78
|
});
|
79
79
|
});
|
80
80
|
if (!mapCalled && mapReturn) {
|
81
81
|
indexEntries.push({
|
82
|
-
key: [charwise.encode(mapReturn),
|
82
|
+
key: [charwise.encode(mapReturn), _id],
|
83
83
|
value: null
|
84
84
|
});
|
85
85
|
}
|
@@ -102,9 +102,6 @@ export class DbIndex {
|
|
102
102
|
*/
|
103
103
|
constructor(database, name, mapFn, clock = null, opts = {}) {
|
104
104
|
this.database = database;
|
105
|
-
if (!database.indexBlocks) {
|
106
|
-
database.indexBlocks = new TransactionBlockstore(database?.name + '.indexes', database.blocks.valet?.getKeyMaterial());
|
107
|
-
}
|
108
105
|
if (typeof name === 'function') {
|
109
106
|
// app is using deprecated API, remove in 0.7
|
110
107
|
opts = clock || {};
|
@@ -249,7 +246,16 @@ export class DbIndex {
|
|
249
246
|
return { result: [] };
|
250
247
|
if (query.includeDocs === undefined)
|
251
248
|
query.includeDocs = this.includeDocsDefault;
|
252
|
-
if (query.
|
249
|
+
if (query.prefix) {
|
250
|
+
// ensure prefix is an array
|
251
|
+
if (!Array.isArray(query.prefix))
|
252
|
+
query.prefix = [query.prefix];
|
253
|
+
const start = [...query.prefix, NaN];
|
254
|
+
const end = [...query.prefix, Infinity];
|
255
|
+
const prefixRange = [start, end].map(key => charwise.encode(key));
|
256
|
+
return await this.applyQuery(await this.indexByKey.root.range(...prefixRange), query);
|
257
|
+
}
|
258
|
+
else if (query.range) {
|
253
259
|
const encodedRange = query.range.map(key => charwise.encode(key));
|
254
260
|
return await this.applyQuery(await this.indexByKey.root.range(...encodedRange), query);
|
255
261
|
}
|
package/dist/src/fireproof.d.ts
CHANGED
@@ -1,5 +1,195 @@
|
|
1
1
|
import * as multiformats from 'multiformats';
|
2
|
-
import { CID } from 'multiformats';
|
2
|
+
import { Link, CID } from 'multiformats';
|
3
|
+
import * as multiformats_cid from 'multiformats/cid';
|
4
|
+
|
5
|
+
type AnyLink = Link<unknown, number, number, 1|0>
|
6
|
+
|
7
|
+
declare class Valet {
|
8
|
+
constructor(name: string, keyMaterial: any);
|
9
|
+
idb: any;
|
10
|
+
name: any;
|
11
|
+
uploadQueue: any;
|
12
|
+
alreadyEnqueued: Set<any>;
|
13
|
+
keyMaterial: any;
|
14
|
+
keyId: string;
|
15
|
+
valetRoot: any;
|
16
|
+
valetRootCid: any;
|
17
|
+
valetRootCarCid: any;
|
18
|
+
valetCidBlocks: VMemoryBlockstore;
|
19
|
+
instanceId: string;
|
20
|
+
/**
|
21
|
+
* Function installed by the database to upload car files
|
22
|
+
* @type {null|function(string, Uint8Array):Promise<void>}
|
23
|
+
*/
|
24
|
+
uploadFunction: null | ((arg0: string, arg1: Uint8Array) => Promise<void>);
|
25
|
+
getKeyMaterial(): any;
|
26
|
+
setKeyMaterial(km: any): void;
|
27
|
+
/**
|
28
|
+
* Group the blocks into a car and write it to the valet.
|
29
|
+
* @param {import('./blockstore.js').InnerBlockstore} innerBlockstore
|
30
|
+
* @param {Set<string>} cids
|
31
|
+
* @returns {Promise<void>}
|
32
|
+
* @memberof Valet
|
33
|
+
*/
|
34
|
+
writeTransaction(innerBlockstore: InnerBlockstore, cids: Set<string>): Promise<void>;
|
35
|
+
withDB: (dbWorkFun: any) => Promise<any>;
|
36
|
+
/**
|
37
|
+
* Iterate over all blocks in the store.
|
38
|
+
*
|
39
|
+
* @yields {{cid: string, value: Uint8Array}}
|
40
|
+
* @returns {AsyncGenerator<any, any, any>}
|
41
|
+
*/
|
42
|
+
cids(): AsyncGenerator<any, any, any>;
|
43
|
+
setRootCarCid(cid: any): void;
|
44
|
+
getCarCIDForCID(cid: any): Promise<{
|
45
|
+
result: any;
|
46
|
+
}>;
|
47
|
+
OLDgetCarCIDForCID(cid: any): Promise<{
|
48
|
+
result: any;
|
49
|
+
}>;
|
50
|
+
getCombinedReader(carCid: any): Promise<{
|
51
|
+
root: any;
|
52
|
+
put: (cid: any, bytes: any) => Promise<void>;
|
53
|
+
get: (cid: any) => Promise<any>;
|
54
|
+
}>;
|
55
|
+
/**
|
56
|
+
*
|
57
|
+
* @param {string} carCid
|
58
|
+
* @param {*} value
|
59
|
+
*/
|
60
|
+
parkCar(carCid: string, value: any, cids: any): Promise<void>;
|
61
|
+
remoteBlockFunction: any;
|
62
|
+
getCarReader(carCid: any): Promise<{
|
63
|
+
root: any;
|
64
|
+
get: (dataCID: any) => Promise<any>;
|
65
|
+
}>;
|
66
|
+
getValetBlock(dataCID: any): Promise<any>;
|
67
|
+
}
|
68
|
+
declare class VMemoryBlockstore {
|
69
|
+
/** @type {Map<string, Uint8Array>} */
|
70
|
+
blocks: Map<string, Uint8Array>;
|
71
|
+
instanceId: string;
|
72
|
+
get(cid: any): Promise<{
|
73
|
+
cid: any;
|
74
|
+
bytes: Uint8Array;
|
75
|
+
}>;
|
76
|
+
/**
|
77
|
+
* @param {import('../src/link').AnyLink} cid
|
78
|
+
* @param {Uint8Array} bytes
|
79
|
+
*/
|
80
|
+
put(cid: AnyLink, bytes: Uint8Array): Promise<void>;
|
81
|
+
entries(): Generator<{
|
82
|
+
cid: multiformats_cid.Link<unknown, number, number, multiformats_cid.Version>;
|
83
|
+
bytes: Uint8Array;
|
84
|
+
}, void, unknown>;
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* @typedef {{ get: (link: import('../src/link').AnyLink) => Promise<AnyBlock | undefined> }} BlockFetcher
|
89
|
+
*/
|
90
|
+
/**
|
91
|
+
* @typedef {Object} AnyBlock
|
92
|
+
* @property {import('./link').AnyLink} cid - The CID of the block
|
93
|
+
* @property {Uint8Array} bytes - The block's data
|
94
|
+
*
|
95
|
+
* @typedef {Object} Blockstore
|
96
|
+
* @property {function(import('./link').AnyLink): Promise<AnyBlock|undefined>} get - A function to retrieve a block by CID
|
97
|
+
* @property {function(import('./link').AnyLink, Uint8Array): Promise<void>} put - A function to store a block's data and CID
|
98
|
+
*
|
99
|
+
* A blockstore that caches writes to a transaction and only persists them when committed.
|
100
|
+
*/
|
101
|
+
declare class TransactionBlockstore {
|
102
|
+
constructor(name: any, encryptionKey: any);
|
103
|
+
/** @type {Map<string, Uint8Array>} */
|
104
|
+
committedBlocks: Map<string, Uint8Array>;
|
105
|
+
/** @type {Valet} */
|
106
|
+
valet: Valet;
|
107
|
+
instanceId: string;
|
108
|
+
inflightTransactions: Set<any>;
|
109
|
+
syncs: Set<any>;
|
110
|
+
remoteBlockFunction: any;
|
111
|
+
/**
|
112
|
+
* Get a block from the store.
|
113
|
+
*
|
114
|
+
* @param {import('./link').AnyLink} cid
|
115
|
+
* @returns {Promise<AnyBlock | undefined>}
|
116
|
+
*/
|
117
|
+
get(cid: AnyLink): Promise<AnyBlock | undefined>;
|
118
|
+
transactionsGet(key: any): Promise<any>;
|
119
|
+
committedGet(key: any): Promise<any>;
|
120
|
+
clearCommittedCache(): Promise<void>;
|
121
|
+
networkGet(key: any): Promise<any>;
|
122
|
+
/**
|
123
|
+
* Add a block to the store. Usually bound to a transaction by a closure.
|
124
|
+
* It sets the lastCid property to the CID of the block that was put.
|
125
|
+
* This is used by the transaction as the head of the car when written to the valet.
|
126
|
+
* We don't have to worry about which transaction we are when we are here because
|
127
|
+
* we are the transactionBlockstore.
|
128
|
+
*
|
129
|
+
* @param {import('./link').AnyLink} cid
|
130
|
+
* @param {Uint8Array} bytes
|
131
|
+
*/
|
132
|
+
put(cid: AnyLink, bytes: Uint8Array): void;
|
133
|
+
/**
|
134
|
+
* Iterate over all blocks in the store.
|
135
|
+
*
|
136
|
+
* @yields {{cid: string, bytes: Uint8Array}}
|
137
|
+
* @returns {AsyncGenerator<any, any, any>}
|
138
|
+
*/
|
139
|
+
entries(): AsyncGenerator<any, any, any>;
|
140
|
+
/**
|
141
|
+
* Begin a transaction. Ensures the uncommited blocks are empty at the begining.
|
142
|
+
* Returns the blocks to read and write during the transaction.
|
143
|
+
* @returns {InnerBlockstore}
|
144
|
+
* @memberof TransactionBlockstore
|
145
|
+
*/
|
146
|
+
begin(label?: string): InnerBlockstore;
|
147
|
+
/**
|
148
|
+
* Commit the transaction. Writes the blocks to the store.
|
149
|
+
* @returns {Promise<void>}
|
150
|
+
* @memberof TransactionBlockstore
|
151
|
+
*/
|
152
|
+
commit(innerBlockstore: any, doSync?: boolean): Promise<void>;
|
153
|
+
doCommit: (innerBlockstore: any) => Promise<void>;
|
154
|
+
/**
|
155
|
+
* Retire the transaction. Clears the uncommited blocks.
|
156
|
+
* @returns {void}
|
157
|
+
* @memberof TransactionBlockstore
|
158
|
+
*/
|
159
|
+
retire(innerBlockstore: any): void;
|
160
|
+
}
|
161
|
+
declare class InnerBlockstore {
|
162
|
+
constructor(label: any, parentBlockstore: any);
|
163
|
+
/** @type {Map<string, Uint8Array>} */
|
164
|
+
blocks: Map<string, Uint8Array>;
|
165
|
+
lastCid: any;
|
166
|
+
label: string;
|
167
|
+
parentBlockstore: any;
|
168
|
+
/**
|
169
|
+
* @param {import('./link').AnyLink} cid
|
170
|
+
* @returns {Promise<AnyBlock | undefined>}
|
171
|
+
*/
|
172
|
+
get(cid: AnyLink): Promise<AnyBlock | undefined>;
|
173
|
+
/**
|
174
|
+
* @param {import('./link').AnyLink} cid
|
175
|
+
* @param {Uint8Array} bytes
|
176
|
+
*/
|
177
|
+
put(cid: AnyLink, bytes: Uint8Array): Promise<void>;
|
178
|
+
entries(): Generator<{
|
179
|
+
cid: multiformats.Link<unknown, number, number, multiformats.Version>;
|
180
|
+
bytes: Uint8Array;
|
181
|
+
}, void, unknown>;
|
182
|
+
}
|
183
|
+
type AnyBlock = {
|
184
|
+
/**
|
185
|
+
* - The CID of the block
|
186
|
+
*/
|
187
|
+
cid: AnyLink;
|
188
|
+
/**
|
189
|
+
* - The block's data
|
190
|
+
*/
|
191
|
+
bytes: Uint8Array;
|
192
|
+
};
|
3
193
|
|
4
194
|
/**
|
5
195
|
* @class Fireproof
|
@@ -7,21 +197,21 @@ import { CID } from 'multiformats';
|
|
7
197
|
* This is the main class for saving and loading JSON and other documents with the database. You can find additional examples and
|
8
198
|
* usage guides in the repository README.
|
9
199
|
*
|
10
|
-
* @param {import('./blockstore.js').TransactionBlockstore} blocks - The block storage instance to use documents and indexes
|
11
200
|
* @param {CID[]} clock - The Merkle clock head to use for the Fireproof instance.
|
12
201
|
* @param {object} [config] - Optional configuration options for the Fireproof instance.
|
13
202
|
* @param {object} [authCtx] - Optional authorization context object to use for any authentication checks.
|
14
203
|
*
|
15
204
|
*/
|
16
205
|
declare class Database {
|
17
|
-
constructor(
|
206
|
+
constructor(name: any, clock: any, config?: {});
|
18
207
|
listeners: Set<any>;
|
19
208
|
indexes: Map<any, any>;
|
20
209
|
rootCache: any;
|
21
210
|
eventsCache: Map<any, any>;
|
22
211
|
name: any;
|
23
212
|
instanceId: string;
|
24
|
-
blocks:
|
213
|
+
blocks: TransactionBlockstore;
|
214
|
+
indexBlocks: TransactionBlockstore;
|
25
215
|
clock: any;
|
26
216
|
config: {};
|
27
217
|
/**
|
@@ -38,12 +228,13 @@ declare class Database {
|
|
38
228
|
* @instance
|
39
229
|
*/
|
40
230
|
clockToJSON(clock?: any): string[];
|
41
|
-
hydrate({ clock, name, key }: {
|
231
|
+
hydrate({ clock, name, key, car, indexCar }: {
|
42
232
|
clock: any;
|
43
233
|
name: any;
|
44
234
|
key: any;
|
235
|
+
car: any;
|
236
|
+
indexCar: any;
|
45
237
|
}): void;
|
46
|
-
indexBlocks: any;
|
47
238
|
maybeSaveClock(): void;
|
48
239
|
/**
|
49
240
|
* Triggers a notification to all listeners
|
@@ -405,6 +596,7 @@ declare class Fireproof {
|
|
405
596
|
* @returns {Database} - a new Fireproof instance
|
406
597
|
*/
|
407
598
|
static storage: (name?: any, opts?: {}) => Database;
|
599
|
+
static fromConfig(name: any, existingConfig: any, opts?: {}): any;
|
408
600
|
static fromJSON(json: any, database: any): any;
|
409
601
|
static snapshot(database: any, clock: any): any;
|
410
602
|
static zoom(database: any, clock: any): Promise<any>;
|