@hashtree/nostr 0.1.14 → 0.2.6
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 +106 -176
- package/dist/eventCollection.d.ts +1 -1
- package/dist/eventCollection.d.ts.map +1 -1
- package/dist/eventCollection.js +2 -7
- package/dist/eventCollection.js.map +1 -1
- package/dist/eventKeys.d.ts +1 -0
- package/dist/eventKeys.d.ts.map +1 -1
- package/dist/eventKeys.js +7 -1
- package/dist/eventKeys.js.map +1 -1
- package/dist/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +76 -70
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/resolver/nostr.d.ts.map +1 -1
- package/dist/resolver/nostr.js +37 -25
- package/dist/resolver/nostr.js.map +1 -1
- package/dist/snapshot.d.ts +5 -0
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/snapshot.js +11 -3
- package/dist/snapshot.js.map +1 -1
- package/dist/treeEventSnapshotPermalinks.js +2 -2
- package/dist/treeEventSnapshotPermalinks.js.map +1 -1
- package/dist/treeEventSnapshots.d.ts.map +1 -1
- package/dist/treeEventSnapshots.js +11 -12
- package/dist/treeEventSnapshots.js.map +1 -1
- package/package.json +18 -14
- package/src/eventCollection.ts +189 -0
- package/src/eventKeys.ts +155 -0
- package/src/eventValidation.ts +71 -0
- package/src/events.ts +929 -0
- package/src/index.ts +122 -0
- package/src/replaceablePublish.ts +204 -0
- package/src/resolver/index.ts +14 -0
- package/src/resolver/nostr.ts +1212 -0
- package/src/snapshot.ts +214 -0
- package/src/treeEventSnapshotPermalinks.ts +150 -0
- package/src/treeEventSnapshots.ts +283 -0
- package/dist/webrtc/index.d.ts +0 -6
- package/dist/webrtc/index.d.ts.map +0 -1
- package/dist/webrtc/index.js +0 -7
- package/dist/webrtc/index.js.map +0 -1
- package/dist/webrtc/lruCache.d.ts +0 -20
- package/dist/webrtc/lruCache.d.ts.map +0 -1
- package/dist/webrtc/lruCache.js +0 -59
- package/dist/webrtc/lruCache.js.map +0 -1
- package/dist/webrtc/peer.d.ts +0 -133
- package/dist/webrtc/peer.d.ts.map +0 -1
- package/dist/webrtc/peer.js +0 -617
- package/dist/webrtc/peer.js.map +0 -1
- package/dist/webrtc/peerSelector.d.ts +0 -2
- package/dist/webrtc/peerSelector.d.ts.map +0 -1
- package/dist/webrtc/peerSelector.js +0 -2
- package/dist/webrtc/peerSelector.js.map +0 -1
- package/dist/webrtc/protocol.d.ts +0 -3
- package/dist/webrtc/protocol.d.ts.map +0 -1
- package/dist/webrtc/protocol.js +0 -2
- package/dist/webrtc/protocol.js.map +0 -1
- package/dist/webrtc/store.d.ts +0 -217
- package/dist/webrtc/store.d.ts.map +0 -1
- package/dist/webrtc/store.js +0 -1320
- package/dist/webrtc/store.js.map +0 -1
- package/dist/webrtc/types.d.ts +0 -101
- package/dist/webrtc/types.d.ts.map +0 -1
- package/dist/webrtc/types.js +0 -5
- package/dist/webrtc/types.js.map +0 -1
package/src/snapshot.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { objectKeyFromHex } from '@hashtree/core';
|
|
2
|
+
import { HashTree, cid, fromHex, type CID, type Store, type TreeVisibility } from '@hashtree/core';
|
|
3
|
+
import type { StoredNostrEvent } from './events.js';
|
|
4
|
+
|
|
5
|
+
const HEX_64 = /^[0-9a-f]{64}$/;
|
|
6
|
+
const HEX_128 = /^[0-9a-f]{128}$/;
|
|
7
|
+
const MAX_SNAPSHOT_BYTES = 256 * 1024;
|
|
8
|
+
const textEncoder = new TextEncoder();
|
|
9
|
+
const textDecoder = new TextDecoder();
|
|
10
|
+
|
|
11
|
+
export const HASHTREE_ROOT_KIND = 30064;
|
|
12
|
+
export const HASHTREE_LEGACY_ROOT_KIND = 30078;
|
|
13
|
+
export const HASHTREE_ROOT_KINDS = [HASHTREE_ROOT_KIND, HASHTREE_LEGACY_ROOT_KIND] as const;
|
|
14
|
+
export const HASHTREE_LABEL = 'hashtree';
|
|
15
|
+
|
|
16
|
+
export function isHashtreeRootKind(kind: number): boolean {
|
|
17
|
+
return kind === HASHTREE_ROOT_KIND || kind === HASHTREE_LEGACY_ROOT_KIND;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ParsedHashtreeRootEvent {
|
|
21
|
+
event: StoredNostrEvent;
|
|
22
|
+
treeName: string;
|
|
23
|
+
rootCid: CID;
|
|
24
|
+
visibility: TreeVisibility;
|
|
25
|
+
labels: string[];
|
|
26
|
+
encryptedKey?: string;
|
|
27
|
+
keyId?: string;
|
|
28
|
+
selfEncryptedKey?: string;
|
|
29
|
+
selfEncryptedLinkKey?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SnapshotTreeLike {
|
|
33
|
+
putFile(
|
|
34
|
+
data: Uint8Array,
|
|
35
|
+
options?: { unencrypted?: boolean },
|
|
36
|
+
): Promise<{ cid: CID; size: number }>;
|
|
37
|
+
readFileRange(id: CID, start: number, end?: number): Promise<Uint8Array | null>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type SnapshotTarget = Store | SnapshotTreeLike;
|
|
41
|
+
|
|
42
|
+
function isSnapshotTreeLike(target: SnapshotTarget): target is SnapshotTreeLike {
|
|
43
|
+
return (
|
|
44
|
+
typeof (target as SnapshotTreeLike).putFile === 'function' &&
|
|
45
|
+
typeof (target as SnapshotTreeLike).readFileRange === 'function'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getSnapshotTree(target: SnapshotTarget): SnapshotTreeLike {
|
|
50
|
+
if (isSnapshotTreeLike(target)) {
|
|
51
|
+
return target;
|
|
52
|
+
}
|
|
53
|
+
return new HashTree({ store: target });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function assertStringTags(tags: unknown): asserts tags is string[][] {
|
|
57
|
+
if (!Array.isArray(tags)) {
|
|
58
|
+
throw new Error('Nostr event tags must be an array');
|
|
59
|
+
}
|
|
60
|
+
for (const tag of tags) {
|
|
61
|
+
if (!Array.isArray(tag) || tag.some((value) => typeof value !== 'string')) {
|
|
62
|
+
throw new Error('Nostr event tags must be an array of string arrays');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function uniqueLabels(labels: string[]): string[] {
|
|
68
|
+
const seen = new Set<string>();
|
|
69
|
+
const result: string[] = [];
|
|
70
|
+
for (const label of labels) {
|
|
71
|
+
if (!label || seen.has(label)) continue;
|
|
72
|
+
seen.add(label);
|
|
73
|
+
result.push(label);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function normalizeSignedNostrEvent(event: StoredNostrEvent): StoredNostrEvent {
|
|
79
|
+
const { id, pubkey, created_at, kind, tags, content, sig } = event;
|
|
80
|
+
if (!HEX_64.test(id)) {
|
|
81
|
+
throw new Error('Nostr event id must be a lowercase 64-character hex string');
|
|
82
|
+
}
|
|
83
|
+
if (!HEX_64.test(pubkey)) {
|
|
84
|
+
throw new Error('Nostr event pubkey must be a lowercase 64-character hex string');
|
|
85
|
+
}
|
|
86
|
+
if (!Number.isInteger(created_at) || created_at < 0) {
|
|
87
|
+
throw new Error('Nostr event created_at must be a non-negative integer');
|
|
88
|
+
}
|
|
89
|
+
if (!Number.isInteger(kind) || kind < 0) {
|
|
90
|
+
throw new Error('Nostr event kind must be a non-negative integer');
|
|
91
|
+
}
|
|
92
|
+
assertStringTags(tags);
|
|
93
|
+
if (typeof content !== 'string') {
|
|
94
|
+
throw new Error('Nostr event content must be a string');
|
|
95
|
+
}
|
|
96
|
+
if (!HEX_128.test(sig)) {
|
|
97
|
+
throw new Error('Nostr event sig must be a lowercase 128-character hex string');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
id,
|
|
102
|
+
pubkey,
|
|
103
|
+
created_at,
|
|
104
|
+
kind,
|
|
105
|
+
tags: tags.map((tag) => [...tag]),
|
|
106
|
+
content,
|
|
107
|
+
sig,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function encodeSignedNostrEventJson(event: StoredNostrEvent): Uint8Array {
|
|
112
|
+
const normalized = normalizeSignedNostrEvent(event);
|
|
113
|
+
return textEncoder.encode(JSON.stringify({
|
|
114
|
+
id: normalized.id,
|
|
115
|
+
pubkey: normalized.pubkey,
|
|
116
|
+
created_at: normalized.created_at,
|
|
117
|
+
kind: normalized.kind,
|
|
118
|
+
tags: normalized.tags,
|
|
119
|
+
content: normalized.content,
|
|
120
|
+
sig: normalized.sig,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function decodeSignedNostrEventJson(data: Uint8Array): StoredNostrEvent {
|
|
125
|
+
const parsed = JSON.parse(textDecoder.decode(data)) as Record<string, unknown>;
|
|
126
|
+
return normalizeSignedNostrEvent({
|
|
127
|
+
id: typeof parsed.id === 'string' ? parsed.id : '',
|
|
128
|
+
pubkey: typeof parsed.pubkey === 'string' ? parsed.pubkey : '',
|
|
129
|
+
created_at: typeof parsed.created_at === 'number' ? parsed.created_at : NaN,
|
|
130
|
+
kind: typeof parsed.kind === 'number' ? parsed.kind : NaN,
|
|
131
|
+
tags: parsed.tags,
|
|
132
|
+
content: typeof parsed.content === 'string' ? parsed.content : '',
|
|
133
|
+
sig: typeof parsed.sig === 'string' ? parsed.sig : '',
|
|
134
|
+
} as StoredNostrEvent);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function storeSignedNostrEventSnapshot(target: SnapshotTarget, event: StoredNostrEvent): Promise<CID> {
|
|
138
|
+
const tree = getSnapshotTree(target);
|
|
139
|
+
return (await tree.putFile(encodeSignedNostrEventJson(event), { unencrypted: true })).cid;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function readSignedNostrEventSnapshot(
|
|
143
|
+
target: SnapshotTarget,
|
|
144
|
+
snapshotCid: CID,
|
|
145
|
+
maxBytes = MAX_SNAPSHOT_BYTES,
|
|
146
|
+
): Promise<StoredNostrEvent> {
|
|
147
|
+
const tree = getSnapshotTree(target);
|
|
148
|
+
const bytes = await tree.readFileRange(snapshotCid, 0, maxBytes + 1);
|
|
149
|
+
if (!bytes) {
|
|
150
|
+
throw new Error('Signed Nostr event snapshot is missing');
|
|
151
|
+
}
|
|
152
|
+
if (bytes.length > maxBytes) {
|
|
153
|
+
throw new Error(`Signed Nostr event snapshot exceeds ${maxBytes} bytes`);
|
|
154
|
+
}
|
|
155
|
+
return decodeSignedNostrEventJson(bytes);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function hasLabel(event: Pick<StoredNostrEvent, 'tags'>, label: string): boolean {
|
|
159
|
+
return event.tags.some((tag) => tag[0] === 'l' && tag[1] === label);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function hasAnyLabel(event: Pick<StoredNostrEvent, 'tags'>): boolean {
|
|
163
|
+
return event.tags.some((tag) => tag[0] === 'l');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function parseHashtreeRootEvent(event: StoredNostrEvent): ParsedHashtreeRootEvent | null {
|
|
167
|
+
const normalized = normalizeSignedNostrEvent(event);
|
|
168
|
+
if (!isHashtreeRootKind(normalized.kind)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const treeName = normalized.tags.find((tag) => tag[0] === 'd')?.[1];
|
|
173
|
+
const hashHex = normalized.tags.find((tag) => tag[0] === 'hash')?.[1];
|
|
174
|
+
if (!treeName || !hashHex || !HEX_64.test(hashHex)) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (hasAnyLabel(normalized) && !hasLabel(normalized, HASHTREE_LABEL)) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const labels = uniqueLabels(
|
|
183
|
+
normalized.tags
|
|
184
|
+
.filter((tag) => tag[0] === 'l' && typeof tag[1] === 'string' && tag[1].length > 0)
|
|
185
|
+
.map((tag) => tag[1]!)
|
|
186
|
+
);
|
|
187
|
+
const keyHex = normalized.tags.find((tag) => tag[0] === 'key')?.[1];
|
|
188
|
+
const encryptedKey = normalized.tags.find((tag) => tag[0] === 'encryptedKey')?.[1];
|
|
189
|
+
const keyId = normalized.tags.find((tag) => tag[0] === 'keyId')?.[1];
|
|
190
|
+
const selfEncryptedKey = normalized.tags.find((tag) => tag[0] === 'selfEncryptedKey')?.[1];
|
|
191
|
+
const selfEncryptedLinkKey = normalized.tags.find((tag) => tag[0] === 'selfEncryptedLinkKey')?.[1];
|
|
192
|
+
|
|
193
|
+
const visibility: TreeVisibility = encryptedKey
|
|
194
|
+
? 'link-visible'
|
|
195
|
+
: selfEncryptedKey
|
|
196
|
+
? 'private'
|
|
197
|
+
: 'public';
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
return {
|
|
201
|
+
event: normalized,
|
|
202
|
+
treeName,
|
|
203
|
+
rootCid: cid(fromHex(hashHex), visibility === 'public' && keyHex !== undefined ? objectKeyFromHex(keyHex) : undefined),
|
|
204
|
+
visibility,
|
|
205
|
+
labels,
|
|
206
|
+
encryptedKey,
|
|
207
|
+
keyId,
|
|
208
|
+
selfEncryptedKey,
|
|
209
|
+
selfEncryptedLinkKey,
|
|
210
|
+
};
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { nhashDecode } from '@hashtree/core';
|
|
2
|
+
|
|
3
|
+
const NHASH_PATTERN = /^nhash1[a-z0-9]+$/;
|
|
4
|
+
const LINK_KEY_PATTERN = /^(?:[a-f0-9]{2}){32,33}$/i;
|
|
5
|
+
|
|
6
|
+
export interface TreeEventSnapshotPermalink {
|
|
7
|
+
snapshotNhash: string;
|
|
8
|
+
path: string[];
|
|
9
|
+
linkKey?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BuildTreeEventSnapshotPermalinkOptions {
|
|
13
|
+
prefix?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function safeDecode(value: string): string {
|
|
17
|
+
try {
|
|
18
|
+
return decodeURIComponent(value);
|
|
19
|
+
} catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function extractPermalinkCandidate(input: string | URL): string {
|
|
25
|
+
const raw = `${input instanceof URL ? input.toString() : input}`.trim();
|
|
26
|
+
if (!raw) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const parsed = input instanceof URL ? input : new URL(raw);
|
|
32
|
+
if (parsed.hash.startsWith('#/')) {
|
|
33
|
+
return parsed.hash.slice(1);
|
|
34
|
+
}
|
|
35
|
+
return `/${parsed.host}${parsed.pathname}${parsed.search}`;
|
|
36
|
+
} catch {
|
|
37
|
+
if (raw.startsWith('#/')) {
|
|
38
|
+
return raw.slice(1);
|
|
39
|
+
}
|
|
40
|
+
return raw;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeSnapshotNhash(value: string): string | null {
|
|
45
|
+
const trimmed = value.trim();
|
|
46
|
+
if (!NHASH_PATTERN.test(trimmed)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const decoded = nhashDecode(trimmed);
|
|
52
|
+
if (decoded.key) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return trimmed;
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeTreeEventSnapshotLinkKey(value?: string | null): string | null {
|
|
62
|
+
const trimmed = value?.trim();
|
|
63
|
+
if (!trimmed) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (!LINK_KEY_PATTERN.test(trimmed)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return trimmed.toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeTreeEventSnapshotPermalink(
|
|
73
|
+
value: TreeEventSnapshotPermalink,
|
|
74
|
+
): TreeEventSnapshotPermalink {
|
|
75
|
+
const snapshotNhash = normalizeSnapshotNhash(value.snapshotNhash);
|
|
76
|
+
if (!snapshotNhash) {
|
|
77
|
+
throw new Error(`Invalid tree event snapshot nhash: ${value.snapshotNhash}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const linkKey = normalizeTreeEventSnapshotLinkKey(value.linkKey);
|
|
81
|
+
if (value.linkKey?.trim() && !linkKey) {
|
|
82
|
+
throw new Error('Invalid tree event snapshot link key');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
snapshotNhash,
|
|
87
|
+
path: value.path.filter(Boolean),
|
|
88
|
+
...(linkKey ? { linkKey } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function buildTreeEventSnapshotPermalink(
|
|
93
|
+
value: TreeEventSnapshotPermalink,
|
|
94
|
+
options: BuildTreeEventSnapshotPermalinkOptions = {},
|
|
95
|
+
): string {
|
|
96
|
+
const normalized = normalizeTreeEventSnapshotPermalink(value);
|
|
97
|
+
const encodedPath = normalized.path.map((part) => encodeURIComponent(part)).join('/');
|
|
98
|
+
const query = new URLSearchParams();
|
|
99
|
+
query.set('snapshot', '1');
|
|
100
|
+
if (normalized.linkKey) {
|
|
101
|
+
query.set('k', normalized.linkKey);
|
|
102
|
+
}
|
|
103
|
+
const prefix = options.prefix ?? '';
|
|
104
|
+
return `${prefix}${normalized.snapshotNhash}${encodedPath ? `/${encodedPath}` : ''}?${query.toString()}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function parseTreeEventSnapshotPermalink(input: string | URL): TreeEventSnapshotPermalink | null {
|
|
108
|
+
const candidate = extractPermalinkCandidate(input);
|
|
109
|
+
if (!candidate) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const [pathPart, queryPart = ''] = candidate.split('?', 2);
|
|
114
|
+
const parts = pathPart
|
|
115
|
+
.replace(/^\/+/, '')
|
|
116
|
+
.split('/')
|
|
117
|
+
.filter(Boolean)
|
|
118
|
+
.map(safeDecode);
|
|
119
|
+
|
|
120
|
+
if (parts[0] === 'nhash') {
|
|
121
|
+
parts.shift();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const snapshotNhash = parts.shift();
|
|
125
|
+
if (!snapshotNhash) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const normalizedSnapshotNhash = normalizeSnapshotNhash(snapshotNhash);
|
|
130
|
+
if (!normalizedSnapshotNhash) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const params = new URLSearchParams(queryPart);
|
|
135
|
+
if (params.get('snapshot')?.trim() !== '1') {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const rawLinkKey = params.get('k');
|
|
140
|
+
const linkKey = normalizeTreeEventSnapshotLinkKey(rawLinkKey);
|
|
141
|
+
if (rawLinkKey?.trim() && !linkKey) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
snapshotNhash: normalizedSnapshotNhash,
|
|
147
|
+
path: parts,
|
|
148
|
+
...(linkKey ? { linkKey } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decryptKeyFromLink,
|
|
3
|
+
fromHex,
|
|
4
|
+
nhashEncode,
|
|
5
|
+
toHex,
|
|
6
|
+
type CID,
|
|
7
|
+
} from '@hashtree/core';
|
|
8
|
+
import type { StoredNostrEvent } from './events.js';
|
|
9
|
+
import { normalizeTreeEventSnapshotLinkKey } from './treeEventSnapshotPermalinks.js';
|
|
10
|
+
import { compareReplaceableEvents } from './eventKeys.js';
|
|
11
|
+
import type { Nip19Like, NostrFilter } from './resolver/nostr.js';
|
|
12
|
+
import {
|
|
13
|
+
HASHTREE_ROOT_KINDS,
|
|
14
|
+
parseHashtreeRootEvent,
|
|
15
|
+
readSignedNostrEventSnapshot,
|
|
16
|
+
storeSignedNostrEventSnapshot,
|
|
17
|
+
type ParsedHashtreeRootEvent,
|
|
18
|
+
type SnapshotTarget,
|
|
19
|
+
} from './snapshot.js';
|
|
20
|
+
|
|
21
|
+
const DEFAULT_SNAPSHOT_FETCH_LIMIT = 20;
|
|
22
|
+
|
|
23
|
+
export interface TreeEventSnapshotInfo extends ParsedHashtreeRootEvent {
|
|
24
|
+
snapshotCid: CID;
|
|
25
|
+
snapshotNhash: string;
|
|
26
|
+
npub: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TreeEventSnapshotQuery extends NostrFilter {
|
|
30
|
+
limit?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface FetchLatestTreeEventSnapshotConfig {
|
|
34
|
+
snapshotTarget: SnapshotTarget;
|
|
35
|
+
nip19: Nip19Like;
|
|
36
|
+
fetchEvents: (filter: TreeEventSnapshotQuery) => Promise<StoredNostrEvent[]>;
|
|
37
|
+
snapshotFetchLimit?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WatchTreeEventSnapshotsConfig extends FetchLatestTreeEventSnapshotConfig {
|
|
41
|
+
subscribeEvents: (
|
|
42
|
+
filter: TreeEventSnapshotQuery,
|
|
43
|
+
onEvent: (event: StoredNostrEvent) => void,
|
|
44
|
+
) => () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function snapshotInfoFromParsed(
|
|
48
|
+
parsed: ParsedHashtreeRootEvent,
|
|
49
|
+
snapshotCid: CID,
|
|
50
|
+
nip19: Nip19Like,
|
|
51
|
+
): TreeEventSnapshotInfo {
|
|
52
|
+
return {
|
|
53
|
+
...parsed,
|
|
54
|
+
snapshotCid,
|
|
55
|
+
snapshotNhash: nhashEncode(snapshotCid),
|
|
56
|
+
npub: nip19.npubEncode(parsed.event.pubkey),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function tryParseHashtreeRootEvent(event: StoredNostrEvent): ParsedHashtreeRootEvent | null {
|
|
61
|
+
try {
|
|
62
|
+
return parseHashtreeRootEvent(event);
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeLinkKey(linkKey?: string | Uint8Array | null): Uint8Array | null {
|
|
69
|
+
if (!linkKey) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (typeof linkKey !== 'string') return linkKey;
|
|
73
|
+
const hex = normalizeTreeEventSnapshotLinkKey(linkKey);
|
|
74
|
+
return hex ? fromHex(hex) : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isValidTreeEventFor(
|
|
78
|
+
event: StoredNostrEvent,
|
|
79
|
+
pubkey: string,
|
|
80
|
+
treeName: string,
|
|
81
|
+
): boolean {
|
|
82
|
+
if (event.pubkey !== pubkey) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const parsed = tryParseHashtreeRootEvent(event);
|
|
86
|
+
return parsed?.treeName === treeName;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function compareTreeEventSnapshots(a: TreeEventSnapshotInfo, b: TreeEventSnapshotInfo): number {
|
|
90
|
+
return compareReplaceableEvents(a.event, b.event);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isNewerTreeEventSnapshot(
|
|
94
|
+
candidate: TreeEventSnapshotInfo,
|
|
95
|
+
current: TreeEventSnapshotInfo,
|
|
96
|
+
): boolean {
|
|
97
|
+
return compareTreeEventSnapshots(candidate, current) > 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function snapshotMatchesRootCid(
|
|
101
|
+
snapshot: TreeEventSnapshotInfo | null | undefined,
|
|
102
|
+
rootCid: CID | null | undefined,
|
|
103
|
+
): boolean {
|
|
104
|
+
if (!snapshot || !rootCid) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (toHex(snapshot.rootCid.hash) !== toHex(rootCid.hash)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (snapshot.visibility !== 'public') {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
const snapshotKey = snapshot.rootCid.key ? toHex(snapshot.rootCid.key) : null;
|
|
114
|
+
const rootKey = rootCid.key ? toHex(rootCid.key) : null;
|
|
115
|
+
if (snapshotKey === null || rootKey === null) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return snapshotKey === rootKey;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function resolveSnapshotRootCid(
|
|
122
|
+
snapshot: TreeEventSnapshotInfo,
|
|
123
|
+
linkKey?: string | Uint8Array | null,
|
|
124
|
+
): Promise<CID | null> {
|
|
125
|
+
if (snapshot.visibility === 'public') {
|
|
126
|
+
return snapshot.rootCid;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (snapshot.visibility === 'link-visible' && snapshot.encryptedKey) {
|
|
130
|
+
const normalizedLinkKey = normalizeLinkKey(linkKey);
|
|
131
|
+
if (!normalizedLinkKey) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const decryptedKey = await decryptKeyFromLink(
|
|
136
|
+
fromHex(snapshot.encryptedKey),
|
|
137
|
+
normalizedLinkKey,
|
|
138
|
+
);
|
|
139
|
+
if (decryptedKey) {
|
|
140
|
+
return { hash: snapshot.rootCid.hash, key: decryptedKey };
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function storeTreeEventSnapshot(
|
|
151
|
+
snapshotTarget: SnapshotTarget,
|
|
152
|
+
nip19: Nip19Like,
|
|
153
|
+
event: StoredNostrEvent,
|
|
154
|
+
): Promise<TreeEventSnapshotInfo | null> {
|
|
155
|
+
const parsed = tryParseHashtreeRootEvent(event);
|
|
156
|
+
if (!parsed) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const snapshotCid = await storeSignedNostrEventSnapshot(snapshotTarget, parsed.event);
|
|
160
|
+
return snapshotInfoFromParsed(parsed, snapshotCid, nip19);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function readTreeEventSnapshot(
|
|
164
|
+
snapshotTarget: SnapshotTarget,
|
|
165
|
+
nip19: Nip19Like,
|
|
166
|
+
snapshotCid: CID,
|
|
167
|
+
maxBytes?: number,
|
|
168
|
+
): Promise<TreeEventSnapshotInfo | null> {
|
|
169
|
+
try {
|
|
170
|
+
const event = await readSignedNostrEventSnapshot(snapshotTarget, snapshotCid, maxBytes);
|
|
171
|
+
const parsed = tryParseHashtreeRootEvent(event);
|
|
172
|
+
if (!parsed) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return snapshotInfoFromParsed(parsed, snapshotCid, nip19);
|
|
176
|
+
} catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function fetchLatestTreeEventSnapshot(
|
|
182
|
+
config: FetchLatestTreeEventSnapshotConfig,
|
|
183
|
+
npub: string,
|
|
184
|
+
treeName: string,
|
|
185
|
+
): Promise<TreeEventSnapshotInfo | null> {
|
|
186
|
+
let decoded;
|
|
187
|
+
try {
|
|
188
|
+
decoded = config.nip19.decode(npub);
|
|
189
|
+
} catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
if (decoded.type !== 'npub' || typeof decoded.data !== 'string') {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const events = await config.fetchEvents({
|
|
197
|
+
kinds: [...HASHTREE_ROOT_KINDS],
|
|
198
|
+
authors: [decoded.data],
|
|
199
|
+
'#d': [treeName],
|
|
200
|
+
limit: config.snapshotFetchLimit ?? DEFAULT_SNAPSHOT_FETCH_LIMIT,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const candidates: StoredNostrEvent[] = [];
|
|
204
|
+
for (const event of events) {
|
|
205
|
+
if (!isValidTreeEventFor(event, decoded.data, treeName)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
candidates.push(event);
|
|
209
|
+
}
|
|
210
|
+
if (candidates.length === 0) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
candidates.sort(compareReplaceableEvents);
|
|
215
|
+
return storeTreeEventSnapshot(config.snapshotTarget, config.nip19, candidates[candidates.length - 1]!);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function watchLatestTreeEventSnapshot(
|
|
219
|
+
config: WatchTreeEventSnapshotsConfig,
|
|
220
|
+
npub: string,
|
|
221
|
+
treeName: string,
|
|
222
|
+
onSnapshot: (snapshot: TreeEventSnapshotInfo) => void | Promise<void>,
|
|
223
|
+
): () => void {
|
|
224
|
+
let decoded;
|
|
225
|
+
try {
|
|
226
|
+
decoded = config.nip19.decode(npub);
|
|
227
|
+
} catch {
|
|
228
|
+
return () => {};
|
|
229
|
+
}
|
|
230
|
+
if (decoded.type !== 'npub' || typeof decoded.data !== 'string') {
|
|
231
|
+
return () => {};
|
|
232
|
+
}
|
|
233
|
+
const pubkey = decoded.data;
|
|
234
|
+
|
|
235
|
+
let closed = false;
|
|
236
|
+
let latestSnapshot: TreeEventSnapshotInfo | null = null;
|
|
237
|
+
|
|
238
|
+
const emitIfNewer = async (event: StoredNostrEvent): Promise<void> => {
|
|
239
|
+
if (closed || !isValidTreeEventFor(event, pubkey, treeName)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const snapshot = await storeTreeEventSnapshot(config.snapshotTarget, config.nip19, event);
|
|
244
|
+
if (!snapshot) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (latestSnapshot && compareTreeEventSnapshots(snapshot, latestSnapshot) <= 0) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
latestSnapshot = snapshot;
|
|
252
|
+
await onSnapshot(snapshot);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
fetchLatestTreeEventSnapshot(config, npub, treeName)
|
|
256
|
+
.then(async (snapshot) => {
|
|
257
|
+
if (closed || !snapshot) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (latestSnapshot && compareTreeEventSnapshots(snapshot, latestSnapshot) <= 0) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
latestSnapshot = snapshot;
|
|
264
|
+
await onSnapshot(snapshot);
|
|
265
|
+
})
|
|
266
|
+
.catch(() => {});
|
|
267
|
+
|
|
268
|
+
const unsubscribe = config.subscribeEvents(
|
|
269
|
+
{
|
|
270
|
+
kinds: [...HASHTREE_ROOT_KINDS],
|
|
271
|
+
authors: [pubkey],
|
|
272
|
+
'#d': [treeName],
|
|
273
|
+
},
|
|
274
|
+
(event) => {
|
|
275
|
+
void emitIfNewer(event);
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
return () => {
|
|
280
|
+
closed = true;
|
|
281
|
+
unsubscribe();
|
|
282
|
+
};
|
|
283
|
+
}
|
package/dist/webrtc/index.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { WebRTCStore, DEFAULT_RELAYS } from './store.js';
|
|
2
|
-
export { Peer } from './peer.js';
|
|
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, MSG_TYPE_PUBSUB_INTEREST, MSG_TYPE_PUBSUB_FRAME, MSG_TYPE_PUBSUB_INVENTORY, MSG_TYPE_PUBSUB_WANT, 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, type PubsubInterest, type PubsubFrame, type PubsubInventory, type PubsubWant, } from './types.js';
|
|
5
|
-
export { encodeRequest, encodeResponse, encodePubsubInterest, encodePubsubFrame, encodePubsubInventory, encodePubsubWant, parseMessage, createRequest, createResponse, createFragmentResponse, createPubsubInterest, createPubsubFrame, createPubsubInventory, createPubsubWant, hashToKey, verifyHash, generatePeerHTLConfig, decrementHTLWithPolicy, decrementHTL, shouldForwardHTL, shouldForward, type PeerHTLConfig, type PendingRequest, } from './protocol.js';
|
|
6
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACpB,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,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,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
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { WebRTCStore, DEFAULT_RELAYS } from './store.js';
|
|
2
|
-
export { Peer } from './peer.js';
|
|
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, MSG_TYPE_PUBSUB_INTEREST, MSG_TYPE_PUBSUB_FRAME, MSG_TYPE_PUBSUB_INVENTORY, MSG_TYPE_PUBSUB_WANT, FRAGMENT_SIZE, createMeshNostrEventFrame, validateMeshNostrFrame, parseMeshNostrFrameText, } from './types.js';
|
|
5
|
-
// Protocol functions
|
|
6
|
-
export { encodeRequest, encodeResponse, encodePubsubInterest, encodePubsubFrame, encodePubsubInventory, encodePubsubWant, parseMessage, createRequest, createResponse, createFragmentResponse, createPubsubInterest, createPubsubFrame, createPubsubInventory, createPubsubWant, hashToKey, verifyHash, generatePeerHTLConfig, decrementHTLWithPolicy, decrementHTL, shouldForwardHTL, shouldForward, } from './protocol.js';
|
|
7
|
-
//# sourceMappingURL=index.js.map
|
package/dist/webrtc/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACpB,aAAa,EACb,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,GA8BxB,MAAM,YAAY,CAAC;AAEpB,qBAAqB;AACrB,OAAO,EACL,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,aAAa,GAGd,MAAM,eAAe,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple LRU Cache implementation
|
|
3
|
-
*/
|
|
4
|
-
export declare class LRUCache<K, V> {
|
|
5
|
-
private cache;
|
|
6
|
-
private maxSize;
|
|
7
|
-
constructor(maxSize?: number);
|
|
8
|
-
get(key: K): V | undefined;
|
|
9
|
-
set(key: K, value: V): void;
|
|
10
|
-
has(key: K): boolean;
|
|
11
|
-
delete(key: K): boolean;
|
|
12
|
-
clear(): void;
|
|
13
|
-
get size(): number;
|
|
14
|
-
keys(): IterableIterator<K>;
|
|
15
|
-
values(): IterableIterator<V>;
|
|
16
|
-
entries(): IterableIterator<[K, V]>;
|
|
17
|
-
forEach(callback: (value: V, key: K) => void): void;
|
|
18
|
-
[Symbol.iterator](): IterableIterator<[K, V]>;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=lruCache.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lruCache.d.ts","sourceRoot":"","sources":["../../src/webrtc/lruCache.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC;IACxB,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE,MAAY;IAIjC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAU1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAe3B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIvB,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI3B,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI5B,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAIpC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,GAAG,IAAI;IAInD,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAG9C"}
|