@peerbit/log 1.0.14 → 2.0.0

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/src/heads.ts CHANGED
@@ -4,34 +4,47 @@ import { HeadsCache } from "./heads-cache.js";
4
4
  import { Blocks } from "@peerbit/blocks-interface";
5
5
  import { Keychain } from "@peerbit/crypto";
6
6
  import { Encoding } from "./encoding.js";
7
+ import { Values } from "./values.js";
8
+ import { logger } from "./logger.js";
9
+ import { EntryIndex } from "./entry-index.js";
7
10
 
8
11
  export type CacheUpdateOptions = {
9
12
  cache?: { update?: false; reset?: false } | { update: true; reset?: boolean };
10
13
  };
11
14
 
12
- interface Config {
15
+ interface Log<T> {
13
16
  storage: Blocks;
14
17
  keychain?: Keychain;
15
18
  memory?: SimpleLevel;
16
19
  encoding: Encoding<any>;
20
+ entryIndex: EntryIndex<T>;
21
+ values: Values<T>;
17
22
  }
18
23
  export class HeadsIndex<T> {
19
24
  private _id: Uint8Array;
20
25
  private _index: Set<string> = new Set();
21
- private _gids: Map<string, number>;
26
+ private _gids: Map<string, Map<string, Entry<T>>>; // gid -> hash -> entry
22
27
  private _headsCache: HeadsCache<T> | undefined;
23
- private _config: Config;
28
+ private _config: Log<T>;
29
+ private _onGidRemoved?: (gid: string[]) => Promise<void> | void;
24
30
  constructor(id: Uint8Array) {
25
31
  this._gids = new Map();
26
32
  this._id = id;
27
33
  }
28
34
 
29
- async init(config: Config, options: { entries?: Entry<T>[] } = {}) {
30
- this._config = config;
35
+ async init(
36
+ log: Log<T>,
37
+ options: {
38
+ entries?: Entry<T>[];
39
+ onGidRemoved?: (gid: string[]) => Promise<void> | void;
40
+ } = {}
41
+ ) {
42
+ this._config = log;
43
+ this._onGidRemoved = options.onGidRemoved;
31
44
  await this.reset(options?.entries || []);
32
- if (config.memory) {
45
+ if (log.memory) {
33
46
  this._headsCache = new HeadsCache(this);
34
- return this._headsCache.init(await config.memory.sublevel("heads"));
47
+ return this._headsCache.init(await log.memory.sublevel("heads"));
35
48
  }
36
49
  }
37
50
 
@@ -41,7 +54,7 @@ export class HeadsIndex<T> {
41
54
  replicate?: boolean;
42
55
  reload?: boolean;
43
56
  } & CacheUpdateOptions
44
- ) {
57
+ ): Promise<Entry<T>[] | undefined> {
45
58
  if (!this._headsCache || (this._headsCache.loaded && !options?.reload)) {
46
59
  return;
47
60
  }
@@ -51,25 +64,19 @@ export class HeadsIndex<T> {
51
64
  if (!heads) {
52
65
  return;
53
66
  }
54
- try {
55
- const entries = await Promise.all(
56
- heads.map(async (x) => {
57
- const entry = await Entry.fromMultihash<T>(
58
- this._config.storage,
59
- x,
60
- options
61
- );
62
- entry.init(this._config);
63
- await entry.getGid(); // decrypt gid
64
- return entry;
65
- })
66
- );
67
- await this.reset(entries);
68
- return entries;
69
- } catch (error) {
70
- const q = 123;
71
- throw error;
72
- }
67
+ const entries = await Promise.all(
68
+ heads.map(async (x) => {
69
+ const entry = await this._config.entryIndex.get(x, { load: true });
70
+ if (!entry) {
71
+ logger.error("Failed to load entry from head with hash: " + x);
72
+ return;
73
+ }
74
+ await entry.getMeta(); // TODO types,decrypt gid
75
+ return entry;
76
+ })
77
+ );
78
+ await this.reset(entries.filter((x) => !!x) as Entry<any>[]);
79
+ return entries as Entry<any>[];
73
80
  }
74
81
 
75
82
  get headsCache(): HeadsCache<T> | undefined {
@@ -92,7 +99,7 @@ export class HeadsIndex<T> {
92
99
  return this._index;
93
100
  }
94
101
 
95
- get gids(): Map<string, number> {
102
+ get gids(): Map<string, Map<string, Entry<T>>> {
96
103
  return this._gids;
97
104
  }
98
105
 
@@ -105,10 +112,16 @@ export class HeadsIndex<T> {
105
112
  options: CacheUpdateOptions = { cache: { reset: true, update: true } }
106
113
  ) {
107
114
  this._index.clear();
115
+ const gidKeys = [...this._gids.keys()];
116
+
108
117
  this._gids = new Map();
109
- if (entries) {
118
+ if (entries?.length > 0) {
110
119
  await this.putAll(entries, options); // reset cache = true
111
120
  }
121
+
122
+ if (gidKeys.length > 0) {
123
+ this._onGidRemoved?.(gidKeys);
124
+ }
112
125
  }
113
126
 
114
127
  has(cid: string) {
@@ -116,14 +129,14 @@ export class HeadsIndex<T> {
116
129
  }
117
130
 
118
131
  async put(entry: Entry<T>, options?: CacheUpdateOptions) {
119
- this._putOne(entry);
132
+ await this._putOne(entry);
120
133
  if (options?.cache?.update) {
121
134
  await this._headsCache?.queue({ added: [entry] }, options.cache.reset);
122
135
  }
123
136
  }
124
137
 
125
138
  async putAll(entries: Entry<T>[], options?: CacheUpdateOptions) {
126
- this._putAll(entries);
139
+ await this._putAll(entries);
127
140
  if (options?.cache?.update) {
128
141
  await this._headsCache?.queue({ added: entries }, options.cache.reset);
129
142
  }
@@ -145,7 +158,7 @@ export class HeadsIndex<T> {
145
158
  await this._headsCache?.queue(change, reset);
146
159
  }
147
160
 
148
- private _putOne(entry: Entry<T>) {
161
+ private async _putOne(entry: Entry<T>) {
149
162
  if (!entry.hash) {
150
163
  throw new Error("Missing hash");
151
164
  }
@@ -154,44 +167,53 @@ export class HeadsIndex<T> {
154
167
  }
155
168
 
156
169
  this._index.add(entry.hash);
157
- if (!this._gids.has(entry.gid)) {
158
- this._gids.set(entry.gid, 1);
170
+ const map = this._gids.get(entry.meta.gid);
171
+ if (!map) {
172
+ const newMap = new Map();
173
+ this._gids.set(entry.meta.gid, newMap);
174
+ newMap.set(entry.hash, entry);
159
175
  } else {
160
- this._gids.set(entry.gid, this._gids.get(entry.gid)! + 1);
176
+ map.set(entry.hash, entry);
177
+ }
178
+
179
+ for (const next of entry.next) {
180
+ const indexedEntry = this._config.entryIndex.getShallow(next);
181
+ if (indexedEntry) {
182
+ await this.del(indexedEntry);
183
+ }
161
184
  }
162
185
  }
163
186
 
164
- private _putAll(entries: Entry<T>[]) {
187
+ private async _putAll(entries: Entry<T>[]) {
165
188
  for (const entry of entries) {
166
- this._putOne(entry);
189
+ await this._putOne(entry);
167
190
  }
168
191
  }
169
192
 
170
193
  async del(
171
- entry: { hash: string; gid: string },
194
+ entry: { hash: string; meta: { gid: string } },
172
195
  options?: CacheUpdateOptions
173
- ): Promise<{
174
- removed: boolean;
175
- lastWithGid: boolean;
176
- }> {
196
+ ): Promise<boolean> {
177
197
  const wasHead = this._index.delete(entry.hash);
178
198
  if (!wasHead) {
179
- return {
180
- lastWithGid: false,
181
- removed: false,
182
- };
183
- }
184
- const newValue = this._gids.get(entry.gid)! - 1;
185
- const lastWithGid = newValue <= 0;
186
- if (newValue <= 0) {
187
- this._gids.delete(entry.gid);
188
- } else {
189
- this._gids.set(entry.gid, newValue);
199
+ return false;
200
+ }
201
+ let removedGids: Set<string> | undefined = undefined;
202
+ const map = this._gids.get(entry.meta.gid)!;
203
+ map.delete(entry.hash);
204
+ if (map.size <= 0) {
205
+ this._gids.delete(entry.meta.gid);
206
+ (removedGids || (removedGids = new Set<string>())).add(entry.meta.gid);
190
207
  }
208
+
191
209
  if (!entry.hash) {
192
210
  throw new Error("Missing hash");
193
211
  }
194
212
 
213
+ if (removedGids) {
214
+ await this._onGidRemoved?.([...removedGids]);
215
+ }
216
+
195
217
  if (wasHead && options?.cache?.update) {
196
218
  await this._headsCache?.queue(
197
219
  { removed: [entry.hash] },
@@ -199,10 +221,7 @@ export class HeadsIndex<T> {
199
221
  );
200
222
  }
201
223
 
202
- return {
203
- removed: wasHead,
204
- lastWithGid: lastWithGid,
205
- };
224
+ return wasHead;
206
225
  // this._headsCache = undefined; // TODO do smarter things here, only remove the element needed (?)
207
226
  }
208
227
  }
@@ -1,13 +1,13 @@
1
- import { Entry } from "./entry.js";
1
+ import { Entry, ShallowEntry } from "./entry.js";
2
2
  import { LamportClock as Clock } from "./clock.js";
3
3
  import { compare } from "@peerbit/uint8arrays";
4
4
 
5
5
  const First = (a: any, b: any) => a;
6
6
 
7
7
  export type ISortFunction = <T>(
8
- a: Entry<T>,
9
- b: Entry<T>,
10
- resolveConflict?: (a: Entry<T>, b: Entry<T>) => number
8
+ a: ShallowEntry,
9
+ b: ShallowEntry,
10
+ resolveConflict?: (a: ShallowEntry, b: ShallowEntry) => number
11
11
  ) => number;
12
12
  /**
13
13
  * Sort two entries as Last-Write-Wins (LWW).
@@ -19,13 +19,17 @@ export type ISortFunction = <T>(
19
19
  * @param {Entry} b Second entry
20
20
  * @returns {number} 1 if a is latest, -1 if b is latest
21
21
  */
22
- export const LastWriteWins: ISortFunction = <T>(a: Entry<T>, b: Entry<T>) => {
22
+ export const LastWriteWins: ISortFunction = <T>(
23
+ a: ShallowEntry,
24
+ b: ShallowEntry
25
+ ) => {
23
26
  // Ultimate conflict resolution (take the first/left arg)
24
27
  // Sort two entries by their clock id, if the same always take the first
25
- const sortById = (a: Entry<T>, b: Entry<T>) => SortByClockId(a, b, First);
28
+ const sortById = (a: ShallowEntry, b: ShallowEntry) =>
29
+ SortByClockId(a, b, First);
26
30
  // Sort two entries by their clock time, if concurrent,
27
31
  // determine sorting using provided conflict resolution function
28
- const sortByEntryClocks = (a: Entry<T>, b: Entry<T>) =>
32
+ const sortByEntryClocks = (a: ShallowEntry, b: ShallowEntry) =>
29
33
  SortByClocks(a, b, sortById);
30
34
  // Sort entries by clock time as the primary sort criteria
31
35
  return sortByEntryClocks(a, b);
@@ -40,14 +44,14 @@ export const LastWriteWins: ISortFunction = <T>(a: Entry<T>, b: Entry<T>) => {
40
44
  */
41
45
  export const SortByEntryHash: ISortFunction = (a, b) => {
42
46
  // Ultimate conflict resolution (compare hashes)
43
- const compareHash = (a: Entry<any>, b: Entry<any>) =>
47
+ const compareHash = (a: ShallowEntry, b: ShallowEntry) =>
44
48
  a.hash < b.hash ? -1 : 1;
45
49
  // Sort two entries by their clock id, if the same then compare hashes
46
- const sortById = (a: Entry<any>, b: Entry<any>) =>
50
+ const sortById = (a: ShallowEntry, b: ShallowEntry) =>
47
51
  SortByClockId(a, b, compareHash);
48
52
  // Sort two entries by their clock time, if concurrent,
49
53
  // determine sorting using provided conflict resolution function
50
- const sortByEntryClocks = (a: Entry<any>, b: Entry<any>) =>
54
+ const sortByEntryClocks = (a: ShallowEntry, b: ShallowEntry) =>
51
55
  SortByClocks(a, b, sortById);
52
56
  // Sort entries by clock time as the primary sort criteria
53
57
  return sortByEntryClocks(a, b);
@@ -61,12 +65,12 @@ export const SortByEntryHash: ISortFunction = (a, b) => {
61
65
  * @returns {number} 1 if a is greater, -1 if b is greater
62
66
  */
63
67
  export const SortByClocks: ISortFunction = <T>(
64
- a: Entry<T>,
65
- b: Entry<T>,
66
- resolveConflict?: (a: Entry<any>, b: Entry<any>) => number
68
+ a: ShallowEntry,
69
+ b: ShallowEntry,
70
+ resolveConflict?: (a: ShallowEntry, b: ShallowEntry) => number
67
71
  ) => {
68
72
  // Compare the clocks
69
- const diff = Clock.compare(a.metadata.clock, b.metadata.clock);
73
+ const diff = Clock.compare(a.meta.clock, b.meta.clock);
70
74
  // If the clocks are concurrent, use the provided
71
75
  // conflict resolution function to determine which comes first
72
76
  return diff === 0 ? (resolveConflict || First)(a, b) : diff;
@@ -82,7 +86,7 @@ export const SortByClocks: ISortFunction = <T>(
82
86
  export const SortByClockId: ISortFunction = (a, b, resolveConflict) => {
83
87
  // Sort by ID if clocks are concurrent,
84
88
  // take the entry with a "greater" clock id
85
- const clockCompare = compare(a.metadata.clock.id, b.metadata.clock.id);
89
+ const clockCompare = compare(a.meta.clock.id, b.meta.clock.id);
86
90
  return clockCompare === 0 ? (resolveConflict || First)(a, b) : clockCompare;
87
91
  };
88
92
 
@@ -93,7 +97,7 @@ export const SortByClockId: ISortFunction = (a, b, resolveConflict) => {
93
97
  * @throws {Error} if func ever returns 0
94
98
  */
95
99
  export const NoZeroes = (func: ISortFunction) => {
96
- const comparator = <T>(a: Entry<T>, b: Entry<T>) => {
100
+ const comparator = <T>(a: ShallowEntry, b: ShallowEntry) => {
97
101
  // Validate by calling the function
98
102
  const result = func(a, b, (a, b) => -1);
99
103
  if (result === 0) {
package/src/log.ts CHANGED
@@ -12,7 +12,6 @@ import { SimpleLevel } from "@peerbit/lazy-level";
12
12
  import { EntryIndex } from "./entry-index.js";
13
13
  import * as LogError from "./log-errors.js";
14
14
  import * as Sorting from "./log-sorting.js";
15
- import { isDefined } from "./is-defined.js";
16
15
  import { findUniques } from "./find-uniques.js";
17
16
  import {
18
17
  EncryptionTemplateMaybeEncrypted,
@@ -42,6 +41,7 @@ const { LastWriteWins, NoZeroes } = Sorting;
42
41
 
43
42
  export type LogEvents<T> = {
44
43
  onChange?: (change: Change<T>) => void;
44
+ onGidRemoved?: (gids: string[]) => Promise<void> | void;
45
45
  };
46
46
 
47
47
  export type MemoryProperties = {
@@ -62,19 +62,23 @@ export type LogOptions<T> = LogProperties<T> & LogEvents<T> & MemoryProperties;
62
62
  const ENTRY_CACHE_MAX = 1000; // TODO as param
63
63
 
64
64
  export type AppendOptions<T> = {
65
- type?: EntryType;
66
- gidSeed?: Uint8Array;
67
- nexts?: Entry<any>[];
65
+ meta?: {
66
+ type?: EntryType;
67
+ gidSeed?: Uint8Array;
68
+ data?: Uint8Array;
69
+ timestamp?: Timestamp;
70
+ next?: Entry<any>[];
71
+ };
72
+
68
73
  identity?: Identity;
69
74
  signers?: ((
70
75
  data: Uint8Array
71
76
  ) => Promise<SignatureWithKey> | SignatureWithKey)[];
72
- onGidsShadowed?: (gids: string[]) => void;
77
+
73
78
  trim?: TrimOptions;
74
- timestamp?: Timestamp;
75
79
  encryption?: {
76
80
  keypair: X25519Keypair;
77
- reciever: EncryptionTemplateMaybeEncrypted;
81
+ receiver: EncryptionTemplateMaybeEncrypted;
78
82
  };
79
83
  };
80
84
 
@@ -113,11 +117,11 @@ export class Log<T> {
113
117
  }
114
118
 
115
119
  async open(store: Blocks, identity: Identity, options: LogOptions<T> = {}) {
116
- if (!isDefined(store)) {
120
+ if (store == null) {
117
121
  throw LogError.BlockStoreNotDefinedError();
118
122
  }
119
123
 
120
- if (!isDefined(identity)) {
124
+ if (identity == null) {
121
125
  throw new Error("Identity is required");
122
126
  }
123
127
 
@@ -125,10 +129,10 @@ export class Log<T> {
125
129
  throw new Error("Already open");
126
130
  }
127
131
 
128
- const { encoding, trim, keychain, cache } = options;
132
+ const { encoding, trim, keychain, cache, onGidRemoved } = options;
129
133
  let { sortFn } = options;
130
134
 
131
- if (!isDefined(sortFn)) {
135
+ if (sortFn == null) {
132
136
  sortFn = LastWriteWins;
133
137
  }
134
138
  sortFn = sortFn as Sorting.ISortFunction;
@@ -158,7 +162,7 @@ export class Log<T> {
158
162
  throw new Error("Id not set");
159
163
  }
160
164
  this._headsIndex = new HeadsIndex(id);
161
- await this._headsIndex.init(this);
165
+ await this._headsIndex.init(this, { onGidRemoved });
162
166
  this._entryCache = new Cache({ max: ENTRY_CACHE_MAX });
163
167
  this._entryIndex = new EntryIndex({
164
168
  store: this._storage,
@@ -170,15 +174,15 @@ export class Log<T> {
170
174
  {
171
175
  deleteNode: async (node: EntryNode) => {
172
176
  // TODO check if we have before delete?
173
- const entry = await this.get(node.value.hash);
177
+ const entry = await this.get(node.value);
174
178
  //f (!!entry)
175
179
  const a = this.values.length;
176
180
  if (entry) {
177
181
  this.values.deleteNode(node);
178
- await this.entryIndex.delete(node.value.hash);
179
- await this.headsIndex.del(node.value);
180
- this.nextsIndex.delete(node.value.hash);
181
- await this.storage.rm(node.value.hash);
182
+ await this.headsIndex.del(this.entryIndex.getShallow(node.value)!);
183
+ await this.entryIndex.delete(node.value);
184
+ this.nextsIndex.delete(node.value);
185
+ await this.storage.rm(node.value);
182
186
  }
183
187
  const b = this.values.length;
184
188
  if (a === b) {
@@ -243,6 +247,9 @@ export class Log<T> {
243
247
  get values(): Values<T> {
244
248
  return this._values;
245
249
  }
250
+ get canAppend() {
251
+ return this._canAppend;
252
+ }
246
253
 
247
254
  /**
248
255
  * Checks if a entry is part of the log
@@ -506,8 +513,8 @@ export class Log<T> {
506
513
  options: AppendOptions<T> = {}
507
514
  ): Promise<{ entry: Entry<T>; removed: Entry<T>[] }> {
508
515
  // Update the clock (find the latest clock)
509
- if (options.nexts) {
510
- for (const n of options.nexts) {
516
+ if (options.meta?.next) {
517
+ for (const n of options.meta.next) {
511
518
  if (!n.hash)
512
519
  throw new Error(
513
520
  "Expecting nexts to already be saved. missing hash for one or more entries"
@@ -517,13 +524,12 @@ export class Log<T> {
517
524
 
518
525
  await this.load({ reload: false });
519
526
 
520
- const hasNext = !!options.nexts; // true for [], which means we have explicitly said that nexts are empty
521
- const nexts: Entry<any>[] = options.nexts || (await this.getHeads());
527
+ const nexts: Entry<any>[] = options.meta?.next || (await this.getHeads());
522
528
 
523
529
  // Calculate max time for log/graph
524
530
  const clock = new Clock({
525
531
  id: this._identity.publicKey.bytes,
526
- timestamp: options.timestamp || this._hlc.now(),
532
+ timestamp: options?.meta?.timestamp || this._hlc.now(),
527
533
  });
528
534
 
529
535
  const entry = await Entry.create<T>({
@@ -531,23 +537,28 @@ export class Log<T> {
531
537
  identity: options.identity || this._identity,
532
538
  signers: options.signers,
533
539
  data,
534
- clock,
535
- type: options.type,
540
+ meta: {
541
+ clock,
542
+ type: options.meta?.type,
543
+ gidSeed: options.meta?.gidSeed,
544
+ data: options.meta?.data,
545
+ next: nexts,
546
+ },
547
+
536
548
  encoding: this._encoding,
537
- next: nexts,
538
- gidSeed: options.gidSeed,
549
+
539
550
  encryption: options.encryption
540
551
  ? {
541
552
  keypair: options.encryption.keypair,
542
- reciever: {
543
- ...options.encryption.reciever,
553
+ receiver: {
554
+ ...options.encryption.receiver,
544
555
  },
545
556
  }
546
557
  : undefined,
547
558
  canAppend: this._canAppend,
548
559
  });
549
560
 
550
- if (!isDefined(entry.hash)) {
561
+ if (!entry.hash) {
551
562
  throw new Error("Unexpected");
552
563
  }
553
564
 
@@ -562,35 +573,12 @@ export class Log<T> {
562
573
  }
563
574
  }
564
575
 
565
- const removedGids: Set<string> = new Set();
566
- if (hasNext) {
567
- for (const next of nexts) {
568
- const deletion = await this._headsIndex.del(next);
569
- if (deletion.lastWithGid && next.gid !== entry.gid) {
570
- removedGids.add(next.gid);
571
- }
572
- }
573
- } else {
574
- // next is all heads, which means we should just overwrite
575
- for (const key of this.headsIndex.gids.keys()) {
576
- if (key !== entry.gid) {
577
- removedGids.add(key);
578
- }
579
- }
580
- await this.headsIndex.reset([entry], { cache: { update: false } });
581
- }
582
-
583
576
  await this._entryIndex.set(entry, false); // save === false, because its already saved when Entry.create
584
577
  await this._headsIndex.put(entry, { cache: { update: false } }); // we will update the cache a few lines later *
585
578
  await this._values.put(entry);
586
579
 
587
580
  const removed = await this.processEntry(entry);
588
581
 
589
- // if next contails all gids
590
- if (options.onGidsShadowed && removedGids.size > 0) {
591
- options.onGidsShadowed([...removedGids]);
592
- }
593
-
594
582
  entry.init({ encoding: this._encoding, keychain: this._keychain });
595
583
  // console.log('put entry', entry.hash, (await this._entryIndex._index.size));
596
584
 
@@ -718,7 +706,7 @@ export class Log<T> {
718
706
  return;
719
707
  }
720
708
 
721
- yield next.value.hash;
709
+ yield next.value;
722
710
  counter++;
723
711
 
724
712
  next = nextFn(next);
@@ -782,6 +770,13 @@ export class Log<T> {
782
770
  timeout?: number;
783
771
  } & CacheUpdateOptions
784
772
  ): Promise<void> {
773
+ const definedOptions = {
774
+ ...options,
775
+ cache: options?.cache ?? {
776
+ update: true,
777
+ },
778
+ };
779
+
785
780
  await this.load({ reload: false });
786
781
  if (entriesOrLog.length === 0) {
787
782
  return;
@@ -832,7 +827,6 @@ export class Log<T> {
832
827
  }
833
828
  }
834
829
 
835
- // Resolve missing entries
836
830
  const removedHeads: Entry<T>[] = [];
837
831
  for (const hash of stack) {
838
832
  if (visited.has(hash) || this.has(hash)) {
@@ -851,7 +845,7 @@ export class Log<T> {
851
845
 
852
846
  let nexts: string[];
853
847
  if (
854
- entry.metadata.type !== EntryType.CUT &&
848
+ entry.meta.type !== EntryType.CUT &&
855
849
  (nexts = await entry.getNext())
856
850
  ) {
857
851
  let isRoot = true;
@@ -888,7 +882,12 @@ export class Log<T> {
888
882
  while (entriesBottomUp.length > 0) {
889
883
  const e = entriesBottomUp.shift()!;
890
884
  await this._joining.get(e.hash);
891
- const p = this.joinEntry(e, nextRefs, entriesBottomUp, options).then(
885
+ const p = this.joinEntry(
886
+ e,
887
+ nextRefs,
888
+ entriesBottomUp,
889
+ definedOptions
890
+ ).then(
892
891
  () => this._joining.delete(e.hash) // TODO, if head we run into problems with concurrency here!, we add heads at line 929 but resolve here
893
892
  );
894
893
  this._joining.set(e.hash, p);
@@ -910,7 +909,7 @@ export class Log<T> {
910
909
  return;
911
910
  }
912
911
 
913
- if (!isDefined(e.hash)) {
912
+ if (!e.hash) {
914
913
  throw new Error("Unexpected");
915
914
  }
916
915
 
@@ -929,7 +928,7 @@ export class Log<T> {
929
928
  await this._entryIndex.set(e);
930
929
  await this._values.put(e);
931
930
 
932
- if (e.metadata.type !== EntryType.CUT) {
931
+ if (e.meta.type !== EntryType.CUT) {
933
932
  for (const a of e.next) {
934
933
  if (!this.has(a)) {
935
934
  await this.join([a]);
@@ -973,7 +972,7 @@ export class Log<T> {
973
972
  }
974
973
 
975
974
  private async processEntry(entry: Entry<T>) {
976
- if (entry.metadata.type === EntryType.CUT) {
975
+ if (entry.meta.type === EntryType.CUT) {
977
976
  return this.deleteRecursively(entry, true);
978
977
  }
979
978
  return [];
@@ -985,6 +984,8 @@ export class Log<T> {
985
984
  const promises: Promise<void>[] = [];
986
985
  let counter = 0;
987
986
  const deleted: Entry<T>[] = [];
987
+ const deletedGids = new Set();
988
+
988
989
  while (stack.length > 0) {
989
990
  const entry = stack.pop()!;
990
991
  if ((counter > 0 || !skipFirst) && this.has(entry.hash)) {
@@ -993,6 +994,7 @@ export class Log<T> {
993
994
  await this._values.delete(entry);
994
995
  await this._entryIndex.delete(entry.hash);
995
996
  await this._headsIndex.del(entry);
997
+
996
998
  this._nextsIndex.delete(entry.hash);
997
999
  deleted.push(entry);
998
1000
  promises.push(entry.delete(this._storage));