@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 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
- - scales without incurring developer costs, thanks to Filecoin
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.
@@ -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.getBlock(key);
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.length)
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
- // console.log('optimize sorted', !!ancestor, sorted.length - toSync.length)
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]));
@@ -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(blocks, clock, config = {}) {
32
- this.name = config.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 = 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.indexBlocks = null;
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((cid) => cid.toString());
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())
@@ -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 { TransactionBlockstore, doTransaction } from './blockstore.js';
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), key],
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), key],
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.range) {
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
  }
@@ -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(blocks: any, clock: any, config?: {});
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: any;
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>;