@fireproof/core 0.5.19 → 0.5.21

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.
@@ -153,12 +153,6 @@ const prollyRootFromAncestor = async (events, ancestor, getBlock) => {
153
153
  return root;
154
154
  }
155
155
  };
156
- // async function bigMerge (events, head, getBlock) {
157
- // const allRoots = await Promise.all(head.map(async h => prollyRootFromAncestor(events, h, getBlock)))
158
- // console.log('allRoots', allRoots)
159
- // // todo query over all roots and merge them, but how do they not have a common ancestor? they all start with the _sync root
160
- // throw new Error('not implemented')
161
- // }
162
156
  const doProllyBulk = async (inBlocks, head, event, doFull = false) => {
163
157
  const { getBlock, blocks } = makeGetAndPutBlock(inBlocks); // this is doubled with eventfetcher
164
158
  let bulkSorted = [];
@@ -316,8 +310,6 @@ export async function eventsSince(blocks, head, since) {
316
310
  *
317
311
  */
318
312
  export async function getAll(blocks, head, rootCache = null, doFull = false) {
319
- // todo use the root node left around from put, etc
320
- // move load to a central place
321
313
  if (!head.length) {
322
314
  return { root: null, clockCIDs: new CIDCounter(), cids: new CIDCounter(), result: [] };
323
315
  }
package/dist/src/sync.js CHANGED
@@ -153,7 +153,7 @@ export class Sync {
153
153
  if (!this.peer || !this.isReady)
154
154
  return;
155
155
  // console.log('send update from', this.database.instanceId)
156
- // todo should send updates since last sync
156
+ // todo should send updates since last sync (currently sends each transaction)
157
157
  const newCar = await blocksToCarBlock(blockstore.lastCid, blockstore);
158
158
  this.status = 'sending update car';
159
159
  this.peer.send(newCar.bytes);
package/dist/src/valet.js CHANGED
@@ -52,28 +52,28 @@ export class Valet {
52
52
  // )
53
53
  if (this.uploadFunction) {
54
54
  // todo we can coalesce these into a single car file
55
- return await this.withDB(async (db) => {
56
- for (const task of tasks) {
57
- await this.uploadFunction(task.carCid, task.value);
58
- // update the indexedb to mark this car as no longer pending
59
- const carMeta = await db.get('cidToCar', task.carCid);
60
- delete carMeta.pending;
61
- await db.put('cidToCar', carMeta);
62
- }
63
- });
55
+ // todo remove idb usage here
56
+ for (const task of tasks) {
57
+ await this.uploadFunction(task.carCid, task.value);
58
+ // todo update syncCidMap to say this has been synced
59
+ // const carMeta = await db.get('cidToCar', task.carCid)
60
+ // delete carMeta.pending
61
+ // await db.put('cidToCar', carMeta)
62
+ }
64
63
  }
65
64
  callback();
66
65
  });
67
66
  this.uploadQueue.drain(async () => {
68
- return await this.withDB(async (db) => {
69
- const carKeys = (await db.getAllFromIndex('cidToCar', 'pending')).map(c => c.car);
70
- for (const carKey of carKeys) {
71
- await this.uploadFunction(carKey, await db.get('cars', carKey));
72
- const carMeta = await db.get('cidToCar', carKey);
73
- delete carMeta.pending;
74
- await db.put('cidToCar', carMeta);
75
- }
76
- });
67
+ // todo read syncCidMap and sync any that are still unsynced
68
+ // return await this.withDB(async db => {
69
+ // const carKeys = (await db.getAllFromIndex('cidToCar', 'pending')).map(c => c.car)
70
+ // for (const carKey of carKeys) {
71
+ // await this.uploadFunction(carKey, await db.get('cars', carKey))
72
+ // const carMeta = await db.get('cidToCar', carKey)
73
+ // delete carMeta.pending
74
+ // await db.put('cidToCar', carMeta)
75
+ // }
76
+ // })
77
77
  });
78
78
  }
79
79
  getKeyMaterial() {
@@ -118,16 +118,10 @@ export class Valet {
118
118
  }
119
119
  withDB = async (dbWorkFun) => {
120
120
  if (!this.idb) {
121
- this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 2, {
121
+ this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
122
122
  upgrade(db, oldVersion, newVersion, transaction) {
123
123
  if (oldVersion < 1) {
124
- db.createObjectStore('cars'); // todo use database name
125
- const cidToCar = db.createObjectStore('cidToCar', { keyPath: 'car' });
126
- cidToCar.createIndex('cids', 'cids', { multiEntry: true });
127
- }
128
- if (oldVersion < 2) {
129
- const cidToCar = transaction.objectStore('cidToCar');
130
- cidToCar.createIndex('pending', 'pending');
124
+ db.createObjectStore('cars');
131
125
  }
132
126
  }
133
127
  });
@@ -142,23 +136,22 @@ export class Valet {
142
136
  */
143
137
  async *cids() {
144
138
  // console.log('valet cids')
145
- const db = await this.withDB(async (db) => db);
146
- const tx = db.transaction(['cidToCar'], 'readonly');
147
- let cursor = await tx.store.openCursor();
148
- while (cursor) {
149
- yield { cid: cursor.key, car: cursor.value.car };
150
- cursor = await cursor.continue();
151
- }
139
+ // todo use cidMap
140
+ // while (cursor) {
141
+ // yield { cid: cursor.key, car: cursor.value.car }
142
+ // cursor = await cursor.continue()
143
+ // }
152
144
  }
153
145
  setRootCarCid(cid) {
154
146
  this.valetRootCarCid = cid;
155
147
  this.valetRoot = null;
156
148
  this.valetRootCid = null;
157
149
  }
150
+ // todo memoize this
158
151
  async getCarCIDForCID(cid) {
159
152
  // make a car reader for this.valetRootCarCid
160
153
  if (!this.valetRootCarCid)
161
- return;
154
+ return { result: null };
162
155
  let indexNode;
163
156
  if (this.valetRoot) {
164
157
  indexNode = this.valetRoot;
@@ -179,14 +172,6 @@ export class Valet {
179
172
  // console.log('getCarCIDForCID', cid, got)
180
173
  return { result: got };
181
174
  }
182
- async OLDgetCarCIDForCID(cid) {
183
- const carCid = await this.withDB(async (db) => {
184
- const tx = db.transaction(['cars', 'cidToCar'], 'readonly');
185
- const indexResp = await tx.objectStore('cidToCar').index('cids').get(cid);
186
- return indexResp?.car;
187
- });
188
- return { result: carCid };
189
- }
190
175
  async getCombinedReader(carCid) {
191
176
  let carMapReader;
192
177
  if (this.valetRootCarCid) {
@@ -245,17 +230,19 @@ export class Valet {
245
230
  newValetCidCar = await blocksToCarBlock(this.valetRootCid, saveValetBlocks);
246
231
  }
247
232
  // console.log('newValetCidCar', this.name, Math.floor(newValetCidCar.bytes.length / 1024))
248
- await this.withDB(async (db) => {
249
- const tx = db.transaction(['cars'], 'readwrite');
250
- await tx.objectStore('cars').put(value, carCid.toString());
251
- if (newValetCidCar) {
252
- if (this.valetRootCarCid) {
253
- // await tx.objectStore('cars').delete(this.valetRootCarCid.toString())
254
- }
255
- await tx.objectStore('cars').put(newValetCidCar.bytes, newValetCidCar.cid.toString());
233
+ await this.writeCars([
234
+ {
235
+ cid: carCid,
236
+ bytes: value,
237
+ replaces: null
238
+ },
239
+ {
240
+ cid: newValetCidCar.cid,
241
+ bytes: newValetCidCar.bytes,
242
+ replaces: null
243
+ // replaces: this.valetRootCarCid // todo
256
244
  }
257
- return await tx.done;
258
- });
245
+ ]);
259
246
  this.valetRootCarCid = newValetCidCar.cid; // goes to clock
260
247
  // console.log('parked car', carCid, value.length, Array.from(cids))
261
248
  // upload to web3.storage if we have credentials
@@ -273,6 +260,19 @@ export class Valet {
273
260
  // console.log('no upload function', carCid, value.length, this.uploadFunction)
274
261
  }
275
262
  }
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
276
  remoteBlockFunction = null;
277
277
  async getCarReader(carCid) {
278
278
  carCid = carCid.toString();
@@ -301,7 +301,7 @@ export class Valet {
301
301
  return decoded;
302
302
  };
303
303
  const { blocks } = await blocksFromEncryptedCarBlock(roots[0], readerGetWithCodec, this.keyMaterial);
304
- // last block is the root ???
304
+ // last block is the root ??? todo
305
305
  const rootBlock = blocks[blocks.length - 1];
306
306
  return {
307
307
  root: rootBlock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fireproof/core",
3
- "version": "0.5.19",
3
+ "version": "0.5.21",
4
4
  "description": "Live data for your React app, powered by IPFS",
5
5
  "main": "dist/src/fireproof.js",
6
6
  "module": "dist/src/fireproof.mjs",
package/src/blockstore.js CHANGED
@@ -91,7 +91,6 @@ export class TransactionBlockstore {
91
91
 
92
92
  async networkGet (key) {
93
93
  if (this.remoteBlockFunction) {
94
- // todo why is this on valet?
95
94
  const value = await husher(key, async () => await this.remoteBlockFunction(key))
96
95
  if (value) {
97
96
  // console.log('networkGot: ' + key, value.length)
package/src/database.js CHANGED
@@ -46,7 +46,6 @@ export class Database {
46
46
  * @instance
47
47
  */
48
48
  toJSON () {
49
- // todo this also needs to return the index roots...
50
49
  return {
51
50
  clock: this.clockToJSON(),
52
51
  name: this.name,
@@ -183,8 +182,7 @@ export class Database {
183
182
  const cids = await cidsToProof(allResp.cids)
184
183
  const clockCids = await cidsToProof(allResp.clockCIDs)
185
184
  // console.log('allcids', cids, clockCids)
186
- // todo we need to put the clock head as the last block in the encrypted car
187
- return [...cids, ...clockCids] // need a single block version of clock head, maybe an encoded block for it
185
+ return [...cids, ...clockCids] // clock CID last -- need to handle multiple entry clocks
188
186
  }
189
187
 
190
188
  async allStoredCIDs () {
@@ -226,7 +224,7 @@ export class Database {
226
224
  const clock = opts.clock || this.clock
227
225
  const resp = await get(this.blocks, clock, charwise.encode(key), this.rootCache)
228
226
  this.rootCache = { root: resp.root, clockCIDs: resp.clockCIDs }
229
- // this tombstone is temporary until we can get the prolly tree to delete
227
+ // ? this tombstone is temporary until we can get the prolly tree to delete
230
228
  if (!resp || resp.result === null) {
231
229
  throw new Error('Not found')
232
230
  }
package/src/db-index.js CHANGED
@@ -306,7 +306,7 @@ export class DbIndex {
306
306
  async innerUpdateIndex (inBlocks) {
307
307
  // const callTag = Math.random().toString(36).substring(4)
308
308
  // console.log(`updateIndex ${callTag} >`, this.instanceId, this.dbHead?.toString(), this.indexByKey.cid?.toString(), this.indexById.cid?.toString())
309
- // todo remove this hack
309
+ // todo remove this hack in 0.7.0
310
310
  if (ALWAYS_REBUILD) {
311
311
  this.indexById = { root: null, cid: null }
312
312
  this.indexByKey = { root: null, cid: null }
@@ -353,9 +353,7 @@ export class DbIndex {
353
353
  )
354
354
  this.indexByKey = await bulkIndex(blocks, this.indexByKey, oldIndexEntries.concat(indexEntries), dbIndexOpts)
355
355
  this.dbHead = result.clock
356
- }, false /* don't sync transaction -- maybe move this flag to database.indexBlocks? */)
357
- // todo index subscriptions
358
- // this.database.notifyExternal('dbIndex')
356
+ }, false /* don't sync transaction -- todo move this flag to database.indexBlocks, and concept of sync channels */)
359
357
  // console.timeEnd(callTag + '.doTransactionupdateIndex')
360
358
  // console.log(`updateIndex ${callTag} <`, this.instanceId, this.dbHead?.toString(), this.indexByKey.cid?.toString(), this.indexById.cid?.toString())
361
359
  return didT
package/src/fireproof.js CHANGED
@@ -6,6 +6,7 @@ import { DbIndex as Index } from './db-index.js'
6
6
  import { localGet } from './utils.js'
7
7
  import { Sync } from './sync.js'
8
8
 
9
+ // todo remove Listener in 0.7.0
9
10
  export { Index, Listener, Database, Sync }
10
11
 
11
12
  export class Fireproof {
@@ -20,7 +21,7 @@ export class Fireproof {
20
21
  static storage = (name = null, opts = {}) => {
21
22
  if (name) {
22
23
  opts.name = name
23
- // todo this can come from a registry also
24
+ // todo this can come from a registry also eg remote database / config, etc
24
25
  const existing = localGet('fp.' + name)
25
26
  if (existing) {
26
27
  const existingConfig = JSON.parse(existing)
package/src/prolly.js CHANGED
@@ -172,13 +172,6 @@ const prollyRootFromAncestor = async (events, ancestor, getBlock) => {
172
172
  }
173
173
  }
174
174
 
175
- // async function bigMerge (events, head, getBlock) {
176
- // const allRoots = await Promise.all(head.map(async h => prollyRootFromAncestor(events, h, getBlock)))
177
- // console.log('allRoots', allRoots)
178
- // // todo query over all roots and merge them, but how do they not have a common ancestor? they all start with the _sync root
179
- // throw new Error('not implemented')
180
- // }
181
-
182
175
  const doProllyBulk = async (inBlocks, head, event, doFull = false) => {
183
176
  const { getBlock, blocks } = makeGetAndPutBlock(inBlocks) // this is doubled with eventfetcher
184
177
  let bulkSorted = []
@@ -347,8 +340,6 @@ export async function eventsSince (blocks, head, since) {
347
340
  *
348
341
  */
349
342
  export async function getAll (blocks, head, rootCache = null, doFull = false) {
350
- // todo use the root node left around from put, etc
351
- // move load to a central place
352
343
  if (!head.length) {
353
344
  return { root: null, clockCIDs: new CIDCounter(), cids: new CIDCounter(), result: [] }
354
345
  }
package/src/sync.js CHANGED
@@ -162,7 +162,7 @@ export class Sync {
162
162
  async sendUpdate (blockstore) {
163
163
  if (!this.peer || !this.isReady) return
164
164
  // console.log('send update from', this.database.instanceId)
165
- // todo should send updates since last sync
165
+ // todo should send updates since last sync (currently sends each transaction)
166
166
  const newCar = await blocksToCarBlock(blockstore.lastCid, blockstore)
167
167
  this.status = 'sending update car'
168
168
  this.peer.send(newCar.bytes)
package/src/valet.js CHANGED
@@ -61,29 +61,29 @@ export class Valet {
61
61
  // )
62
62
  if (this.uploadFunction) {
63
63
  // todo we can coalesce these into a single car file
64
- return await this.withDB(async db => {
65
- for (const task of tasks) {
66
- await this.uploadFunction(task.carCid, task.value)
67
- // update the indexedb to mark this car as no longer pending
68
- const carMeta = await db.get('cidToCar', task.carCid)
69
- delete carMeta.pending
70
- await db.put('cidToCar', carMeta)
71
- }
72
- })
64
+ // todo remove idb usage here
65
+ for (const task of tasks) {
66
+ await this.uploadFunction(task.carCid, task.value)
67
+ // todo update syncCidMap to say this has been synced
68
+ // const carMeta = await db.get('cidToCar', task.carCid)
69
+ // delete carMeta.pending
70
+ // await db.put('cidToCar', carMeta)
71
+ }
73
72
  }
74
73
  callback()
75
74
  })
76
75
 
77
76
  this.uploadQueue.drain(async () => {
78
- return await this.withDB(async db => {
79
- const carKeys = (await db.getAllFromIndex('cidToCar', 'pending')).map(c => c.car)
80
- for (const carKey of carKeys) {
81
- await this.uploadFunction(carKey, await db.get('cars', carKey))
82
- const carMeta = await db.get('cidToCar', carKey)
83
- delete carMeta.pending
84
- await db.put('cidToCar', carMeta)
85
- }
86
- })
77
+ // todo read syncCidMap and sync any that are still unsynced
78
+ // return await this.withDB(async db => {
79
+ // const carKeys = (await db.getAllFromIndex('cidToCar', 'pending')).map(c => c.car)
80
+ // for (const carKey of carKeys) {
81
+ // await this.uploadFunction(carKey, await db.get('cars', carKey))
82
+ // const carMeta = await db.get('cidToCar', carKey)
83
+ // delete carMeta.pending
84
+ // await db.put('cidToCar', carMeta)
85
+ // }
86
+ // })
87
87
  })
88
88
  }
89
89
 
@@ -129,16 +129,10 @@ export class Valet {
129
129
 
130
130
  withDB = async dbWorkFun => {
131
131
  if (!this.idb) {
132
- this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 2, {
132
+ this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
133
133
  upgrade (db, oldVersion, newVersion, transaction) {
134
134
  if (oldVersion < 1) {
135
- db.createObjectStore('cars') // todo use database name
136
- const cidToCar = db.createObjectStore('cidToCar', { keyPath: 'car' })
137
- cidToCar.createIndex('cids', 'cids', { multiEntry: true })
138
- }
139
- if (oldVersion < 2) {
140
- const cidToCar = transaction.objectStore('cidToCar')
141
- cidToCar.createIndex('pending', 'pending')
135
+ db.createObjectStore('cars')
142
136
  }
143
137
  }
144
138
  })
@@ -154,13 +148,11 @@ export class Valet {
154
148
  */
155
149
  async * cids () {
156
150
  // console.log('valet cids')
157
- const db = await this.withDB(async db => db)
158
- const tx = db.transaction(['cidToCar'], 'readonly')
159
- let cursor = await tx.store.openCursor()
160
- while (cursor) {
161
- yield { cid: cursor.key, car: cursor.value.car }
162
- cursor = await cursor.continue()
163
- }
151
+ // todo use cidMap
152
+ // while (cursor) {
153
+ // yield { cid: cursor.key, car: cursor.value.car }
154
+ // cursor = await cursor.continue()
155
+ // }
164
156
  }
165
157
 
166
158
  setRootCarCid (cid) {
@@ -169,9 +161,10 @@ export class Valet {
169
161
  this.valetRootCid = null
170
162
  }
171
163
 
164
+ // todo memoize this
172
165
  async getCarCIDForCID (cid) {
173
166
  // make a car reader for this.valetRootCarCid
174
- if (!this.valetRootCarCid) return
167
+ if (!this.valetRootCarCid) return { result: null }
175
168
 
176
169
  let indexNode
177
170
  if (this.valetRoot) {
@@ -194,15 +187,6 @@ export class Valet {
194
187
  return { result: got }
195
188
  }
196
189
 
197
- async OLDgetCarCIDForCID (cid) {
198
- const carCid = await this.withDB(async db => {
199
- const tx = db.transaction(['cars', 'cidToCar'], 'readonly')
200
- const indexResp = await tx.objectStore('cidToCar').index('cids').get(cid)
201
- return indexResp?.car
202
- })
203
- return { result: carCid }
204
- }
205
-
206
190
  async getCombinedReader (carCid) {
207
191
  let carMapReader
208
192
  if (this.valetRootCarCid) {
@@ -267,17 +251,20 @@ export class Valet {
267
251
  newValetCidCar = await blocksToCarBlock(this.valetRootCid, saveValetBlocks)
268
252
  }
269
253
  // console.log('newValetCidCar', this.name, Math.floor(newValetCidCar.bytes.length / 1024))
270
- await this.withDB(async db => {
271
- const tx = db.transaction(['cars'], 'readwrite')
272
- await tx.objectStore('cars').put(value, carCid.toString())
273
- if (newValetCidCar) {
274
- if (this.valetRootCarCid) {
275
- // await tx.objectStore('cars').delete(this.valetRootCarCid.toString())
276
- }
277
- await tx.objectStore('cars').put(newValetCidCar.bytes, newValetCidCar.cid.toString())
254
+ await this.writeCars([
255
+ {
256
+ cid: carCid,
257
+ bytes: value,
258
+ replaces: null
259
+ },
260
+ {
261
+ cid: newValetCidCar.cid,
262
+ bytes: newValetCidCar.bytes,
263
+ replaces: null
264
+ // replaces: this.valetRootCarCid // todo
278
265
  }
279
- return await tx.done
280
- })
266
+ ])
267
+
281
268
  this.valetRootCarCid = newValetCidCar.cid // goes to clock
282
269
 
283
270
  // console.log('parked car', carCid, value.length, Array.from(cids))
@@ -296,6 +283,20 @@ export class Valet {
296
283
  }
297
284
  }
298
285
 
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
+
299
300
  remoteBlockFunction = null
300
301
 
301
302
  async getCarReader (carCid) {
@@ -326,7 +327,7 @@ export class Valet {
326
327
  }
327
328
  const { blocks } = await blocksFromEncryptedCarBlock(roots[0], readerGetWithCodec, this.keyMaterial)
328
329
 
329
- // last block is the root ???
330
+ // last block is the root ??? todo
330
331
  const rootBlock = blocks[blocks.length - 1]
331
332
 
332
333
  return {