@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.
Files changed (133) hide show
  1. package/dist/benchmark/append.d.ts +2 -0
  2. package/dist/benchmark/append.d.ts.map +1 -0
  3. package/dist/benchmark/append.js +40 -0
  4. package/dist/benchmark/append.js.map +1 -0
  5. package/dist/benchmark/memory/index.d.ts +2 -0
  6. package/dist/benchmark/memory/index.d.ts.map +1 -0
  7. package/dist/benchmark/memory/index.js +122 -0
  8. package/dist/benchmark/memory/index.js.map +1 -0
  9. package/dist/benchmark/memory/insert.d.ts +2 -0
  10. package/dist/benchmark/memory/insert.d.ts.map +1 -0
  11. package/dist/benchmark/memory/insert.js +59 -0
  12. package/dist/benchmark/memory/insert.js.map +1 -0
  13. package/dist/benchmark/memory/utils.d.ts +13 -0
  14. package/dist/benchmark/memory/utils.d.ts.map +1 -0
  15. package/dist/benchmark/memory/utils.js +2 -0
  16. package/dist/benchmark/memory/utils.js.map +1 -0
  17. package/dist/benchmark/payload.d.ts +2 -0
  18. package/dist/benchmark/payload.d.ts.map +1 -0
  19. package/{lib/esm/__benchmark__/index.js → dist/benchmark/payload.js} +20 -22
  20. package/dist/benchmark/payload.js.map +1 -0
  21. package/dist/src/change.d.ts +6 -0
  22. package/dist/src/change.d.ts.map +1 -0
  23. package/{lib/esm → dist/src}/clock.d.ts +3 -26
  24. package/dist/src/clock.d.ts.map +1 -0
  25. package/{lib/esm → dist/src}/clock.js +30 -39
  26. package/dist/src/clock.js.map +1 -0
  27. package/{lib/esm → dist/src}/difference.d.ts +1 -0
  28. package/dist/src/difference.d.ts.map +1 -0
  29. package/{lib/esm → dist/src}/encoding.d.ts +2 -1
  30. package/dist/src/encoding.d.ts.map +1 -0
  31. package/{lib/esm → dist/src}/encoding.js +2 -2
  32. package/{lib/esm → dist/src}/encoding.js.map +1 -1
  33. package/dist/src/entry-index.d.ts +78 -0
  34. package/dist/src/entry-index.d.ts.map +1 -0
  35. package/dist/src/entry-index.js +316 -0
  36. package/dist/src/entry-index.js.map +1 -0
  37. package/{lib/esm → dist/src}/entry-with-refs.d.ts +2 -1
  38. package/dist/src/entry-with-refs.d.ts.map +1 -0
  39. package/{lib/esm → dist/src}/entry.d.ts +22 -18
  40. package/dist/src/entry.d.ts.map +1 -0
  41. package/{lib/esm → dist/src}/entry.js +69 -42
  42. package/dist/src/entry.js.map +1 -0
  43. package/{lib/esm → dist/src}/find-uniques.d.ts +1 -0
  44. package/dist/src/find-uniques.d.ts.map +1 -0
  45. package/{lib/esm → dist/src}/heads-cache.d.ts +4 -3
  46. package/dist/src/heads-cache.d.ts.map +1 -0
  47. package/{lib/esm → dist/src}/heads-cache.js +9 -10
  48. package/dist/src/heads-cache.js.map +1 -0
  49. package/{lib/esm → dist/src}/index.d.ts +1 -0
  50. package/dist/src/index.d.ts.map +1 -0
  51. package/{lib/esm → dist/src}/log-errors.d.ts +1 -0
  52. package/dist/src/log-errors.d.ts.map +1 -0
  53. package/dist/src/log-sorting.d.ts +35 -0
  54. package/dist/src/log-sorting.d.ts.map +1 -0
  55. package/dist/src/log-sorting.js +105 -0
  56. package/dist/src/log-sorting.js.map +1 -0
  57. package/{lib/esm → dist/src}/log.d.ts +78 -56
  58. package/dist/src/log.d.ts.map +1 -0
  59. package/{lib/esm → dist/src}/log.js +355 -465
  60. package/dist/src/log.js.map +1 -0
  61. package/{lib/esm → dist/src}/logger.d.ts +1 -0
  62. package/dist/src/logger.d.ts.map +1 -0
  63. package/{lib/esm → dist/src}/logger.js.map +1 -1
  64. package/{lib/esm → dist/src}/snapshot.d.ts +5 -4
  65. package/dist/src/snapshot.d.ts.map +1 -0
  66. package/{lib/esm → dist/src}/snapshot.js +10 -10
  67. package/dist/src/snapshot.js.map +1 -0
  68. package/{lib/esm → dist/src}/trim.d.ts +11 -9
  69. package/dist/src/trim.d.ts.map +1 -0
  70. package/{lib/esm → dist/src}/trim.js +46 -35
  71. package/dist/src/trim.js.map +1 -0
  72. package/dist/src/utils.d.ts +4 -0
  73. package/dist/src/utils.d.ts.map +1 -0
  74. package/dist/src/utils.js +12 -0
  75. package/dist/src/utils.js.map +1 -0
  76. package/package.json +104 -78
  77. package/src/change.ts +3 -2
  78. package/src/clock.ts +22 -22
  79. package/src/encoding.ts +4 -4
  80. package/src/entry-index.ts +451 -52
  81. package/src/entry-with-refs.ts +1 -1
  82. package/src/entry.ts +95 -81
  83. package/src/heads-cache.ts +33 -29
  84. package/src/log-sorting.ts +116 -94
  85. package/src/log.ts +482 -571
  86. package/src/logger.ts +1 -0
  87. package/src/snapshot.ts +15 -17
  88. package/src/trim.ts +81 -50
  89. package/src/utils.ts +10 -0
  90. package/lib/esm/__benchmark__/index.d.ts +0 -1
  91. package/lib/esm/__benchmark__/index.js.map +0 -1
  92. package/lib/esm/change.d.ts +0 -5
  93. package/lib/esm/clock.js.map +0 -1
  94. package/lib/esm/entry-index.d.ts +0 -24
  95. package/lib/esm/entry-index.js +0 -74
  96. package/lib/esm/entry-index.js.map +0 -1
  97. package/lib/esm/entry.js.map +0 -1
  98. package/lib/esm/heads-cache.js.map +0 -1
  99. package/lib/esm/heads.d.ts +0 -69
  100. package/lib/esm/heads.js +0 -157
  101. package/lib/esm/heads.js.map +0 -1
  102. package/lib/esm/log-sorting.d.ts +0 -44
  103. package/lib/esm/log-sorting.js +0 -86
  104. package/lib/esm/log-sorting.js.map +0 -1
  105. package/lib/esm/log.js.map +0 -1
  106. package/lib/esm/snapshot.js.map +0 -1
  107. package/lib/esm/trim.js.map +0 -1
  108. package/lib/esm/types.d.ts +0 -6
  109. package/lib/esm/types.js +0 -23
  110. package/lib/esm/types.js.map +0 -1
  111. package/lib/esm/utils.d.ts +0 -2
  112. package/lib/esm/utils.js +0 -3
  113. package/lib/esm/utils.js.map +0 -1
  114. package/lib/esm/values.d.ts +0 -26
  115. package/lib/esm/values.js +0 -131
  116. package/lib/esm/values.js.map +0 -1
  117. package/src/__benchmark__/index.ts +0 -130
  118. package/src/heads.ts +0 -233
  119. package/src/types.ts +0 -12
  120. package/src/values.ts +0 -174
  121. /package/{lib/esm → dist/src}/change.js +0 -0
  122. /package/{lib/esm → dist/src}/change.js.map +0 -0
  123. /package/{lib/esm → dist/src}/difference.js +0 -0
  124. /package/{lib/esm → dist/src}/difference.js.map +0 -0
  125. /package/{lib/esm → dist/src}/entry-with-refs.js +0 -0
  126. /package/{lib/esm → dist/src}/entry-with-refs.js.map +0 -0
  127. /package/{lib/esm → dist/src}/find-uniques.js +0 -0
  128. /package/{lib/esm → dist/src}/find-uniques.js.map +0 -0
  129. /package/{lib/esm → dist/src}/index.js +0 -0
  130. /package/{lib/esm → dist/src}/index.js.map +0 -0
  131. /package/{lib/esm → dist/src}/log-errors.js +0 -0
  132. /package/{lib/esm → dist/src}/log-errors.js.map +0 -0
  133. /package/{lib/esm → dist/src}/logger.js +0 -0
