@hashtree/nostr 0.1.3 → 0.1.5
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/README.md +51 -0
- package/dist/events.d.ts +66 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +426 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/resolver/nostr.d.ts +2 -0
- package/dist/resolver/nostr.d.ts.map +1 -1
- package/dist/resolver/nostr.js +122 -31
- package/dist/resolver/nostr.js.map +1 -1
- package/dist/snapshot.d.ts +30 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +157 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/treeEventSnapshots.d.ts +30 -0
- package/dist/treeEventSnapshots.d.ts.map +1 -0
- package/dist/treeEventSnapshots.js +186 -0
- package/dist/treeEventSnapshots.js.map +1 -0
- package/dist/webrtc/index.d.ts +3 -2
- package/dist/webrtc/index.d.ts.map +1 -1
- package/dist/webrtc/index.js +3 -2
- package/dist/webrtc/index.js.map +1 -1
- package/dist/webrtc/peer.d.ts +12 -1
- package/dist/webrtc/peer.d.ts.map +1 -1
- package/dist/webrtc/peer.js +39 -5
- package/dist/webrtc/peer.js.map +1 -1
- package/dist/webrtc/peerSelector.d.ts +31 -0
- package/dist/webrtc/peerSelector.d.ts.map +1 -0
- package/dist/webrtc/peerSelector.js +348 -0
- package/dist/webrtc/peerSelector.js.map +1 -0
- package/dist/webrtc/protocol.d.ts +15 -4
- package/dist/webrtc/protocol.d.ts.map +1 -1
- package/dist/webrtc/protocol.js +31 -17
- package/dist/webrtc/protocol.js.map +1 -1
- package/dist/webrtc/store.d.ts +31 -4
- package/dist/webrtc/store.d.ts.map +1 -1
- package/dist/webrtc/store.js +362 -99
- package/dist/webrtc/store.js.map +1 -1
- package/dist/webrtc/types.d.ts +66 -4
- package/dist/webrtc/types.d.ts.map +1 -1
- package/dist/webrtc/types.js +71 -9
- package/dist/webrtc/types.js.map +1 -1
- package/package.json +8 -2
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { HashTree, cid, fromHex } from '@hashtree/core';
|
|
2
|
+
const HEX_64 = /^[0-9a-f]{64}$/;
|
|
3
|
+
const HEX_128 = /^[0-9a-f]{128}$/;
|
|
4
|
+
const MAX_SNAPSHOT_BYTES = 256 * 1024;
|
|
5
|
+
const textEncoder = new TextEncoder();
|
|
6
|
+
const textDecoder = new TextDecoder();
|
|
7
|
+
function isSnapshotTreeLike(target) {
|
|
8
|
+
return (typeof target.putFile === 'function' &&
|
|
9
|
+
typeof target.readFileRange === 'function');
|
|
10
|
+
}
|
|
11
|
+
function getSnapshotTree(target) {
|
|
12
|
+
if (isSnapshotTreeLike(target)) {
|
|
13
|
+
return target;
|
|
14
|
+
}
|
|
15
|
+
return new HashTree({ store: target });
|
|
16
|
+
}
|
|
17
|
+
function assertStringTags(tags) {
|
|
18
|
+
if (!Array.isArray(tags)) {
|
|
19
|
+
throw new Error('Nostr event tags must be an array');
|
|
20
|
+
}
|
|
21
|
+
for (const tag of tags) {
|
|
22
|
+
if (!Array.isArray(tag) || tag.some((value) => typeof value !== 'string')) {
|
|
23
|
+
throw new Error('Nostr event tags must be an array of string arrays');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function uniqueLabels(labels) {
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
const result = [];
|
|
30
|
+
for (const label of labels) {
|
|
31
|
+
if (!label || seen.has(label))
|
|
32
|
+
continue;
|
|
33
|
+
seen.add(label);
|
|
34
|
+
result.push(label);
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
export function normalizeSignedNostrEvent(event) {
|
|
39
|
+
const { id, pubkey, created_at, kind, tags, content, sig } = event;
|
|
40
|
+
if (!HEX_64.test(id)) {
|
|
41
|
+
throw new Error('Nostr event id must be a lowercase 64-character hex string');
|
|
42
|
+
}
|
|
43
|
+
if (!HEX_64.test(pubkey)) {
|
|
44
|
+
throw new Error('Nostr event pubkey must be a lowercase 64-character hex string');
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isInteger(created_at) || created_at < 0) {
|
|
47
|
+
throw new Error('Nostr event created_at must be a non-negative integer');
|
|
48
|
+
}
|
|
49
|
+
if (!Number.isInteger(kind) || kind < 0) {
|
|
50
|
+
throw new Error('Nostr event kind must be a non-negative integer');
|
|
51
|
+
}
|
|
52
|
+
assertStringTags(tags);
|
|
53
|
+
if (typeof content !== 'string') {
|
|
54
|
+
throw new Error('Nostr event content must be a string');
|
|
55
|
+
}
|
|
56
|
+
if (!HEX_128.test(sig)) {
|
|
57
|
+
throw new Error('Nostr event sig must be a lowercase 128-character hex string');
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
id,
|
|
61
|
+
pubkey,
|
|
62
|
+
created_at,
|
|
63
|
+
kind,
|
|
64
|
+
tags: tags.map((tag) => [...tag]),
|
|
65
|
+
content,
|
|
66
|
+
sig,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function encodeSignedNostrEventJson(event) {
|
|
70
|
+
const normalized = normalizeSignedNostrEvent(event);
|
|
71
|
+
return textEncoder.encode(JSON.stringify({
|
|
72
|
+
id: normalized.id,
|
|
73
|
+
pubkey: normalized.pubkey,
|
|
74
|
+
created_at: normalized.created_at,
|
|
75
|
+
kind: normalized.kind,
|
|
76
|
+
tags: normalized.tags,
|
|
77
|
+
content: normalized.content,
|
|
78
|
+
sig: normalized.sig,
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
export function decodeSignedNostrEventJson(data) {
|
|
82
|
+
const parsed = JSON.parse(textDecoder.decode(data));
|
|
83
|
+
return normalizeSignedNostrEvent({
|
|
84
|
+
id: typeof parsed.id === 'string' ? parsed.id : '',
|
|
85
|
+
pubkey: typeof parsed.pubkey === 'string' ? parsed.pubkey : '',
|
|
86
|
+
created_at: typeof parsed.created_at === 'number' ? parsed.created_at : NaN,
|
|
87
|
+
kind: typeof parsed.kind === 'number' ? parsed.kind : NaN,
|
|
88
|
+
tags: parsed.tags,
|
|
89
|
+
content: typeof parsed.content === 'string' ? parsed.content : '',
|
|
90
|
+
sig: typeof parsed.sig === 'string' ? parsed.sig : '',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export async function storeSignedNostrEventSnapshot(target, event) {
|
|
94
|
+
const tree = getSnapshotTree(target);
|
|
95
|
+
return (await tree.putFile(encodeSignedNostrEventJson(event), { unencrypted: true })).cid;
|
|
96
|
+
}
|
|
97
|
+
export async function readSignedNostrEventSnapshot(target, snapshotCid, maxBytes = MAX_SNAPSHOT_BYTES) {
|
|
98
|
+
const tree = getSnapshotTree(target);
|
|
99
|
+
const bytes = await tree.readFileRange(snapshotCid, 0, maxBytes + 1);
|
|
100
|
+
if (!bytes) {
|
|
101
|
+
throw new Error('Signed Nostr event snapshot is missing');
|
|
102
|
+
}
|
|
103
|
+
if (bytes.length > maxBytes) {
|
|
104
|
+
throw new Error(`Signed Nostr event snapshot exceeds ${maxBytes} bytes`);
|
|
105
|
+
}
|
|
106
|
+
return decodeSignedNostrEventJson(bytes);
|
|
107
|
+
}
|
|
108
|
+
function hasLabel(event, label) {
|
|
109
|
+
return event.tags.some((tag) => tag[0] === 'l' && tag[1] === label);
|
|
110
|
+
}
|
|
111
|
+
function hasAnyLabel(event) {
|
|
112
|
+
return event.tags.some((tag) => tag[0] === 'l');
|
|
113
|
+
}
|
|
114
|
+
export function parseHashtreeRootEvent(event) {
|
|
115
|
+
const normalized = normalizeSignedNostrEvent(event);
|
|
116
|
+
if (normalized.kind !== 30078) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const treeName = normalized.tags.find((tag) => tag[0] === 'd')?.[1];
|
|
120
|
+
const hashHex = normalized.tags.find((tag) => tag[0] === 'hash')?.[1];
|
|
121
|
+
if (!treeName || !hashHex || !HEX_64.test(hashHex)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
if (hasAnyLabel(normalized) && !hasLabel(normalized, 'hashtree')) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const labels = uniqueLabels(normalized.tags
|
|
128
|
+
.filter((tag) => tag[0] === 'l' && typeof tag[1] === 'string' && tag[1].length > 0)
|
|
129
|
+
.map((tag) => tag[1]));
|
|
130
|
+
const keyHex = normalized.tags.find((tag) => tag[0] === 'key')?.[1];
|
|
131
|
+
const encryptedKey = normalized.tags.find((tag) => tag[0] === 'encryptedKey')?.[1];
|
|
132
|
+
const keyId = normalized.tags.find((tag) => tag[0] === 'keyId')?.[1];
|
|
133
|
+
const selfEncryptedKey = normalized.tags.find((tag) => tag[0] === 'selfEncryptedKey')?.[1];
|
|
134
|
+
const selfEncryptedLinkKey = normalized.tags.find((tag) => tag[0] === 'selfEncryptedLinkKey')?.[1];
|
|
135
|
+
const visibility = encryptedKey
|
|
136
|
+
? 'link-visible'
|
|
137
|
+
: selfEncryptedKey
|
|
138
|
+
? 'private'
|
|
139
|
+
: 'public';
|
|
140
|
+
try {
|
|
141
|
+
return {
|
|
142
|
+
event: normalized,
|
|
143
|
+
treeName,
|
|
144
|
+
rootCid: cid(fromHex(hashHex), visibility === 'public' && keyHex ? fromHex(keyHex) : undefined),
|
|
145
|
+
visibility,
|
|
146
|
+
labels,
|
|
147
|
+
encryptedKey,
|
|
148
|
+
keyId,
|
|
149
|
+
selfEncryptedKey,
|
|
150
|
+
selfEncryptedLinkKey,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CID } from '@hashtree/core';
|
|
2
|
+
import type { StoredNostrEvent } from './events.js';
|
|
3
|
+
import type { Nip19Like, NostrFilter } from './resolver/nostr.js';
|
|
4
|
+
import { type ParsedHashtreeRootEvent, type SnapshotTarget } from './snapshot.js';
|
|
5
|
+
export interface TreeEventSnapshotInfo extends ParsedHashtreeRootEvent {
|
|
6
|
+
snapshotCid: CID;
|
|
7
|
+
snapshotNhash: string;
|
|
8
|
+
npub: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TreeEventSnapshotQuery extends NostrFilter {
|
|
11
|
+
limit?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface FetchLatestTreeEventSnapshotConfig {
|
|
14
|
+
snapshotTarget: SnapshotTarget;
|
|
15
|
+
nip19: Nip19Like;
|
|
16
|
+
fetchEvents: (filter: TreeEventSnapshotQuery) => Promise<StoredNostrEvent[]>;
|
|
17
|
+
snapshotFetchLimit?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface WatchTreeEventSnapshotsConfig extends FetchLatestTreeEventSnapshotConfig {
|
|
20
|
+
subscribeEvents: (filter: TreeEventSnapshotQuery, onEvent: (event: StoredNostrEvent) => void) => () => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function compareTreeEventSnapshots(a: TreeEventSnapshotInfo, b: TreeEventSnapshotInfo): number;
|
|
23
|
+
export declare function isNewerTreeEventSnapshot(candidate: TreeEventSnapshotInfo, current: TreeEventSnapshotInfo): boolean;
|
|
24
|
+
export declare function snapshotMatchesRootCid(snapshot: TreeEventSnapshotInfo | null | undefined, rootCid: CID | null | undefined): boolean;
|
|
25
|
+
export declare function resolveSnapshotRootCid(snapshot: TreeEventSnapshotInfo, linkKey?: string | Uint8Array | null): Promise<CID | null>;
|
|
26
|
+
export declare function storeTreeEventSnapshot(snapshotTarget: SnapshotTarget, nip19: Nip19Like, event: StoredNostrEvent): Promise<TreeEventSnapshotInfo | null>;
|
|
27
|
+
export declare function readTreeEventSnapshot(snapshotTarget: SnapshotTarget, nip19: Nip19Like, snapshotCid: CID, maxBytes?: number): Promise<TreeEventSnapshotInfo | null>;
|
|
28
|
+
export declare function fetchLatestTreeEventSnapshot(config: FetchLatestTreeEventSnapshotConfig, npub: string, treeName: string): Promise<TreeEventSnapshotInfo | null>;
|
|
29
|
+
export declare function watchLatestTreeEventSnapshot(config: WatchTreeEventSnapshotsConfig, npub: string, treeName: string, onSnapshot: (snapshot: TreeEventSnapshotInfo) => void | Promise<void>): () => void;
|
|
30
|
+
//# sourceMappingURL=treeEventSnapshots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treeEventSnapshots.d.ts","sourceRoot":"","sources":["../src/treeEventSnapshots.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,GAAG,EACT,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAIL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,qBAAsB,SAAQ,uBAAuB;IACpE,WAAW,EAAE,GAAG,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kCAAkC;IACjD,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC7E,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,6BAA8B,SAAQ,kCAAkC;IACvF,eAAe,EAAE,CACf,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,KACvC,MAAM,IAAI,CAAC;CACjB;AAiDD,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAEpG;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,qBAAqB,EAChC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAET;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,EAClD,OAAO,EAAE,GAAG,GAAG,IAAI,GAAG,SAAS,GAC9B,OAAO,CAgBT;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,qBAAqB,EAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,GACnC,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAwBrB;AAED,wBAAsB,sBAAsB,CAC1C,cAAc,EAAE,cAAc,EAC9B,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAOvC;AAED,wBAAsB,qBAAqB,CACzC,cAAc,EAAE,cAAc,EAC9B,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,GAAG,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAWvC;AAED,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,kCAAkC,EAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+BvC;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,6BAA6B,EACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACpE,MAAM,IAAI,CA4DZ"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { decryptKeyFromLink, fromHex, nhashEncode, toHex, } from '@hashtree/core';
|
|
2
|
+
import { parseHashtreeRootEvent, readSignedNostrEventSnapshot, storeSignedNostrEventSnapshot, } from './snapshot.js';
|
|
3
|
+
const DEFAULT_SNAPSHOT_FETCH_LIMIT = 20;
|
|
4
|
+
function compareEvents(a, b) {
|
|
5
|
+
if (a.created_at !== b.created_at) {
|
|
6
|
+
return a.created_at - b.created_at;
|
|
7
|
+
}
|
|
8
|
+
return a.id.localeCompare(b.id);
|
|
9
|
+
}
|
|
10
|
+
function snapshotInfoFromParsed(parsed, snapshotCid, nip19) {
|
|
11
|
+
return {
|
|
12
|
+
...parsed,
|
|
13
|
+
snapshotCid,
|
|
14
|
+
snapshotNhash: nhashEncode(snapshotCid),
|
|
15
|
+
npub: nip19.npubEncode(parsed.event.pubkey),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function tryParseHashtreeRootEvent(event) {
|
|
19
|
+
try {
|
|
20
|
+
return parseHashtreeRootEvent(event);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function normalizeLinkKey(linkKey) {
|
|
27
|
+
if (!linkKey) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return typeof linkKey === 'string' ? fromHex(linkKey) : linkKey;
|
|
31
|
+
}
|
|
32
|
+
function isValidTreeEventFor(event, pubkey, treeName) {
|
|
33
|
+
if (event.pubkey !== pubkey) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const parsed = tryParseHashtreeRootEvent(event);
|
|
37
|
+
return parsed?.treeName === treeName;
|
|
38
|
+
}
|
|
39
|
+
export function compareTreeEventSnapshots(a, b) {
|
|
40
|
+
return compareEvents(a.event, b.event);
|
|
41
|
+
}
|
|
42
|
+
export function isNewerTreeEventSnapshot(candidate, current) {
|
|
43
|
+
return compareTreeEventSnapshots(candidate, current) > 0;
|
|
44
|
+
}
|
|
45
|
+
export function snapshotMatchesRootCid(snapshot, rootCid) {
|
|
46
|
+
if (!snapshot || !rootCid) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (toHex(snapshot.rootCid.hash) !== toHex(rootCid.hash)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (snapshot.visibility !== 'public') {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
const snapshotKey = snapshot.rootCid.key ? toHex(snapshot.rootCid.key) : null;
|
|
56
|
+
const rootKey = rootCid.key ? toHex(rootCid.key) : null;
|
|
57
|
+
if (snapshotKey === null || rootKey === null) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return snapshotKey === rootKey;
|
|
61
|
+
}
|
|
62
|
+
export async function resolveSnapshotRootCid(snapshot, linkKey) {
|
|
63
|
+
if (snapshot.visibility === 'public') {
|
|
64
|
+
return snapshot.rootCid;
|
|
65
|
+
}
|
|
66
|
+
if (snapshot.visibility === 'link-visible' && snapshot.encryptedKey) {
|
|
67
|
+
const normalizedLinkKey = normalizeLinkKey(linkKey);
|
|
68
|
+
if (!normalizedLinkKey) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const decryptedKey = await decryptKeyFromLink(fromHex(snapshot.encryptedKey), normalizedLinkKey);
|
|
73
|
+
if (decryptedKey) {
|
|
74
|
+
return { hash: snapshot.rootCid.hash, key: decryptedKey };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
export async function storeTreeEventSnapshot(snapshotTarget, nip19, event) {
|
|
84
|
+
const parsed = tryParseHashtreeRootEvent(event);
|
|
85
|
+
if (!parsed) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const snapshotCid = await storeSignedNostrEventSnapshot(snapshotTarget, parsed.event);
|
|
89
|
+
return snapshotInfoFromParsed(parsed, snapshotCid, nip19);
|
|
90
|
+
}
|
|
91
|
+
export async function readTreeEventSnapshot(snapshotTarget, nip19, snapshotCid, maxBytes) {
|
|
92
|
+
try {
|
|
93
|
+
const event = await readSignedNostrEventSnapshot(snapshotTarget, snapshotCid, maxBytes);
|
|
94
|
+
const parsed = tryParseHashtreeRootEvent(event);
|
|
95
|
+
if (!parsed) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return snapshotInfoFromParsed(parsed, snapshotCid, nip19);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function fetchLatestTreeEventSnapshot(config, npub, treeName) {
|
|
105
|
+
let decoded;
|
|
106
|
+
try {
|
|
107
|
+
decoded = config.nip19.decode(npub);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
if (decoded.type !== 'npub' || typeof decoded.data !== 'string') {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const events = await config.fetchEvents({
|
|
116
|
+
kinds: [30078],
|
|
117
|
+
authors: [decoded.data],
|
|
118
|
+
'#d': [treeName],
|
|
119
|
+
limit: config.snapshotFetchLimit ?? DEFAULT_SNAPSHOT_FETCH_LIMIT,
|
|
120
|
+
});
|
|
121
|
+
const candidates = [];
|
|
122
|
+
for (const event of events) {
|
|
123
|
+
if (!isValidTreeEventFor(event, decoded.data, treeName)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
candidates.push(event);
|
|
127
|
+
}
|
|
128
|
+
if (candidates.length === 0) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
candidates.sort(compareEvents);
|
|
132
|
+
return storeTreeEventSnapshot(config.snapshotTarget, config.nip19, candidates[candidates.length - 1]);
|
|
133
|
+
}
|
|
134
|
+
export function watchLatestTreeEventSnapshot(config, npub, treeName, onSnapshot) {
|
|
135
|
+
let decoded;
|
|
136
|
+
try {
|
|
137
|
+
decoded = config.nip19.decode(npub);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return () => { };
|
|
141
|
+
}
|
|
142
|
+
if (decoded.type !== 'npub' || typeof decoded.data !== 'string') {
|
|
143
|
+
return () => { };
|
|
144
|
+
}
|
|
145
|
+
const pubkey = decoded.data;
|
|
146
|
+
let closed = false;
|
|
147
|
+
let latestSnapshot = null;
|
|
148
|
+
const emitIfNewer = async (event) => {
|
|
149
|
+
if (closed || !isValidTreeEventFor(event, pubkey, treeName)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const snapshot = await storeTreeEventSnapshot(config.snapshotTarget, config.nip19, event);
|
|
153
|
+
if (!snapshot) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (latestSnapshot && compareTreeEventSnapshots(snapshot, latestSnapshot) <= 0) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
latestSnapshot = snapshot;
|
|
160
|
+
await onSnapshot(snapshot);
|
|
161
|
+
};
|
|
162
|
+
fetchLatestTreeEventSnapshot(config, npub, treeName)
|
|
163
|
+
.then(async (snapshot) => {
|
|
164
|
+
if (closed || !snapshot) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (latestSnapshot && compareTreeEventSnapshots(snapshot, latestSnapshot) <= 0) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
latestSnapshot = snapshot;
|
|
171
|
+
await onSnapshot(snapshot);
|
|
172
|
+
})
|
|
173
|
+
.catch(() => { });
|
|
174
|
+
const unsubscribe = config.subscribeEvents({
|
|
175
|
+
kinds: [30078],
|
|
176
|
+
authors: [pubkey],
|
|
177
|
+
'#d': [treeName],
|
|
178
|
+
}, (event) => {
|
|
179
|
+
void emitIfNewer(event);
|
|
180
|
+
});
|
|
181
|
+
return () => {
|
|
182
|
+
closed = true;
|
|
183
|
+
unsubscribe();
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=treeEventSnapshots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treeEventSnapshots.js","sourceRoot":"","sources":["../src/treeEventSnapshots.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,WAAW,EACX,KAAK,GAEN,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,4BAA4B,EAC5B,6BAA6B,GAG9B,MAAM,eAAe,CAAC;AAEvB,MAAM,4BAA4B,GAAG,EAAE,CAAC;AA0BxC,SAAS,aAAa,CAAC,CAAmB,EAAE,CAAmB;IAC7D,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;QAClC,OAAO,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,sBAAsB,CAC7B,MAA+B,EAC/B,WAAgB,EAChB,KAAgB;IAEhB,OAAO;QACL,GAAG,MAAM;QACT,WAAW;QACX,aAAa,EAAE,WAAW,CAAC,WAAW,CAAC;QACvC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAuB;IACxD,IAAI,CAAC;QACH,OAAO,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAoC;IAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAClE,CAAC;AAED,SAAS,mBAAmB,CAC1B,KAAuB,EACvB,MAAc,EACd,QAAgB;IAEhB,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;IAChD,OAAO,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,CAAwB,EAAE,CAAwB;IAC1F,OAAO,aAAa,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,SAAgC,EAChC,OAA8B;IAE9B,OAAO,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAkD,EAClD,OAA+B;IAE/B,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxD,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,WAAW,KAAK,OAAO,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,QAA+B,EAC/B,OAAoC;IAEpC,IAAI,QAAQ,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO,QAAQ,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,KAAK,cAAc,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACpE,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAC9B,iBAAiB,CAClB,CAAC;YACF,IAAI,YAAY,EAAE,CAAC;gBACjB,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;YAC5D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,cAA8B,EAC9B,KAAgB,EAChB,KAAuB;IAEvB,MAAM,MAAM,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,6BAA6B,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACtF,OAAO,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,cAA8B,EAC9B,KAAgB,EAChB,WAAgB,EAChB,QAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,4BAA4B,CAAC,cAAc,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACxF,MAAM,MAAM,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,MAA0C,EAC1C,IAAY,EACZ,QAAgB;IAEhB,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;QACtC,KAAK,EAAE,CAAC,KAAK,CAAC;QACd,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACvB,IAAI,EAAE,CAAC,QAAQ,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC,kBAAkB,IAAI,4BAA4B;KACjE,CAAC,CAAC;IAEH,MAAM,UAAU,GAAuB,EAAE,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;YACxD,SAAS;QACX,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/B,OAAO,sBAAsB,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,CAAC;AACzG,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,MAAqC,EACrC,IAAY,EACZ,QAAgB,EAChB,UAAqE;IAErE,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5B,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,cAAc,GAAiC,IAAI,CAAC;IAExD,MAAM,WAAW,GAAG,KAAK,EAAE,KAAuB,EAAiB,EAAE;QACnE,IAAI,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC5D,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1F,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO;QACT,CAAC;QACD,IAAI,cAAc,IAAI,yBAAyB,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/E,OAAO;QACT,CAAC;QAED,cAAc,GAAG,QAAQ,CAAC;QAC1B,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,4BAA4B,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;SACjD,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QACvB,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,cAAc,IAAI,yBAAyB,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/E,OAAO;QACT,CAAC;QACD,cAAc,GAAG,QAAQ,CAAC;QAC1B,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC,CAAC;SACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEnB,MAAM,WAAW,GAAG,MAAM,CAAC,eAAe,CACxC;QACE,KAAK,EAAE,CAAC,KAAK,CAAC;QACd,OAAO,EAAE,CAAC,MAAM,CAAC;QACjB,IAAI,EAAE,CAAC,QAAQ,CAAC;KACjB,EACD,CAAC,KAAK,EAAE,EAAE;QACR,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC,CACF,CAAC;IAEF,OAAO,GAAG,EAAE;QACV,MAAM,GAAG,IAAI,CAAC;QACd,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/webrtc/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { WebRTCStore, DEFAULT_RELAYS } from './store.js';
|
|
2
2
|
export { Peer } from './peer.js';
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
3
|
+
export { PeerSelector, peerPrincipal, normalizeDispatchConfig, buildHedgedWavePlan, syncSelectorPeers, } from './peerSelector.js';
|
|
4
|
+
export { PeerId, generateUuid, WEBRTC_KIND, MAX_HTL, HtlMode, type HtlPolicy, BLOB_REQUEST_POLICY, MESH_EVENT_POLICY, MESH_PROTOCOL, MESH_PROTOCOL_VERSION, MESH_DEFAULT_HTL, MESH_MAX_HTL, PEER_METADATA_SNAPSHOT_VERSION, MSG_TYPE_REQUEST, MSG_TYPE_RESPONSE, FRAGMENT_SIZE, createMeshNostrEventFrame, validateMeshNostrFrame, parseMeshNostrFrameText, type SignalingMessage, type MeshNostrFrame, type MeshNostrPayload, type WebRTCStoreConfig, type PeerStatus, type WebRTCStoreEvent, type WebRTCStoreEventHandler, type EventSigner, type EventEncrypter, type EventDecrypter, type GiftWrapper, type GiftUnwrapper, type SignedEvent, type PeerPool, type PeerClassifier, type PoolConfig, type SelectionStrategy, type RequestDispatchConfig, type PersistedPeerMetadata, type PeerMetadataSnapshot, type MeshStats, type WebRTCStats, type BandwidthSample, type DataRequest, type DataResponse, } from './types.js';
|
|
5
|
+
export { encodeRequest, encodeResponse, parseMessage, createRequest, createResponse, createFragmentResponse, hashToKey, verifyHash, generatePeerHTLConfig, decrementHTLWithPolicy, decrementHTL, shouldForwardHTL, shouldForward, type PeerHTLConfig, type PendingRequest, } from './protocol.js';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,MAAM,EACN,YAAY,EACZ,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,OAAO,EACP,OAAO,EACP,KAAK,SAAS,EACd,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,8BAA8B,EAC9B,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC"}
|
package/dist/webrtc/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { WebRTCStore, DEFAULT_RELAYS } from './store.js';
|
|
2
2
|
export { Peer } from './peer.js';
|
|
3
|
-
export {
|
|
3
|
+
export { PeerSelector, peerPrincipal, normalizeDispatchConfig, buildHedgedWavePlan, syncSelectorPeers, } from './peerSelector.js';
|
|
4
|
+
export { PeerId, generateUuid, WEBRTC_KIND, MAX_HTL, HtlMode, BLOB_REQUEST_POLICY, MESH_EVENT_POLICY, MESH_PROTOCOL, MESH_PROTOCOL_VERSION, MESH_DEFAULT_HTL, MESH_MAX_HTL, PEER_METADATA_SNAPSHOT_VERSION, MSG_TYPE_REQUEST, MSG_TYPE_RESPONSE, FRAGMENT_SIZE, createMeshNostrEventFrame, validateMeshNostrFrame, parseMeshNostrFrameText, } from './types.js';
|
|
4
5
|
// Protocol functions
|
|
5
|
-
export { encodeRequest, encodeResponse, parseMessage, createRequest, createResponse, createFragmentResponse, hashToKey, verifyHash, generatePeerHTLConfig, decrementHTL, shouldForward, } from './protocol.js';
|
|
6
|
+
export { encodeRequest, encodeResponse, parseMessage, createRequest, createResponse, createFragmentResponse, hashToKey, verifyHash, generatePeerHTLConfig, decrementHTLWithPolicy, decrementHTL, shouldForwardHTL, shouldForward, } from './protocol.js';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/dist/webrtc/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,MAAM,EACN,YAAY,EACZ,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webrtc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,OAAO,EACP,OAAO,EAEP,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,8BAA8B,EAC9B,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,GA0BxB,MAAM,YAAY,CAAC;AAEpB,qBAAqB;AACrB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,aAAa,GAGd,MAAM,eAAe,CAAC"}
|
package/dist/webrtc/peer.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* WebRTC peer connection for hashtree data exchange
|
|
3
3
|
*/
|
|
4
4
|
import type { Store, Hash } from '@hashtree/core';
|
|
5
|
-
import type { SignalingMessage, PeerId } from './types.js';
|
|
5
|
+
import type { SignalingMessage, PeerId, MeshNostrFrame } from './types.js';
|
|
6
|
+
import { PeerHTLConfig } from './protocol.js';
|
|
6
7
|
export declare class Peer {
|
|
7
8
|
readonly peerId: string;
|
|
8
9
|
readonly pubkey: string;
|
|
@@ -26,6 +27,7 @@ export declare class Peer {
|
|
|
26
27
|
private candidateBatchTimeout;
|
|
27
28
|
private queuedRemoteCandidates;
|
|
28
29
|
private onForwardRequest?;
|
|
30
|
+
private onMeshFrame?;
|
|
29
31
|
private stats;
|
|
30
32
|
private pendingReassemblies;
|
|
31
33
|
private reassemblyCleanupInterval?;
|
|
@@ -41,6 +43,7 @@ export declare class Peer {
|
|
|
41
43
|
onClose: () => void;
|
|
42
44
|
onConnected?: () => void;
|
|
43
45
|
onForwardRequest?: (hash: Uint8Array, excludePeerId: string, htl: number) => Promise<Uint8Array | null>;
|
|
46
|
+
onMeshFrame?: (fromPeerId: string, frame: MeshNostrFrame) => Promise<void> | void;
|
|
44
47
|
requestTimeout?: number;
|
|
45
48
|
debug?: boolean;
|
|
46
49
|
});
|
|
@@ -89,6 +92,14 @@ export declare class Peer {
|
|
|
89
92
|
bytesReceived: number;
|
|
90
93
|
bytesForwarded: number;
|
|
91
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Get per-peer HTL config used for probabilistic decrements.
|
|
97
|
+
*/
|
|
98
|
+
getHTLConfig(): PeerHTLConfig;
|
|
99
|
+
/**
|
|
100
|
+
* Send a relayless mesh signaling frame over text channel.
|
|
101
|
+
*/
|
|
102
|
+
sendMeshFrameText(frame: MeshNostrFrame): boolean;
|
|
92
103
|
/**
|
|
93
104
|
* Send data to this peer for a hash they previously requested
|
|
94
105
|
* Returns true if this peer had requested this hash
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../src/webrtc/peer.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EACV,gBAAgB,EAGhB,MAAM,
|
|
1
|
+
{"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../src/webrtc/peer.ts"],"names":[],"mappings":"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"}
|
package/dist/webrtc/peer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BLOB_REQUEST_POLICY, MSG_TYPE_REQUEST, MSG_TYPE_RESPONSE, FRAGMENT_SIZE, FRAGMENT_STALL_TIMEOUT, FRAGMENT_TOTAL_TIMEOUT, MAX_PENDING_REASSEMBLIES, parseMeshNostrFrameText, } from './types.js';
|
|
2
2
|
import { LRUCache } from './lruCache.js';
|
|
3
3
|
import { encodeRequest, encodeResponse, parseMessage, createRequest, createResponse, createFragmentResponse, isFragmented, clearPendingRequests, generatePeerHTLConfig, decrementHTL, shouldForward, hashToKey, verifyHash, } from './protocol.js';
|
|
4
4
|
const ICE_SERVERS = [
|
|
@@ -39,6 +39,8 @@ export class Peer {
|
|
|
39
39
|
// Callback to forward request to other peers when we don't have data locally
|
|
40
40
|
// htl parameter is the decremented HTL to use when forwarding
|
|
41
41
|
onForwardRequest;
|
|
42
|
+
// Callback for relayless mesh signaling frames received over data channel text.
|
|
43
|
+
onMeshFrame;
|
|
42
44
|
// Per-peer stats tracking
|
|
43
45
|
stats = {
|
|
44
46
|
requestsSent: 0,
|
|
@@ -70,6 +72,7 @@ export class Peer {
|
|
|
70
72
|
this.onClose = options.onClose;
|
|
71
73
|
this.onConnected = options.onConnected;
|
|
72
74
|
this.onForwardRequest = options.onForwardRequest;
|
|
75
|
+
this.onMeshFrame = options.onMeshFrame;
|
|
73
76
|
this.requestTimeout = options.requestTimeout ?? 500;
|
|
74
77
|
this.debug = options.debug ?? false;
|
|
75
78
|
this.createdAt = Date.now();
|
|
@@ -77,7 +80,7 @@ export class Peer {
|
|
|
77
80
|
this.htlConfig = generatePeerHTLConfig();
|
|
78
81
|
// Perfect negotiation: polite peer (smaller ID) rolls back on collision
|
|
79
82
|
this.myPeerId = options.myPeerId;
|
|
80
|
-
this.isPolite = options.myPeerId < options.peerId.
|
|
83
|
+
this.isPolite = options.myPeerId < options.peerId.toString();
|
|
81
84
|
// Start fragment reassembly cleanup interval
|
|
82
85
|
this.reassemblyCleanupInterval = setInterval(() => this.cleanupStaleReassemblies(), 5000);
|
|
83
86
|
this.pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
|
|
@@ -165,9 +168,19 @@ export class Peer {
|
|
|
165
168
|
this.close();
|
|
166
169
|
};
|
|
167
170
|
channel.onmessage = async (event) => {
|
|
168
|
-
|
|
171
|
+
if (typeof event.data === 'string') {
|
|
172
|
+
const frame = parseMeshNostrFrameText(event.data);
|
|
173
|
+
if (frame && this.onMeshFrame) {
|
|
174
|
+
await this.onMeshFrame(this.peerId, frame);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
169
178
|
if (event.data instanceof ArrayBuffer) {
|
|
170
179
|
await this.handleMessage(event.data);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (typeof Blob !== 'undefined' && event.data instanceof Blob) {
|
|
183
|
+
await this.handleMessage(await event.data.arrayBuffer());
|
|
171
184
|
}
|
|
172
185
|
};
|
|
173
186
|
}
|
|
@@ -227,7 +240,7 @@ export class Peer {
|
|
|
227
240
|
}
|
|
228
241
|
}
|
|
229
242
|
async handleRequest(req) {
|
|
230
|
-
const htl = req.htl ??
|
|
243
|
+
const htl = req.htl ?? BLOB_REQUEST_POLICY.maxHtl;
|
|
231
244
|
const hash = req.h;
|
|
232
245
|
const hashKey = hashToKey(hash);
|
|
233
246
|
this.stats.requestsReceived++;
|
|
@@ -376,7 +389,7 @@ export class Peer {
|
|
|
376
389
|
* Request data by hash from this peer
|
|
377
390
|
* @param htl Hops To Live - decremented before sending
|
|
378
391
|
*/
|
|
379
|
-
async request(hash, htl =
|
|
392
|
+
async request(hash, htl = BLOB_REQUEST_POLICY.maxHtl) {
|
|
380
393
|
if (!this.dataChannel || this.dataChannel.readyState !== 'open') {
|
|
381
394
|
return null;
|
|
382
395
|
}
|
|
@@ -412,6 +425,27 @@ export class Peer {
|
|
|
412
425
|
getStats() {
|
|
413
426
|
return { ...this.stats };
|
|
414
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* Get per-peer HTL config used for probabilistic decrements.
|
|
430
|
+
*/
|
|
431
|
+
getHTLConfig() {
|
|
432
|
+
return this.htlConfig;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Send a relayless mesh signaling frame over text channel.
|
|
436
|
+
*/
|
|
437
|
+
sendMeshFrameText(frame) {
|
|
438
|
+
if (!this.dataChannel || this.dataChannel.readyState !== 'open') {
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
this.dataChannel.send(JSON.stringify(frame));
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
415
449
|
/**
|
|
416
450
|
* Send data to this peer for a hash they previously requested
|
|
417
451
|
* Returns true if this peer had requested this hash
|