@peerbit/log 1.0.2

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 (92) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +11 -0
  3. package/lib/esm/change.d.ts +5 -0
  4. package/lib/esm/change.js +2 -0
  5. package/lib/esm/change.js.map +1 -0
  6. package/lib/esm/clock.d.ts +87 -0
  7. package/lib/esm/clock.js +260 -0
  8. package/lib/esm/clock.js.map +1 -0
  9. package/lib/esm/difference.d.ts +1 -0
  10. package/lib/esm/difference.js +20 -0
  11. package/lib/esm/difference.js.map +1 -0
  12. package/lib/esm/encoding.d.ts +7 -0
  13. package/lib/esm/encoding.js +20 -0
  14. package/lib/esm/encoding.js.map +1 -0
  15. package/lib/esm/entry-index.d.ts +21 -0
  16. package/lib/esm/entry-index.js +63 -0
  17. package/lib/esm/entry-index.js.map +1 -0
  18. package/lib/esm/entry-with-refs.d.ts +5 -0
  19. package/lib/esm/entry-with-refs.js +2 -0
  20. package/lib/esm/entry-with-refs.js.map +1 -0
  21. package/lib/esm/entry.d.ts +179 -0
  22. package/lib/esm/entry.js +591 -0
  23. package/lib/esm/entry.js.map +1 -0
  24. package/lib/esm/find-uniques.d.ts +1 -0
  25. package/lib/esm/find-uniques.js +12 -0
  26. package/lib/esm/find-uniques.js.map +1 -0
  27. package/lib/esm/heads-cache.d.ts +64 -0
  28. package/lib/esm/heads-cache.js +317 -0
  29. package/lib/esm/heads-cache.js.map +1 -0
  30. package/lib/esm/heads.d.ts +63 -0
  31. package/lib/esm/heads.js +143 -0
  32. package/lib/esm/heads.js.map +1 -0
  33. package/lib/esm/hrtime.d.ts +5 -0
  34. package/lib/esm/hrtime.js +71 -0
  35. package/lib/esm/hrtime.js.map +1 -0
  36. package/lib/esm/index.d.ts +11 -0
  37. package/lib/esm/index.js +11 -0
  38. package/lib/esm/index.js.map +1 -0
  39. package/lib/esm/is-defined.d.ts +1 -0
  40. package/lib/esm/is-defined.js +2 -0
  41. package/lib/esm/is-defined.js.map +1 -0
  42. package/lib/esm/log-errors.d.ts +5 -0
  43. package/lib/esm/log-errors.js +6 -0
  44. package/lib/esm/log-errors.js.map +1 -0
  45. package/lib/esm/log-sorting.d.ts +44 -0
  46. package/lib/esm/log-sorting.js +86 -0
  47. package/lib/esm/log-sorting.js.map +1 -0
  48. package/lib/esm/log.d.ts +205 -0
  49. package/lib/esm/log.js +1004 -0
  50. package/lib/esm/log.js.map +1 -0
  51. package/lib/esm/logger.d.ts +2 -0
  52. package/lib/esm/logger.js +4 -0
  53. package/lib/esm/logger.js.map +1 -0
  54. package/lib/esm/package.json +3 -0
  55. package/lib/esm/snapshot.d.ts +22 -0
  56. package/lib/esm/snapshot.js +83 -0
  57. package/lib/esm/snapshot.js.map +1 -0
  58. package/lib/esm/trim.d.ts +49 -0
  59. package/lib/esm/trim.js +203 -0
  60. package/lib/esm/trim.js.map +1 -0
  61. package/lib/esm/types.d.ts +6 -0
  62. package/lib/esm/types.js +23 -0
  63. package/lib/esm/types.js.map +1 -0
  64. package/lib/esm/utils.d.ts +2 -0
  65. package/lib/esm/utils.js +3 -0
  66. package/lib/esm/utils.js.map +1 -0
  67. package/lib/esm/values.d.ts +33 -0
  68. package/lib/esm/values.js +141 -0
  69. package/lib/esm/values.js.map +1 -0
  70. package/package.json +79 -0
  71. package/src/change.ts +2 -0
  72. package/src/clock.ts +280 -0
  73. package/src/difference.ts +22 -0
  74. package/src/encoding.ts +27 -0
  75. package/src/entry-index.ts +78 -0
  76. package/src/entry-with-refs.ts +6 -0
  77. package/src/entry.ts +749 -0
  78. package/src/find-uniques.ts +14 -0
  79. package/src/heads-cache.ts +400 -0
  80. package/src/heads.ts +208 -0
  81. package/src/hrtime.ts +78 -0
  82. package/src/index.ts +11 -0
  83. package/src/is-defined.ts +1 -0
  84. package/src/log-errors.ts +9 -0
  85. package/src/log-sorting.ts +108 -0
  86. package/src/log.ts +1262 -0
  87. package/src/logger.ts +3 -0
  88. package/src/snapshot.ts +103 -0
  89. package/src/trim.ts +269 -0
  90. package/src/types.ts +12 -0
  91. package/src/utils.ts +2 -0
  92. package/src/values.ts +193 -0
