@peerbit/log 5.0.10 → 6.0.0-b712c6b
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/src/entry-index.d.ts +7 -0
- package/dist/src/entry-index.d.ts.map +1 -1
- package/dist/src/entry-index.js +32 -1
- package/dist/src/entry-index.js.map +1 -1
- package/dist/src/entry.d.ts +2 -7
- package/dist/src/entry.d.ts.map +1 -1
- package/dist/src/entry.js.map +1 -1
- package/dist/src/log.d.ts +5 -6
- package/dist/src/log.d.ts.map +1 -1
- package/dist/src/log.js +114 -80
- package/dist/src/log.js.map +1 -1
- package/package.json +17 -17
- package/src/entry-index.ts +42 -2
- package/src/entry.ts +2 -2
- package/src/log.ts +158 -108
package/src/log.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { deserialize, field, fixedArray, variant } from "@dao-xyz/borsh";
|
|
2
2
|
import { type AnyStore } from "@peerbit/any-store";
|
|
3
|
-
import { type Blocks, cidifyString } from "@peerbit/blocks-interface";
|
|
3
|
+
import { type Blocks, type GetOptions, cidifyString } from "@peerbit/blocks-interface";
|
|
4
4
|
import {
|
|
5
5
|
type Identity,
|
|
6
6
|
SignatureWithKey,
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
sha256Base64Sync,
|
|
10
10
|
} from "@peerbit/crypto";
|
|
11
11
|
import { type Indices } from "@peerbit/indexer-interface";
|
|
12
|
-
import { create } from "@peerbit/indexer-sqlite3";
|
|
13
12
|
import { type CryptoKeychain } from "@peerbit/keychain";
|
|
14
13
|
import { type Change } from "./change.js";
|
|
15
14
|
import {
|
|
@@ -38,6 +37,16 @@ import { Trim, type TrimOptions } from "./trim.js";
|
|
|
38
37
|
|
|
39
38
|
const { LastWriteWins } = Sorting;
|
|
40
39
|
|
|
40
|
+
type CreateSqliteIndexer = typeof import("@peerbit/indexer-sqlite3").create;
|
|
41
|
+
let sqliteCreate: CreateSqliteIndexer | undefined;
|
|
42
|
+
const createDefaultIndexer = async (): Promise<Indices> => {
|
|
43
|
+
if (!sqliteCreate) {
|
|
44
|
+
const mod = await import("@peerbit/indexer-sqlite3");
|
|
45
|
+
sqliteCreate = mod.create;
|
|
46
|
+
}
|
|
47
|
+
return sqliteCreate();
|
|
48
|
+
};
|
|
49
|
+
|
|
41
50
|
export type LogEvents<T> = {
|
|
42
51
|
onChange?: (change: Change<T> /* , reference?: R */) => void;
|
|
43
52
|
onGidRemoved?: (gids: string[]) => Promise<void> | void;
|
|
@@ -55,6 +64,10 @@ export type LogProperties<T> = {
|
|
|
55
64
|
sortFn?: Sorting.SortFn;
|
|
56
65
|
trim?: TrimOptions;
|
|
57
66
|
canAppend?: CanAppend<T>;
|
|
67
|
+
resolveRemotePeers?: (
|
|
68
|
+
hash: string,
|
|
69
|
+
options?: { signal?: AbortSignal },
|
|
70
|
+
) => Promise<string[] | undefined> | string[] | undefined;
|
|
58
71
|
};
|
|
59
72
|
|
|
60
73
|
export type LogOptions<T> = LogProperties<T> & LogEvents<T> & MemoryProperties;
|
|
@@ -159,13 +172,21 @@ export class Log<T> {
|
|
|
159
172
|
|
|
160
173
|
this._closeController = new AbortController();
|
|
161
174
|
|
|
162
|
-
const {
|
|
175
|
+
const {
|
|
176
|
+
encoding,
|
|
177
|
+
trim,
|
|
178
|
+
keychain,
|
|
179
|
+
indexer,
|
|
180
|
+
onGidRemoved,
|
|
181
|
+
sortFn,
|
|
182
|
+
resolveRemotePeers,
|
|
183
|
+
} = options;
|
|
163
184
|
|
|
164
185
|
// TODO do correctly with tie breaks
|
|
165
186
|
this._sortFn = sortFn || LastWriteWins;
|
|
166
187
|
|
|
167
188
|
this._storage = store;
|
|
168
|
-
this._indexer = indexer || (await
|
|
189
|
+
this._indexer = indexer || (await createDefaultIndexer());
|
|
169
190
|
await this._indexer.start?.();
|
|
170
191
|
|
|
171
192
|
this._encoding = encoding || NO_ENCODING;
|
|
@@ -194,6 +215,7 @@ export class Log<T> {
|
|
|
194
215
|
).init({ schema: ShallowEntry }),
|
|
195
216
|
publicKey: this._identity.publicKey,
|
|
196
217
|
sort: this._sortFn,
|
|
218
|
+
resolveRemotePeers,
|
|
197
219
|
});
|
|
198
220
|
await this._entryIndex.init();
|
|
199
221
|
/* this._values = new Values(this._entryIndex, this._sortFn); */
|
|
@@ -357,23 +379,15 @@ export class Log<T> {
|
|
|
357
379
|
* Get an entry.
|
|
358
380
|
* @param {string} [hash] The hashes of the entry
|
|
359
381
|
*/
|
|
360
|
-
get(
|
|
361
|
-
hash: string,
|
|
362
|
-
options?: { remote?: { timeout?: number } | boolean },
|
|
363
|
-
): Promise<Entry<T> | undefined> {
|
|
382
|
+
get(hash: string, options?: GetOptions): Promise<Entry<T> | undefined> {
|
|
364
383
|
return this._entryIndex.get(
|
|
365
384
|
hash,
|
|
366
385
|
options
|
|
367
386
|
? {
|
|
368
387
|
type: "full",
|
|
369
|
-
remote: options
|
|
370
|
-
timeout:
|
|
371
|
-
typeof options?.remote !== "boolean"
|
|
372
|
-
? options.remote.timeout
|
|
373
|
-
: undefined,
|
|
374
|
-
},
|
|
388
|
+
remote: options.remote,
|
|
375
389
|
ignoreMissing: true, // always return undefined instead of throwing errors on missing entries
|
|
376
|
-
|
|
390
|
+
}
|
|
377
391
|
: { type: "full", ignoreMissing: true },
|
|
378
392
|
);
|
|
379
393
|
}
|
|
@@ -629,103 +643,128 @@ export class Log<T> {
|
|
|
629
643
|
},
|
|
630
644
|
): Promise<void> {
|
|
631
645
|
let entries: Entry<T>[];
|
|
632
|
-
|
|
646
|
+
const references: Map<string, Entry<T>> = new Map();
|
|
647
|
+
|
|
648
|
+
const fromCache = new Map<string, string[] | null>();
|
|
649
|
+
const resolveRemoteFrom = async (hash: string, signal?: AbortSignal) => {
|
|
650
|
+
const cached = fromCache.get(hash);
|
|
651
|
+
if (cached !== undefined) return cached === null ? undefined : cached;
|
|
652
|
+
|
|
653
|
+
let from: string[] | undefined;
|
|
654
|
+
try {
|
|
655
|
+
from = await this.entryIndex.properties.resolveRemotePeers?.(hash, { signal });
|
|
656
|
+
} catch {
|
|
657
|
+
from = undefined;
|
|
658
|
+
}
|
|
659
|
+
const normalized = from && from.length > 0 ? from : undefined;
|
|
660
|
+
fromCache.set(hash, normalized ?? null);
|
|
661
|
+
return normalized;
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const remote: NonNullable<Exclude<GetOptions["remote"], boolean>> = {
|
|
665
|
+
timeout: options?.timeout,
|
|
666
|
+
signal: this._closeController.signal,
|
|
667
|
+
};
|
|
633
668
|
|
|
634
669
|
if (entriesOrLog instanceof Log) {
|
|
635
670
|
if (entriesOrLog.entryIndex.length === 0) return;
|
|
636
671
|
entries = await entriesOrLog.toArray();
|
|
637
|
-
for (const element of entries)
|
|
638
|
-
references.set(element.hash, element);
|
|
639
|
-
}
|
|
672
|
+
for (const element of entries) references.set(element.hash, element);
|
|
640
673
|
} else if (Array.isArray(entriesOrLog)) {
|
|
641
|
-
if (entriesOrLog.length === 0)
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
674
|
+
if (entriesOrLog.length === 0) return;
|
|
644
675
|
|
|
645
676
|
entries = [];
|
|
646
677
|
for (const element of entriesOrLog) {
|
|
647
678
|
if (element instanceof Entry) {
|
|
648
679
|
entries.push(element);
|
|
649
680
|
references.set(element.hash, element);
|
|
650
|
-
|
|
651
|
-
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (typeof element === "string") {
|
|
685
|
+
if ((await this.entryIndex.getShallow(element)) != null && !options?.reset) {
|
|
652
686
|
continue; // already in log
|
|
653
687
|
}
|
|
654
688
|
|
|
655
|
-
|
|
689
|
+
const from = await resolveRemoteFrom(element, this._closeController.signal);
|
|
690
|
+
const entry = await Entry.fromMultihash<T>(this._storage, element, {
|
|
656
691
|
remote: {
|
|
657
|
-
timeout:
|
|
692
|
+
timeout: remote.timeout,
|
|
693
|
+
signal: remote.signal,
|
|
694
|
+
...(from && from.length > 0 ? { from } : {}),
|
|
658
695
|
},
|
|
659
696
|
});
|
|
660
|
-
if (!entry) {
|
|
661
|
-
throw new Error("Missing entry in join by hash: " + element);
|
|
662
|
-
}
|
|
663
697
|
entries.push(entry);
|
|
664
|
-
|
|
665
|
-
|
|
698
|
+
references.set(entry.hash, entry);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (element instanceof ShallowEntry) {
|
|
703
|
+
if (
|
|
704
|
+
(await this.entryIndex.getShallow(element.hash)) != null &&
|
|
705
|
+
!options?.reset
|
|
706
|
+
) {
|
|
666
707
|
continue; // already in log
|
|
667
708
|
}
|
|
668
709
|
|
|
669
|
-
|
|
670
|
-
this._storage,
|
|
710
|
+
const from = await resolveRemoteFrom(
|
|
671
711
|
element.hash,
|
|
672
|
-
|
|
673
|
-
remote: {
|
|
674
|
-
timeout: options?.timeout,
|
|
675
|
-
},
|
|
676
|
-
},
|
|
712
|
+
this._closeController.signal,
|
|
677
713
|
);
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
714
|
+
const entry = await Entry.fromMultihash<T>(this._storage, element.hash, {
|
|
715
|
+
remote: {
|
|
716
|
+
timeout: remote.timeout,
|
|
717
|
+
signal: remote.signal,
|
|
718
|
+
...(from && from.length > 0 ? { from } : {}),
|
|
719
|
+
},
|
|
720
|
+
});
|
|
681
721
|
entries.push(entry);
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
722
|
+
references.set(entry.hash, entry);
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
685
725
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
726
|
+
entries.push(element.entry);
|
|
727
|
+
references.set(element.entry.hash, element.entry);
|
|
728
|
+
for (const ref of element.references) {
|
|
729
|
+
references.set(ref.hash, ref);
|
|
689
730
|
}
|
|
690
731
|
}
|
|
691
732
|
} else {
|
|
692
|
-
|
|
693
|
-
if (all.length === 0)
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
|
|
733
|
+
const all = await entriesOrLog.all(); // TODO dont load all at once
|
|
734
|
+
if (all.length === 0) return;
|
|
697
735
|
entries = all;
|
|
698
736
|
}
|
|
699
737
|
|
|
700
|
-
|
|
738
|
+
const heads: Map<string, boolean> = new Map();
|
|
701
739
|
for (const entry of entries) {
|
|
702
|
-
if (heads.has(entry.hash))
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
740
|
+
if (heads.has(entry.hash)) continue;
|
|
705
741
|
heads.set(entry.hash, true);
|
|
706
|
-
for (const next of await entry.getNext())
|
|
707
|
-
heads.set(next, false);
|
|
708
|
-
}
|
|
742
|
+
for (const next of await entry.getNext()) heads.set(next, false);
|
|
709
743
|
}
|
|
710
744
|
|
|
711
745
|
for (const entry of entries) {
|
|
712
|
-
|
|
713
|
-
|
|
746
|
+
const isHead = heads.get(entry.hash)!;
|
|
747
|
+
const prev = this._joining.get(entry.hash);
|
|
714
748
|
if (prev) {
|
|
715
749
|
await prev;
|
|
716
|
-
|
|
717
|
-
const p = this.joinRecursively(entry, {
|
|
718
|
-
references,
|
|
719
|
-
isHead,
|
|
720
|
-
...options,
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
this._joining.set(entry.hash, p);
|
|
724
|
-
p.finally(() => {
|
|
725
|
-
this._joining.delete(entry.hash);
|
|
726
|
-
});
|
|
727
|
-
await p;
|
|
750
|
+
continue;
|
|
728
751
|
}
|
|
752
|
+
|
|
753
|
+
const p = this.joinRecursively(entry, {
|
|
754
|
+
references,
|
|
755
|
+
isHead,
|
|
756
|
+
reset: options?.reset,
|
|
757
|
+
verifySignatures: options?.verifySignatures,
|
|
758
|
+
trim: options?.trim,
|
|
759
|
+
onChange: options?.onChange,
|
|
760
|
+
remote,
|
|
761
|
+
resolveRemoteFrom,
|
|
762
|
+
});
|
|
763
|
+
this._joining.set(entry.hash, p);
|
|
764
|
+
p.finally(() => {
|
|
765
|
+
this._joining.delete(entry.hash);
|
|
766
|
+
});
|
|
767
|
+
await p;
|
|
729
768
|
}
|
|
730
769
|
}
|
|
731
770
|
|
|
@@ -746,12 +785,14 @@ export class Log<T> {
|
|
|
746
785
|
isHead: boolean;
|
|
747
786
|
reset?: boolean;
|
|
748
787
|
onChange?: OnChange<T>;
|
|
749
|
-
remote?:
|
|
750
|
-
|
|
751
|
-
|
|
788
|
+
remote?: GetOptions["remote"];
|
|
789
|
+
resolveRemoteFrom?: (
|
|
790
|
+
hash: string,
|
|
791
|
+
signal?: AbortSignal,
|
|
792
|
+
) => Promise<string[] | undefined>;
|
|
752
793
|
},
|
|
753
794
|
): Promise<boolean> {
|
|
754
|
-
if (this.entryIndex.length > (options
|
|
795
|
+
if (this.entryIndex.length > (options.length ?? Number.MAX_SAFE_INTEGER)) {
|
|
755
796
|
return false;
|
|
756
797
|
}
|
|
757
798
|
|
|
@@ -759,17 +800,15 @@ export class Log<T> {
|
|
|
759
800
|
throw new Error("Unexpected");
|
|
760
801
|
}
|
|
761
802
|
|
|
762
|
-
if ((await this.
|
|
803
|
+
if ((await this.entryIndex.getShallow(entry.hash)) != null && !options.reset) {
|
|
763
804
|
return false;
|
|
764
805
|
}
|
|
765
806
|
|
|
766
807
|
entry.init(this);
|
|
767
808
|
|
|
768
|
-
if (options
|
|
809
|
+
if (options.verifySignatures) {
|
|
769
810
|
if (!(await entry.verifySignatures())) {
|
|
770
|
-
throw new Error(
|
|
771
|
-
'Invalid signature entry with hash "' + entry.hash + '"',
|
|
772
|
-
);
|
|
811
|
+
throw new Error(`Invalid signature entry with hash "${entry.hash}"`);
|
|
773
812
|
}
|
|
774
813
|
}
|
|
775
814
|
|
|
@@ -791,34 +830,45 @@ export class Log<T> {
|
|
|
791
830
|
}
|
|
792
831
|
|
|
793
832
|
if (entry.meta.type !== EntryType.CUT) {
|
|
833
|
+
const remote =
|
|
834
|
+
options.remote && typeof options.remote === "object"
|
|
835
|
+
? options.remote
|
|
836
|
+
: undefined;
|
|
837
|
+
|
|
794
838
|
for (const a of entry.meta.next) {
|
|
795
839
|
const prev = this._joining.get(a);
|
|
796
840
|
if (prev) {
|
|
797
841
|
await prev;
|
|
798
|
-
|
|
799
|
-
const nested =
|
|
800
|
-
options.references?.get(a) ||
|
|
801
|
-
(await Entry.fromMultihash<T>(this._storage, a, {
|
|
802
|
-
remote: { timeout: options?.remote?.timeout },
|
|
803
|
-
}));
|
|
804
|
-
if (!nested) {
|
|
805
|
-
throw new Error("Missing entry in joinRecursively: " + a);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
const p = this.joinRecursively(
|
|
809
|
-
nested,
|
|
810
|
-
options.isHead ? { ...options, isHead: false } : options,
|
|
811
|
-
);
|
|
812
|
-
this._joining.set(nested.hash, p);
|
|
813
|
-
p.finally(() => {
|
|
814
|
-
this._joining.delete(nested.hash);
|
|
815
|
-
});
|
|
816
|
-
await p;
|
|
842
|
+
continue;
|
|
817
843
|
}
|
|
844
|
+
if ((await this.entryIndex.getShallow(a)) != null && !options.reset) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const from = await options.resolveRemoteFrom?.(a, remote?.signal);
|
|
849
|
+
const nested =
|
|
850
|
+
options.references?.get(a) ??
|
|
851
|
+
(await Entry.fromMultihash<T>(this._storage, a, {
|
|
852
|
+
remote: {
|
|
853
|
+
timeout: remote?.timeout,
|
|
854
|
+
signal: remote?.signal,
|
|
855
|
+
...(from && from.length > 0 ? { from } : {}),
|
|
856
|
+
},
|
|
857
|
+
}));
|
|
858
|
+
|
|
859
|
+
const p = this.joinRecursively(
|
|
860
|
+
nested,
|
|
861
|
+
options.isHead ? { ...options, isHead: false } : options,
|
|
862
|
+
);
|
|
863
|
+
this._joining.set(nested.hash, p);
|
|
864
|
+
p.finally(() => {
|
|
865
|
+
this._joining.delete(nested.hash);
|
|
866
|
+
});
|
|
867
|
+
await p;
|
|
818
868
|
}
|
|
819
869
|
}
|
|
820
870
|
|
|
821
|
-
if (this
|
|
871
|
+
if (this._canAppend && !(await this._canAppend(entry))) {
|
|
822
872
|
return false;
|
|
823
873
|
}
|
|
824
874
|
|
|
@@ -835,23 +885,23 @@ export class Log<T> {
|
|
|
835
885
|
| PendingDelete<T>
|
|
836
886
|
| { entry: Entry<T>; fn: undefined }
|
|
837
887
|
)[] = await this.processEntry(entry);
|
|
838
|
-
const trimmed = await this.trim(options
|
|
888
|
+
const trimmed = await this.trim(options.trim);
|
|
839
889
|
|
|
840
890
|
if (trimmed) {
|
|
841
|
-
for (const
|
|
842
|
-
pendingDeletes.push({ entry, fn: undefined });
|
|
891
|
+
for (const removedEntry of trimmed) {
|
|
892
|
+
pendingDeletes.push({ entry: removedEntry, fn: undefined });
|
|
843
893
|
}
|
|
844
894
|
}
|
|
845
895
|
|
|
846
896
|
const removed = pendingDeletes.map((x) => x.entry);
|
|
847
897
|
|
|
848
|
-
await options
|
|
898
|
+
await options.onChange?.({
|
|
849
899
|
added: [{ head: options.isHead, entry }],
|
|
850
|
-
removed
|
|
900
|
+
removed,
|
|
851
901
|
});
|
|
852
902
|
await this._onChange?.({
|
|
853
903
|
added: [{ head: options.isHead, entry }],
|
|
854
|
-
removed
|
|
904
|
+
removed,
|
|
855
905
|
});
|
|
856
906
|
|
|
857
907
|
await Promise.all(pendingDeletes.map((x) => x.fn?.()));
|