@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
package/src/log.ts
CHANGED
|
@@ -1,79 +1,80 @@
|
|
|
1
|
+
import { deserialize, field, fixedArray, variant } from "@dao-xyz/borsh";
|
|
2
|
+
import { type AnyStore } from "@peerbit/any-store";
|
|
3
|
+
import { type Blocks, cidifyString } from "@peerbit/blocks-interface";
|
|
1
4
|
import {
|
|
5
|
+
type Identity,
|
|
2
6
|
SignatureWithKey,
|
|
7
|
+
X25519Keypair,
|
|
3
8
|
randomBytes,
|
|
4
9
|
sha256Base64Sync,
|
|
5
|
-
Identity,
|
|
6
|
-
X25519Keypair
|
|
7
10
|
} from "@peerbit/crypto";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import * as LogError from "./log-errors.js";
|
|
13
|
-
import * as Sorting from "./log-sorting.js";
|
|
14
|
-
import { findUniques } from "./find-uniques.js";
|
|
15
|
-
import {
|
|
16
|
-
EncryptionTemplateMaybeEncrypted,
|
|
17
|
-
Entry,
|
|
18
|
-
Payload,
|
|
19
|
-
CanAppend,
|
|
20
|
-
EntryType
|
|
21
|
-
} from "./entry.js";
|
|
11
|
+
import { type Indices } from "@peerbit/indexer-interface";
|
|
12
|
+
import { create } from "@peerbit/indexer-sqlite3";
|
|
13
|
+
import { type Keychain } from "@peerbit/keychain";
|
|
14
|
+
import { type Change } from "./change.js";
|
|
22
15
|
import {
|
|
23
|
-
HLC,
|
|
24
16
|
LamportClock as Clock,
|
|
17
|
+
HLC,
|
|
25
18
|
LamportClock,
|
|
26
|
-
Timestamp
|
|
19
|
+
Timestamp,
|
|
27
20
|
} from "./clock.js";
|
|
21
|
+
import { type Encoding, NO_ENCODING } from "./encoding.js";
|
|
22
|
+
import {
|
|
23
|
+
EntryIndex,
|
|
24
|
+
type MaybeResolveOptions,
|
|
25
|
+
type ResultsIterator,
|
|
26
|
+
type ReturnTypeFromResolveOptions,
|
|
27
|
+
} from "./entry-index.js";
|
|
28
|
+
import { type EntryWithRefs } from "./entry-with-refs.js";
|
|
29
|
+
import {
|
|
30
|
+
type CanAppend,
|
|
31
|
+
type EncryptionTemplateMaybeEncrypted,
|
|
32
|
+
Entry,
|
|
33
|
+
EntryType,
|
|
34
|
+
Payload,
|
|
35
|
+
ShallowEntry,
|
|
36
|
+
type ShallowOrFullEntry,
|
|
37
|
+
} from "./entry.js";
|
|
38
|
+
import { findUniques } from "./find-uniques.js";
|
|
39
|
+
import * as LogError from "./log-errors.js";
|
|
40
|
+
import * as Sorting from "./log-sorting.js";
|
|
41
|
+
import { Trim, type TrimOptions } from "./trim.js";
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
import { Encoding, NO_ENCODING } from "./encoding.js";
|
|
31
|
-
import { CacheUpdateOptions, HeadsIndex } from "./heads.js";
|
|
32
|
-
import { EntryNode, Values } from "./values.js";
|
|
33
|
-
import { Trim, TrimOptions } from "./trim.js";
|
|
34
|
-
import { logger } from "./logger.js";
|
|
35
|
-
import { Change } from "./change.js";
|
|
36
|
-
import { EntryWithRefs } from "./entry-with-refs.js";
|
|
37
|
-
import { Blocks } from "@peerbit/blocks-interface";
|
|
38
|
-
import { cidifyString } from "@peerbit/blocks";
|
|
39
|
-
import { Keychain } from "@peerbit/keychain";
|
|
40
|
-
|
|
41
|
-
const { LastWriteWins, NoZeroes } = Sorting;
|
|
43
|
+
const { LastWriteWins } = Sorting;
|
|
42
44
|
|
|
43
45
|
export type LogEvents<T> = {
|
|
44
|
-
onChange?: (change: Change<T>) => void;
|
|
46
|
+
onChange?: (change: Change<T> /* , reference?: R */) => void;
|
|
45
47
|
onGidRemoved?: (gids: string[]) => Promise<void> | void;
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
export type MemoryProperties = {
|
|
49
|
-
|
|
51
|
+
storage?: AnyStore;
|
|
52
|
+
indexer?: Indices;
|
|
50
53
|
};
|
|
51
54
|
|
|
52
55
|
export type LogProperties<T> = {
|
|
53
56
|
keychain?: Keychain;
|
|
54
57
|
encoding?: Encoding<T>;
|
|
55
58
|
clock?: LamportClock;
|
|
56
|
-
sortFn?: Sorting.
|
|
59
|
+
sortFn?: Sorting.SortFn;
|
|
57
60
|
trim?: TrimOptions;
|
|
58
61
|
canAppend?: CanAppend<T>;
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
export type LogOptions<T> = LogProperties<T> & LogEvents<T> & MemoryProperties;
|
|
62
65
|
|
|
63
|
-
const ENTRY_CACHE_MAX = 1000; // TODO as param
|
|
64
|
-
|
|
65
66
|
export type AppendOptions<T> = {
|
|
66
67
|
meta?: {
|
|
67
68
|
type?: EntryType;
|
|
68
69
|
gidSeed?: Uint8Array;
|
|
69
70
|
data?: Uint8Array;
|
|
70
71
|
timestamp?: Timestamp;
|
|
71
|
-
next?: Entry<any>[];
|
|
72
|
+
next?: Entry<any>[] | ShallowEntry[];
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
identity?: Identity;
|
|
75
76
|
signers?: ((
|
|
76
|
-
data: Uint8Array
|
|
77
|
+
data: Uint8Array,
|
|
77
78
|
) => Promise<SignatureWithKey> | SignatureWithKey)[];
|
|
78
79
|
|
|
79
80
|
trim?: TrimOptions;
|
|
@@ -81,37 +82,62 @@ export type AppendOptions<T> = {
|
|
|
81
82
|
keypair: X25519Keypair;
|
|
82
83
|
receiver: EncryptionTemplateMaybeEncrypted;
|
|
83
84
|
};
|
|
85
|
+
onChange?: OnChange<T>;
|
|
86
|
+
canAppend?: CanAppend<T>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
type OnChange<T> = (
|
|
90
|
+
change: Change<T>,
|
|
91
|
+
reference?: undefined,
|
|
92
|
+
) => void | Promise<void>;
|
|
93
|
+
|
|
94
|
+
export type JoinableEntry = {
|
|
95
|
+
meta: {
|
|
96
|
+
clock: {
|
|
97
|
+
timestamp: Timestamp;
|
|
98
|
+
};
|
|
99
|
+
next: string[];
|
|
100
|
+
gid: string;
|
|
101
|
+
type: EntryType;
|
|
102
|
+
};
|
|
103
|
+
hash: string;
|
|
84
104
|
};
|
|
85
105
|
|
|
106
|
+
export const ENTRY_JOIN_SHAPE = {
|
|
107
|
+
hash: true,
|
|
108
|
+
meta: { type: true, next: true, gid: true, clock: true },
|
|
109
|
+
} as const;
|
|
110
|
+
|
|
86
111
|
@variant(0)
|
|
87
112
|
export class Log<T> {
|
|
88
113
|
@field({ type: fixedArray("u8", 32) })
|
|
89
114
|
private _id: Uint8Array;
|
|
90
115
|
|
|
91
|
-
private _sortFn
|
|
92
|
-
private _storage
|
|
93
|
-
private _hlc
|
|
116
|
+
/* private _sortFn!: Sorting.ISortFunction; */
|
|
117
|
+
private _storage!: Blocks;
|
|
118
|
+
private _hlc!: HLC;
|
|
94
119
|
|
|
95
120
|
// Identity
|
|
96
|
-
private _identity
|
|
121
|
+
private _identity!: Identity;
|
|
97
122
|
|
|
98
123
|
// Keeping track of entries
|
|
99
|
-
private _entryIndex
|
|
100
|
-
private _headsIndex
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
private _entryIndex!: EntryIndex<T>;
|
|
125
|
+
/* private _headsIndex!: HeadsIndex<T>;
|
|
126
|
+
private _values!: Values<T>;
|
|
127
|
+
*/
|
|
103
128
|
// Index of all next pointers in this log
|
|
104
|
-
private _nextsIndex
|
|
129
|
+
/* private _nextsIndex!: Map<string, Set<string>>; */
|
|
105
130
|
private _keychain?: Keychain;
|
|
106
|
-
private _encoding
|
|
107
|
-
private _trim
|
|
108
|
-
private _entryCache: Cache<Entry<T>>;
|
|
109
|
-
|
|
131
|
+
private _encoding!: Encoding<T>;
|
|
132
|
+
private _trim!: Trim<T>;
|
|
110
133
|
private _canAppend?: CanAppend<T>;
|
|
111
|
-
private _onChange?:
|
|
134
|
+
private _onChange?: OnChange<T>;
|
|
112
135
|
private _closed = true;
|
|
113
|
-
private
|
|
114
|
-
private
|
|
136
|
+
private _closeController!: AbortController;
|
|
137
|
+
private _loadedOnce = false;
|
|
138
|
+
private _indexer!: Indices;
|
|
139
|
+
private _joining!: Map<string, Promise<any>>; // entry hashes that are currently joining into this log
|
|
140
|
+
private _sortFn!: Sorting.SortFn;
|
|
115
141
|
|
|
116
142
|
constructor(properties?: { id?: Uint8Array }) {
|
|
117
143
|
this._id = properties?.id || randomBytes(32);
|
|
@@ -130,20 +156,16 @@ export class Log<T> {
|
|
|
130
156
|
throw new Error("Already open");
|
|
131
157
|
}
|
|
132
158
|
|
|
133
|
-
|
|
134
|
-
let { sortFn } = options;
|
|
159
|
+
this._closeController = new AbortController();
|
|
135
160
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
161
|
+
const { encoding, trim, keychain, indexer, onGidRemoved, sortFn } = options;
|
|
162
|
+
|
|
163
|
+
// TODO do correctly with tie breaks
|
|
164
|
+
this._sortFn = sortFn || LastWriteWins;
|
|
140
165
|
|
|
141
|
-
this._sortFn = NoZeroes(sortFn);
|
|
142
166
|
this._storage = store;
|
|
143
|
-
this.
|
|
144
|
-
|
|
145
|
-
await this._memory.open();
|
|
146
|
-
}
|
|
167
|
+
this._indexer = indexer || (await create());
|
|
168
|
+
await this._indexer.start?.();
|
|
147
169
|
|
|
148
170
|
this._encoding = encoding || NO_ENCODING;
|
|
149
171
|
this._joining = new Map();
|
|
@@ -157,50 +179,37 @@ export class Log<T> {
|
|
|
157
179
|
// Clock
|
|
158
180
|
this._hlc = new HLC();
|
|
159
181
|
|
|
160
|
-
this._nextsIndex = new Map();
|
|
161
182
|
const id = this.id;
|
|
162
183
|
if (!id) {
|
|
163
184
|
throw new Error("Id not set");
|
|
164
185
|
}
|
|
165
|
-
|
|
166
|
-
await this._headsIndex.init(this, { onGidRemoved });
|
|
167
|
-
this._entryCache = new Cache({ max: ENTRY_CACHE_MAX });
|
|
186
|
+
|
|
168
187
|
this._entryIndex = new EntryIndex({
|
|
169
188
|
store: this._storage,
|
|
170
189
|
init: (e) => e.init(this),
|
|
171
|
-
|
|
190
|
+
onGidRemoved,
|
|
191
|
+
index: await (
|
|
192
|
+
await this._indexer.scope("heads")
|
|
193
|
+
).init({ schema: ShallowEntry }),
|
|
194
|
+
publicKey: this._identity.publicKey,
|
|
195
|
+
sort: this._sortFn,
|
|
172
196
|
});
|
|
173
|
-
|
|
197
|
+
await this._entryIndex.init();
|
|
198
|
+
/* this._values = new Values(this._entryIndex, this._sortFn); */
|
|
199
|
+
|
|
174
200
|
this._trim = new Trim(
|
|
175
201
|
{
|
|
176
|
-
|
|
177
|
-
deleteNode: async (node:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (entry) {
|
|
183
|
-
this.values.deleteNode(node);
|
|
184
|
-
await Promise.all([
|
|
185
|
-
this.headsIndex.del(this.entryIndex.getShallow(node.value)!),
|
|
186
|
-
this.entryIndex.delete(node.value)
|
|
187
|
-
]);
|
|
188
|
-
this.nextsIndex.delete(node.value);
|
|
189
|
-
await this.blocks.rm(node.value);
|
|
190
|
-
}
|
|
191
|
-
const b = this.values.length;
|
|
192
|
-
if (a === b) {
|
|
193
|
-
/* throw new Error(
|
|
194
|
-
"Unexpected miss match between log size and entry index size: " +
|
|
195
|
-
this.values.length +
|
|
196
|
-
this.entryIndex._index.size
|
|
197
|
-
); */
|
|
198
|
-
}
|
|
199
|
-
return entry;
|
|
202
|
+
index: this._entryIndex,
|
|
203
|
+
deleteNode: async (node: ShallowEntry) => {
|
|
204
|
+
const resolved = await this.get(node.hash);
|
|
205
|
+
await this._entryIndex.delete(node.hash);
|
|
206
|
+
await this._storage.rm(node.hash);
|
|
207
|
+
return resolved;
|
|
200
208
|
},
|
|
201
|
-
|
|
209
|
+
sortFn: this._sortFn,
|
|
210
|
+
getLength: () => this.length,
|
|
202
211
|
},
|
|
203
|
-
trim
|
|
212
|
+
trim,
|
|
204
213
|
);
|
|
205
214
|
|
|
206
215
|
this._canAppend = async (entry) => {
|
|
@@ -214,6 +223,7 @@ export class Log<T> {
|
|
|
214
223
|
|
|
215
224
|
this._onChange = options?.onChange;
|
|
216
225
|
this._closed = false;
|
|
226
|
+
this._closeController = new AbortController();
|
|
217
227
|
}
|
|
218
228
|
|
|
219
229
|
private _idString: string | undefined;
|
|
@@ -244,15 +254,12 @@ export class Log<T> {
|
|
|
244
254
|
* Returns the length of the log.
|
|
245
255
|
*/
|
|
246
256
|
get length() {
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
get values(): Values<T> {
|
|
251
|
-
if (this.closed) {
|
|
257
|
+
if (this._closed) {
|
|
252
258
|
throw new Error("Closed");
|
|
253
259
|
}
|
|
254
|
-
return this.
|
|
260
|
+
return this._entryIndex.length;
|
|
255
261
|
}
|
|
262
|
+
|
|
256
263
|
get canAppend() {
|
|
257
264
|
return this._canAppend;
|
|
258
265
|
}
|
|
@@ -264,41 +271,24 @@ export class Log<T> {
|
|
|
264
271
|
*/
|
|
265
272
|
|
|
266
273
|
has(cid: string) {
|
|
267
|
-
return this._entryIndex.
|
|
274
|
+
return this._entryIndex.has(cid);
|
|
268
275
|
}
|
|
269
276
|
/**
|
|
270
277
|
* Get all entries sorted. Don't use this method anywhere where performance matters
|
|
271
278
|
*/
|
|
272
|
-
toArray(): Promise<Entry<T>[]> {
|
|
279
|
+
async toArray(): Promise<Entry<T>[]> {
|
|
273
280
|
// we call init, because the values might be unitialized
|
|
274
|
-
return this.
|
|
281
|
+
return this.entryIndex.query([], this.sortFn.sort, true).all();
|
|
275
282
|
}
|
|
276
283
|
|
|
277
284
|
/**
|
|
278
285
|
* Returns the head index
|
|
279
286
|
*/
|
|
280
|
-
get headsIndex(): HeadsIndex<T> {
|
|
281
|
-
return this._headsIndex;
|
|
282
|
-
}
|
|
283
287
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
async getHeads(): Promise<Entry<T>[]> {
|
|
289
|
-
const heads: Promise<Entry<T> | undefined>[] = new Array(
|
|
290
|
-
this.headsIndex.index.size
|
|
291
|
-
);
|
|
292
|
-
let i = 0;
|
|
293
|
-
for (const hash of this.headsIndex.index) {
|
|
294
|
-
heads[i++] = this._entryIndex.get(hash).then((x) => x?.init(this));
|
|
295
|
-
}
|
|
296
|
-
const resolved = await Promise.all(heads);
|
|
297
|
-
const defined = resolved.filter((x): x is Entry<T> => !!x);
|
|
298
|
-
if (defined.length !== resolved.length) {
|
|
299
|
-
logger.error("Failed to resolve all heads");
|
|
300
|
-
}
|
|
301
|
-
return defined;
|
|
288
|
+
getHeads<R extends MaybeResolveOptions = false>(
|
|
289
|
+
resolve: R = false as R,
|
|
290
|
+
): ResultsIterator<ReturnTypeFromResolveOptions<R, T>> {
|
|
291
|
+
return this.entryIndex.getHeads(undefined, resolve);
|
|
302
292
|
}
|
|
303
293
|
|
|
304
294
|
/**
|
|
@@ -334,10 +324,6 @@ export class Log<T> {
|
|
|
334
324
|
return this._storage;
|
|
335
325
|
}
|
|
336
326
|
|
|
337
|
-
get nextsIndex(): Map<string, Set<string>> {
|
|
338
|
-
return this._nextsIndex;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
327
|
get entryIndex(): EntryIndex<T> {
|
|
342
328
|
return this._entryIndex;
|
|
343
329
|
}
|
|
@@ -367,88 +353,103 @@ export class Log<T> {
|
|
|
367
353
|
}
|
|
368
354
|
|
|
369
355
|
/**
|
|
370
|
-
*
|
|
356
|
+
* Get an entry.
|
|
371
357
|
* @param {string} [hash] The hashes of the entry
|
|
372
358
|
*/
|
|
373
359
|
get(
|
|
374
360
|
hash: string,
|
|
375
|
-
options?: { timeout?: number }
|
|
361
|
+
options?: { timeout?: number },
|
|
376
362
|
): Promise<Entry<T> | undefined> {
|
|
377
|
-
return this._entryIndex.get(
|
|
363
|
+
return this._entryIndex.get(
|
|
364
|
+
hash,
|
|
365
|
+
options ? { type: "full", timeout: options.timeout } : undefined,
|
|
366
|
+
);
|
|
378
367
|
}
|
|
379
368
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
// End result
|
|
391
|
-
const result: { [key: string]: Entry<T> } = {};
|
|
392
|
-
let count = 0;
|
|
393
|
-
// Named function for getting an entry from the log
|
|
394
|
-
const getEntry = (e: string) => this.get(e);
|
|
395
|
-
|
|
396
|
-
// Add an entry to the stack and traversed nodes index
|
|
397
|
-
const addToStack = (entry: Entry<T>) => {
|
|
398
|
-
// If we've already processed the Entry<T>, don't add it to the stack
|
|
399
|
-
if (!entry || traversed[entry.hash]) {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// Add the entry in front of the stack and sort
|
|
404
|
-
stack = [entry, ...stack].sort(this._sortFn).reverse();
|
|
405
|
-
// Add to the cache of processed entries
|
|
406
|
-
traversed[entry.hash] = true;
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
const addEntry = (rootEntry: Entry<T>) => {
|
|
410
|
-
result[rootEntry.hash] = rootEntry;
|
|
411
|
-
traversed[rootEntry.hash] = true;
|
|
412
|
-
count++;
|
|
413
|
-
};
|
|
369
|
+
/**
|
|
370
|
+
* Get a entry with shallow representation
|
|
371
|
+
* @param {string} [hash] The hashes of the entry
|
|
372
|
+
*/
|
|
373
|
+
async getShallow(
|
|
374
|
+
hash: string,
|
|
375
|
+
options?: { timeout?: number },
|
|
376
|
+
): Promise<ShallowEntry | undefined> {
|
|
377
|
+
return (await this._entryIndex.getShallow(hash))?.value;
|
|
378
|
+
}
|
|
414
379
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
380
|
+
/*
|
|
381
|
+
async traverse(
|
|
382
|
+
rootEntries: Entry<T>[],
|
|
383
|
+
amount = -1,
|
|
384
|
+
endHash?: string
|
|
385
|
+
): Promise<{ [key: string]: Entry<T> }> {
|
|
386
|
+
// Sort the given given root entries and use as the starting stack
|
|
387
|
+
let stack: Entry<T>[] = rootEntries.sort(this._sortFn).reverse();
|
|
388
|
+
|
|
389
|
+
// Cache for checking if we've processed an entry already
|
|
390
|
+
let traversed: { [key: string]: boolean } = {};
|
|
391
|
+
// End result
|
|
392
|
+
const result: { [key: string]: Entry<T> } = {};
|
|
393
|
+
let count = 0;
|
|
394
|
+
// Named function for getting an entry from the log
|
|
395
|
+
const getEntry = (e: string) => this.get(e);
|
|
396
|
+
|
|
397
|
+
// Add an entry to the stack and traversed nodes index
|
|
398
|
+
const addToStack = (entry: Entry<T>) => {
|
|
399
|
+
// If we've already processed the Entry<T>, don't add it to the stack
|
|
400
|
+
if (!entry || traversed[entry.hash]) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Add the entry in front of the stack and sort
|
|
405
|
+
stack = [entry, ...stack].sort(this._sortFn).reverse();
|
|
406
|
+
// Add to the cache of processed entries
|
|
407
|
+
traversed[entry.hash] = true;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const addEntry = (rootEntry: Entry<T>) => {
|
|
411
|
+
result[rootEntry.hash] = rootEntry;
|
|
412
|
+
traversed[rootEntry.hash] = true;
|
|
413
|
+
count++;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// Start traversal
|
|
417
|
+
// Process stack until it's empty (traversed the full log)
|
|
418
|
+
// or when we have the requested amount of entries
|
|
419
|
+
// If requested entry amount is -1, traverse all
|
|
420
|
+
while (stack.length > 0 && (count < amount || amount < 0)) {
|
|
421
|
+
// eslint-disable-line no-unmodified-loop-condition
|
|
422
|
+
// Get the next element from the stack
|
|
423
|
+
const entry = stack.shift();
|
|
424
|
+
if (!entry) {
|
|
425
|
+
throw new Error("Unexpected");
|
|
426
|
+
}
|
|
427
|
+
// Add to the result
|
|
428
|
+
addEntry(entry);
|
|
429
|
+
// If it is the specified end hash, break out of the while loop
|
|
430
|
+
if (endHash && endHash === entry.hash) break;
|
|
431
|
+
|
|
432
|
+
// Add entry's next references to the stack
|
|
433
|
+
const entries = (await Promise.all(entry.next.map(getEntry))).filter(
|
|
434
|
+
(x) => !!x
|
|
435
|
+
) as Entry<any>[];
|
|
436
|
+
entries.forEach(addToStack);
|
|
425
437
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
// Add entry's next references to the stack
|
|
432
|
-
const entries = (await Promise.all(entry.next.map(getEntry))).filter(
|
|
433
|
-
(x) => !!x
|
|
434
|
-
) as Entry<any>[];
|
|
435
|
-
entries.forEach(addToStack);
|
|
438
|
+
|
|
439
|
+
stack = [];
|
|
440
|
+
traversed = {};
|
|
441
|
+
// End result
|
|
442
|
+
return result;
|
|
436
443
|
}
|
|
437
|
-
|
|
438
|
-
stack = [];
|
|
439
|
-
traversed = {};
|
|
440
|
-
// End result
|
|
441
|
-
return result;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
+
*/
|
|
444
445
|
async getReferenceSamples(
|
|
445
446
|
from: Entry<T>,
|
|
446
|
-
options?: { pointerCount?: number; memoryLimit?: number }
|
|
447
|
+
options?: { pointerCount?: number; memoryLimit?: number },
|
|
447
448
|
): Promise<Entry<T>[]> {
|
|
448
449
|
const hashes = new Set<string>();
|
|
449
450
|
const pointerCount = options?.pointerCount || 0;
|
|
450
451
|
const memoryLimit = options?.memoryLimit;
|
|
451
|
-
const maxDistance = Math.min(pointerCount, this.
|
|
452
|
+
const maxDistance = Math.min(pointerCount, this.entryIndex.length);
|
|
452
453
|
if (maxDistance === 0) {
|
|
453
454
|
return [];
|
|
454
455
|
}
|
|
@@ -508,31 +509,36 @@ export class Log<T> {
|
|
|
508
509
|
|
|
509
510
|
/**
|
|
510
511
|
* Append an entry to the log.
|
|
511
|
-
* @param {
|
|
512
|
-
* @
|
|
512
|
+
* @param {T} data The data to be appended
|
|
513
|
+
* @param {AppendOptions} [options] The options for the append
|
|
514
|
+
* @returns {{ entry: Entry<T>; removed: ShallowEntry[] }} The appended entry and an array of removed entries
|
|
513
515
|
*/
|
|
514
516
|
async append(
|
|
515
517
|
data: T,
|
|
516
|
-
options: AppendOptions<T> = {}
|
|
517
|
-
): Promise<{ entry: Entry<T>; removed:
|
|
518
|
+
options: AppendOptions<T> = {},
|
|
519
|
+
): Promise<{ entry: Entry<T>; removed: ShallowOrFullEntry<T>[] }> {
|
|
518
520
|
// Update the clock (find the latest clock)
|
|
519
521
|
if (options.meta?.next) {
|
|
520
522
|
for (const n of options.meta.next) {
|
|
521
523
|
if (!n.hash)
|
|
522
524
|
throw new Error(
|
|
523
|
-
"Expecting nexts to already be saved. missing hash for one or more entries"
|
|
525
|
+
"Expecting nexts to already be saved. missing hash for one or more entries",
|
|
524
526
|
);
|
|
525
527
|
}
|
|
526
528
|
}
|
|
527
529
|
|
|
528
530
|
await this.load({ reload: false });
|
|
529
531
|
|
|
530
|
-
const nexts:
|
|
532
|
+
const nexts: Sorting.SortableEntry[] =
|
|
533
|
+
options.meta?.next ||
|
|
534
|
+
(await this.entryIndex
|
|
535
|
+
.getHeads(undefined, { type: "shape", shape: Sorting.ENTRY_SORT_SHAPE })
|
|
536
|
+
.all());
|
|
531
537
|
|
|
532
538
|
// Calculate max time for log/graph
|
|
533
539
|
const clock = new Clock({
|
|
534
540
|
id: this._identity.publicKey.bytes,
|
|
535
|
-
timestamp: options?.meta?.timestamp || this._hlc.now()
|
|
541
|
+
timestamp: options?.meta?.timestamp || this._hlc.now(),
|
|
536
542
|
});
|
|
537
543
|
|
|
538
544
|
const entry = await Entry.create<T>({
|
|
@@ -545,80 +551,82 @@ export class Log<T> {
|
|
|
545
551
|
type: options.meta?.type,
|
|
546
552
|
gidSeed: options.meta?.gidSeed,
|
|
547
553
|
data: options.meta?.data,
|
|
548
|
-
next: nexts
|
|
554
|
+
next: nexts,
|
|
549
555
|
},
|
|
550
556
|
|
|
551
557
|
encoding: this._encoding,
|
|
552
|
-
|
|
553
558
|
encryption: options.encryption
|
|
554
559
|
? {
|
|
555
560
|
keypair: options.encryption.keypair,
|
|
556
561
|
receiver: {
|
|
557
|
-
...options.encryption.receiver
|
|
558
|
-
}
|
|
562
|
+
...options.encryption.receiver,
|
|
563
|
+
},
|
|
559
564
|
}
|
|
560
565
|
: undefined,
|
|
561
|
-
canAppend: this._canAppend
|
|
566
|
+
canAppend: options.canAppend || this._canAppend,
|
|
562
567
|
});
|
|
563
568
|
|
|
564
569
|
if (!entry.hash) {
|
|
565
570
|
throw new Error("Unexpected");
|
|
566
571
|
}
|
|
567
572
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
await this.
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
573
|
+
if (entry.meta.type !== EntryType.CUT) {
|
|
574
|
+
for (const e of nexts) {
|
|
575
|
+
if (!(await this.has(e.hash))) {
|
|
576
|
+
let entry: Entry<any>;
|
|
577
|
+
if (e instanceof Entry) {
|
|
578
|
+
entry = e;
|
|
579
|
+
} else {
|
|
580
|
+
let resolved = await this.entryIndex.get(e.hash);
|
|
581
|
+
if (!resolved) {
|
|
582
|
+
// eslint-disable-next-line no-console
|
|
583
|
+
console.warn("Unexpected missing entry when joining", e.hash);
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
entry = resolved;
|
|
587
|
+
}
|
|
588
|
+
await this.join([entry]);
|
|
589
|
+
}
|
|
580
590
|
}
|
|
581
591
|
}
|
|
582
592
|
|
|
583
|
-
await this.
|
|
584
|
-
|
|
585
|
-
|
|
593
|
+
await this.entryIndex.put(entry, {
|
|
594
|
+
unique: true,
|
|
595
|
+
isHead: true,
|
|
596
|
+
toMultiHash: false,
|
|
597
|
+
});
|
|
586
598
|
|
|
587
|
-
const removed = await this.processEntry(entry);
|
|
599
|
+
const removed: ShallowOrFullEntry<T>[] = await this.processEntry(entry);
|
|
588
600
|
|
|
589
601
|
entry.init({ encoding: this._encoding, keychain: this._keychain });
|
|
590
602
|
|
|
591
603
|
const trimmed = await this.trim(options?.trim);
|
|
592
604
|
|
|
593
|
-
|
|
594
|
-
|
|
605
|
+
if (trimmed) {
|
|
606
|
+
for (const entry of trimmed) {
|
|
607
|
+
removed.push(entry);
|
|
608
|
+
}
|
|
595
609
|
}
|
|
596
610
|
|
|
597
611
|
const changes: Change<T> = {
|
|
598
612
|
added: [entry],
|
|
599
|
-
removed
|
|
613
|
+
removed,
|
|
600
614
|
};
|
|
601
615
|
|
|
602
|
-
await this.
|
|
603
|
-
await this._onChange?.(changes);
|
|
616
|
+
await (options?.onChange || this._onChange)?.(changes);
|
|
604
617
|
return { entry, removed };
|
|
605
618
|
}
|
|
606
619
|
|
|
607
|
-
async reset(entries
|
|
608
|
-
|
|
609
|
-
this._entryIndex
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
cache: this._entryCache
|
|
613
|
-
});
|
|
614
|
-
await this.headsIndex.reset([]);
|
|
615
|
-
this._values = new Values(this._entryIndex, this._sortFn, []);
|
|
616
|
-
await this.join(entries);
|
|
620
|
+
async reset(entries?: Entry<T>[]) {
|
|
621
|
+
const heads = await this.getHeads(true).all();
|
|
622
|
+
await this._entryIndex.clear();
|
|
623
|
+
await this._onChange?.({ added: [], removed: heads });
|
|
624
|
+
await this.join(entries || heads);
|
|
617
625
|
}
|
|
618
626
|
|
|
619
627
|
async remove(
|
|
620
|
-
entry:
|
|
621
|
-
options?: { recursively?: boolean }
|
|
628
|
+
entry: ShallowOrFullEntry<T> | ShallowOrFullEntry<T>[],
|
|
629
|
+
options?: { recursively?: boolean },
|
|
622
630
|
): Promise<Change<T>> {
|
|
623
631
|
await this.load({ reload: false });
|
|
624
632
|
const entries = Array.isArray(entry) ? entry : [entry];
|
|
@@ -626,39 +634,36 @@ export class Log<T> {
|
|
|
626
634
|
if (entries.length === 0) {
|
|
627
635
|
return {
|
|
628
636
|
added: [],
|
|
629
|
-
removed: []
|
|
637
|
+
removed: [],
|
|
630
638
|
};
|
|
631
639
|
}
|
|
632
640
|
|
|
641
|
+
const change: Change<T> = {
|
|
642
|
+
added: [],
|
|
643
|
+
removed: Array.isArray(entry) ? entry : [entry],
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
await this._onChange?.(change);
|
|
647
|
+
|
|
633
648
|
if (options?.recursively) {
|
|
634
649
|
await this.deleteRecursively(entry);
|
|
635
650
|
} else {
|
|
636
651
|
for (const entry of entries) {
|
|
637
|
-
await this.delete(entry);
|
|
652
|
+
await this.delete(entry.hash);
|
|
638
653
|
}
|
|
639
654
|
}
|
|
640
655
|
|
|
641
|
-
const change: Change<T> = {
|
|
642
|
-
added: [],
|
|
643
|
-
removed: Array.isArray(entry) ? entry : [entry]
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
/* await Promise.all([
|
|
647
|
-
this._logCache?.queue(change),
|
|
648
|
-
this._onUpdate(change),
|
|
649
|
-
]); */
|
|
650
|
-
await this._onChange?.(change);
|
|
651
656
|
return change;
|
|
652
657
|
}
|
|
653
658
|
|
|
654
|
-
iterator(options?: {
|
|
659
|
+
/* iterator(options?: {
|
|
655
660
|
from?: "tail" | "head";
|
|
656
661
|
amount?: number;
|
|
657
662
|
}): IterableIterator<string> {
|
|
658
663
|
const from = options?.from || "tail";
|
|
659
664
|
const amount = typeof options?.amount === "number" ? options?.amount : -1;
|
|
660
665
|
let next = from === "tail" ? this._values.tail : this._values.head;
|
|
661
|
-
const nextFn = from === "tail" ? (e) => e.prev : (e) => e.next;
|
|
666
|
+
const nextFn = from === "tail" ? (e: any) => e.prev : (e: any) => e.next;
|
|
662
667
|
return (function* () {
|
|
663
668
|
let counter = 0;
|
|
664
669
|
while (next) {
|
|
@@ -672,275 +677,214 @@ export class Log<T> {
|
|
|
672
677
|
next = nextFn(next);
|
|
673
678
|
}
|
|
674
679
|
})();
|
|
675
|
-
}
|
|
680
|
+
} */
|
|
676
681
|
|
|
677
682
|
async trim(option: TrimOptions | undefined = this._trim.options) {
|
|
678
683
|
return this._trim.trim(option);
|
|
679
684
|
}
|
|
680
685
|
|
|
681
|
-
/**
|
|
682
|
-
*
|
|
683
|
-
* @param entries
|
|
684
|
-
* @returns change
|
|
685
|
-
*/
|
|
686
|
-
/* async sync(
|
|
687
|
-
entries: (EntryWithRefs<T> | Entry<T> | string)[],
|
|
688
|
-
options: {
|
|
689
|
-
canAppend?: CanAppend<T>;
|
|
690
|
-
onChange?: (change: Change<T>) => void | Promise<void>;
|
|
691
|
-
timeout?: number;
|
|
692
|
-
} = {}
|
|
693
|
-
): Promise<void> {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
logger.debug(`Sync request #${entries.length}`);
|
|
697
|
-
const entriesToJoin: (Entry<T> | string)[] = [];
|
|
698
|
-
for (const e of entries) {
|
|
699
|
-
if (e instanceof Entry || typeof e === "string") {
|
|
700
|
-
entriesToJoin.push(e);
|
|
701
|
-
} else {
|
|
702
|
-
for (const ref of e.references) {
|
|
703
|
-
entriesToJoin.push(ref);
|
|
704
|
-
}
|
|
705
|
-
entriesToJoin.push(e.entry);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
await this.join(entriesToJoin, {
|
|
710
|
-
canAppend: (entry) => {
|
|
711
|
-
const canAppend = options?.canAppend || this.canAppend;
|
|
712
|
-
return !canAppend || canAppend(entry);
|
|
713
|
-
},
|
|
714
|
-
onChange: (change) => {
|
|
715
|
-
options?.onChange?.(change);
|
|
716
|
-
return this._onChange?.({
|
|
717
|
-
added: change.added,
|
|
718
|
-
removed: change.removed,
|
|
719
|
-
});
|
|
720
|
-
},
|
|
721
|
-
timeout: options.timeout,
|
|
722
|
-
});
|
|
723
|
-
} */
|
|
724
|
-
|
|
725
686
|
async join(
|
|
726
|
-
entriesOrLog:
|
|
687
|
+
entriesOrLog:
|
|
688
|
+
| (string | Entry<T> | ShallowEntry | EntryWithRefs<T>)[]
|
|
689
|
+
| Log<T>
|
|
690
|
+
| ResultsIterator<Entry<any>>,
|
|
727
691
|
options?: {
|
|
728
692
|
verifySignatures?: boolean;
|
|
729
693
|
trim?: TrimOptions;
|
|
730
694
|
timeout?: number;
|
|
731
|
-
}
|
|
695
|
+
},
|
|
732
696
|
): Promise<void> {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
697
|
+
let entries: Entry<T>[];
|
|
698
|
+
let references: Map<string, Entry<T>> = new Map();
|
|
699
|
+
|
|
700
|
+
if (entriesOrLog instanceof Log) {
|
|
701
|
+
if (entriesOrLog.entryIndex.length === 0) return;
|
|
702
|
+
entries = await entriesOrLog.toArray();
|
|
703
|
+
for (const element of entries) {
|
|
704
|
+
references.set(element.hash, element);
|
|
705
|
+
}
|
|
706
|
+
} else if (Array.isArray(entriesOrLog)) {
|
|
707
|
+
if (entriesOrLog.length === 0) {
|
|
708
|
+
return;
|
|
737
709
|
}
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
await this.load({ reload: false });
|
|
741
|
-
if (entriesOrLog.length === 0) {
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
/* const joinLength = options?.length ?? Number.MAX_SAFE_INTEGER; TODO */
|
|
745
|
-
const visited = new Set<string>();
|
|
746
|
-
const nextRefs: Map<string, Entry<T>[]> = new Map();
|
|
747
|
-
const entriesBottomUp: Entry<T>[] = [];
|
|
748
|
-
const stack: string[] = [];
|
|
749
|
-
const resolvedEntries: Map<string, Entry<T>> = new Map();
|
|
750
|
-
const entries = Array.isArray(entriesOrLog)
|
|
751
|
-
? entriesOrLog
|
|
752
|
-
: await entriesOrLog.values.toArray();
|
|
753
|
-
|
|
754
|
-
// Build a list of already resolved entries, and filter out already joined entries
|
|
755
|
-
for (const e of entries) {
|
|
756
|
-
// TODO, do this less ugly
|
|
757
|
-
let hash: string;
|
|
758
|
-
if (e instanceof Entry) {
|
|
759
|
-
hash = e.hash;
|
|
760
|
-
resolvedEntries.set(e.hash, e);
|
|
761
|
-
if (this.has(hash)) {
|
|
762
|
-
continue;
|
|
763
|
-
}
|
|
764
|
-
stack.push(hash);
|
|
765
|
-
} else if (typeof e === "string") {
|
|
766
|
-
hash = e;
|
|
767
710
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
711
|
+
entries = [];
|
|
712
|
+
for (const element of entriesOrLog) {
|
|
713
|
+
if (element instanceof Entry) {
|
|
714
|
+
entries.push(element);
|
|
715
|
+
references.set(element.hash, element);
|
|
716
|
+
} else if (typeof element === "string") {
|
|
717
|
+
let entry = await Entry.fromMultihash<T>(this._storage, element, {
|
|
718
|
+
timeout: options?.timeout,
|
|
719
|
+
});
|
|
720
|
+
if (!entry) {
|
|
721
|
+
throw new Error("Missing entry in join by hash: " + element);
|
|
722
|
+
}
|
|
723
|
+
entries.push(entry);
|
|
724
|
+
} else if (element instanceof ShallowEntry) {
|
|
725
|
+
let entry = await Entry.fromMultihash<T>(
|
|
726
|
+
this._storage,
|
|
727
|
+
element.hash,
|
|
728
|
+
{
|
|
729
|
+
timeout: options?.timeout,
|
|
730
|
+
},
|
|
731
|
+
);
|
|
732
|
+
if (!entry) {
|
|
733
|
+
throw new Error("Missing entry in join by hash: " + element.hash);
|
|
734
|
+
}
|
|
735
|
+
entries.push(entry);
|
|
736
|
+
} else {
|
|
737
|
+
entries.push(element.entry);
|
|
738
|
+
references.set(element.entry.hash, element.entry);
|
|
779
739
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (this.has(e2.hash)) {
|
|
783
|
-
continue;
|
|
740
|
+
for (const ref of element.references) {
|
|
741
|
+
references.set(ref.hash, ref);
|
|
784
742
|
}
|
|
785
|
-
stack.push(e2.hash);
|
|
786
743
|
}
|
|
787
744
|
}
|
|
745
|
+
} else {
|
|
746
|
+
let all = await entriesOrLog.all(); // TODO dont load all at once
|
|
747
|
+
if (all.length === 0) {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
entries = all;
|
|
788
752
|
}
|
|
789
753
|
|
|
790
|
-
|
|
791
|
-
for (const
|
|
792
|
-
if (
|
|
754
|
+
let heads: Map<string, boolean> = new Map();
|
|
755
|
+
for (const entry of entries) {
|
|
756
|
+
if (heads.has(entry.hash)) {
|
|
793
757
|
continue;
|
|
794
758
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
resolvedEntries.get(hash) ||
|
|
799
|
-
(await Entry.fromMultihash<T>(this._storage, hash, {
|
|
800
|
-
timeout: options?.timeout
|
|
801
|
-
}));
|
|
802
|
-
|
|
803
|
-
entry.init(this);
|
|
804
|
-
resolvedEntries.set(entry.hash, entry);
|
|
805
|
-
|
|
806
|
-
let nexts: string[];
|
|
807
|
-
if (
|
|
808
|
-
entry.meta.type !== EntryType.CUT &&
|
|
809
|
-
(nexts = await entry.getNext())
|
|
810
|
-
) {
|
|
811
|
-
let isRoot = true;
|
|
812
|
-
for (const next of nexts) {
|
|
813
|
-
if (!this.has(next)) {
|
|
814
|
-
isRoot = false;
|
|
815
|
-
} else {
|
|
816
|
-
if (this._headsIndex.has(next)) {
|
|
817
|
-
const toRemove = (await this.get(next, options))!;
|
|
818
|
-
await this._headsIndex.del(toRemove);
|
|
819
|
-
removedHeads.push(toRemove);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
let nextIndexSet = nextRefs.get(next);
|
|
823
|
-
if (!nextIndexSet) {
|
|
824
|
-
nextIndexSet = [];
|
|
825
|
-
nextIndexSet.push(entry);
|
|
826
|
-
nextRefs.set(next, nextIndexSet);
|
|
827
|
-
} else {
|
|
828
|
-
nextIndexSet.push(entry);
|
|
829
|
-
}
|
|
830
|
-
if (!visited.has(next)) {
|
|
831
|
-
stack.push(next);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (isRoot) {
|
|
835
|
-
entriesBottomUp.push(entry);
|
|
836
|
-
}
|
|
837
|
-
} else {
|
|
838
|
-
entriesBottomUp.push(entry);
|
|
759
|
+
heads.set(entry.hash, true);
|
|
760
|
+
for (const next of await entry.getNext()) {
|
|
761
|
+
heads.set(next, false);
|
|
839
762
|
}
|
|
840
763
|
}
|
|
841
764
|
|
|
842
|
-
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
);
|
|
853
|
-
this._joining.set(e.hash, p);
|
|
765
|
+
for (const entry of entries) {
|
|
766
|
+
const p = this.joinRecursively(entry, {
|
|
767
|
+
references,
|
|
768
|
+
isHead: heads.get(entry.hash)!,
|
|
769
|
+
...options,
|
|
770
|
+
});
|
|
771
|
+
this._joining.set(entry.hash, p);
|
|
772
|
+
p.finally(() => {
|
|
773
|
+
this._joining.delete(entry.hash);
|
|
774
|
+
});
|
|
854
775
|
await p;
|
|
855
776
|
}
|
|
856
777
|
}
|
|
857
778
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
779
|
+
/**
|
|
780
|
+
* Bottom up join of entries into the log
|
|
781
|
+
* @param entry
|
|
782
|
+
* @param options
|
|
783
|
+
* @returns
|
|
784
|
+
*/
|
|
785
|
+
|
|
786
|
+
private async joinRecursively(
|
|
787
|
+
entry: Entry<T>,
|
|
788
|
+
options: {
|
|
863
789
|
verifySignatures?: boolean;
|
|
864
790
|
trim?: TrimOptions;
|
|
865
791
|
length?: number;
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
792
|
+
references?: Map<string, Entry<T>>;
|
|
793
|
+
isHead: boolean;
|
|
794
|
+
timeout?: number;
|
|
795
|
+
},
|
|
796
|
+
) {
|
|
797
|
+
if (this.entryIndex.length > (options?.length ?? Number.MAX_SAFE_INTEGER)) {
|
|
869
798
|
return;
|
|
870
799
|
}
|
|
871
800
|
|
|
872
|
-
if (!
|
|
801
|
+
if (!entry.hash) {
|
|
873
802
|
throw new Error("Unexpected");
|
|
874
803
|
}
|
|
875
804
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
for (const [_k, v] of headsWithGid) {
|
|
879
|
-
if (v.meta.type === EntryType.CUT && v.next.includes(e.hash)) {
|
|
880
|
-
return; // already deleted
|
|
881
|
-
}
|
|
882
|
-
}
|
|
805
|
+
if (await this.has(entry.hash)) {
|
|
806
|
+
return;
|
|
883
807
|
}
|
|
884
808
|
|
|
885
|
-
|
|
886
|
-
if (options?.verifySignatures) {
|
|
887
|
-
if (!(await e.verifySignatures())) {
|
|
888
|
-
throw new Error('Invalid signature entry with hash "' + e.hash + '"');
|
|
889
|
-
}
|
|
890
|
-
}
|
|
809
|
+
entry.init(this);
|
|
891
810
|
|
|
892
|
-
|
|
893
|
-
|
|
811
|
+
if (options?.verifySignatures) {
|
|
812
|
+
if (!(await entry.verifySignatures())) {
|
|
813
|
+
throw new Error(
|
|
814
|
+
'Invalid signature entry with hash "' + entry.hash + '"',
|
|
815
|
+
);
|
|
894
816
|
}
|
|
817
|
+
}
|
|
895
818
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
819
|
+
if (this?._canAppend && !(await this?._canAppend(entry))) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
899
822
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
823
|
+
const headsWithGid: JoinableEntry[] = await this.entryIndex
|
|
824
|
+
.getHeads(entry.gid, { type: "shape", shape: ENTRY_JOIN_SHAPE })
|
|
825
|
+
.all();
|
|
826
|
+
if (headsWithGid) {
|
|
827
|
+
for (const v of headsWithGid) {
|
|
828
|
+
// TODO second argument should be a time compare instead? what about next nexts?
|
|
829
|
+
// and check the cut entry is newer than the current 'entry'
|
|
830
|
+
if (
|
|
831
|
+
v.meta.type === EntryType.CUT &&
|
|
832
|
+
v.meta.next.includes(entry.hash) &&
|
|
833
|
+
Sorting.compare(entry, v, this._sortFn) < 0
|
|
834
|
+
) {
|
|
835
|
+
return; // already deleted
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
905
839
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
840
|
+
if (entry.meta.type !== EntryType.CUT) {
|
|
841
|
+
for (const a of entry.next) {
|
|
842
|
+
if (!(await this.has(a))) {
|
|
843
|
+
const nested =
|
|
844
|
+
options.references?.get(a) ||
|
|
845
|
+
(await Entry.fromMultihash<T>(this._storage, a, {
|
|
846
|
+
timeout: options?.timeout,
|
|
847
|
+
}));
|
|
848
|
+
|
|
849
|
+
if (!nested) {
|
|
850
|
+
throw new Error("Missing entry in joinRecursively: " + a);
|
|
913
851
|
}
|
|
852
|
+
|
|
853
|
+
const p = this.joinRecursively(
|
|
854
|
+
nested,
|
|
855
|
+
options.isHead ? { ...options, isHead: false } : options,
|
|
856
|
+
);
|
|
857
|
+
this._joining.set(nested.hash, p);
|
|
858
|
+
p.finally(() => {
|
|
859
|
+
this._joining.delete(nested.hash);
|
|
860
|
+
});
|
|
861
|
+
await p;
|
|
914
862
|
}
|
|
915
863
|
}
|
|
864
|
+
}
|
|
916
865
|
|
|
917
|
-
|
|
918
|
-
|
|
866
|
+
const clock = await entry.getClock();
|
|
867
|
+
this._hlc.update(clock.timestamp);
|
|
919
868
|
|
|
920
|
-
|
|
921
|
-
|
|
869
|
+
await this._entryIndex.put(entry, {
|
|
870
|
+
unique: false,
|
|
871
|
+
isHead: options.isHead,
|
|
872
|
+
toMultiHash: true,
|
|
873
|
+
});
|
|
922
874
|
|
|
875
|
+
const removed: ShallowOrFullEntry<T>[] = await this.processEntry(entry);
|
|
876
|
+
const trimmed = await this.trim(options?.trim);
|
|
877
|
+
|
|
878
|
+
if (trimmed) {
|
|
923
879
|
for (const entry of trimmed) {
|
|
924
880
|
removed.push(entry);
|
|
925
881
|
}
|
|
926
|
-
|
|
927
|
-
await this?._onChange?.({ added: [e], removed: removed });
|
|
928
882
|
}
|
|
929
883
|
|
|
930
|
-
|
|
931
|
-
if (forward) {
|
|
932
|
-
if (this._headsIndex.has(e.hash)) {
|
|
933
|
-
await this._headsIndex.del(e, options);
|
|
934
|
-
}
|
|
935
|
-
for (const en of forward) {
|
|
936
|
-
stack.push(en);
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
await this.headsIndex.put(e, options);
|
|
940
|
-
}
|
|
884
|
+
await this?._onChange?.({ added: [entry], removed: removed });
|
|
941
885
|
}
|
|
942
886
|
|
|
943
|
-
private async processEntry(entry: Entry<T>) {
|
|
887
|
+
private async processEntry(entry: Entry<T>): Promise<ShallowEntry[]> {
|
|
944
888
|
if (entry.meta.type === EntryType.CUT) {
|
|
945
889
|
return this.deleteRecursively(entry, true);
|
|
946
890
|
}
|
|
@@ -948,33 +892,40 @@ export class Log<T> {
|
|
|
948
892
|
}
|
|
949
893
|
|
|
950
894
|
/// TODO simplify methods below
|
|
951
|
-
async deleteRecursively(
|
|
895
|
+
async deleteRecursively(
|
|
896
|
+
from: ShallowOrFullEntry<T> | ShallowOrFullEntry<T>[],
|
|
897
|
+
skipFirst = false,
|
|
898
|
+
) {
|
|
952
899
|
const stack = Array.isArray(from) ? [...from] : [from];
|
|
953
|
-
const promises: Promise<void>[] = [];
|
|
900
|
+
const promises: (Promise<void> | void)[] = [];
|
|
954
901
|
let counter = 0;
|
|
955
|
-
const deleted:
|
|
902
|
+
const deleted: ShallowEntry[] = [];
|
|
956
903
|
|
|
957
904
|
while (stack.length > 0) {
|
|
958
905
|
const entry = stack.pop()!;
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
this.
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
906
|
+
const skip = counter === 0 && skipFirst;
|
|
907
|
+
if (!skip) {
|
|
908
|
+
const has = await this.has(entry.hash);
|
|
909
|
+
if (has) {
|
|
910
|
+
// 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.
|
|
911
|
+
const deletedEntry = await this.delete(entry.hash);
|
|
912
|
+
if (deletedEntry) {
|
|
913
|
+
/* this._nextsIndex.delete(entry.hash); */
|
|
914
|
+
deleted.push(deletedEntry);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
969
917
|
}
|
|
970
918
|
|
|
971
|
-
for (const next of entry.next) {
|
|
972
|
-
const nextFromNext = this.
|
|
973
|
-
|
|
974
|
-
nextFromNext.delete(entry.hash);
|
|
975
|
-
}
|
|
919
|
+
for (const next of entry.meta.next) {
|
|
920
|
+
const nextFromNext = this.entryIndex.getHasNext(next);
|
|
921
|
+
const entriesThatHasNext = await nextFromNext.all();
|
|
976
922
|
|
|
977
|
-
if
|
|
923
|
+
// if there are no entries which is not of "CUT" type, we can safely delete the next entry
|
|
924
|
+
// figureately speaking, these means where are cutting all branches to a stem, so we can delete the stem as well
|
|
925
|
+
let hasAlternativeNext = !!entriesThatHasNext.find(
|
|
926
|
+
(x) => x.meta.type !== EntryType.CUT,
|
|
927
|
+
);
|
|
928
|
+
if (!hasAlternativeNext) {
|
|
978
929
|
const ne = await this.get(next);
|
|
979
930
|
if (ne) {
|
|
980
931
|
stack.push(ne);
|
|
@@ -987,34 +938,10 @@ export class Log<T> {
|
|
|
987
938
|
return deleted;
|
|
988
939
|
}
|
|
989
940
|
|
|
990
|
-
async delete(
|
|
991
|
-
this._trim.deleteFromCache(
|
|
992
|
-
await this.
|
|
993
|
-
|
|
994
|
-
update: false
|
|
995
|
-
}
|
|
996
|
-
}); // cache is not updated here, but at *
|
|
997
|
-
await this._values.delete(entry);
|
|
998
|
-
await this._entryIndex.delete(entry.hash);
|
|
999
|
-
this._nextsIndex.delete(entry.hash);
|
|
1000
|
-
const newHeads: string[] = [];
|
|
1001
|
-
for (const next of entry.next) {
|
|
1002
|
-
const ne = await this.get(next);
|
|
1003
|
-
if (ne) {
|
|
1004
|
-
const nexts = this._nextsIndex.get(next)!;
|
|
1005
|
-
nexts.delete(entry.hash);
|
|
1006
|
-
if (nexts.size === 0) {
|
|
1007
|
-
await this._headsIndex.put(ne);
|
|
1008
|
-
newHeads.push(ne.hash);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
await this._headsIndex.updateHeadsCache({
|
|
1013
|
-
added: newHeads,
|
|
1014
|
-
removed: [entry.hash]
|
|
1015
|
-
}); // * here
|
|
1016
|
-
|
|
1017
|
-
return entry.delete(this._storage);
|
|
941
|
+
async delete(hash: string): Promise<ShallowEntry | undefined> {
|
|
942
|
+
await this._trim.deleteFromCache(hash);
|
|
943
|
+
const removedEntry = await this._entryIndex.delete(hash);
|
|
944
|
+
return removedEntry;
|
|
1018
945
|
}
|
|
1019
946
|
|
|
1020
947
|
/**
|
|
@@ -1027,7 +954,7 @@ export class Log<T> {
|
|
|
1027
954
|
*/
|
|
1028
955
|
async toString(
|
|
1029
956
|
payloadMapper: (payload: Payload<T>) => string = (payload) =>
|
|
1030
|
-
(payload.getValue(this.encoding) as any).toString()
|
|
957
|
+
(payload.getValue(this.encoding) as any).toString(),
|
|
1031
958
|
): Promise<string> {
|
|
1032
959
|
return (
|
|
1033
960
|
await Promise.all(
|
|
@@ -1037,53 +964,42 @@ export class Log<T> {
|
|
|
1037
964
|
.map(async (e, idx) => {
|
|
1038
965
|
const parents: Entry<any>[] = Entry.findDirectChildren(
|
|
1039
966
|
e,
|
|
1040
|
-
await this.toArray()
|
|
967
|
+
await this.toArray(),
|
|
1041
968
|
);
|
|
1042
969
|
const len = parents.length;
|
|
1043
970
|
let padding = new Array(Math.max(len - 1, 0));
|
|
1044
971
|
padding = len > 1 ? padding.fill(" ") : padding;
|
|
1045
972
|
padding = len > 0 ? padding.concat(["└─"]) : padding;
|
|
1046
|
-
/* istanbul ignore next */
|
|
1047
973
|
return (
|
|
1048
974
|
padding.join("") +
|
|
1049
|
-
(payloadMapper
|
|
975
|
+
(payloadMapper?.(e.payload) || (e.payload as any as string))
|
|
1050
976
|
);
|
|
1051
|
-
})
|
|
977
|
+
}),
|
|
1052
978
|
)
|
|
1053
979
|
).join("\n");
|
|
1054
980
|
}
|
|
1055
|
-
async idle() {
|
|
1056
|
-
await this._headsIndex.headsCache?.idle();
|
|
1057
|
-
}
|
|
1058
981
|
|
|
1059
982
|
async close() {
|
|
1060
983
|
// Don't return early here if closed = true, because "load" might create processes that needs to be closed
|
|
1061
984
|
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
|
|
1062
|
-
|
|
1063
|
-
await this.
|
|
1064
|
-
|
|
1065
|
-
this.
|
|
1066
|
-
this._headsIndex = undefined as any;
|
|
1067
|
-
this._memory = undefined as any;
|
|
1068
|
-
this._values = undefined as any;
|
|
985
|
+
this._closeController.abort();
|
|
986
|
+
await this._indexer?.stop?.();
|
|
987
|
+
this._indexer = undefined as any;
|
|
988
|
+
this._loadedOnce = false;
|
|
1069
989
|
}
|
|
1070
990
|
|
|
1071
991
|
async drop() {
|
|
1072
992
|
// Don't return early here if closed = true, because "load" might create processes that needs to be closed
|
|
1073
993
|
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
|
|
1074
|
-
|
|
1075
|
-
await
|
|
1076
|
-
|
|
1077
|
-
);
|
|
1078
|
-
await this._headsIndex?.drop();
|
|
1079
|
-
await this._entryCache?.clear();
|
|
1080
|
-
await this._memory?.clear();
|
|
1081
|
-
await this._memory?.close();
|
|
994
|
+
this._closeController.abort();
|
|
995
|
+
await this.entryIndex?.clear();
|
|
996
|
+
await this._indexer?.drop();
|
|
997
|
+
await this._indexer?.stop?.();
|
|
1082
998
|
}
|
|
1083
999
|
|
|
1084
1000
|
async recover() {
|
|
1085
1001
|
// merge existing
|
|
1086
|
-
const existing = await this.getHeads();
|
|
1002
|
+
const existing = await this.getHeads(true).all();
|
|
1087
1003
|
|
|
1088
1004
|
const allHeads: Map<string, Entry<any>> = new Map();
|
|
1089
1005
|
for (const head of existing) {
|
|
@@ -1119,47 +1035,42 @@ export class Log<T> {
|
|
|
1119
1035
|
opts: {
|
|
1120
1036
|
heads?: Entry<T>[];
|
|
1121
1037
|
fetchEntryTimeout?: number;
|
|
1122
|
-
|
|
1038
|
+
reset?: boolean;
|
|
1123
1039
|
ignoreMissing?: boolean;
|
|
1124
|
-
|
|
1040
|
+
timeout?: number;
|
|
1041
|
+
reload?: boolean;
|
|
1042
|
+
} = {},
|
|
1125
1043
|
) {
|
|
1126
1044
|
if (this.closed) {
|
|
1127
1045
|
throw new Error("Closed");
|
|
1128
1046
|
}
|
|
1129
1047
|
|
|
1048
|
+
if (this._loadedOnce && !opts.reload && !opts.reset) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
this._loadedOnce = true;
|
|
1053
|
+
|
|
1130
1054
|
const providedCustomHeads = Array.isArray(opts["heads"]);
|
|
1055
|
+
|
|
1131
1056
|
const heads = providedCustomHeads
|
|
1132
1057
|
? (opts["heads"] as Array<Entry<T>>)
|
|
1133
|
-
: await this.
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1058
|
+
: await this._entryIndex
|
|
1059
|
+
.getHeads(undefined, {
|
|
1060
|
+
type: "full",
|
|
1061
|
+
signal: this._closeController.signal,
|
|
1062
|
+
ignoreMissing: opts.ignoreMissing,
|
|
1063
|
+
timeout: opts.timeout,
|
|
1064
|
+
})
|
|
1065
|
+
.all();
|
|
1140
1066
|
|
|
1141
1067
|
if (heads) {
|
|
1142
1068
|
// Load the log
|
|
1143
|
-
if (providedCustomHeads) {
|
|
1144
|
-
await this.reset(heads);
|
|
1069
|
+
if (providedCustomHeads || opts.reset) {
|
|
1070
|
+
await this.reset(heads as any as Entry<any>[]);
|
|
1145
1071
|
} else {
|
|
1146
|
-
/*
|
|
1147
|
-
TODO feat amount load
|
|
1148
|
-
const amount = (opts as { amount?: number }).amount;
|
|
1149
|
-
if (amount != null && amount >= 0 && amount < heads.length) {
|
|
1150
|
-
throw new Error(
|
|
1151
|
-
"You are not loading all heads, this will lead to unexpected behaviours on write. Please load at least load: " +
|
|
1152
|
-
amount +
|
|
1153
|
-
" entries"
|
|
1154
|
-
);
|
|
1155
|
-
} */
|
|
1156
|
-
|
|
1157
1072
|
await this.join(heads instanceof Entry ? [heads] : heads, {
|
|
1158
|
-
/* length: amount, */
|
|
1159
1073
|
timeout: opts?.fetchEntryTimeout,
|
|
1160
|
-
cache: {
|
|
1161
|
-
update: false
|
|
1162
|
-
}
|
|
1163
1074
|
});
|
|
1164
1075
|
}
|
|
1165
1076
|
}
|
|
@@ -1173,14 +1084,14 @@ export class Log<T> {
|
|
|
1173
1084
|
id?: Uint8Array;
|
|
1174
1085
|
/* length?: number; TODO */
|
|
1175
1086
|
timeout?: number;
|
|
1176
|
-
} & LogOptions<T> = { id: randomBytes(32) }
|
|
1087
|
+
} & LogOptions<T> = { id: randomBytes(32) },
|
|
1177
1088
|
): Promise<Log<T>> {
|
|
1178
1089
|
const log = new Log<T>(options.id && { id: options.id });
|
|
1179
1090
|
await log.open(store, identity, options);
|
|
1180
1091
|
await log.join(!Array.isArray(entryOrHash) ? [entryOrHash] : entryOrHash, {
|
|
1181
1092
|
timeout: options.timeout,
|
|
1182
1093
|
trim: options.trim,
|
|
1183
|
-
verifySignatures: true
|
|
1094
|
+
verifySignatures: true,
|
|
1184
1095
|
});
|
|
1185
1096
|
return log;
|
|
1186
1097
|
}
|
|
@@ -1191,13 +1102,13 @@ export class Log<T> {
|
|
|
1191
1102
|
* Finds entries that are the heads of this collection,
|
|
1192
1103
|
* ie. entries that are not referenced by other entries.
|
|
1193
1104
|
*
|
|
1194
|
-
* @param {Array<Entry<T>>} entries Entries to search heads from
|
|
1105
|
+
* @param {Array<Entry<T>>} entries - Entries to search heads from
|
|
1195
1106
|
* @returns {Array<Entry<T>>}
|
|
1196
1107
|
*/
|
|
1197
1108
|
static findHeads<T>(entries: Entry<T>[]) {
|
|
1198
1109
|
const indexReducer = (
|
|
1199
1110
|
res: { [key: string]: string },
|
|
1200
|
-
entry: Entry<any
|
|
1111
|
+
entry: Entry<any>,
|
|
1201
1112
|
) => {
|
|
1202
1113
|
const addToResult = (e: string) => (res[e] = entry.hash);
|
|
1203
1114
|
entry.next.forEach(addToResult);
|
|
@@ -1246,7 +1157,7 @@ export class Log<T> {
|
|
|
1246
1157
|
res: Entry<T>[],
|
|
1247
1158
|
entries: Entry<T>[],
|
|
1248
1159
|
_idx: any,
|
|
1249
|
-
_arr: any
|
|
1160
|
+
_arr: any,
|
|
1250
1161
|
) => res.concat(findUniques(entries, "hash"));
|
|
1251
1162
|
const exists = (e: string) => hashes[e] === undefined;
|
|
1252
1163
|
const findFromReverseIndex = (e: string) => reverseIndex[e];
|
|
@@ -1270,7 +1181,7 @@ export class Log<T> {
|
|
|
1270
1181
|
res: string[],
|
|
1271
1182
|
entry: Entry<any>,
|
|
1272
1183
|
idx: number,
|
|
1273
|
-
arr: Entry<any>[]
|
|
1184
|
+
arr: Entry<any>[],
|
|
1274
1185
|
) => {
|
|
1275
1186
|
const addToResult = (e: string) => {
|
|
1276
1187
|
/* istanbul ignore else */
|