package/src/clock.ts CHANGED
@@ -22,21 +22,23 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
22
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
23
  SOFTWARE.
24
24
  */
25
-
26
- import { field, variant } from "@dao-xyz/borsh";
27
- import { compare, equals } from "@peerbit/uint8arrays";
25
+ import {
26
+ field,
27
+ /* , variant */
28
+ } from "@dao-xyz/borsh";
28
29
  import { hrtime } from "@peerbit/time";
30
+ import { compare, equals } from "uint8arrays";
29
31
 
30
32
  const hrTimeNow = hrtime.bigint();
31
33
  const startTime = BigInt(Date.now()) * BigInt(1e6) - hrTimeNow;
32
34
  const bigintTime = () => startTime + hrtime.bigint();
33
35
 
34
- export function fromBits(low, high, unsigned, target?) {
36
+ export function fromBits(low: any, high: any, unsigned: any, target?: any) {
35
37
  if (target === undefined || target === null) {
36
38
  return {
37
39
  low: low | 0,
38
40
  high: high | 0,
39
- unsigned: !!unsigned
41
+ unsigned: !!unsigned,
40
42
  };
41
43
  }
42
44
  target.low = low | 0;
@@ -49,14 +51,14 @@ const n1e6 = BigInt(1e6);
49
51
  const UINT64_MAX = 18446744073709551615n;
50
52
  const UINT32_MAX = 0xffffffff;
51
53
 
52
- function bigIntCoerce(input, fallback) {
54
+ function bigIntCoerce(input: any, fallback: any) {
53
55
  if (typeof input === "bigint") return input;
54
56
  if (typeof input === "number" || typeof input === "string")
55
57
  return BigInt(input);
56
58
  return fallback;
57
59
  }
58
60
 
59
- @variant(0)
61
+ /* @variant(0) */
60
62
  export class Timestamp {
61
63
  @field({ type: "u64" })
62
64
  wallTime: bigint;
@@ -64,11 +66,9 @@ export class Timestamp {
64
66
  @field({ type: "u32" })
65
67
  logical: number;
66
68
 
67
- constructor(properties?: { wallTime: bigint; logical?: number }) {
68
- if (properties) {
69
- this.wallTime = properties.wallTime;
70
- this.logical = properties.logical || 0;
71
- }
69
+ constructor(properties: { wallTime: bigint; logical?: number }) {
70
+ this.wallTime = properties.wallTime;
71
+ this.logical = properties.logical || 0;
72
72
  }
73
73
 
74
74
  static compare(a: Timestamp, b: Timestamp) {
@@ -92,7 +92,7 @@ export class Timestamp {
92
92
  clone(): Timestamp {
93
93
  return new Timestamp({
94
94
  wallTime: this.wallTime,
95
- logical: this.logical
95
+ logical: this.logical,
96
96
  });
97
97
  }
98
98
 
@@ -114,14 +114,14 @@ export class HLC {
114
114
  wallTimeUpperBound?: bigint;
115
115
  toleratedForwardClockJump?: bigint;
116
116
  last?: Timestamp;
117
- } = {}
117
+ } = {},
118
118
  ) {
119
119
  this.wallTime = properties.wallTime || bigintTime;
120
120
  this.maxOffset = bigIntCoerce(properties.maxOffset, 0n);
121
121
  this.wallTimeUpperBound = bigIntCoerce(properties.wallTimeUpperBound, 0n);
122
122
  this.toleratedForwardClockJump = bigIntCoerce(
123
123
  properties.toleratedForwardClockJump,
124
- 0n
124
+ 0n,
125
125
  );
126
126
  this.last = new Timestamp({ wallTime: this.wallTime() });
127
127
  if (properties.last) {
@@ -180,7 +180,7 @@ export class ClockOffsetError extends Error {
180
180
  offset / n1e6
181
181
  }ms ahead of the wall time, exceeding the 'maxOffset' limit of ${
182
182
  maxOffset / n1e6
183
- }ms.`
183
+ }ms.`,
184
184
  );
185
185
  this.offset = offset;
186
186
  this.maxOffset = maxOffset;
@@ -194,7 +194,7 @@ export class WallTimeOverflowError extends Error {
194
194
  super(
195
195
  `The wall time ${time / n1e6}ms exceeds the max time of ${
196
196
  maxTime / n1e6
197
- }ms.`
197
+ }ms.`,
198
198
  );
199
199
  this.time = time;
200
200
  this.maxTime = maxTime;
@@ -208,14 +208,14 @@ export class ForwardJumpError extends Error {
208
208
  super(
209
209
  `Detected a forward time jump of ${
210
210
  timejump / n1e6
211
- }ms, which exceed the allowed tolerance of ${tolerance / n1e6}ms.`
211
+ }ms, which exceed the allowed tolerance of ${tolerance / n1e6}ms.`,
212
212
  );
213
213
  this.timejump = timejump;
214
214
  this.tolerance = tolerance;
215
215
  }
216
216
  }
217
217
 
218
- @variant(0)
218
+ /* @variant(0) */
219
219
  export class LamportClock {
220
220
  @field({ type: Uint8Array })
221
221
  id: Uint8Array;
@@ -228,13 +228,13 @@ export class LamportClock {
228
228
  if (!properties.timestamp) {
229
229
  this.timestamp = new Timestamp({
230
230
  wallTime: bigintTime(),
231
- logical: 0
231
+ logical: 0,
232
232
  });
233
233
  } else {
234
234
  if (typeof properties.timestamp === "number") {
235
235
  this.timestamp = new Timestamp({
236
236
  wallTime: bigintTime(),
237
- logical: properties.timestamp
237
+ logical: properties.timestamp,
238
238
  });
239
239
  } else {
240
240
  this.timestamp = properties.timestamp;
@@ -245,7 +245,7 @@ export class LamportClock {
245
245
  clone() {
246
246
  return new LamportClock({
247
247
  id: this.id,
248
- timestamp: this.timestamp.clone()
248
+ timestamp: this.timestamp.clone(),
249
249
  });
250
250
  }
251
251
 
package/src/encoding.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AbstractType, deserialize, serialize } from "@dao-xyz/borsh";
1
+ import { type AbstractType, deserialize, serialize } from "@dao-xyz/borsh";
2
2
 
3
3
  export interface Encoding<T> {
4
4
  encoder: (data: T) => Uint8Array;
@@ -9,19 +9,19 @@ export const NO_ENCODING: Encoding<any> = {
9
9
  if (obj instanceof Uint8Array === false) {
10
10
  throw new Error(
11
11
  "With NO_ENCODING only Uint8arrays are allowed, received: " +
12
- (obj?.["constructor"]?.["name"] || typeof obj)
12
+ (obj?.["constructor"]?.["name"] || typeof obj),
13
13
  );
14
14
  }
15
15
  return obj;
16
16
  },
17
17
  decoder: (bytes: Uint8Array) => {
18
18
  return bytes;
19
- }
19
+ },
20
20
  };
21
21
 
22
22
  export const BORSH_ENCODING = <T>(clazz: AbstractType<T>): Encoding<T> => {
23
23
  return {
24
24
  decoder: (bytes: Uint8Array) => deserialize(bytes, clazz),
25
- encoder: (data: any) => serialize(data)
25
+ encoder: (data: any) => serialize(data),
26
26
  };
27
27
  };
@@ -1,36 +1,291 @@
1
+ import { deserialize, serialize } from "@dao-xyz/borsh";
2
+ import { type Blocks } from "@peerbit/blocks-interface";
1
3
  import { Cache } from "@peerbit/cache";
2
- import { Entry, ShallowEntry } from "./entry.js";
3
- import { deserialize } from "@dao-xyz/borsh";
4
+ import type { PublicSignKey } from "@peerbit/crypto";
5
+ import {
6
+ BoolQuery,
7
+ CountRequest,
8
+ DeleteRequest,
9
+ type Index,
10
+ Not,
11
+ Or,
12
+ type Query,
13
+ SearchRequest,
14
+ type Shape,
15
+ Sort,
16
+ SortDirection,
17
+ StringMatch,
18
+ StringMatchMethod,
19
+ SumRequest,
20
+ iterate,
21
+ toId,
22
+ } from "@peerbit/indexer-interface";
23
+ import {
24
+ Entry,
25
+ EntryType,
26
+ type ShallowEntry,
27
+ type ShallowOrFullEntry,
28
+ } from "./entry.js";
29
+ import type { SortFn } from "./log-sorting.js";
4
30
  import { logger } from "./logger.js";
5
- import { Blocks } from "@peerbit/blocks-interface";
31
+
32
+ export type ResultsIterator<T> = {
33
+ close: () => void | Promise<void>;
34
+ next: (number: number) => T[] | Promise<T[]>;
35
+ done: () => boolean;
36
+ all(): T[] | Promise<T[]>;
37
+ };
38
+
39
+ const ENTRY_CACHE_MAX_SIZE = 10; // TODO as param for log
40
+
41
+ type ResolveFullyOptions =
42
+ | true
43
+ | {
44
+ type: "full";
45
+ replicate?: boolean;
46
+ signal?: AbortSignal;
47
+ timeout?: number;
48
+ ignoreMissing?: boolean;
49
+ };
50
+ type ResolveShapeOptions = { type: "shape"; shape: Shape };
51
+ export type MaybeResolveOptions =
52
+ | false
53
+ | ResolveFullyOptions
54
+ | ResolveShapeOptions;
55
+ export type ReturnTypeFromResolveOptions<
56
+ R extends MaybeResolveOptions,
57
+ T,
58
+ > = R extends false | undefined
59
+ ? ShallowEntry
60
+ : R extends { type: "shape" }
61
+ ? any
62
+ : Entry<T>;
6
63
 
7
64
  export class EntryIndex<T> {
8
- _cache: Cache<Entry<T> | null>;
9
- _blocks: Blocks;
10
- _init: (entry: Entry<T>) => void;
11
- _index: Map<string, ShallowEntry>;
12
-
13
- constructor(properties: {
14
- store: Blocks;
15
- init: (entry: Entry<T>) => void;
16
- cache: Cache<Entry<T>>;
17
- }) {
18
- this._cache = properties.cache;
19
- this._blocks = properties.store;
20
- this._init = properties.init;
21
- this._index = new Map();
22
- }
23
-
24
- async set(v: Entry<T>, toMultihash = true) {
25
- if (toMultihash) {
26
- const existingHash = v.hash;
27
- v.hash = undefined as any;
65
+ private cache: Cache<Entry<T>>;
66
+ private sortReversed: Sort[];
67
+ private initialied = false;
68
+ private _length: number;
69
+ private insertionPromises: Map<string, Promise<void>>;
70
+ constructor(
71
+ readonly properties: {
72
+ store: Blocks;
73
+ publicKey: PublicSignKey;
74
+ init: (entry: Entry<T>) => void;
75
+ cache?: Cache<Entry<T>>;
76
+ index: Index<ShallowEntry>;
77
+ sort: SortFn;
78
+ onGidRemoved?: (gid: string[]) => Promise<void> | void;
79
+ },
80
+ ) {
81
+ this.sortReversed = properties.sort.sort.map((x) =>
82
+ deserialize(serialize(x), Sort),
83
+ );
84
+ this.sortReversed.map(
85
+ (x) =>
86
+ (x.direction =
87
+ x.direction === SortDirection.DESC
88
+ ? SortDirection.ASC
89
+ : SortDirection.DESC),
90
+ );
91
+ this.cache = properties.cache ?? new Cache({ max: ENTRY_CACHE_MAX_SIZE });
92
+ this._length = 0;
93
+ this.insertionPromises = new Map();
94
+ }
95
+
96
+ getHeads<R extends MaybeResolveOptions = false>(
97
+ gid?: string,
98
+ resolve: R = false as R,
99
+ ): ResultsIterator<ReturnTypeFromResolveOptions<R, T>> {
100
+ const query: Query[] = [];
101
+ query.push(new BoolQuery({ key: "head", value: true }));
102
+ if (gid) {
103
+ query.push(
104
+ new StringMatch({
105
+ key: ["meta", "gid"],
106
+ value: gid,
107
+ caseInsensitive: false,
108
+ method: StringMatchMethod.exact,
109
+ }),
110
+ );
111
+ }
112
+ return this.query(query, undefined, resolve);
113
+ }
114
+
115
+ getHasNext<R extends MaybeResolveOptions>(
116
+ next: string,
117
+ resolve?: R,
118
+ ): ResultsIterator<ReturnTypeFromResolveOptions<R, T>> {
119
+ const query: Query[] = [
120
+ new StringMatch({
121
+ key: ["meta", "next"],
122
+ value: next,
123
+ caseInsensitive: false,
124
+ method: StringMatchMethod.exact,
125
+ }),
126
+ ];
127
+ return this.query(query, undefined, resolve);
128
+ }
129
+
130
+ countHasNext(next: string, excludeHash: string | undefined = undefined) {
131
+ const query: Query[] = [
132
+ new StringMatch({
133
+ key: ["meta", "next"],
134
+ value: next,
135
+ caseInsensitive: false,
136
+ method: StringMatchMethod.exact,
137
+ }),
138
+ ];
139
+ if (excludeHash) {
140
+ query.push(
141
+ new Not(
142
+ new StringMatch({
143
+ key: ["hash"],
144
+ value: excludeHash,
145
+ caseInsensitive: false,
146
+ method: StringMatchMethod.exact,
147
+ }),
148
+ ),
149
+ );
150
+ }
151
+ return this.properties.index.count(new CountRequest({ query }));
152
+ }
153
+
154
+ query<R extends MaybeResolveOptions>(
155
+ query: Query[],
156
+ sort = this.properties.sort.sort,
157
+ options?: R,
158
+ ): ResultsIterator<ReturnTypeFromResolveOptions<R, T>> {
159
+ const iterator = iterate(
160
+ this.properties.index,
161
+ new SearchRequest({ query, sort }),
162
+ );
163
+ let resolveInFull = options
164
+ ? options === true
165
+ ? true
166
+ : options.type === "full"
167
+ : false;
168
+ let resolveInFullOptions: ResolveFullyOptions | undefined = resolveInFull
169
+ ? (options as ResolveFullyOptions)
170
+ : undefined;
171
+ let nextShape = resolveInFull
172
+ ? ({ hash: true } as const)
173
+ : ((options as { shape: Shape })?.shape as Shape);
174
+
175
+ const next = async (
176
+ amount: number,
177
+ ): Promise<ReturnTypeFromResolveOptions<R, T>[]> => {
178
+ const results = await iterator.next(amount, { shape: nextShape });
179
+ if (resolveInFull) {
180
+ const maybeResolved = await Promise.all(
181
+ results.results.map((x) =>
182
+ this.resolve(x.value.hash, resolveInFullOptions),
183
+ ),
184
+ );
185
+ return maybeResolved.filter((x) => !!x) as ReturnTypeFromResolveOptions<
186
+ R,
187
+ T
188
+ >[];
189
+ } else {
190
+ return results.results.map(
191
+ (x) => x.value,
192
+ ) as ReturnTypeFromResolveOptions<R, T>[];
193
+ }
194
+ };
195
+
196
+ return {
197
+ close: iterator.close,
198
+ done: iterator.done,
199
+ next,
200
+ all: async () => {
201
+ const results: ReturnTypeFromResolveOptions<R, T>[] = [];
202
+ while (!iterator.done()) {
203
+ for (const element of await next(100)) {
204
+ results.push(element);
205
+ }
206
+ }
207
+ await iterator.close();
208
+ return results;
209
+ },
210
+ };
211
+ }
212
+
213
+ async getOldest<
214
+ T extends boolean,
215
+ R = T extends true ? Entry<any> : ShallowEntry,
216
+ >(resolve?: T): Promise<R | undefined> {
217
+ const iterator = this.query([], this.properties.sort.sort, resolve);
218
+ const results = await iterator.next(1);
219
+ await iterator.close();
220
+ return results[0] as R;
221
+ }
222
+
223
+ async getNewest<
224
+ T extends boolean,
225
+ R = T extends true ? Entry<any> : ShallowEntry,
226
+ >(resolve?: T): Promise<R | undefined> {
227
+ const iterator = this.query([], this.sortReversed, resolve);
228
+ const results = await iterator.next(1);
229
+ await iterator.close();
230
+ return results[0] as R;
231
+ }
232
+
233
+ async getBefore<
234
+ T extends boolean,
235
+ R = T extends true ? Entry<any> : ShallowEntry,
236
+ >(before: ShallowOrFullEntry<any>, resolve?: T): Promise<R | undefined> {
237
+ const iterator = this.query(
238
+ this.properties.sort.before(before),
239
+ this.sortReversed,
240
+ resolve,
241
+ );
242
+ const results = await iterator.next(1);
243
+ await iterator.close();
244
+ return results[0] as R;
245
+ }
246
+ async getAfter<
247
+ T extends boolean,
248
+ R = T extends true ? Entry<any> : ShallowEntry,
249
+ >(before: ShallowOrFullEntry<any>, resolve?: T): Promise<R | undefined> {
250
+ const iterator = this.query(
251
+ this.properties.sort.after(before),
252
+ this.properties.sort.sort,
253
+ resolve,
254
+ );
255
+ const results = await iterator.next(1);
256
+ await iterator.close();
257
+
258
+ return results[0] as R;
259
+ }
260
+
261
+ async get(k: string, options?: ResolveFullyOptions) {
262
+ return this.resolve(k, options);
263
+ }
264
+
265
+ async getShallow(k: string) {
266
+ return this.properties.index.get(toId(k));
267
+ }
268
+
269
+ async has(k: string) {
270
+ const result = await this.properties.index.get(toId(k), {
271
+ shape: { hash: true },
272
+ });
273
+ return result != null;
274
+ }
275
+
276
+ async put(
277
+ entry: Entry<any>,
278
+ properties: { unique: boolean; isHead: boolean; toMultiHash: boolean },
279
+ ) {
280
+ if (properties.toMultiHash) {
281
+ const existingHash = entry.hash;
282
+ entry.hash = undefined as any;
28
283
  try {
29
- const hash = await Entry.toMultihash(this._blocks, v);
30
- v.hash = existingHash;
31
- if (v.hash === undefined) {
32
- v.hash = hash; // can happen if you sync entries that you load directly from ipfs
33
- } else if (existingHash !== v.hash) {
284
+ const hash = await Entry.toMultihash(this.properties.store, entry);
285
+ entry.hash = existingHash;
286
+ if (entry.hash === undefined) {
287
+ entry.hash = hash; // can happen if you sync entries that you load directly from ipfs
288
+ } else if (existingHash !== entry.hash) {
34
289
  logger.error("Head hash didn't match the contents");
35
290
  throw new Error("Head hash didn't match the contents");
36
291
  }
@@ -38,42 +293,192 @@ export class EntryIndex<T> {
38
293
  logger.error(error);
39
294
  throw error;
40
295
  }
296
+ } else {
297
+ if (!entry.hash) {
298
+ throw new Error("Missing hash");
299
+ }
300
+ }
301
+
302
+ const existingPromise = this.insertionPromises.get(entry.hash);
303
+ if (existingPromise) {
304
+ return existingPromise;
305
+ } else {
306
+ const fn = async () => {
307
+ this.cache.add(entry.hash, entry);
308
+
309
+ if (properties.unique === true || !(await this.has(entry.hash))) {
310
+ this._length++;
311
+ }
312
+
313
+ await this.properties.index.put(entry.toShallow(properties.isHead));
314
+
315
+ // check if gids has been shadowed, by query all nexts that have a different gid
316
+ if (this.properties.onGidRemoved && entry.meta.next.length > 0) {
317
+ let nextMatches: Query[] = [];
318
+
319
+ for (const next of entry.meta.next) {
320
+ nextMatches.push(
321
+ new StringMatch({
322
+ key: ["hash"],
323
+ value: next,
324
+ caseInsensitive: false,
325
+ method: StringMatchMethod.exact,
326
+ }),
327
+ );
328
+ }
329
+
330
+ const nextsWithOthersGids: { hash: string; meta: { gid: string } }[] =
331
+ await this.query(
332
+ [
333
+ new Or(nextMatches),
334
+ new Not(
335
+ new StringMatch({
336
+ key: ["meta", "gid"],
337
+ value: entry.meta.gid,
338
+ }),
339
+ ),
340
+ ],
341
+ undefined,
342
+ { type: "shape", shape: { hash: true, meta: { gid: true } } },
343
+ ).all();
344
+
345
+ let shadowedGids = new Set<string>();
346
+ for (const next of nextsWithOthersGids) {
347
+ // check that this entry is not referenced by other
348
+ const nexts = await this.countHasNext(next.hash, entry.hash);
349
+ if (nexts > 0) {
350
+ continue;
351
+ }
352
+ shadowedGids.add(next.meta.gid);
353
+ }
354
+
355
+ if (shadowedGids.size > 0) {
356
+ this.properties.onGidRemoved?.([...shadowedGids]);
357
+ }
358
+ }
359
+
360
+ // mark all next entries as not heads
361
+ await this.privateUpdateNextHeadProperty(entry, false);
362
+
363
+ this.insertionPromises.delete(entry.hash);
364
+ };
365
+ const promise = fn();
366
+ this.insertionPromises.set(entry.hash, promise);
367
+ return promise;
368
+ }
369
+ }
370
+
371
+ async delete(k: string) {
372
+ this.cache.del(k);
373
+
374
+ let shallow = (await this.getShallow(k))?.value;
375
+ if (!shallow) {
376
+ return; // already deleted
377
+ }
378
+
379
+ let deleted = await this.properties.index.del(
380
+ new DeleteRequest({ query: { hash: k } }),
381
+ );
382
+ await this.properties.store.rm(k);
383
+
384
+ if (deleted.length > 0) {
385
+ this._length -= deleted.length;
386
+
387
+ // mark all next entries as new heads
388
+ await this.privateUpdateNextHeadProperty(shallow, true);
389
+ return shallow;
390
+ }
391
+ }
392
+
393
+ async getMemoryUsage() {
394
+ return this.properties.index.sum(new SumRequest({ key: "payloadSize" }));
395
+ }
396
+
397
+ private async privateUpdateNextHeadProperty(
398
+ from: ShallowEntry | Entry<any>,
399
+ isHead: boolean,
400
+ ) {
401
+ if (from.meta.type === EntryType.CUT) {
402
+ // if the next is a cut, we can't update it, since it's not in the index
403
+ return;
404
+ }
405
+
406
+ for (const next of from.meta.next) {
407
+ const indexedEntry = await this.properties.index.get(toId(next));
408
+
409
+ if (!indexedEntry) {
410
+ continue; // we could end up here because another entry with same next ref is of CUT and has removed it from the index
411
+ }
412
+
413
+ if (isHead) {
414
+ const noPointersToNext = (await this.countHasNext(next)) === 0;
415
+ if (noPointersToNext) {
416
+ indexedEntry.value.head = true;
417
+ if (indexedEntry) {
418
+ await this.properties.index.put(indexedEntry.value);
419
+ }
420
+ }
421
+ } else {
422
+ indexedEntry.value.head = false;
423
+ if (indexedEntry) {
424
+ await this.properties.index.put(indexedEntry.value);
425
+ }
426
+ }
427
+ }
428
+ }
429
+
430
+ async clear() {
431
+ const iterator = await this.query([], undefined, false);
432
+ while (!iterator.done()) {
433
+ const results = await iterator.next(100);
434
+ for (const result of results) {
435
+ await this.delete(result.hash);
436
+ }
41
437
  }
42
- this._cache.add(v.hash, v);
43
- this._index.set(v.hash, v.toShallow());
438
+ await this.properties.index.drop();
439
+ await this.properties.index.start();
440
+ this.cache.clear();
44
441
  }
45
- has(k: string) {
46
- return this._index.has(k);
442
+
443
+ get length() {
444
+ if (!this.initialied) {
445
+ throw new Error("Not initialized");
446
+ }
447
+ return this._length;
448
+ }
449
+
450
+ async init() {
451
+ this._length = await this.properties.index.getSize();
452
+ this.initialied = true;
47
453
  }
48
454
 
49
- async get(
455
+ private async resolve(
50
456
  k: string,
51
- options?: { load?: boolean; replicate?: boolean; timeout?: number }
457
+ options?: ResolveFullyOptions,
52
458
  ): Promise<Entry<T> | undefined> {
53
- if (this._index.has(k) || options?.load) {
54
- let mem = this._cache.get(k);
459
+ let coercedOptions = typeof options === "object" ? options : undefined;
460
+ if (await this.has(k)) {
461
+ let mem = this.cache.get(k);
55
462
  if (mem === undefined) {
56
- mem = await this.getFromStore(k, options);
463
+ mem = await this.resolveFromStore(k, coercedOptions);
57
464
  if (mem) {
58
- this._init(mem);
465
+ this.properties.init(mem);
59
466
  mem.hash = k;
467
+ } else if (coercedOptions?.ignoreMissing !== true) {
468
+ throw new Error("Failed to load entry from head with hash: " + k);
60
469
  }
61
- this._cache.add(k, mem);
470
+ this.cache.add(k, mem ?? undefined);
62
471
  }
63
472
  return mem ? mem : undefined;
64
473
  }
65
474
  return undefined;
66
475
  }
67
476
 
68
- getShallow(k: string) {
69
- return this._index.get(k);
70
- }
71
-
72
- private async getFromStore(
477
+ private async resolveFromStore(
73
478
  k: string,
74
- options?: { replicate?: boolean; timeout?: number }
479
+ options?: { signal?: AbortSignal; replicate?: boolean; timeout?: number },
75
480
  ): Promise<Entry<T> | null> {
76
- const value = await this._blocks.get(k, options);
481
+ const value = await this.properties.store.get(k, options);
77
482
  if (value) {
78
483
  const entry = deserialize(value, Entry);
79
484
  entry.size = value.length;
@@ -81,10 +486,4 @@ export class EntryIndex<T> {
81
486
  }
82
487
  return null;
83
488
  }
84
-
85
- async delete(k: string) {
86
- this._cache.del(k);
87
- this._index.delete(k);
88
- return this._blocks.rm(k);
89
- }
90
489
  }