@fireproof/core 0.7.3-dev.2 → 0.8.0-dev.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -207,6 +207,11 @@ export const doTransaction = async (label, blockstore, doFun, doSync = true) =>
207
207
  const innerBlockstore = blockstore.begin(label);
208
208
  try {
209
209
  const result = await doFun(innerBlockstore);
210
+ // console.log('doTransaction', label, 'result', result.head)
211
+ if (result && result.head) {
212
+ innerBlockstore.head = result.head;
213
+ }
214
+ // pass the latest clock head for writing to the valet
210
215
  // @ts-ignore
211
216
  await blockstore.commit(innerBlockstore, doSync);
212
217
  return result;
@@ -223,6 +228,7 @@ export const doTransaction = async (label, blockstore, doFun, doSync = true) =>
223
228
  export class InnerBlockstore {
224
229
  /** @type {Map<string, Uint8Array>} */
225
230
  blocks = new Map();
231
+ head = [];
226
232
  lastCid = null;
227
233
  label = '';
228
234
  parentBlockstore = null;
package/dist/crypto.js CHANGED
@@ -11,7 +11,11 @@ const encrypt = async function* ({ get, cids, hasher, key, cache, chunker, root
11
11
  let eroot;
12
12
  for (const string of cids) {
13
13
  const cid = CID.parse(string);
14
- const unencrypted = await get(cid);
14
+ let unencrypted = await get(cid);
15
+ if (!unencrypted.cid) {
16
+ unencrypted = { cid, bytes: unencrypted };
17
+ }
18
+ // console.log('unencrypted', unencrypted)
15
19
  const block = await encode({ ...await codec.encrypt({ ...unencrypted, key }), codec, hasher });
16
20
  // console.log(`encrypting ${string} as ${block.cid}`)
17
21
  yield block;
package/dist/database.js CHANGED
@@ -4,6 +4,7 @@ import { doTransaction, TransactionBlockstore } from './blockstore.js';
4
4
  import charwise from 'charwise';
5
5
  import { CID } from 'multiformats';
6
6
  import { DbIndex as Index } from './db-index.js';
7
+ import { Remote } from './remote.js';
7
8
  // TypeScript Types
8
9
  // eslint-disable-next-line no-unused-vars
9
10
  // import { CID } from 'multiformats/dist/types/src/cid.js'
@@ -27,12 +28,15 @@ export class Database {
27
28
  indexes = new Map();
28
29
  rootCache = null;
29
30
  eventsCache = new Map();
31
+ remote = null;
32
+ name = '';
30
33
  constructor(name, config = {}) {
31
34
  this.name = name;
32
35
  this.clock = [];
33
36
  this.instanceId = `fp.${this.name}.${Math.random().toString(36).substring(2, 7)}`;
34
37
  this.blocks = new TransactionBlockstore(name, config);
35
38
  this.indexBlocks = new TransactionBlockstore(name ? name + '.indexes' : null, { primary: config.index });
39
+ this.remote = new Remote(this, name, config);
36
40
  this.config = config;
37
41
  // todo we can wait for index blocks elsewhere
38
42
  this.ready = Promise.all([this.blocks.ready, this.indexBlocks.ready]).then(([blocksReady, indexReady]) => {
@@ -49,7 +53,7 @@ export class Database {
49
53
  clock.add(cid);
50
54
  }
51
55
  if (header.index) {
52
- this.indexBlocks.valet.primary.setCarCidMapCarCid(header.index.car);
56
+ this.indexBlocks.valet.primary.setLastCar(header.index.car);
53
57
  this.indexBlocks.valet.primary.setKeyMaterial(header.index.key);
54
58
  }
55
59
  if (header.indexes) {
@@ -83,11 +87,11 @@ export class Database {
83
87
  }
84
88
  toHeader() {
85
89
  return {
86
- clock: this.clockToJSON(),
90
+ // clock: this.clockToJSON(),
87
91
  name: this.name,
88
92
  index: {
89
93
  key: this.indexBlocks.valet?.primary.keyMaterial,
90
- car: this.indexBlocks.valet?.primary.valetRootCarCid?.toString()
94
+ car: this.indexBlocks.valet?.primary.lastCar?.toString()
91
95
  },
92
96
  indexes: [...this.indexes.values()].map(index => index.toJSON())
93
97
  };
@@ -101,9 +105,9 @@ export class Database {
101
105
  clockToJSON(clock = null) {
102
106
  return (clock || this.clock).map(cid => cid.toString());
103
107
  }
104
- maybeSaveClock() {
108
+ async maybeSaveClock() {
105
109
  if (this.name && this.blocks.valet) {
106
- this.blocks.valet.saveHeader(this.toHeader());
110
+ await this.blocks.valet.saveHeader(this.toHeader());
107
111
  }
108
112
  }
109
113
  index(name) {
@@ -257,11 +261,14 @@ export class Database {
257
261
  * @memberof Fireproof
258
262
  * @instance
259
263
  */
260
- async put({ _id, _proof, ...doc }) {
264
+ async put({ _id, _proof, _clock, ...doc }) {
261
265
  await this.ready;
262
266
  const id = _id || 'f' + Math.random().toString(36).slice(2);
267
+ doc = JSON.parse(JSON.stringify(doc));
268
+ if (_clock)
269
+ doc._clock = _clock;
263
270
  await this.runValidation({ _id: id, ...doc });
264
- return await this.putToProllyTree({ key: id, value: doc }, doc._clock);
271
+ return await this.putToProllyTree({ key: id, value: doc }, _clock);
265
272
  }
266
273
  /**
267
274
  * Deletes a document from the database
package/dist/fireproof.js CHANGED
@@ -49,6 +49,7 @@ class Fireproof {
49
49
  }
50
50
  static snapshot(database, clock) {
51
51
  const definition = database.toJSON();
52
+ definition.clock = database.clockToJSON();
52
53
  if (clock) {
53
54
  definition.clock = clock.map(c => parseCID(c));
54
55
  definition.indexes.forEach(index => {
package/dist/loader.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Browser } from './storage/browser.js';
2
2
  import { Rest } from './storage/rest.js';
3
- import { UCAN } from './storage/ucan.js';
4
3
  export const Loader = {
5
4
  appropriate: (name, config = {}) => {
6
5
  if (config.StorageClass) {
@@ -9,9 +8,6 @@ export const Loader = {
9
8
  if (config.type === 'rest') {
10
9
  return new Rest(name, config);
11
10
  }
12
- if (config.type === 'ucan') {
13
- return new UCAN(name, config);
14
- }
15
11
  return new Browser(name, config);
16
12
  }
17
13
  };
package/dist/prolly.js CHANGED
@@ -280,7 +280,7 @@ export async function root(inBlocks, head, doFull = false) {
280
280
  bigPut(nb);
281
281
  }
282
282
  // console.log('root root', newProllyRootNode.constructor.name, newProllyRootNode)
283
- return { clockCIDs, node: newProllyRootNode };
283
+ return { clockCIDs, node: newProllyRootNode, head };
284
284
  }, false);
285
285
  // return { clockCIDs, node: newProllyRootNode }
286
286
  }
package/dist/remote.js ADDED
@@ -0,0 +1,102 @@
1
+ // when you call database.connect(email)
2
+ // it will return a promise that resolves when the user is logged in
3
+ // and sends you an email
4
+ import { create } from '@web3-storage/w3up-client';
5
+ import * as w3clock from '@web3-storage/clock/client';
6
+ import { CID } from 'multiformats';
7
+ export class Remote {
8
+ client = null;
9
+ name = 'unset';
10
+ config = {};
11
+ constructor(database, name, config) {
12
+ this.name = name;
13
+ this.config = config;
14
+ this.database = database;
15
+ }
16
+ async clock(cid) {
17
+ // const did = this.client.currentSpace()
18
+ const agent = this.client.agent();
19
+ const head = await w3clock.head({ issuer: agent, with: agent.did(), proofs: [] });
20
+ console.log('head', head, JSON.stringify(head.root.data.ocm.out));
21
+ const headCids = head.root.data.ocm.out.ok.head;
22
+ const blocks = await Promise.all([this.database.blocks.get(CID.parse(cid)),
23
+ ...headCids.map(c => this.database.blocks.get(c))]);
24
+ console.log('blocks', blocks);
25
+ const adv = await w3clock.advance({ issuer: agent, with: agent.did(), proofs: [] }, CID.parse(cid), { blocks });
26
+ console.log('adv', adv, JSON.stringify(adv.root.data.ocm.out));
27
+ return { head, adv };
28
+ }
29
+ async sync(cid) {
30
+ // fetch the remote clock headCids using w3clock.head
31
+ const agent = this.client.agent();
32
+ const head = await w3clock.head({ issuer: agent, with: agent.did(), proofs: [] });
33
+ console.log('head', head, JSON.stringify(head.root.data.ocm.out));
34
+ const headCids = head.root.data.ocm.out.ok.head;
35
+ const lastSyncHead = await this.database.blocks.valet.primary.getLastSynced();
36
+ console.log('lastSyncHead', lastSyncHead);
37
+ const headSet = new Set(headCids.map(c => c.toString()));
38
+ const lastSyncSet = new Set(lastSyncHead.map(c => c.toString()));
39
+ // are they the same?
40
+ const same = headSet.size === lastSyncSet.size && [...headSet].every(value => lastSyncSet.has(value));
41
+ // if the headCids and the lastSyncHead are the same, we are in sync and can push
42
+ if (same) {
43
+ const currentHead = this.database.clock;
44
+ const currentHeadSet = new Set(currentHead.map(c => c.toString()));
45
+ console.log('synced with cloud', headSet, lastSyncSet);
46
+ // are they the same?
47
+ const currentSame = headSet.size === currentHeadSet.size && [...headSet].every(value => currentHeadSet.has(value));
48
+ if (currentSame) {
49
+ // we are in sync, do nothing
50
+ return true;
51
+ }
52
+ else {
53
+ console.log('push to cloud', headSet, currentHeadSet);
54
+ // we are ahead of the remote, push our clock
55
+ // const lastCompact = this.database.blocks.valet.primary.getLastCompact()
56
+ // get a compact since the last sync
57
+ console.log('we are ahead of the remote, push our clock');
58
+ // const compact = this.database.blocks.valet.primary.getCompactSince(lastSyncHead)
59
+ }
60
+ }
61
+ else {
62
+ // we are behind, fetch the remote
63
+ console.log('we are behind, fetch the remote');
64
+ }
65
+ // if it is the same as the local (current metadata carcid? `newValetCidCar` / sync clock), do nothing, we are in sync
66
+ // if it is the same as our previously pushed clock event, but our local clock is ahead of it, we need to push our clock
67
+ // - we can store the previous clock event cid in the metadata
68
+ // - sending our updates:
69
+ // - get the _last_sync and _last_compact values from our metadata
70
+ // - if last sync is after last compact
71
+ // - make a merged car file for the syncs
72
+ // - else
73
+ // - upload the car file for the last compact
74
+ // - make a merge car file for any uncompacted car files since the last compact, it should base its cidMap on the compact car file (as we go the sync stream will need to track it's own cidMap)
75
+ // - if there is only one car file, it is the merge car file (already based on last compact)
76
+ // - upload the merge car file
77
+ // - create a new clock block with the current w3clock.head as parent and the merge car file cid as the data
78
+ // - update the remote clock with the new clock block (it doesn't need to fetch the car file, and we dont need to store the clock blocks locally, just the most recent one)
79
+ //
80
+ // else if the remote head is not contained by our clock, it is is ahead of the local sync clock.
81
+ // - get the car file it points to from its data field
82
+ // - merge to the local clock (park that car so we have both carcid indexes)
83
+ // - calculate a new root from the merged head, and update the local clock
84
+ }
85
+ async connect(email) {
86
+ try {
87
+ const client = await create();
88
+ await client.authorize(email);
89
+ const claims = await client.capability.access.claim();
90
+ console.log('claims', claims);
91
+ const space = await client.createSpace('fp.' + this.name);
92
+ console.log('space', space);
93
+ await client.setCurrentSpace(space.did());
94
+ await client.registerSpace(email);
95
+ this.client = client;
96
+ console.log('client', client);
97
+ }
98
+ catch (err) {
99
+ console.error('registration failed: ', err);
100
+ }
101
+ }
102
+ }
@@ -113,6 +113,7 @@ declare class InnerBlockstore {
113
113
  constructor(label: any, parentBlockstore: any);
114
114
  /** @type {Map<string, Uint8Array>} */
115
115
  blocks: Map<string, Uint8Array>;
116
+ head: any[];
116
117
  lastCid: any;
117
118
  label: string;
118
119
  parentBlockstore: any;
@@ -159,7 +160,8 @@ declare class Database {
159
160
  indexes: Map<any, any>;
160
161
  rootCache: any;
161
162
  eventsCache: Map<any, any>;
162
- name: any;
163
+ remote: any;
164
+ name: string;
163
165
  clock: any[];
164
166
  instanceId: string;
165
167
  blocks: TransactionBlockstore;
@@ -174,8 +176,7 @@ declare class Database {
174
176
  */
175
177
  toJSON(): any;
176
178
  toHeader(): {
177
- clock: string[];
178
- name: any;
179
+ name: string;
179
180
  index: {
180
181
  key: any;
181
182
  car: any;
@@ -189,7 +190,7 @@ declare class Database {
189
190
  * @instance
190
191
  */
191
192
  clockToJSON(clock?: any): string[];
192
- maybeSaveClock(): void;
193
+ maybeSaveClock(): Promise<void>;
193
194
  index(name: any): any;
194
195
  /**
195
196
  * Triggers a notification to all listeners
@@ -250,7 +251,7 @@ declare class Database {
250
251
  * @memberof Fireproof
251
252
  * @instance
252
253
  */
253
- put({ _id, _proof, ...doc }: any): Promise<{
254
+ put({ _id, _proof, _clock, ...doc }: any): Promise<{
254
255
  id: string;
255
256
  clock: CID[];
256
257
  }>;