@peerbit/log 3.0.34-aa577a5 → 3.0.34-cccc078
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/{index.d.ts.map → memory/index.d.ts.map} +1 -1
- 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/dist/benchmark/{index.js → payload.js} +14 -14
- package/dist/benchmark/payload.js.map +1 -0
- package/dist/src/change.d.ts +2 -2
- package/dist/src/change.d.ts.map +1 -1
- package/dist/src/change.js +1 -1
- package/dist/src/change.js.map +1 -1
- package/dist/src/clock.d.ts +0 -24
- package/dist/src/clock.d.ts.map +1 -1
- package/dist/src/clock.js +28 -35
- package/dist/src/clock.js.map +1 -1
- package/dist/src/encoding.d.ts.map +1 -1
- package/dist/src/encoding.js +2 -2
- package/dist/src/encoding.js.map +1 -1
- package/dist/src/entry-index.d.ts +70 -17
- package/dist/src/entry-index.d.ts.map +1 -1
- package/dist/src/entry-index.js +281 -41
- package/dist/src/entry-index.js.map +1 -1
- package/dist/src/entry-with-refs.d.ts +1 -1
- package/dist/src/entry-with-refs.d.ts.map +1 -1
- package/dist/src/entry-with-refs.js +1 -1
- package/dist/src/entry-with-refs.js.map +1 -1
- package/dist/src/entry.d.ts +18 -15
- package/dist/src/entry.d.ts.map +1 -1
- package/dist/src/entry.js +62 -36
- package/dist/src/entry.js.map +1 -1
- package/dist/src/find-uniques.d.ts.map +1 -1
- package/dist/src/heads-cache.d.ts +1 -1
- package/dist/src/heads-cache.d.ts.map +1 -1
- package/dist/src/heads-cache.js +6 -7
- package/dist/src/heads-cache.js.map +1 -1
- package/dist/src/log-sorting.d.ts +27 -37
- package/dist/src/log-sorting.d.ts.map +1 -1
- package/dist/src/log-sorting.js +92 -74
- package/dist/src/log-sorting.js.map +1 -1
- package/dist/src/log.d.ts +71 -54
- package/dist/src/log.d.ts.map +1 -1
- package/dist/src/log.js +349 -468
- package/dist/src/log.js.map +1 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js.map +1 -1
- package/dist/src/snapshot.d.ts +2 -2
- package/dist/src/snapshot.d.ts.map +1 -1
- package/dist/src/snapshot.js +5 -5
- package/dist/src/snapshot.js.map +1 -1
- package/dist/src/trim.d.ts +9 -8
- package/dist/src/trim.d.ts.map +1 -1
- package/dist/src/trim.js +43 -40
- package/dist/src/trim.js.map +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +1 -1
- package/dist/src/utils.js.map +1 -1
- package/package.json +15 -13
- package/src/change.ts +3 -2
- package/src/clock.ts +25 -19
- package/src/encoding.ts +3 -3
- package/src/entry-index.ts +451 -52
- package/src/entry-with-refs.ts +1 -1
- package/src/entry.ts +89 -72
- package/src/heads-cache.ts +27 -21
- package/src/log-sorting.ts +116 -94
- package/src/log.ts +465 -564
- package/src/logger.ts +1 -0
- package/src/snapshot.ts +10 -10
- package/src/trim.ts +75 -50
- package/src/utils.ts +6 -8
- package/dist/benchmark/index.js.map +0 -1
- package/dist/src/heads.d.ts +0 -70
- package/dist/src/heads.d.ts.map +0 -1
- package/dist/src/heads.js +0 -164
- package/dist/src/heads.js.map +0 -1
- package/dist/src/types.d.ts +0 -7
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js +0 -21
- package/dist/src/types.js.map +0 -1
- package/dist/src/values.d.ts +0 -27
- package/dist/src/values.d.ts.map +0 -1
- package/dist/src/values.js +0 -134
- package/dist/src/values.js.map +0 -1
- package/src/heads.ts +0 -233
- package/src/types.ts +0 -10
- package/src/values.ts +0 -174
- /package/dist/benchmark/{index.d.ts → memory/index.d.ts} +0 -0
package/src/clock.ts
CHANGED
|
@@ -22,10 +22,12 @@ 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
|
-
|
|
27
|
-
|
|
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;
|
|
@@ -36,7 +38,7 @@ export function fromBits(low: any, high: any, unsigned: any, target?: any) {
|
|
|
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;
|
|
@@ -56,7 +58,7 @@ function bigIntCoerce(input: any, fallback: any) {
|
|
|
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;
|
|
@@ -90,7 +92,7 @@ export class Timestamp {
|
|
|
90
92
|
clone(): Timestamp {
|
|
91
93
|
return new Timestamp({
|
|
92
94
|
wallTime: this.wallTime,
|
|
93
|
-
logical: this.logical
|
|
95
|
+
logical: this.logical,
|
|
94
96
|
});
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -112,14 +114,14 @@ export class HLC {
|
|
|
112
114
|
wallTimeUpperBound?: bigint;
|
|
113
115
|
toleratedForwardClockJump?: bigint;
|
|
114
116
|
last?: Timestamp;
|
|
115
|
-
} = {}
|
|
117
|
+
} = {},
|
|
116
118
|
) {
|
|
117
119
|
this.wallTime = properties.wallTime || bigintTime;
|
|
118
120
|
this.maxOffset = bigIntCoerce(properties.maxOffset, 0n);
|
|
119
121
|
this.wallTimeUpperBound = bigIntCoerce(properties.wallTimeUpperBound, 0n);
|
|
120
122
|
this.toleratedForwardClockJump = bigIntCoerce(
|
|
121
123
|
properties.toleratedForwardClockJump,
|
|
122
|
-
0n
|
|
124
|
+
0n,
|
|
123
125
|
);
|
|
124
126
|
this.last = new Timestamp({ wallTime: this.wallTime() });
|
|
125
127
|
if (properties.last) {
|
|
@@ -174,9 +176,11 @@ export class ClockOffsetError extends Error {
|
|
|
174
176
|
maxOffset: bigint;
|
|
175
177
|
constructor(offset: bigint, maxOffset: bigint) {
|
|
176
178
|
super(
|
|
177
|
-
`The received time is ${
|
|
178
|
-
|
|
179
|
-
}ms
|
|
179
|
+
`The received time is ${
|
|
180
|
+
offset / n1e6
|
|
181
|
+
}ms ahead of the wall time, exceeding the 'maxOffset' limit of ${
|
|
182
|
+
maxOffset / n1e6
|
|
183
|
+
}ms.`,
|
|
180
184
|
);
|
|
181
185
|
this.offset = offset;
|
|
182
186
|
this.maxOffset = maxOffset;
|
|
@@ -188,8 +192,9 @@ export class WallTimeOverflowError extends Error {
|
|
|
188
192
|
maxTime: bigint;
|
|
189
193
|
constructor(time: bigint, maxTime: bigint) {
|
|
190
194
|
super(
|
|
191
|
-
`The wall time ${time / n1e6}ms exceeds the max time of ${
|
|
192
|
-
|
|
195
|
+
`The wall time ${time / n1e6}ms exceeds the max time of ${
|
|
196
|
+
maxTime / n1e6
|
|
197
|
+
}ms.`,
|
|
193
198
|
);
|
|
194
199
|
this.time = time;
|
|
195
200
|
this.maxTime = maxTime;
|
|
@@ -201,15 +206,16 @@ export class ForwardJumpError extends Error {
|
|
|
201
206
|
tolerance: bigint;
|
|
202
207
|
constructor(timejump: bigint, tolerance: bigint) {
|
|
203
208
|
super(
|
|
204
|
-
`Detected a forward time jump of ${
|
|
205
|
-
|
|
209
|
+
`Detected a forward time jump of ${
|
|
210
|
+
timejump / n1e6
|
|
211
|
+
}ms, which exceed the allowed tolerance of ${tolerance / n1e6}ms.`,
|
|
206
212
|
);
|
|
207
213
|
this.timejump = timejump;
|
|
208
214
|
this.tolerance = tolerance;
|
|
209
215
|
}
|
|
210
216
|
}
|
|
211
217
|
|
|
212
|
-
@variant(0)
|
|
218
|
+
/* @variant(0) */
|
|
213
219
|
export class LamportClock {
|
|
214
220
|
@field({ type: Uint8Array })
|
|
215
221
|
id: Uint8Array;
|
|
@@ -222,13 +228,13 @@ export class LamportClock {
|
|
|
222
228
|
if (!properties.timestamp) {
|
|
223
229
|
this.timestamp = new Timestamp({
|
|
224
230
|
wallTime: bigintTime(),
|
|
225
|
-
logical: 0
|
|
231
|
+
logical: 0,
|
|
226
232
|
});
|
|
227
233
|
} else {
|
|
228
234
|
if (typeof properties.timestamp === "number") {
|
|
229
235
|
this.timestamp = new Timestamp({
|
|
230
236
|
wallTime: bigintTime(),
|
|
231
|
-
logical: properties.timestamp
|
|
237
|
+
logical: properties.timestamp,
|
|
232
238
|
});
|
|
233
239
|
} else {
|
|
234
240
|
this.timestamp = properties.timestamp;
|
|
@@ -239,7 +245,7 @@ export class LamportClock {
|
|
|
239
245
|
clone() {
|
|
240
246
|
return new LamportClock({
|
|
241
247
|
id: this.id,
|
|
242
|
-
timestamp: this.timestamp.clone()
|
|
248
|
+
timestamp: this.timestamp.clone(),
|
|
243
249
|
});
|
|
244
250
|
}
|
|
245
251
|
|
package/src/encoding.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|
package/src/entry-index.ts
CHANGED
|
@@ -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 {
|
|
3
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
constructor(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
} else if (existingHash !==
|
|
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
|
+
}
|
|
41
427
|
}
|
|
42
|
-
this._cache.add(v.hash, v);
|
|
43
|
-
this._index.set(v.hash, v.toShallow());
|
|
44
428
|
}
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
}
|
|
437
|
+
}
|
|
438
|
+
await this.properties.index.drop();
|
|
439
|
+
await this.properties.index.start();
|
|
440
|
+
this.cache.clear();
|
|
47
441
|
}
|
|
48
442
|
|
|
49
|
-
|
|
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;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
private async resolve(
|
|
50
456
|
k: string,
|
|
51
|
-
options?:
|
|
457
|
+
options?: ResolveFullyOptions,
|
|
52
458
|
): Promise<Entry<T> | undefined> {
|
|
53
|
-
|
|
54
|
-
|
|
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.
|
|
463
|
+
mem = await this.resolveFromStore(k, coercedOptions);
|
|
57
464
|
if (mem) {
|
|
58
|
-
this.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
}
|
package/src/entry-with-refs.ts
CHANGED