@peerbit/log 3.0.34 → 4.0.0-55cebfe
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/dist/benchmark/append.d.ts +2 -0
- package/dist/benchmark/append.d.ts.map +1 -0
- package/dist/benchmark/append.js +40 -0
- package/dist/benchmark/append.js.map +1 -0
- package/dist/benchmark/memory/index.d.ts +2 -0
- package/dist/benchmark/memory/index.d.ts.map +1 -0
- package/dist/benchmark/memory/index.js +122 -0
- package/dist/benchmark/memory/index.js.map +1 -0
- package/dist/benchmark/memory/insert.d.ts +2 -0
- package/dist/benchmark/memory/insert.d.ts.map +1 -0
- package/dist/benchmark/memory/insert.js +59 -0
- package/dist/benchmark/memory/insert.js.map +1 -0
- package/dist/benchmark/memory/utils.d.ts +13 -0
- package/dist/benchmark/memory/utils.d.ts.map +1 -0
- package/dist/benchmark/memory/utils.js +2 -0
- package/dist/benchmark/memory/utils.js.map +1 -0
- package/dist/benchmark/payload.d.ts +2 -0
- package/dist/benchmark/payload.d.ts.map +1 -0
- package/{lib/esm/__benchmark__/index.js → dist/benchmark/payload.js} +20 -22
- package/dist/benchmark/payload.js.map +1 -0
- package/dist/src/change.d.ts +6 -0
- package/dist/src/change.d.ts.map +1 -0
- package/{lib/esm → dist/src}/clock.d.ts +3 -26
- package/dist/src/clock.d.ts.map +1 -0
- package/{lib/esm → dist/src}/clock.js +30 -39
- package/dist/src/clock.js.map +1 -0
- package/{lib/esm → dist/src}/difference.d.ts +1 -0
- package/dist/src/difference.d.ts.map +1 -0
- package/{lib/esm → dist/src}/encoding.d.ts +2 -1
- package/dist/src/encoding.d.ts.map +1 -0
- package/{lib/esm → dist/src}/encoding.js +2 -2
- package/{lib/esm → dist/src}/encoding.js.map +1 -1
- package/dist/src/entry-index.d.ts +78 -0
- package/dist/src/entry-index.d.ts.map +1 -0
- package/dist/src/entry-index.js +316 -0
- package/dist/src/entry-index.js.map +1 -0
- package/{lib/esm → dist/src}/entry-with-refs.d.ts +2 -1
- package/dist/src/entry-with-refs.d.ts.map +1 -0
- package/{lib/esm → dist/src}/entry.d.ts +22 -18
- package/dist/src/entry.d.ts.map +1 -0
- package/{lib/esm → dist/src}/entry.js +69 -42
- package/dist/src/entry.js.map +1 -0
- package/{lib/esm → dist/src}/find-uniques.d.ts +1 -0
- package/dist/src/find-uniques.d.ts.map +1 -0
- package/{lib/esm → dist/src}/heads-cache.d.ts +4 -3
- package/dist/src/heads-cache.d.ts.map +1 -0
- package/{lib/esm → dist/src}/heads-cache.js +9 -10
- package/dist/src/heads-cache.js.map +1 -0
- package/{lib/esm → dist/src}/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/{lib/esm → dist/src}/log-errors.d.ts +1 -0
- package/dist/src/log-errors.d.ts.map +1 -0
- package/dist/src/log-sorting.d.ts +35 -0
- package/dist/src/log-sorting.d.ts.map +1 -0
- package/dist/src/log-sorting.js +105 -0
- package/dist/src/log-sorting.js.map +1 -0
- package/{lib/esm → dist/src}/log.d.ts +78 -56
- package/dist/src/log.d.ts.map +1 -0
- package/{lib/esm → dist/src}/log.js +355 -465
- package/dist/src/log.js.map +1 -0
- package/{lib/esm → dist/src}/logger.d.ts +1 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/{lib/esm → dist/src}/logger.js.map +1 -1
- package/{lib/esm → dist/src}/snapshot.d.ts +5 -4
- package/dist/src/snapshot.d.ts.map +1 -0
- package/{lib/esm → dist/src}/snapshot.js +10 -10
- package/dist/src/snapshot.js.map +1 -0
- package/{lib/esm → dist/src}/trim.d.ts +11 -9
- package/dist/src/trim.d.ts.map +1 -0
- package/{lib/esm → dist/src}/trim.js +46 -35
- package/dist/src/trim.js.map +1 -0
- package/dist/src/utils.d.ts +4 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +12 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +104 -78
- package/src/change.ts +3 -2
- package/src/clock.ts +22 -22
- package/src/encoding.ts +4 -4
- package/src/entry-index.ts +451 -52
- package/src/entry-with-refs.ts +1 -1
- package/src/entry.ts +95 -81
- package/src/heads-cache.ts +33 -29
- package/src/log-sorting.ts +116 -94
- package/src/log.ts +482 -571
- package/src/logger.ts +1 -0
- package/src/snapshot.ts +15 -17
- package/src/trim.ts +81 -50
- package/src/utils.ts +10 -0
- package/lib/esm/__benchmark__/index.d.ts +0 -1
- package/lib/esm/__benchmark__/index.js.map +0 -1
- package/lib/esm/change.d.ts +0 -5
- package/lib/esm/clock.js.map +0 -1
- package/lib/esm/entry-index.d.ts +0 -24
- package/lib/esm/entry-index.js +0 -74
- package/lib/esm/entry-index.js.map +0 -1
- package/lib/esm/entry.js.map +0 -1
- package/lib/esm/heads-cache.js.map +0 -1
- package/lib/esm/heads.d.ts +0 -69
- package/lib/esm/heads.js +0 -157
- package/lib/esm/heads.js.map +0 -1
- package/lib/esm/log-sorting.d.ts +0 -44
- package/lib/esm/log-sorting.js +0 -86
- package/lib/esm/log-sorting.js.map +0 -1
- package/lib/esm/log.js.map +0 -1
- package/lib/esm/snapshot.js.map +0 -1
- package/lib/esm/trim.js.map +0 -1
- package/lib/esm/types.d.ts +0 -6
- package/lib/esm/types.js +0 -23
- package/lib/esm/types.js.map +0 -1
- package/lib/esm/utils.d.ts +0 -2
- package/lib/esm/utils.js +0 -3
- package/lib/esm/utils.js.map +0 -1
- package/lib/esm/values.d.ts +0 -26
- package/lib/esm/values.js +0 -131
- package/lib/esm/values.js.map +0 -1
- package/src/__benchmark__/index.ts +0 -130
- package/src/heads.ts +0 -233
- package/src/types.ts +0 -12
- package/src/values.ts +0 -174
- /package/{lib/esm → dist/src}/change.js +0 -0
- /package/{lib/esm → dist/src}/change.js.map +0 -0
- /package/{lib/esm → dist/src}/difference.js +0 -0
- /package/{lib/esm → dist/src}/difference.js.map +0 -0
- /package/{lib/esm → dist/src}/entry-with-refs.js +0 -0
- /package/{lib/esm → dist/src}/entry-with-refs.js.map +0 -0
- /package/{lib/esm → dist/src}/find-uniques.js +0 -0
- /package/{lib/esm → dist/src}/find-uniques.js.map +0 -0
- /package/{lib/esm → dist/src}/index.js +0 -0
- /package/{lib/esm → dist/src}/index.js.map +0 -0
- /package/{lib/esm → dist/src}/log-errors.js +0 -0
- /package/{lib/esm → dist/src}/log-errors.js.map +0 -0
- /package/{lib/esm → dist/src}/logger.js +0 -0
|
@@ -8,45 +8,53 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
var Log_1;
|
|
11
|
-
import { randomBytes, sha256Base64Sync } from "@peerbit/crypto";
|
|
12
|
-
import { Cache } from "@peerbit/cache";
|
|
13
|
-
import { EntryIndex } from "./entry-index.js";
|
|
14
|
-
import * as LogError from "./log-errors.js";
|
|
15
|
-
import * as Sorting from "./log-sorting.js";
|
|
16
|
-
import { findUniques } from "./find-uniques.js";
|
|
17
|
-
import { Entry, EntryType } from "./entry.js";
|
|
18
|
-
import { HLC, LamportClock as Clock } from "./clock.js";
|
|
19
11
|
import { deserialize, field, fixedArray, variant } from "@dao-xyz/borsh";
|
|
12
|
+
import {} from "@peerbit/any-store";
|
|
13
|
+
import { cidifyString } from "@peerbit/blocks-interface";
|
|
14
|
+
import { SignatureWithKey, X25519Keypair, randomBytes, sha256Base64Sync, } from "@peerbit/crypto";
|
|
15
|
+
import {} from "@peerbit/indexer-interface";
|
|
16
|
+
import { create } from "@peerbit/indexer-sqlite3";
|
|
17
|
+
import {} from "@peerbit/keychain";
|
|
18
|
+
import {} from "./change.js";
|
|
19
|
+
import { LamportClock as Clock, HLC, LamportClock, Timestamp, } from "./clock.js";
|
|
20
20
|
import { NO_ENCODING } from "./encoding.js";
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
21
|
+
import { EntryIndex, } from "./entry-index.js";
|
|
22
|
+
import {} from "./entry-with-refs.js";
|
|
23
|
+
import { Entry, EntryType, Payload, ShallowEntry, } from "./entry.js";
|
|
24
|
+
import { findUniques } from "./find-uniques.js";
|
|
25
|
+
import * as LogError from "./log-errors.js";
|
|
26
|
+
import * as Sorting from "./log-sorting.js";
|
|
23
27
|
import { Trim } from "./trim.js";
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const { LastWriteWins } = Sorting;
|
|
29
|
+
export const ENTRY_JOIN_SHAPE = {
|
|
30
|
+
hash: true,
|
|
31
|
+
meta: { type: true, next: true, gid: true, clock: true },
|
|
32
|
+
};
|
|
28
33
|
let Log = Log_1 = class Log {
|
|
29
34
|
_id;
|
|
30
|
-
_sortFn;
|
|
35
|
+
/* private _sortFn!: Sorting.ISortFunction; */
|
|
31
36
|
_storage;
|
|
32
37
|
_hlc;
|
|
33
38
|
// Identity
|
|
34
39
|
_identity;
|
|
35
40
|
// Keeping track of entries
|
|
36
41
|
_entryIndex;
|
|
37
|
-
_headsIndex
|
|
38
|
-
|
|
42
|
+
/* private _headsIndex!: HeadsIndex<T>;
|
|
43
|
+
private _values!: Values<T>;
|
|
44
|
+
*/
|
|
39
45
|
// Index of all next pointers in this log
|
|
40
|
-
_nextsIndex
|
|
46
|
+
/* private _nextsIndex!: Map<string, Set<string>>; */
|
|
41
47
|
_keychain;
|
|
42
48
|
_encoding;
|
|
43
49
|
_trim;
|
|
44
|
-
_entryCache;
|
|
45
50
|
_canAppend;
|
|
46
51
|
_onChange;
|
|
47
52
|
_closed = true;
|
|
48
|
-
|
|
53
|
+
_closeController;
|
|
54
|
+
_loadedOnce = false;
|
|
55
|
+
_indexer;
|
|
49
56
|
_joining; // entry hashes that are currently joining into this log
|
|
57
|
+
_sortFn;
|
|
50
58
|
constructor(properties) {
|
|
51
59
|
this._id = properties?.id || randomBytes(32);
|
|
52
60
|
}
|
|
@@ -60,18 +68,13 @@ let Log = Log_1 = class Log {
|
|
|
60
68
|
if (this.closed === false) {
|
|
61
69
|
throw new Error("Already open");
|
|
62
70
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
sortFn = sortFn;
|
|
69
|
-
this._sortFn = NoZeroes(sortFn);
|
|
71
|
+
this._closeController = new AbortController();
|
|
72
|
+
const { encoding, trim, keychain, indexer, onGidRemoved, sortFn } = options;
|
|
73
|
+
// TODO do correctly with tie breaks
|
|
74
|
+
this._sortFn = sortFn || LastWriteWins;
|
|
70
75
|
this._storage = store;
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
await this._memory.open();
|
|
74
|
-
}
|
|
76
|
+
this._indexer = indexer || (await create());
|
|
77
|
+
await this._indexer.start?.();
|
|
75
78
|
this._encoding = encoding || NO_ENCODING;
|
|
76
79
|
this._joining = new Map();
|
|
77
80
|
// Identity
|
|
@@ -80,47 +83,30 @@ let Log = Log_1 = class Log {
|
|
|
80
83
|
this._keychain = keychain;
|
|
81
84
|
// Clock
|
|
82
85
|
this._hlc = new HLC();
|
|
83
|
-
this._nextsIndex = new Map();
|
|
84
86
|
const id = this.id;
|
|
85
87
|
if (!id) {
|
|
86
88
|
throw new Error("Id not set");
|
|
87
89
|
}
|
|
88
|
-
this._headsIndex = new HeadsIndex(id);
|
|
89
|
-
await this._headsIndex.init(this, { onGidRemoved });
|
|
90
|
-
this._entryCache = new Cache({ max: ENTRY_CACHE_MAX });
|
|
91
90
|
this._entryIndex = new EntryIndex({
|
|
92
91
|
store: this._storage,
|
|
93
92
|
init: (e) => e.init(this),
|
|
94
|
-
|
|
93
|
+
onGidRemoved,
|
|
94
|
+
index: await (await this._indexer.scope("heads")).init({ schema: ShallowEntry }),
|
|
95
|
+
publicKey: this._identity.publicKey,
|
|
96
|
+
sort: this._sortFn,
|
|
95
97
|
});
|
|
96
|
-
|
|
98
|
+
await this._entryIndex.init();
|
|
99
|
+
/* this._values = new Values(this._entryIndex, this._sortFn); */
|
|
97
100
|
this._trim = new Trim({
|
|
98
|
-
|
|
101
|
+
index: this._entryIndex,
|
|
99
102
|
deleteNode: async (node) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (entry) {
|
|
105
|
-
this.values.deleteNode(node);
|
|
106
|
-
await Promise.all([
|
|
107
|
-
this.headsIndex.del(this.entryIndex.getShallow(node.value)),
|
|
108
|
-
this.entryIndex.delete(node.value)
|
|
109
|
-
]);
|
|
110
|
-
this.nextsIndex.delete(node.value);
|
|
111
|
-
await this.blocks.rm(node.value);
|
|
112
|
-
}
|
|
113
|
-
const b = this.values.length;
|
|
114
|
-
if (a === b) {
|
|
115
|
-
/* throw new Error(
|
|
116
|
-
"Unexpected miss match between log size and entry index size: " +
|
|
117
|
-
this.values.length +
|
|
118
|
-
this.entryIndex._index.size
|
|
119
|
-
); */
|
|
120
|
-
}
|
|
121
|
-
return entry;
|
|
103
|
+
const resolved = await this.get(node.hash);
|
|
104
|
+
await this._entryIndex.delete(node.hash);
|
|
105
|
+
await this._storage.rm(node.hash);
|
|
106
|
+
return resolved;
|
|
122
107
|
},
|
|
123
|
-
|
|
108
|
+
sortFn: this._sortFn,
|
|
109
|
+
getLength: () => this.length,
|
|
124
110
|
}, trim);
|
|
125
111
|
this._canAppend = async (entry) => {
|
|
126
112
|
if (options?.canAppend) {
|
|
@@ -132,6 +118,7 @@ let Log = Log_1 = class Log {
|
|
|
132
118
|
};
|
|
133
119
|
this._onChange = options?.onChange;
|
|
134
120
|
this._closed = false;
|
|
121
|
+
this._closeController = new AbortController();
|
|
135
122
|
}
|
|
136
123
|
_idString;
|
|
137
124
|
get idString() {
|
|
@@ -157,13 +144,10 @@ let Log = Log_1 = class Log {
|
|
|
157
144
|
* Returns the length of the log.
|
|
158
145
|
*/
|
|
159
146
|
get length() {
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
get values() {
|
|
163
|
-
if (this.closed) {
|
|
147
|
+
if (this._closed) {
|
|
164
148
|
throw new Error("Closed");
|
|
165
149
|
}
|
|
166
|
-
return this.
|
|
150
|
+
return this._entryIndex.length;
|
|
167
151
|
}
|
|
168
152
|
get canAppend() {
|
|
169
153
|
return this._canAppend;
|
|
@@ -174,36 +158,20 @@ let Log = Log_1 = class Log {
|
|
|
174
158
|
* @returns {boolean}
|
|
175
159
|
*/
|
|
176
160
|
has(cid) {
|
|
177
|
-
return this._entryIndex.
|
|
161
|
+
return this._entryIndex.has(cid);
|
|
178
162
|
}
|
|
179
163
|
/**
|
|
180
164
|
* Get all entries sorted. Don't use this method anywhere where performance matters
|
|
181
165
|
*/
|
|
182
|
-
toArray() {
|
|
166
|
+
async toArray() {
|
|
183
167
|
// we call init, because the values might be unitialized
|
|
184
|
-
return this.
|
|
168
|
+
return this.entryIndex.query([], this.sortFn.sort, true).all();
|
|
185
169
|
}
|
|
186
170
|
/**
|
|
187
171
|
* Returns the head index
|
|
188
172
|
*/
|
|
189
|
-
|
|
190
|
-
return this.
|
|
191
|
-
}
|
|
192
|
-
get memory() {
|
|
193
|
-
return this._memory;
|
|
194
|
-
}
|
|
195
|
-
async getHeads() {
|
|
196
|
-
const heads = new Array(this.headsIndex.index.size);
|
|
197
|
-
let i = 0;
|
|
198
|
-
for (const hash of this.headsIndex.index) {
|
|
199
|
-
heads[i++] = this._entryIndex.get(hash).then((x) => x?.init(this));
|
|
200
|
-
}
|
|
201
|
-
const resolved = await Promise.all(heads);
|
|
202
|
-
const defined = resolved.filter((x) => !!x);
|
|
203
|
-
if (defined.length !== resolved.length) {
|
|
204
|
-
logger.error("Failed to resolve all heads");
|
|
205
|
-
}
|
|
206
|
-
return defined;
|
|
173
|
+
getHeads(resolve = false) {
|
|
174
|
+
return this.entryIndex.getHeads(undefined, resolve);
|
|
207
175
|
}
|
|
208
176
|
/**
|
|
209
177
|
* Returns an array of Entry objects that reference entries which
|
|
@@ -233,9 +201,6 @@ let Log = Log_1 = class Log {
|
|
|
233
201
|
get blocks() {
|
|
234
202
|
return this._storage;
|
|
235
203
|
}
|
|
236
|
-
get nextsIndex() {
|
|
237
|
-
return this._nextsIndex;
|
|
238
|
-
}
|
|
239
204
|
get entryIndex() {
|
|
240
205
|
return this._entryIndex;
|
|
241
206
|
}
|
|
@@ -259,68 +224,89 @@ let Log = Log_1 = class Log {
|
|
|
259
224
|
this._identity = identity;
|
|
260
225
|
}
|
|
261
226
|
/**
|
|
262
|
-
*
|
|
227
|
+
* Get an entry.
|
|
263
228
|
* @param {string} [hash] The hashes of the entry
|
|
264
229
|
*/
|
|
265
230
|
get(hash, options) {
|
|
266
|
-
return this._entryIndex.get(hash, options);
|
|
267
|
-
}
|
|
268
|
-
async traverse(rootEntries, amount = -1, endHash) {
|
|
269
|
-
// Sort the given given root entries and use as the starting stack
|
|
270
|
-
let stack = rootEntries.sort(this._sortFn).reverse();
|
|
271
|
-
// Cache for checking if we've processed an entry already
|
|
272
|
-
let traversed = {};
|
|
273
|
-
// End result
|
|
274
|
-
const result = {};
|
|
275
|
-
let count = 0;
|
|
276
|
-
// Named function for getting an entry from the log
|
|
277
|
-
const getEntry = (e) => this.get(e);
|
|
278
|
-
// Add an entry to the stack and traversed nodes index
|
|
279
|
-
const addToStack = (entry) => {
|
|
280
|
-
// If we've already processed the Entry<T>, don't add it to the stack
|
|
281
|
-
if (!entry || traversed[entry.hash]) {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
// Add the entry in front of the stack and sort
|
|
285
|
-
stack = [entry, ...stack].sort(this._sortFn).reverse();
|
|
286
|
-
// Add to the cache of processed entries
|
|
287
|
-
traversed[entry.hash] = true;
|
|
288
|
-
};
|
|
289
|
-
const addEntry = (rootEntry) => {
|
|
290
|
-
result[rootEntry.hash] = rootEntry;
|
|
291
|
-
traversed[rootEntry.hash] = true;
|
|
292
|
-
count++;
|
|
293
|
-
};
|
|
294
|
-
// Start traversal
|
|
295
|
-
// Process stack until it's empty (traversed the full log)
|
|
296
|
-
// or when we have the requested amount of entries
|
|
297
|
-
// If requested entry amount is -1, traverse all
|
|
298
|
-
while (stack.length > 0 && (count < amount || amount < 0)) {
|
|
299
|
-
// eslint-disable-line no-unmodified-loop-condition
|
|
300
|
-
// Get the next element from the stack
|
|
301
|
-
const entry = stack.shift();
|
|
302
|
-
if (!entry) {
|
|
303
|
-
throw new Error("Unexpected");
|
|
304
|
-
}
|
|
305
|
-
// Add to the result
|
|
306
|
-
addEntry(entry);
|
|
307
|
-
// If it is the specified end hash, break out of the while loop
|
|
308
|
-
if (endHash && endHash === entry.hash)
|
|
309
|
-
break;
|
|
310
|
-
// Add entry's next references to the stack
|
|
311
|
-
const entries = (await Promise.all(entry.next.map(getEntry))).filter((x) => !!x);
|
|
312
|
-
entries.forEach(addToStack);
|
|
313
|
-
}
|
|
314
|
-
stack = [];
|
|
315
|
-
traversed = {};
|
|
316
|
-
// End result
|
|
317
|
-
return result;
|
|
231
|
+
return this._entryIndex.get(hash, options ? { type: "full", timeout: options.timeout } : undefined);
|
|
318
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Get a entry with shallow representation
|
|
235
|
+
* @param {string} [hash] The hashes of the entry
|
|
236
|
+
*/
|
|
237
|
+
async getShallow(hash, options) {
|
|
238
|
+
return (await this._entryIndex.getShallow(hash))?.value;
|
|
239
|
+
}
|
|
240
|
+
/*
|
|
241
|
+
async traverse(
|
|
242
|
+
rootEntries: Entry<T>[],
|
|
243
|
+
amount = -1,
|
|
244
|
+
endHash?: string
|
|
245
|
+
): Promise<{ [key: string]: Entry<T> }> {
|
|
246
|
+
// Sort the given given root entries and use as the starting stack
|
|
247
|
+
let stack: Entry<T>[] = rootEntries.sort(this._sortFn).reverse();
|
|
248
|
+
|
|
249
|
+
// Cache for checking if we've processed an entry already
|
|
250
|
+
let traversed: { [key: string]: boolean } = {};
|
|
251
|
+
// End result
|
|
252
|
+
const result: { [key: string]: Entry<T> } = {};
|
|
253
|
+
let count = 0;
|
|
254
|
+
// Named function for getting an entry from the log
|
|
255
|
+
const getEntry = (e: string) => this.get(e);
|
|
256
|
+
|
|
257
|
+
// Add an entry to the stack and traversed nodes index
|
|
258
|
+
const addToStack = (entry: Entry<T>) => {
|
|
259
|
+
// If we've already processed the Entry<T>, don't add it to the stack
|
|
260
|
+
if (!entry || traversed[entry.hash]) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Add the entry in front of the stack and sort
|
|
265
|
+
stack = [entry, ...stack].sort(this._sortFn).reverse();
|
|
266
|
+
// Add to the cache of processed entries
|
|
267
|
+
traversed[entry.hash] = true;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const addEntry = (rootEntry: Entry<T>) => {
|
|
271
|
+
result[rootEntry.hash] = rootEntry;
|
|
272
|
+
traversed[rootEntry.hash] = true;
|
|
273
|
+
count++;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// Start traversal
|
|
277
|
+
// Process stack until it's empty (traversed the full log)
|
|
278
|
+
// or when we have the requested amount of entries
|
|
279
|
+
// If requested entry amount is -1, traverse all
|
|
280
|
+
while (stack.length > 0 && (count < amount || amount < 0)) {
|
|
281
|
+
// eslint-disable-line no-unmodified-loop-condition
|
|
282
|
+
// Get the next element from the stack
|
|
283
|
+
const entry = stack.shift();
|
|
284
|
+
if (!entry) {
|
|
285
|
+
throw new Error("Unexpected");
|
|
286
|
+
}
|
|
287
|
+
// Add to the result
|
|
288
|
+
addEntry(entry);
|
|
289
|
+
// If it is the specified end hash, break out of the while loop
|
|
290
|
+
if (endHash && endHash === entry.hash) break;
|
|
291
|
+
|
|
292
|
+
// Add entry's next references to the stack
|
|
293
|
+
const entries = (await Promise.all(entry.next.map(getEntry))).filter(
|
|
294
|
+
(x) => !!x
|
|
295
|
+
) as Entry<any>[];
|
|
296
|
+
entries.forEach(addToStack);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
stack = [];
|
|
300
|
+
traversed = {};
|
|
301
|
+
// End result
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
*/
|
|
319
305
|
async getReferenceSamples(from, options) {
|
|
320
306
|
const hashes = new Set();
|
|
321
307
|
const pointerCount = options?.pointerCount || 0;
|
|
322
308
|
const memoryLimit = options?.memoryLimit;
|
|
323
|
-
const maxDistance = Math.min(pointerCount, this.
|
|
309
|
+
const maxDistance = Math.min(pointerCount, this.entryIndex.length);
|
|
324
310
|
if (maxDistance === 0) {
|
|
325
311
|
return [];
|
|
326
312
|
}
|
|
@@ -378,8 +364,9 @@ let Log = Log_1 = class Log {
|
|
|
378
364
|
}
|
|
379
365
|
/**
|
|
380
366
|
* Append an entry to the log.
|
|
381
|
-
* @param {
|
|
382
|
-
* @
|
|
367
|
+
* @param {T} data The data to be appended
|
|
368
|
+
* @param {AppendOptions} [options] The options for the append
|
|
369
|
+
* @returns {{ entry: Entry<T>; removed: ShallowEntry[] }} The appended entry and an array of removed entries
|
|
383
370
|
*/
|
|
384
371
|
async append(data, options = {}) {
|
|
385
372
|
// Update the clock (find the latest clock)
|
|
@@ -390,11 +377,14 @@ let Log = Log_1 = class Log {
|
|
|
390
377
|
}
|
|
391
378
|
}
|
|
392
379
|
await this.load({ reload: false });
|
|
393
|
-
const nexts = options.meta?.next ||
|
|
380
|
+
const nexts = options.meta?.next ||
|
|
381
|
+
(await this.entryIndex
|
|
382
|
+
.getHeads(undefined, { type: "shape", shape: Sorting.ENTRY_SORT_SHAPE })
|
|
383
|
+
.all());
|
|
394
384
|
// Calculate max time for log/graph
|
|
395
385
|
const clock = new Clock({
|
|
396
386
|
id: this._identity.publicKey.bytes,
|
|
397
|
-
timestamp: options?.meta?.timestamp || this._hlc.now()
|
|
387
|
+
timestamp: options?.meta?.timestamp || this._hlc.now(),
|
|
398
388
|
});
|
|
399
389
|
const entry = await Entry.create({
|
|
400
390
|
store: this._storage,
|
|
@@ -406,63 +396,67 @@ let Log = Log_1 = class Log {
|
|
|
406
396
|
type: options.meta?.type,
|
|
407
397
|
gidSeed: options.meta?.gidSeed,
|
|
408
398
|
data: options.meta?.data,
|
|
409
|
-
next: nexts
|
|
399
|
+
next: nexts,
|
|
410
400
|
},
|
|
411
401
|
encoding: this._encoding,
|
|
412
402
|
encryption: options.encryption
|
|
413
403
|
? {
|
|
414
404
|
keypair: options.encryption.keypair,
|
|
415
405
|
receiver: {
|
|
416
|
-
...options.encryption.receiver
|
|
417
|
-
}
|
|
406
|
+
...options.encryption.receiver,
|
|
407
|
+
},
|
|
418
408
|
}
|
|
419
409
|
: undefined,
|
|
420
|
-
canAppend: this._canAppend
|
|
410
|
+
canAppend: options.canAppend || this._canAppend,
|
|
421
411
|
});
|
|
422
412
|
if (!entry.hash) {
|
|
423
413
|
throw new Error("Unexpected");
|
|
424
414
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
await this.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
415
|
+
if (entry.meta.type !== EntryType.CUT) {
|
|
416
|
+
for (const e of nexts) {
|
|
417
|
+
if (!(await this.has(e.hash))) {
|
|
418
|
+
let entry;
|
|
419
|
+
if (e instanceof Entry) {
|
|
420
|
+
entry = e;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
let resolved = await this.entryIndex.get(e.hash);
|
|
424
|
+
if (!resolved) {
|
|
425
|
+
// eslint-disable-next-line no-console
|
|
426
|
+
console.warn("Unexpected missing entry when joining", e.hash);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
entry = resolved;
|
|
430
|
+
}
|
|
431
|
+
await this.join([entry]);
|
|
432
|
+
}
|
|
437
433
|
}
|
|
438
434
|
}
|
|
439
|
-
await this.
|
|
440
|
-
|
|
441
|
-
|
|
435
|
+
await this.entryIndex.put(entry, {
|
|
436
|
+
unique: true,
|
|
437
|
+
isHead: true,
|
|
438
|
+
toMultiHash: false,
|
|
439
|
+
});
|
|
442
440
|
const removed = await this.processEntry(entry);
|
|
443
441
|
entry.init({ encoding: this._encoding, keychain: this._keychain });
|
|
444
442
|
const trimmed = await this.trim(options?.trim);
|
|
445
|
-
|
|
446
|
-
|
|
443
|
+
if (trimmed) {
|
|
444
|
+
for (const entry of trimmed) {
|
|
445
|
+
removed.push(entry);
|
|
446
|
+
}
|
|
447
447
|
}
|
|
448
448
|
const changes = {
|
|
449
449
|
added: [entry],
|
|
450
|
-
removed
|
|
450
|
+
removed,
|
|
451
451
|
};
|
|
452
|
-
await this.
|
|
453
|
-
await this._onChange?.(changes);
|
|
452
|
+
await (options?.onChange || this._onChange)?.(changes);
|
|
454
453
|
return { entry, removed };
|
|
455
454
|
}
|
|
456
455
|
async reset(entries) {
|
|
457
|
-
|
|
458
|
-
this._entryIndex
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
cache: this._entryCache
|
|
462
|
-
});
|
|
463
|
-
await this.headsIndex.reset([]);
|
|
464
|
-
this._values = new Values(this._entryIndex, this._sortFn, []);
|
|
465
|
-
await this.join(entries);
|
|
456
|
+
const heads = await this.getHeads(true).all();
|
|
457
|
+
await this._entryIndex.clear();
|
|
458
|
+
await this._onChange?.({ added: [], removed: heads });
|
|
459
|
+
await this.join(entries || heads);
|
|
466
460
|
}
|
|
467
461
|
async remove(entry, options) {
|
|
468
462
|
await this.load({ reload: false });
|
|
@@ -470,267 +464,200 @@ let Log = Log_1 = class Log {
|
|
|
470
464
|
if (entries.length === 0) {
|
|
471
465
|
return {
|
|
472
466
|
added: [],
|
|
473
|
-
removed: []
|
|
467
|
+
removed: [],
|
|
474
468
|
};
|
|
475
469
|
}
|
|
470
|
+
const change = {
|
|
471
|
+
added: [],
|
|
472
|
+
removed: Array.isArray(entry) ? entry : [entry],
|
|
473
|
+
};
|
|
474
|
+
await this._onChange?.(change);
|
|
476
475
|
if (options?.recursively) {
|
|
477
476
|
await this.deleteRecursively(entry);
|
|
478
477
|
}
|
|
479
478
|
else {
|
|
480
479
|
for (const entry of entries) {
|
|
481
|
-
await this.delete(entry);
|
|
480
|
+
await this.delete(entry.hash);
|
|
482
481
|
}
|
|
483
482
|
}
|
|
484
|
-
const change = {
|
|
485
|
-
added: [],
|
|
486
|
-
removed: Array.isArray(entry) ? entry : [entry]
|
|
487
|
-
};
|
|
488
|
-
/* await Promise.all([
|
|
489
|
-
this._logCache?.queue(change),
|
|
490
|
-
this._onUpdate(change),
|
|
491
|
-
]); */
|
|
492
|
-
await this._onChange?.(change);
|
|
493
483
|
return change;
|
|
494
484
|
}
|
|
495
|
-
iterator(options
|
|
485
|
+
/* iterator(options?: {
|
|
486
|
+
from?: "tail" | "head";
|
|
487
|
+
amount?: number;
|
|
488
|
+
}): IterableIterator<string> {
|
|
496
489
|
const from = options?.from || "tail";
|
|
497
490
|
const amount = typeof options?.amount === "number" ? options?.amount : -1;
|
|
498
491
|
let next = from === "tail" ? this._values.tail : this._values.head;
|
|
499
|
-
const nextFn = from === "tail" ? (e) => e.prev : (e) => e.next;
|
|
492
|
+
const nextFn = from === "tail" ? (e: any) => e.prev : (e: any) => e.next;
|
|
500
493
|
return (function* () {
|
|
501
494
|
let counter = 0;
|
|
502
495
|
while (next) {
|
|
503
496
|
if (amount >= 0 && counter >= amount) {
|
|
504
497
|
return;
|
|
505
498
|
}
|
|
499
|
+
|
|
506
500
|
yield next.value;
|
|
507
501
|
counter++;
|
|
502
|
+
|
|
508
503
|
next = nextFn(next);
|
|
509
504
|
}
|
|
510
505
|
})();
|
|
511
|
-
}
|
|
506
|
+
} */
|
|
512
507
|
async trim(option = this._trim.options) {
|
|
513
508
|
return this._trim.trim(option);
|
|
514
509
|
}
|
|
515
|
-
/**
|
|
516
|
-
*
|
|
517
|
-
* @param entries
|
|
518
|
-
* @returns change
|
|
519
|
-
*/
|
|
520
|
-
/* async sync(
|
|
521
|
-
entries: (EntryWithRefs<T> | Entry<T> | string)[],
|
|
522
|
-
options: {
|
|
523
|
-
canAppend?: CanAppend<T>;
|
|
524
|
-
onChange?: (change: Change<T>) => void | Promise<void>;
|
|
525
|
-
timeout?: number;
|
|
526
|
-
} = {}
|
|
527
|
-
): Promise<void> {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
logger.debug(`Sync request #${entries.length}`);
|
|
531
|
-
const entriesToJoin: (Entry<T> | string)[] = [];
|
|
532
|
-
for (const e of entries) {
|
|
533
|
-
if (e instanceof Entry || typeof e === "string") {
|
|
534
|
-
entriesToJoin.push(e);
|
|
535
|
-
} else {
|
|
536
|
-
for (const ref of e.references) {
|
|
537
|
-
entriesToJoin.push(ref);
|
|
538
|
-
}
|
|
539
|
-
entriesToJoin.push(e.entry);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
await this.join(entriesToJoin, {
|
|
544
|
-
canAppend: (entry) => {
|
|
545
|
-
const canAppend = options?.canAppend || this.canAppend;
|
|
546
|
-
return !canAppend || canAppend(entry);
|
|
547
|
-
},
|
|
548
|
-
onChange: (change) => {
|
|
549
|
-
options?.onChange?.(change);
|
|
550
|
-
return this._onChange?.({
|
|
551
|
-
added: change.added,
|
|
552
|
-
removed: change.removed,
|
|
553
|
-
});
|
|
554
|
-
},
|
|
555
|
-
timeout: options.timeout,
|
|
556
|
-
});
|
|
557
|
-
} */
|
|
558
510
|
async join(entriesOrLog, options) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
511
|
+
let entries;
|
|
512
|
+
let references = new Map();
|
|
513
|
+
if (entriesOrLog instanceof Log_1) {
|
|
514
|
+
if (entriesOrLog.entryIndex.length === 0)
|
|
515
|
+
return;
|
|
516
|
+
entries = await entriesOrLog.toArray();
|
|
517
|
+
for (const element of entries) {
|
|
518
|
+
references.set(element.hash, element);
|
|
563
519
|
}
|
|
564
|
-
};
|
|
565
|
-
await this.load({ reload: false });
|
|
566
|
-
if (entriesOrLog.length === 0) {
|
|
567
|
-
return;
|
|
568
520
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
const entriesBottomUp = [];
|
|
573
|
-
const stack = [];
|
|
574
|
-
const resolvedEntries = new Map();
|
|
575
|
-
const entries = Array.isArray(entriesOrLog)
|
|
576
|
-
? entriesOrLog
|
|
577
|
-
: await entriesOrLog.values.toArray();
|
|
578
|
-
// Build a list of already resolved entries, and filter out already joined entries
|
|
579
|
-
for (const e of entries) {
|
|
580
|
-
// TODO, do this less ugly
|
|
581
|
-
let hash;
|
|
582
|
-
if (e instanceof Entry) {
|
|
583
|
-
hash = e.hash;
|
|
584
|
-
resolvedEntries.set(e.hash, e);
|
|
585
|
-
if (this.has(hash)) {
|
|
586
|
-
continue;
|
|
587
|
-
}
|
|
588
|
-
stack.push(hash);
|
|
521
|
+
else if (Array.isArray(entriesOrLog)) {
|
|
522
|
+
if (entriesOrLog.length === 0) {
|
|
523
|
+
return;
|
|
589
524
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (
|
|
593
|
-
|
|
525
|
+
entries = [];
|
|
526
|
+
for (const element of entriesOrLog) {
|
|
527
|
+
if (element instanceof Entry) {
|
|
528
|
+
entries.push(element);
|
|
529
|
+
references.set(element.hash, element);
|
|
594
530
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
531
|
+
else if (typeof element === "string") {
|
|
532
|
+
let entry = await Entry.fromMultihash(this._storage, element, {
|
|
533
|
+
timeout: options?.timeout,
|
|
534
|
+
});
|
|
535
|
+
if (!entry) {
|
|
536
|
+
throw new Error("Missing entry in join by hash: " + element);
|
|
537
|
+
}
|
|
538
|
+
entries.push(entry);
|
|
539
|
+
}
|
|
540
|
+
else if (element instanceof ShallowEntry) {
|
|
541
|
+
let entry = await Entry.fromMultihash(this._storage, element.hash, {
|
|
542
|
+
timeout: options?.timeout,
|
|
543
|
+
});
|
|
544
|
+
if (!entry) {
|
|
545
|
+
throw new Error("Missing entry in join by hash: " + element.hash);
|
|
546
|
+
}
|
|
547
|
+
entries.push(entry);
|
|
602
548
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
549
|
+
else {
|
|
550
|
+
entries.push(element.entry);
|
|
551
|
+
references.set(element.entry.hash, element.entry);
|
|
552
|
+
for (const ref of element.references) {
|
|
553
|
+
references.set(ref.hash, ref);
|
|
608
554
|
}
|
|
609
|
-
stack.push(e2.hash);
|
|
610
555
|
}
|
|
611
556
|
}
|
|
612
557
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
if (
|
|
616
|
-
|
|
558
|
+
else {
|
|
559
|
+
let all = await entriesOrLog.all(); // TODO dont load all at once
|
|
560
|
+
if (all.length === 0) {
|
|
561
|
+
return;
|
|
617
562
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
resolvedEntries.set(entry.hash, entry);
|
|
625
|
-
let nexts;
|
|
626
|
-
if (entry.meta.type !== EntryType.CUT &&
|
|
627
|
-
(nexts = await entry.getNext())) {
|
|
628
|
-
let isRoot = true;
|
|
629
|
-
for (const next of nexts) {
|
|
630
|
-
if (!this.has(next)) {
|
|
631
|
-
isRoot = false;
|
|
632
|
-
}
|
|
633
|
-
else {
|
|
634
|
-
if (this._headsIndex.has(next)) {
|
|
635
|
-
const toRemove = (await this.get(next, options));
|
|
636
|
-
await this._headsIndex.del(toRemove);
|
|
637
|
-
removedHeads.push(toRemove);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
let nextIndexSet = nextRefs.get(next);
|
|
641
|
-
if (!nextIndexSet) {
|
|
642
|
-
nextIndexSet = [];
|
|
643
|
-
nextIndexSet.push(entry);
|
|
644
|
-
nextRefs.set(next, nextIndexSet);
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
647
|
-
nextIndexSet.push(entry);
|
|
648
|
-
}
|
|
649
|
-
if (!visited.has(next)) {
|
|
650
|
-
stack.push(next);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
if (isRoot) {
|
|
654
|
-
entriesBottomUp.push(entry);
|
|
655
|
-
}
|
|
563
|
+
entries = all;
|
|
564
|
+
}
|
|
565
|
+
let heads = new Map();
|
|
566
|
+
for (const entry of entries) {
|
|
567
|
+
if (heads.has(entry.hash)) {
|
|
568
|
+
continue;
|
|
656
569
|
}
|
|
657
|
-
|
|
658
|
-
|
|
570
|
+
heads.set(entry.hash, true);
|
|
571
|
+
for (const next of await entry.getNext()) {
|
|
572
|
+
heads.set(next, false);
|
|
659
573
|
}
|
|
660
574
|
}
|
|
661
|
-
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
575
|
+
for (const entry of entries) {
|
|
576
|
+
const p = this.joinRecursively(entry, {
|
|
577
|
+
references,
|
|
578
|
+
isHead: heads.get(entry.hash),
|
|
579
|
+
...options,
|
|
580
|
+
});
|
|
581
|
+
this._joining.set(entry.hash, p);
|
|
582
|
+
p.finally(() => {
|
|
583
|
+
this._joining.delete(entry.hash);
|
|
584
|
+
});
|
|
667
585
|
await p;
|
|
668
586
|
}
|
|
669
587
|
}
|
|
670
|
-
|
|
671
|
-
|
|
588
|
+
/**
|
|
589
|
+
* Bottom up join of entries into the log
|
|
590
|
+
* @param entry
|
|
591
|
+
* @param options
|
|
592
|
+
* @returns
|
|
593
|
+
*/
|
|
594
|
+
async joinRecursively(entry, options) {
|
|
595
|
+
if (this.entryIndex.length > (options?.length ?? Number.MAX_SAFE_INTEGER)) {
|
|
672
596
|
return;
|
|
673
597
|
}
|
|
674
|
-
if (!
|
|
598
|
+
if (!entry.hash) {
|
|
675
599
|
throw new Error("Unexpected");
|
|
676
600
|
}
|
|
677
|
-
|
|
601
|
+
if (await this.has(entry.hash)) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
entry.init(this);
|
|
605
|
+
if (options?.verifySignatures) {
|
|
606
|
+
if (!(await entry.verifySignatures())) {
|
|
607
|
+
throw new Error('Invalid signature entry with hash "' + entry.hash + '"');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (this?._canAppend && !(await this?._canAppend(entry))) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const headsWithGid = await this.entryIndex
|
|
614
|
+
.getHeads(entry.gid, { type: "shape", shape: ENTRY_JOIN_SHAPE })
|
|
615
|
+
.all();
|
|
678
616
|
if (headsWithGid) {
|
|
679
|
-
for (const
|
|
680
|
-
|
|
617
|
+
for (const v of headsWithGid) {
|
|
618
|
+
// TODO second argument should be a time compare instead? what about next nexts?
|
|
619
|
+
// and check the cut entry is newer than the current 'entry'
|
|
620
|
+
if (v.meta.type === EntryType.CUT &&
|
|
621
|
+
v.meta.next.includes(entry.hash) &&
|
|
622
|
+
Sorting.compare(entry, v, this._sortFn) < 0) {
|
|
681
623
|
return; // already deleted
|
|
682
624
|
}
|
|
683
625
|
}
|
|
684
626
|
}
|
|
685
|
-
if (
|
|
686
|
-
|
|
687
|
-
if (!(await
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
// Update the internal entry index
|
|
695
|
-
await this._entryIndex.set(e);
|
|
696
|
-
await this._values.put(e);
|
|
697
|
-
if (e.meta.type !== EntryType.CUT) {
|
|
698
|
-
for (const a of e.next) {
|
|
699
|
-
if (!this.has(a)) {
|
|
700
|
-
await this.join([a]);
|
|
701
|
-
}
|
|
702
|
-
let nextIndexSet = this._nextsIndex.get(a);
|
|
703
|
-
if (!nextIndexSet) {
|
|
704
|
-
nextIndexSet = new Set();
|
|
705
|
-
nextIndexSet.add(e.hash);
|
|
706
|
-
this._nextsIndex.set(a, nextIndexSet);
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
nextIndexSet.add(a);
|
|
627
|
+
if (entry.meta.type !== EntryType.CUT) {
|
|
628
|
+
for (const a of entry.next) {
|
|
629
|
+
if (!(await this.has(a))) {
|
|
630
|
+
const nested = options.references?.get(a) ||
|
|
631
|
+
(await Entry.fromMultihash(this._storage, a, {
|
|
632
|
+
timeout: options?.timeout,
|
|
633
|
+
}));
|
|
634
|
+
if (!nested) {
|
|
635
|
+
throw new Error("Missing entry in joinRecursively: " + a);
|
|
710
636
|
}
|
|
637
|
+
const p = this.joinRecursively(nested, options.isHead ? { ...options, isHead: false } : options);
|
|
638
|
+
this._joining.set(nested.hash, p);
|
|
639
|
+
p.finally(() => {
|
|
640
|
+
this._joining.delete(nested.hash);
|
|
641
|
+
});
|
|
642
|
+
await p;
|
|
711
643
|
}
|
|
712
644
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
645
|
+
}
|
|
646
|
+
const clock = await entry.getClock();
|
|
647
|
+
this._hlc.update(clock.timestamp);
|
|
648
|
+
await this._entryIndex.put(entry, {
|
|
649
|
+
unique: false,
|
|
650
|
+
isHead: options.isHead,
|
|
651
|
+
toMultiHash: true,
|
|
652
|
+
});
|
|
653
|
+
const removed = await this.processEntry(entry);
|
|
654
|
+
const trimmed = await this.trim(options?.trim);
|
|
655
|
+
if (trimmed) {
|
|
717
656
|
for (const entry of trimmed) {
|
|
718
657
|
removed.push(entry);
|
|
719
658
|
}
|
|
720
|
-
await this?._onChange?.({ added: [e], removed: removed });
|
|
721
|
-
}
|
|
722
|
-
const forward = nextRefs.get(e.hash);
|
|
723
|
-
if (forward) {
|
|
724
|
-
if (this._headsIndex.has(e.hash)) {
|
|
725
|
-
await this._headsIndex.del(e, options);
|
|
726
|
-
}
|
|
727
|
-
for (const en of forward) {
|
|
728
|
-
stack.push(en);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
await this.headsIndex.put(e, options);
|
|
733
659
|
}
|
|
660
|
+
await this?._onChange?.({ added: [entry], removed: removed });
|
|
734
661
|
}
|
|
735
662
|
async processEntry(entry) {
|
|
736
663
|
if (entry.meta.type === EntryType.CUT) {
|
|
@@ -746,22 +673,25 @@ let Log = Log_1 = class Log {
|
|
|
746
673
|
const deleted = [];
|
|
747
674
|
while (stack.length > 0) {
|
|
748
675
|
const entry = stack.pop();
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
this.
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
for (const next of entry.next) {
|
|
760
|
-
const nextFromNext = this._nextsIndex.get(next);
|
|
761
|
-
if (nextFromNext) {
|
|
762
|
-
nextFromNext.delete(entry.hash);
|
|
676
|
+
const skip = counter === 0 && skipFirst;
|
|
677
|
+
if (!skip) {
|
|
678
|
+
const has = await this.has(entry.hash);
|
|
679
|
+
if (has) {
|
|
680
|
+
// TODO test last argument: It is for when multiple heads point to the same entry, hence we might visit it multiple times? or a concurrent delete process is doing it before us.
|
|
681
|
+
const deletedEntry = await this.delete(entry.hash);
|
|
682
|
+
if (deletedEntry) {
|
|
683
|
+
/* this._nextsIndex.delete(entry.hash); */
|
|
684
|
+
deleted.push(deletedEntry);
|
|
685
|
+
}
|
|
763
686
|
}
|
|
764
|
-
|
|
687
|
+
}
|
|
688
|
+
for (const next of entry.meta.next) {
|
|
689
|
+
const nextFromNext = this.entryIndex.getHasNext(next);
|
|
690
|
+
const entriesThatHasNext = await nextFromNext.all();
|
|
691
|
+
// if there are no entries which is not of "CUT" type, we can safely delete the next entry
|
|
692
|
+
// figureately speaking, these means where are cutting all branches to a stem, so we can delete the stem as well
|
|
693
|
+
let hasAlternativeNext = !!entriesThatHasNext.find((x) => x.meta.type !== EntryType.CUT);
|
|
694
|
+
if (!hasAlternativeNext) {
|
|
765
695
|
const ne = await this.get(next);
|
|
766
696
|
if (ne) {
|
|
767
697
|
stack.push(ne);
|
|
@@ -773,33 +703,10 @@ let Log = Log_1 = class Log {
|
|
|
773
703
|
await Promise.all(promises);
|
|
774
704
|
return deleted;
|
|
775
705
|
}
|
|
776
|
-
async delete(
|
|
777
|
-
this._trim.deleteFromCache(
|
|
778
|
-
await this.
|
|
779
|
-
|
|
780
|
-
update: false
|
|
781
|
-
}
|
|
782
|
-
}); // cache is not updated here, but at *
|
|
783
|
-
await this._values.delete(entry);
|
|
784
|
-
await this._entryIndex.delete(entry.hash);
|
|
785
|
-
this._nextsIndex.delete(entry.hash);
|
|
786
|
-
const newHeads = [];
|
|
787
|
-
for (const next of entry.next) {
|
|
788
|
-
const ne = await this.get(next);
|
|
789
|
-
if (ne) {
|
|
790
|
-
const nexts = this._nextsIndex.get(next);
|
|
791
|
-
nexts.delete(entry.hash);
|
|
792
|
-
if (nexts.size === 0) {
|
|
793
|
-
await this._headsIndex.put(ne);
|
|
794
|
-
newHeads.push(ne.hash);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
await this._headsIndex.updateHeadsCache({
|
|
799
|
-
added: newHeads,
|
|
800
|
-
removed: [entry.hash]
|
|
801
|
-
}); // * here
|
|
802
|
-
return entry.delete(this._storage);
|
|
706
|
+
async delete(hash) {
|
|
707
|
+
await this._trim.deleteFromCache(hash);
|
|
708
|
+
const removedEntry = await this._entryIndex.delete(hash);
|
|
709
|
+
return removedEntry;
|
|
803
710
|
}
|
|
804
711
|
/**
|
|
805
712
|
* Returns the log entries as a formatted string.
|
|
@@ -819,37 +726,29 @@ let Log = Log_1 = class Log {
|
|
|
819
726
|
let padding = new Array(Math.max(len - 1, 0));
|
|
820
727
|
padding = len > 1 ? padding.fill(" ") : padding;
|
|
821
728
|
padding = len > 0 ? padding.concat(["└─"]) : padding;
|
|
822
|
-
/* istanbul ignore next */
|
|
823
729
|
return (padding.join("") +
|
|
824
|
-
(payloadMapper
|
|
730
|
+
(payloadMapper?.(e.payload) || e.payload));
|
|
825
731
|
}))).join("\n");
|
|
826
732
|
}
|
|
827
|
-
async idle() {
|
|
828
|
-
await this._headsIndex.headsCache?.idle();
|
|
829
|
-
}
|
|
830
733
|
async close() {
|
|
831
734
|
// Don't return early here if closed = true, because "load" might create processes that needs to be closed
|
|
832
735
|
this._closed = true; // closed = true before doing below, else we might try to open the headsIndex cache because it is closed as we assume log is still open
|
|
833
|
-
|
|
834
|
-
await this.
|
|
835
|
-
|
|
836
|
-
this.
|
|
837
|
-
this._headsIndex = undefined;
|
|
838
|
-
this._memory = undefined;
|
|
839
|
-
this._values = undefined;
|
|
736
|
+
this._closeController.abort();
|
|
737
|
+
await this._indexer?.stop?.();
|
|
738
|
+
this._indexer = undefined;
|
|
739
|
+
this._loadedOnce = false;
|
|
840
740
|
}
|
|
841
741
|
async drop() {
|
|
842
742
|
// Don't return early here if closed = true, because "load" might create processes that needs to be closed
|
|
843
743
|
this._closed = true; // closed = true before doing below, else we might try to open the headsIndex cache because it is closed as we assume log is still open
|
|
844
|
-
|
|
845
|
-
await this.
|
|
846
|
-
await this.
|
|
847
|
-
await this.
|
|
848
|
-
await this._memory?.close();
|
|
744
|
+
this._closeController.abort();
|
|
745
|
+
await this.entryIndex?.clear();
|
|
746
|
+
await this._indexer?.drop();
|
|
747
|
+
await this._indexer?.stop?.();
|
|
849
748
|
}
|
|
850
749
|
async recover() {
|
|
851
750
|
// merge existing
|
|
852
|
-
const existing = await this.getHeads();
|
|
751
|
+
const existing = await this.getHeads(true).all();
|
|
853
752
|
const allHeads = new Map();
|
|
854
753
|
for (const head of existing) {
|
|
855
754
|
allHeads.set(head.hash, head);
|
|
@@ -878,42 +777,33 @@ let Log = Log_1 = class Log {
|
|
|
878
777
|
// assume they are valid, (let access control reject them if not)
|
|
879
778
|
await this.load({ reload: true, heads: [...allHeads.values()] });
|
|
880
779
|
}
|
|
881
|
-
async load(opts = {
|
|
780
|
+
async load(opts = {}) {
|
|
882
781
|
if (this.closed) {
|
|
883
782
|
throw new Error("Closed");
|
|
884
783
|
}
|
|
784
|
+
if (this._loadedOnce && !opts.reload && !opts.reset) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
this._loadedOnce = true;
|
|
885
788
|
const providedCustomHeads = Array.isArray(opts["heads"]);
|
|
886
789
|
const heads = providedCustomHeads
|
|
887
790
|
? opts["heads"]
|
|
888
|
-
: await this.
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
791
|
+
: await this._entryIndex
|
|
792
|
+
.getHeads(undefined, {
|
|
793
|
+
type: "full",
|
|
794
|
+
signal: this._closeController.signal,
|
|
892
795
|
ignoreMissing: opts.ignoreMissing,
|
|
893
|
-
|
|
894
|
-
})
|
|
796
|
+
timeout: opts.timeout,
|
|
797
|
+
})
|
|
798
|
+
.all();
|
|
895
799
|
if (heads) {
|
|
896
800
|
// Load the log
|
|
897
|
-
if (providedCustomHeads) {
|
|
801
|
+
if (providedCustomHeads || opts.reset) {
|
|
898
802
|
await this.reset(heads);
|
|
899
803
|
}
|
|
900
804
|
else {
|
|
901
|
-
/*
|
|
902
|
-
TODO feat amount load
|
|
903
|
-
const amount = (opts as { amount?: number }).amount;
|
|
904
|
-
if (amount != null && amount >= 0 && amount < heads.length) {
|
|
905
|
-
throw new Error(
|
|
906
|
-
"You are not loading all heads, this will lead to unexpected behaviours on write. Please load at least load: " +
|
|
907
|
-
amount +
|
|
908
|
-
" entries"
|
|
909
|
-
);
|
|
910
|
-
} */
|
|
911
805
|
await this.join(heads instanceof Entry ? [heads] : heads, {
|
|
912
|
-
/* length: amount, */
|
|
913
806
|
timeout: opts?.fetchEntryTimeout,
|
|
914
|
-
cache: {
|
|
915
|
-
update: false
|
|
916
|
-
}
|
|
917
807
|
});
|
|
918
808
|
}
|
|
919
809
|
}
|
|
@@ -924,7 +814,7 @@ let Log = Log_1 = class Log {
|
|
|
924
814
|
await log.join(!Array.isArray(entryOrHash) ? [entryOrHash] : entryOrHash, {
|
|
925
815
|
timeout: options.timeout,
|
|
926
816
|
trim: options.trim,
|
|
927
|
-
verifySignatures: true
|
|
817
|
+
verifySignatures: true,
|
|
928
818
|
});
|
|
929
819
|
return log;
|
|
930
820
|
}
|
|
@@ -934,7 +824,7 @@ let Log = Log_1 = class Log {
|
|
|
934
824
|
* Finds entries that are the heads of this collection,
|
|
935
825
|
* ie. entries that are not referenced by other entries.
|
|
936
826
|
*
|
|
937
|
-
* @param {Array<Entry<T>>} entries Entries to search heads from
|
|
827
|
+
* @param {Array<Entry<T>>} entries - Entries to search heads from
|
|
938
828
|
* @returns {Array<Entry<T>>}
|
|
939
829
|
*/
|
|
940
830
|
static findHeads(entries) {
|