@@ -0,0 +1,141 @@
1
+ import yallist from "yallist";
2
+ export class Values {
3
+ /**
4
+ * Keep track of sorted elements in descending sort order (i.e. newest elements)
5
+ */
6
+ _values;
7
+ _sortFn;
8
+ _byteLength;
9
+ _entryIndex;
10
+ constructor(entryIndex, sortFn, entries = []) {
11
+ this._values = yallist.create(entries
12
+ .slice()
13
+ .sort(sortFn)
14
+ .reverse()
15
+ .map((x) => {
16
+ if (!x.hash)
17
+ throw new Error("Unexpected");
18
+ return {
19
+ hash: x.hash,
20
+ byteLength: x._payload.byteLength,
21
+ gid: x.gid,
22
+ };
23
+ }));
24
+ this._byteLength = 0;
25
+ entries.forEach((entry) => {
26
+ this._byteLength += entry._payload.byteLength;
27
+ });
28
+ this._sortFn = sortFn;
29
+ this._entryIndex = entryIndex;
30
+ }
31
+ toArray() {
32
+ return Promise.all(this._values.toArrayReverse().map((x) => this._entryIndex.get(x.hash))).then((arr) => arr.filter((x) => !!x)); // we do reverse because we assume the log is only meaningful if we read it from start to end
33
+ }
34
+ get head() {
35
+ return this._values.head;
36
+ }
37
+ get tail() {
38
+ return this._values.tail;
39
+ }
40
+ get length() {
41
+ return this._values.length;
42
+ }
43
+ _putPromise = new Map();
44
+ async put(value) {
45
+ let promise = this._putPromise.get(value.hash);
46
+ if (promise) {
47
+ return promise;
48
+ }
49
+ promise = this._put(value).then((v) => {
50
+ this._putPromise.delete(value.hash);
51
+ return v;
52
+ });
53
+ this._putPromise.set(value.hash, promise);
54
+ return promise;
55
+ }
56
+ async _put(value) {
57
+ // assume we want to insert at head (or somehere close)
58
+ let walker = this._values.head;
59
+ let last = undefined;
60
+ while (walker) {
61
+ const walkerValue = await this.getEntry(walker);
62
+ if (!walkerValue) {
63
+ throw new Error("Missing walker value");
64
+ }
65
+ if (walkerValue.hash === value.hash) {
66
+ return; // already exist!
67
+ }
68
+ if (this._sortFn(walkerValue, value) < 0) {
69
+ break;
70
+ }
71
+ last = walker;
72
+ walker = walker.next;
73
+ continue;
74
+ }
75
+ this._byteLength += value._payload.byteLength;
76
+ if (!value.hash) {
77
+ throw new Error("Unexpected");
78
+ }
79
+ _insertAfter(this._values, last, {
80
+ byteLength: value._payload.byteLength,
81
+ gid: value.gid,
82
+ hash: value.hash,
83
+ });
84
+ }
85
+ async delete(value) {
86
+ const hash = typeof value === "string" ? value : value.hash;
87
+ // Assume we want to delete at tail (or somwhere close)
88
+ let walker = this._values.tail;
89
+ while (walker) {
90
+ const walkerValue = await this.getEntry(walker);
91
+ if (!walkerValue) {
92
+ throw new Error("Missing walker value");
93
+ }
94
+ if (walkerValue.hash === hash) {
95
+ this._values.removeNode(walker);
96
+ this._byteLength -= walkerValue._payload.byteLength;
97
+ return;
98
+ }
99
+ walker = walker.prev; // prev will be undefined if you do removeNode(walker)
100
+ }
101
+ throw new Error("Failed to delete, entry does not exist" +
102
+ " ??? " +
103
+ this.length +
104
+ " ??? " +
105
+ hash);
106
+ }
107
+ deleteNode(node) {
108
+ this._values.removeNode(node);
109
+ this._byteLength -= node.value.byteLength;
110
+ return;
111
+ }
112
+ pop() {
113
+ const value = this._values.pop();
114
+ if (value) {
115
+ this._byteLength -= value.byteLength;
116
+ }
117
+ return value;
118
+ }
119
+ get byteLength() {
120
+ return this._byteLength;
121
+ }
122
+ async getEntry(node) {
123
+ return this._entryIndex.get(node.value.hash);
124
+ }
125
+ }
126
+ function _insertAfter(self, node, value) {
127
+ const inserted = !node
128
+ ? new yallist.Node(value, null, self.head, self)
129
+ : new yallist.Node(value, node, node.next, self);
130
+ // is tail
131
+ if (inserted.next === null) {
132
+ self.tail = inserted;
133
+ }
134
+ // is head
135
+ if (inserted.prev === null) {
136
+ self.head = inserted;
137
+ }
138
+ self.length++;
139
+ return inserted;
140
+ }
141
+ //# sourceMappingURL=values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.js","sourceRoot":"","sources":["../../src/values.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@peerbit/log",
3
+ "version": "1.0.2",
4
+ "description": "Append-only log CRDT",
5
+ "author": "dao.xyz",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "module": "lib/esm/index.js",
9
+ "types": "lib/esm/index.d.ts",
10
+ "exports": {
11
+ "import": "./lib/esm/index.js",
12
+ "require": "./lib/cjs/index.js"
13
+ },
14
+ "files": [
15
+ "lib",
16
+ "src",
17
+ "!src/**/__tests__",
18
+ "!lib/**/__tests__",
19
+ "LICENSE"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "engines": {
25
+ "node": ">=16.15.1"
26
+ },
27
+ "keywords": [
28
+ "ipfs",
29
+ "log",
30
+ "crdts",
31
+ "crdt"
32
+ ],
33
+ "dependencies": {
34
+ "@dao-xyz/borsh": "^5.1.5",
35
+ "@peerbit/blocks-interface": "^1.0.1",
36
+ "@peerbit/cache": "1.0.0",
37
+ "@peerbit/crypto": "1.0.1",
38
+ "@peerbit/logger": "1.0.0",
39
+ "libp2p": "^0.45.9",
40
+ "p-queue": "^7.3.3",
41
+ "path-browserify": "^1.0.1",
42
+ "yallist": "^4.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@peerbit/test-utils": "1.0.2",
46
+ "@types/yallist": "^4.0.1",
47
+ "assert": "^2.0.0",
48
+ "json-stringify-deterministic": "^1.0.7"
49
+ },
50
+ "contributors": [
51
+ "haadcode",
52
+ "aphelionz",
53
+ "shamb0t",
54
+ "thiagodelgado111",
55
+ "mistakia",
56
+ "satazor",
57
+ "RichardLitt",
58
+ "greenkeeperio-bot",
59
+ "chrisdostert",
60
+ "zachferland",
61
+ "kaibakker",
62
+ "dignifiedquire",
63
+ "adam-palazzo"
64
+ ],
65
+ "scripts": {
66
+ "clean": "shx rm -rf lib/*",
67
+ "build": "yarn clean && tsc -p tsconfig.json",
68
+ "postbuild": "echo '{\"type\":\"module\"} ' | node ../../node_modules/.bin/json > lib/esm/package.json",
69
+ "test": "node --experimental-vm-modules ./../../node_modules/.bin/jest test -c ../../jest.config.ts --runInBand --forceExit",
70
+ "test:unit": "node --experimental-vm-modules ../../node_modules/.bin/jest test -c ../../jest.config.unit.ts --runInBand --forceExit",
71
+ "test:integration": "node --experimental-vm-modules ../node_modules/.bin/jest test -c ../../jest.config.integration.ts --runInBand --forceExit",
72
+ "benchmark": "nyc --require ts-node/register benchmark-runner -r -b --baselineLimit 1000",
73
+ "benchmark:stress": "benchmark-runner -r --grep stress"
74
+ },
75
+ "localMaintainers": [
76
+ "dao.xyz"
77
+ ],
78
+ "gitHead": "595db9f1efebf604393eddfff5f678f5d8f16142"
79
+ }
package/src/change.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Entry } from "./entry";
2
+ export type Change<T> = { added: Entry<T>[]; removed: Entry<T>[] };
package/src/clock.ts ADDED
@@ -0,0 +1,280 @@
1
+ /**
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2021 Martin Heidegger
5
+ Copyright (c) 2022 dao.xyz
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
24
+ */
25
+
26
+ import { field, variant } from "@dao-xyz/borsh";
27
+ import { compare, equals } from "@peerbit/uint8arrays";
28
+ import hrtime from "./hrtime.js";
29
+
30
+ const hrTimeNow = hrtime.bigint();
31
+ const startTime = BigInt(Date.now()) * BigInt(1e6) - hrTimeNow;
32
+ const bigintTime = () => startTime + hrtime.bigint();
33
+
34
+ export function fromBits(low, high, unsigned, target?) {
35
+ if (target === undefined || target === null) {
36
+ return {
37
+ low: low | 0,
38
+ high: high | 0,
39
+ unsigned: !!unsigned,
40
+ };
41
+ }
42
+ target.low = low | 0;
43
+ target.high = high | 0;
44
+ target.unsigned = !!unsigned;
45
+ return target;
46
+ }
47
+
48
+ const n1e6 = BigInt(1e6);
49
+ const UINT64_MAX = 18446744073709551615n;
50
+ const UINT32_MAX = 0xffffffff;
51
+
52
+ function bigIntCoerce(input, fallback) {
53
+ if (typeof input === "bigint") return input;
54
+ if (typeof input === "number" || typeof input === "string")
55
+ return BigInt(input);
56
+ return fallback;
57
+ }
58
+
59
+ @variant(0)
60
+ export class Timestamp {
61
+ @field({ type: "u64" })
62
+ wallTime: bigint;
63
+
64
+ @field({ type: "u32" })
65
+ logical: number;
66
+
67
+ constructor(properties?: { wallTime: bigint; logical?: number }) {
68
+ if (properties) {
69
+ this.wallTime = properties.wallTime;
70
+ this.logical = properties.logical || 0;
71
+ }
72
+ }
73
+
74
+ static compare(a: Timestamp, b: Timestamp) {
75
+ if (a.wallTime > b.wallTime) return 1;
76
+ if (a.wallTime < b.wallTime) return -1;
77
+ if (a.logical > b.logical) return 1;
78
+ if (a.logical < b.logical) return -1;
79
+ return 0;
80
+ }
81
+
82
+ static bigger(a: Timestamp, b: Timestamp) {
83
+ return a.compare(b) === -1 ? b : a;
84
+ }
85
+ static smaller(a: Timestamp, b: Timestamp) {
86
+ return a.compare(b) === 1 ? b : a;
87
+ }
88
+
89
+ compare(other: Timestamp) {
90
+ return Timestamp.compare(this, other);
91
+ }
92
+ clone(): Timestamp {
93
+ return new Timestamp({
94
+ wallTime: this.wallTime,
95
+ logical: this.logical,
96
+ });
97
+ }
98
+
99
+ toString() {
100
+ return `Timestamp: wallTime: ${this.wallTime}, logical: ${this.logical}`;
101
+ }
102
+ }
103
+
104
+ export class HLC {
105
+ maxOffset: bigint;
106
+ wallTimeUpperBound: bigint;
107
+ toleratedForwardClockJump: bigint;
108
+ last: Timestamp;
109
+ wallTime: () => bigint;
110
+ constructor(
111
+ properties: {
112
+ wallTime?: () => bigint;
113
+ maxOffset?: bigint;
114
+ wallTimeUpperBound?: bigint;
115
+ toleratedForwardClockJump?: bigint;
116
+ last?: Timestamp;
117
+ } = {}
118
+ ) {
119
+ this.wallTime = properties.wallTime || bigintTime;
120
+ this.maxOffset = bigIntCoerce(properties.maxOffset, 0n);
121
+ this.wallTimeUpperBound = bigIntCoerce(properties.wallTimeUpperBound, 0n);
122
+ this.toleratedForwardClockJump = bigIntCoerce(
123
+ properties.toleratedForwardClockJump,
124
+ 0n
125
+ );
126
+ this.last = new Timestamp({ wallTime: this.wallTime() });
127
+ if (properties.last) {
128
+ this.last = Timestamp.bigger(properties.last, this.last);
129
+ }
130
+ }
131
+
132
+ now() {
133
+ return this.update(this.last);
134
+ }
135
+
136
+ validateOffset(offset: bigint) {
137
+ if (
138
+ this.toleratedForwardClockJump > 0n &&
139
+ -offset > this.toleratedForwardClockJump
140
+ ) {
141
+ throw new ForwardJumpError(-offset, this.toleratedForwardClockJump);
142
+ }
143
+ if (this.maxOffset > 0n && offset > this.maxOffset) {
144
+ throw new ClockOffsetError(offset, this.maxOffset);
145
+ }
146
+ }
147
+
148
+ update(other: Timestamp) {
149
+ const last = Timestamp.bigger(other, this.last);
150
+ let wallTime = this.wallTime();
151
+ const offset = last.wallTime - wallTime;
152
+ this.validateOffset(offset);
153
+ let logical: number;
154
+ if (offset < 0n) {
155
+ logical = 0;
156
+ } else {
157
+ wallTime = last.wallTime;
158
+ logical = last.logical + 1;
159
+ if (logical > UINT32_MAX) {
160
+ wallTime += 1n;
161
+ logical = 0;
162
+ }
163
+ }
164
+ const maxWallTime =
165
+ this.wallTimeUpperBound > 0n ? this.wallTimeUpperBound : UINT64_MAX;
166
+ if (wallTime > maxWallTime) {
167
+ throw new WallTimeOverflowError(wallTime, maxWallTime);
168
+ }
169
+ this.last = new Timestamp({ wallTime, logical });
170
+ return this.last;
171
+ }
172
+ }
173
+
174
+ export class ClockOffsetError extends Error {
175
+ offset: bigint;
176
+ maxOffset: bigint;
177
+ constructor(offset: bigint, maxOffset: bigint) {
178
+ super(
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.`
184
+ );
185
+ this.offset = offset;
186
+ this.maxOffset = maxOffset;
187
+ }
188
+ }
189
+
190
+ export class WallTimeOverflowError extends Error {
191
+ time: bigint;
192
+ maxTime: bigint;
193
+ constructor(time: bigint, maxTime: bigint) {
194
+ super(
195
+ `The wall time ${time / n1e6}ms exceeds the max time of ${
196
+ maxTime / n1e6
197
+ }ms.`
198
+ );
199
+ this.time = time;
200
+ this.maxTime = maxTime;
201
+ }
202
+ }
203
+
204
+ export class ForwardJumpError extends Error {
205
+ timejump: bigint;
206
+ tolerance: bigint;
207
+ constructor(timejump: bigint, tolerance: bigint) {
208
+ super(
209
+ `Detected a forward time jump of ${
210
+ timejump / n1e6
211
+ }ms, which exceed the allowed tolerance of ${tolerance / n1e6}ms.`
212
+ );
213
+ this.timejump = timejump;
214
+ this.tolerance = tolerance;
215
+ }
216
+ }
217
+
218
+ @variant(0)
219
+ export class LamportClock {
220
+ @field({ type: Uint8Array })
221
+ id: Uint8Array;
222
+
223
+ @field({ type: Timestamp })
224
+ timestamp: Timestamp;
225
+
226
+ constructor(properties?: { id: Uint8Array; timestamp?: Timestamp | number }) {
227
+ if (properties) {
228
+ this.id = properties.id;
229
+ if (!properties.timestamp) {
230
+ this.timestamp = new Timestamp({
231
+ wallTime: bigintTime(),
232
+ logical: 0,
233
+ });
234
+ } else {
235
+ if (typeof properties.timestamp === "number") {
236
+ this.timestamp = new Timestamp({
237
+ wallTime: bigintTime(),
238
+ logical: properties.timestamp,
239
+ });
240
+ } else {
241
+ this.timestamp = properties.timestamp;
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ clone() {
248
+ return new LamportClock({
249
+ id: this.id,
250
+ timestamp: this.timestamp.clone(),
251
+ });
252
+ }
253
+
254
+ equals(other: LamportClock): boolean {
255
+ return (
256
+ equals(this.id, other.id) && this.timestamp.compare(other.timestamp) === 0
257
+ );
258
+ }
259
+
260
+ /**
261
+ * Not optimized, dont use for performance critical things
262
+ * @returns
263
+ */
264
+ advance() {
265
+ const h = new HLC();
266
+ h.update(new Timestamp(this.timestamp));
267
+ return new LamportClock({ id: this.id, timestamp: h.now() });
268
+ }
269
+
270
+ static compare(a: LamportClock, b: LamportClock) {
271
+ // Calculate the "distance" based on the clock, ie. lower or greater
272
+
273
+ const timestamp = a.timestamp.compare(b.timestamp);
274
+ if (timestamp !== 0) return timestamp;
275
+
276
+ // If the sequence number is the same (concurrent events),
277
+ // and the IDs are different, take the one with a "lower" id
278
+ return compare(a.id, b.id);
279
+ }
280
+ }
@@ -0,0 +1,22 @@
1
+ export const difference = (a: any, b: any, key: string) => {
2
+ // Indices for quick lookups
3
+ const processed: { [key: string]: any } = {};
4
+ const existing: { [key: string]: any } = {};
5
+
6
+ // Create an index of the first collection
7
+ const addToIndex = (e: any) => (existing[key ? e[key] : e] = true);
8
+ a.forEach(addToIndex);
9
+
10
+ // Reduce to entries that are not in the first collection
11
+ const reducer = (res: any, entry: any) => {
12
+ const isInFirst = existing[key ? entry[key] : entry] !== undefined;
13
+ const hasBeenProcessed = processed[key ? entry[key] : entry] !== undefined;
14
+ if (!isInFirst && !hasBeenProcessed) {
15
+ res.push(entry);
16
+ processed[key ? entry[key] : entry] = true;
17
+ }
18
+ return res;
19
+ };
20
+
21
+ return b.reduce(reducer, []);
22
+ };
@@ -0,0 +1,27 @@
1
+ import { AbstractType, deserialize, serialize } from "@dao-xyz/borsh";
2
+
3
+ export interface Encoding<T> {
4
+ encoder: (data: T) => Uint8Array;
5
+ decoder: (bytes: Uint8Array) => T;
6
+ }
7
+ export const NO_ENCODING: Encoding<any> = {
8
+ encoder: (obj: Uint8Array) => {
9
+ if (obj instanceof Uint8Array === false) {
10
+ throw new Error(
11
+ "With NO_ENCODING only Uint8arrays are allowed, recieved: " +
12
+ (typeof obj === "object" ? obj.constructor.name : typeof obj)
13
+ );
14
+ }
15
+ return obj;
16
+ },
17
+ decoder: (bytes: Uint8Array) => {
18
+ return bytes;
19
+ },
20
+ };
21
+
22
+ export const BORSH_ENCODING = <T>(clazz: AbstractType<T>): Encoding<T> => {
23
+ return {
24
+ decoder: (bytes: Uint8Array) => deserialize(bytes, clazz),
25
+ encoder: (data: any) => serialize(data),
26
+ };
27
+ };
@@ -0,0 +1,78 @@
1
+ import { Cache } from "@peerbit/cache";
2
+ import { Entry } from "./entry.js";
3
+ import { deserialize } from "@dao-xyz/borsh";
4
+ import { logger } from "./logger.js";
5
+ import { Blocks } from "@peerbit/blocks-interface";
6
+
7
+ export class EntryIndex<T> {
8
+ _cache: Cache<Entry<T> | null>;
9
+ _store: Blocks;
10
+ _init: (entry: Entry<T>) => void;
11
+ _index: Set<string>;
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._store = properties.store;
20
+ this._init = properties.init;
21
+ this._index = new Set();
22
+ }
23
+
24
+ async set(v: Entry<T>, toMultihash = true) {
25
+ if (toMultihash) {
26
+ const existingHash = v.hash;
27
+ v.hash = undefined as any;
28
+ try {
29
+ const hash = await Entry.toMultihash(this._store, 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) {
34
+ logger.error("Head hash didn't match the contents");
35
+ throw new Error("Head hash didn't match the contents");
36
+ }
37
+ } catch (error) {
38
+ logger.error(error);
39
+ throw error;
40
+ }
41
+ }
42
+ this._cache.add(v.hash, v);
43
+ this._index.add(v.hash);
44
+ }
45
+
46
+ async get(
47
+ k: string,
48
+ options?: { replicate?: boolean; timeout?: number }
49
+ ): Promise<Entry<T> | undefined> {
50
+ if (this._index.has(k)) {
51
+ let mem = this._cache.get(k);
52
+ if (mem === undefined) {
53
+ mem = await this.getFromStore(k, options);
54
+ if (mem) {
55
+ this._init(mem);
56
+ mem.hash = k;
57
+ }
58
+ this._cache.add(k, mem);
59
+ }
60
+ return mem ? mem : undefined;
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ private async getFromStore(
66
+ k: string,
67
+ options?: { replicate?: boolean; timeout?: number }
68
+ ): Promise<Entry<T> | null> {
69
+ const value = await this._store.get(k, options);
70
+ return value ? deserialize(value, Entry) : null;
71
+ }
72
+
73
+ async delete(k: string) {
74
+ this._cache.del(k);
75
+ this._index.delete(k);
76
+ return this._store.rm(k);
77
+ }
78
+ }
@@ -0,0 +1,6 @@
1
+ import { Entry } from "./entry.js";
2
+
3
+ export interface EntryWithRefs<T> {
4
+ entry: Entry<T>;
5
+ references: Entry<T>[];
6
+ }