@hashtree/worker 0.1.26 → 0.2.1
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 +16 -16
- package/package.json +23 -19
- package/src/app-runtime.ts +393 -0
- package/src/capabilities/blossomBandwidthTracker.ts +74 -0
- package/src/capabilities/blossomTransport.ts +179 -0
- package/src/capabilities/connectivity.ts +54 -0
- package/src/capabilities/idbStorage.ts +94 -0
- package/src/capabilities/meshRouterStore.ts +426 -0
- package/src/capabilities/rootResolver.ts +497 -0
- package/src/client-id.ts +137 -0
- package/src/client.ts +501 -0
- package/{dist/entry.js → src/entry.ts} +1 -1
- package/src/htree-path.ts +53 -0
- package/src/htree-url.ts +156 -0
- package/src/index.ts +76 -0
- package/src/mediaStreaming.ts +64 -0
- package/src/p2p/boundedQueue.ts +168 -0
- package/src/p2p/errorMessage.ts +6 -0
- package/src/p2p/index.ts +48 -0
- package/src/p2p/lruCache.ts +78 -0
- package/src/p2p/meshQueryRouter.ts +361 -0
- package/src/p2p/protocol.ts +11 -0
- package/src/p2p/queryForwardingMachine.ts +197 -0
- package/src/p2p/signaling.ts +284 -0
- package/src/p2p/uploadRateLimiter.ts +85 -0
- package/src/p2p/webrtcController.ts +1168 -0
- package/src/p2p/webrtcProxy.ts +519 -0
- package/src/privacyGuards.ts +31 -0
- package/src/protocol.ts +124 -0
- package/src/relay/identity.ts +86 -0
- package/src/relay/mediaHandler.ts +1633 -0
- package/src/relay/ndk.ts +590 -0
- package/{dist/iris/nostr-wasm.js → src/relay/nostr-wasm.ts} +4 -1
- package/src/relay/nostr.ts +249 -0
- package/src/relay/protocol.ts +361 -0
- package/src/relay/publicAssetUrl.ts +25 -0
- package/src/relay/rootPathResolver.ts +50 -0
- package/src/relay/shims.d.ts +17 -0
- package/src/relay/signing.ts +332 -0
- package/src/relay/treeRootCache.ts +354 -0
- package/src/relay/treeRootSubscription.ts +577 -0
- package/src/relay/utils/constants.ts +139 -0
- package/src/relay/utils/errorMessage.ts +7 -0
- package/src/relay/utils/lruCache.ts +79 -0
- package/src/relay/webrtc.ts +5 -0
- package/src/relay/webrtcSignaling.ts +108 -0
- package/src/relay/worker.ts +1787 -0
- package/src/relay-client.ts +265 -0
- package/src/relay-entry.ts +1 -0
- package/src/runtime-network.ts +134 -0
- package/src/runtime.ts +153 -0
- package/src/transferableBytes.ts +5 -0
- package/src/tree-root.ts +851 -0
- package/src/types.ts +8 -0
- package/src/worker.ts +975 -0
- package/LICENSE +0 -21
- package/dist/app-runtime.d.ts +0 -60
- package/dist/app-runtime.d.ts.map +0 -1
- package/dist/app-runtime.js +0 -271
- package/dist/app-runtime.js.map +0 -1
- package/dist/capabilities/blossomBandwidthTracker.d.ts +0 -26
- package/dist/capabilities/blossomBandwidthTracker.d.ts.map +0 -1
- package/dist/capabilities/blossomBandwidthTracker.js +0 -53
- package/dist/capabilities/blossomBandwidthTracker.js.map +0 -1
- package/dist/capabilities/blossomTransport.d.ts +0 -22
- package/dist/capabilities/blossomTransport.d.ts.map +0 -1
- package/dist/capabilities/blossomTransport.js +0 -144
- package/dist/capabilities/blossomTransport.js.map +0 -1
- package/dist/capabilities/connectivity.d.ts +0 -3
- package/dist/capabilities/connectivity.d.ts.map +0 -1
- package/dist/capabilities/connectivity.js +0 -49
- package/dist/capabilities/connectivity.js.map +0 -1
- package/dist/capabilities/idbStorage.d.ts +0 -25
- package/dist/capabilities/idbStorage.d.ts.map +0 -1
- package/dist/capabilities/idbStorage.js +0 -73
- package/dist/capabilities/idbStorage.js.map +0 -1
- package/dist/capabilities/meshRouterStore.d.ts +0 -71
- package/dist/capabilities/meshRouterStore.d.ts.map +0 -1
- package/dist/capabilities/meshRouterStore.js +0 -316
- package/dist/capabilities/meshRouterStore.js.map +0 -1
- package/dist/capabilities/rootResolver.d.ts +0 -10
- package/dist/capabilities/rootResolver.d.ts.map +0 -1
- package/dist/capabilities/rootResolver.js +0 -393
- package/dist/capabilities/rootResolver.js.map +0 -1
- package/dist/client-id.d.ts +0 -18
- package/dist/client-id.d.ts.map +0 -1
- package/dist/client-id.js +0 -98
- package/dist/client-id.js.map +0 -1
- package/dist/client.d.ts +0 -61
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -417
- package/dist/client.js.map +0 -1
- package/dist/entry.d.ts +0 -2
- package/dist/entry.d.ts.map +0 -1
- package/dist/entry.js.map +0 -1
- package/dist/htree-path.d.ts +0 -13
- package/dist/htree-path.d.ts.map +0 -1
- package/dist/htree-path.js +0 -38
- package/dist/htree-path.js.map +0 -1
- package/dist/htree-url.d.ts +0 -22
- package/dist/htree-url.d.ts.map +0 -1
- package/dist/htree-url.js +0 -118
- package/dist/htree-url.js.map +0 -1
- package/dist/index.d.ts +0 -17
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -8
- package/dist/index.js.map +0 -1
- package/dist/iris/identity.d.ts +0 -36
- package/dist/iris/identity.d.ts.map +0 -1
- package/dist/iris/identity.js +0 -78
- package/dist/iris/identity.js.map +0 -1
- package/dist/iris/mediaHandler.d.ts +0 -64
- package/dist/iris/mediaHandler.d.ts.map +0 -1
- package/dist/iris/mediaHandler.js +0 -1285
- package/dist/iris/mediaHandler.js.map +0 -1
- package/dist/iris/ndk.d.ts +0 -96
- package/dist/iris/ndk.d.ts.map +0 -1
- package/dist/iris/ndk.js +0 -502
- package/dist/iris/ndk.js.map +0 -1
- package/dist/iris/nostr-wasm.d.ts +0 -14
- package/dist/iris/nostr-wasm.d.ts.map +0 -1
- package/dist/iris/nostr-wasm.js.map +0 -1
- package/dist/iris/nostr.d.ts +0 -60
- package/dist/iris/nostr.d.ts.map +0 -1
- package/dist/iris/nostr.js +0 -207
- package/dist/iris/nostr.js.map +0 -1
- package/dist/iris/protocol.d.ts +0 -583
- package/dist/iris/protocol.d.ts.map +0 -1
- package/dist/iris/protocol.js +0 -16
- package/dist/iris/protocol.js.map +0 -1
- package/dist/iris/publicAssetUrl.d.ts +0 -6
- package/dist/iris/publicAssetUrl.d.ts.map +0 -1
- package/dist/iris/publicAssetUrl.js +0 -14
- package/dist/iris/publicAssetUrl.js.map +0 -1
- package/dist/iris/rootPathResolver.d.ts +0 -9
- package/dist/iris/rootPathResolver.d.ts.map +0 -1
- package/dist/iris/rootPathResolver.js +0 -32
- package/dist/iris/rootPathResolver.js.map +0 -1
- package/dist/iris/signing.d.ts +0 -50
- package/dist/iris/signing.d.ts.map +0 -1
- package/dist/iris/signing.js +0 -299
- package/dist/iris/signing.js.map +0 -1
- package/dist/iris/treeRootCache.d.ts +0 -86
- package/dist/iris/treeRootCache.d.ts.map +0 -1
- package/dist/iris/treeRootCache.js +0 -269
- package/dist/iris/treeRootCache.js.map +0 -1
- package/dist/iris/treeRootSubscription.d.ts +0 -55
- package/dist/iris/treeRootSubscription.d.ts.map +0 -1
- package/dist/iris/treeRootSubscription.js +0 -479
- package/dist/iris/treeRootSubscription.js.map +0 -1
- package/dist/iris/utils/constants.d.ts +0 -76
- package/dist/iris/utils/constants.d.ts.map +0 -1
- package/dist/iris/utils/constants.js +0 -113
- package/dist/iris/utils/constants.js.map +0 -1
- package/dist/iris/utils/errorMessage.d.ts +0 -5
- package/dist/iris/utils/errorMessage.d.ts.map +0 -1
- package/dist/iris/utils/errorMessage.js +0 -8
- package/dist/iris/utils/errorMessage.js.map +0 -1
- package/dist/iris/utils/lruCache.d.ts +0 -26
- package/dist/iris/utils/lruCache.d.ts.map +0 -1
- package/dist/iris/utils/lruCache.js +0 -66
- package/dist/iris/utils/lruCache.js.map +0 -1
- package/dist/iris/webrtc.d.ts +0 -2
- package/dist/iris/webrtc.d.ts.map +0 -1
- package/dist/iris/webrtc.js +0 -3
- package/dist/iris/webrtc.js.map +0 -1
- package/dist/iris/webrtcSignaling.d.ts +0 -37
- package/dist/iris/webrtcSignaling.d.ts.map +0 -1
- package/dist/iris/webrtcSignaling.js +0 -86
- package/dist/iris/webrtcSignaling.js.map +0 -1
- package/dist/iris/worker.d.ts +0 -12
- package/dist/iris/worker.d.ts.map +0 -1
- package/dist/iris/worker.js +0 -1529
- package/dist/iris/worker.js.map +0 -1
- package/dist/iris-client.d.ts +0 -31
- package/dist/iris-client.d.ts.map +0 -1
- package/dist/iris-client.js +0 -197
- package/dist/iris-client.js.map +0 -1
- package/dist/iris-entry.d.ts +0 -2
- package/dist/iris-entry.d.ts.map +0 -1
- package/dist/iris-entry.js +0 -2
- package/dist/iris-entry.js.map +0 -1
- package/dist/mediaStreaming.d.ts +0 -7
- package/dist/mediaStreaming.d.ts.map +0 -1
- package/dist/mediaStreaming.js +0 -48
- package/dist/mediaStreaming.js.map +0 -1
- package/dist/p2p/boundedQueue.d.ts +0 -79
- package/dist/p2p/boundedQueue.d.ts.map +0 -1
- package/dist/p2p/boundedQueue.js +0 -134
- package/dist/p2p/boundedQueue.js.map +0 -1
- package/dist/p2p/errorMessage.d.ts +0 -5
- package/dist/p2p/errorMessage.d.ts.map +0 -1
- package/dist/p2p/errorMessage.js +0 -7
- package/dist/p2p/errorMessage.js.map +0 -1
- package/dist/p2p/index.d.ts +0 -8
- package/dist/p2p/index.d.ts.map +0 -1
- package/dist/p2p/index.js +0 -6
- package/dist/p2p/index.js.map +0 -1
- package/dist/p2p/lruCache.d.ts +0 -26
- package/dist/p2p/lruCache.d.ts.map +0 -1
- package/dist/p2p/lruCache.js +0 -65
- package/dist/p2p/lruCache.js.map +0 -1
- package/dist/p2p/meshQueryRouter.d.ts +0 -44
- package/dist/p2p/meshQueryRouter.d.ts.map +0 -1
- package/dist/p2p/meshQueryRouter.js +0 -228
- package/dist/p2p/meshQueryRouter.js.map +0 -1
- package/dist/p2p/protocol.d.ts +0 -10
- package/dist/p2p/protocol.d.ts.map +0 -1
- package/dist/p2p/protocol.js +0 -2
- package/dist/p2p/protocol.js.map +0 -1
- package/dist/p2p/queryForwardingMachine.d.ts +0 -46
- package/dist/p2p/queryForwardingMachine.d.ts.map +0 -1
- package/dist/p2p/queryForwardingMachine.js +0 -144
- package/dist/p2p/queryForwardingMachine.js.map +0 -1
- package/dist/p2p/signaling.d.ts +0 -63
- package/dist/p2p/signaling.d.ts.map +0 -1
- package/dist/p2p/signaling.js +0 -185
- package/dist/p2p/signaling.js.map +0 -1
- package/dist/p2p/uploadRateLimiter.d.ts +0 -21
- package/dist/p2p/uploadRateLimiter.d.ts.map +0 -1
- package/dist/p2p/uploadRateLimiter.js +0 -62
- package/dist/p2p/uploadRateLimiter.js.map +0 -1
- package/dist/p2p/webrtcController.d.ts +0 -168
- package/dist/p2p/webrtcController.d.ts.map +0 -1
- package/dist/p2p/webrtcController.js +0 -902
- package/dist/p2p/webrtcController.js.map +0 -1
- package/dist/p2p/webrtcProxy.d.ts +0 -62
- package/dist/p2p/webrtcProxy.d.ts.map +0 -1
- package/dist/p2p/webrtcProxy.js +0 -447
- package/dist/p2p/webrtcProxy.js.map +0 -1
- package/dist/privacyGuards.d.ts +0 -14
- package/dist/privacyGuards.d.ts.map +0 -1
- package/dist/privacyGuards.js +0 -27
- package/dist/privacyGuards.js.map +0 -1
- package/dist/protocol.d.ts +0 -225
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -2
- package/dist/protocol.js.map +0 -1
- package/dist/runtime-network.d.ts +0 -23
- package/dist/runtime-network.d.ts.map +0 -1
- package/dist/runtime-network.js +0 -105
- package/dist/runtime-network.js.map +0 -1
- package/dist/runtime.d.ts +0 -23
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -122
- package/dist/runtime.js.map +0 -1
- package/dist/tree-root.d.ts +0 -201
- package/dist/tree-root.d.ts.map +0 -1
- package/dist/tree-root.js +0 -632
- package/dist/tree-root.js.map +0 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/worker.d.ts +0 -9
- package/dist/worker.d.ts.map +0 -1
- package/dist/worker.js +0 -797
- package/dist/worker.js.map +0 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import type { SignalingMessage } from '@hashtree/nostr';
|
|
2
|
+
|
|
3
|
+
type DirectedSignalingMessage = Exclude<SignalingMessage, { type: 'hello' }>;
|
|
4
|
+
|
|
5
|
+
export const SIGNALING_KIND = 25050;
|
|
6
|
+
export const HELLO_TAG = 'hello';
|
|
7
|
+
export const MAX_EVENT_AGE_SEC = 30;
|
|
8
|
+
const HELLO_EXPIRATION_SEC = 5 * 60;
|
|
9
|
+
|
|
10
|
+
export interface SignalingEventLike {
|
|
11
|
+
pubkey: string;
|
|
12
|
+
created_at?: number;
|
|
13
|
+
tags: string[][];
|
|
14
|
+
content: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface GiftSeal {
|
|
18
|
+
pubkey: string;
|
|
19
|
+
kind: number;
|
|
20
|
+
content: string;
|
|
21
|
+
tags: string[][];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SignalingTemplate {
|
|
25
|
+
kind: number;
|
|
26
|
+
created_at: number;
|
|
27
|
+
tags: string[][];
|
|
28
|
+
content: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SignalingInnerEvent {
|
|
32
|
+
kind: number;
|
|
33
|
+
content: string;
|
|
34
|
+
tags: string[][];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SignalingFilters {
|
|
38
|
+
since: number;
|
|
39
|
+
helloFilter: {
|
|
40
|
+
kinds: number[];
|
|
41
|
+
'#l': string[];
|
|
42
|
+
since: number;
|
|
43
|
+
};
|
|
44
|
+
directedFilter: {
|
|
45
|
+
kinds: number[];
|
|
46
|
+
'#p': string[];
|
|
47
|
+
since: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface SendSignalingMessageOptions<TEvent extends SignalingEventLike> {
|
|
52
|
+
msg: SignalingMessage;
|
|
53
|
+
recipientPubkey?: string;
|
|
54
|
+
signEvent: (template: SignalingTemplate) => Promise<TEvent>;
|
|
55
|
+
giftWrap: (innerEvent: SignalingInnerEvent, recipientPubkey: string) => Promise<TEvent>;
|
|
56
|
+
publish: (event: TEvent) => Promise<void>;
|
|
57
|
+
nowMs?: () => number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface DecodeSignalingEventOptions<TEvent extends SignalingEventLike> {
|
|
61
|
+
event: TEvent;
|
|
62
|
+
giftUnwrap: (event: TEvent) => Promise<GiftSeal | null>;
|
|
63
|
+
nowMs?: () => number;
|
|
64
|
+
maxEventAgeSec?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface DecodedSignalingEvent {
|
|
68
|
+
senderPubkey: string;
|
|
69
|
+
message: SignalingMessage;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getSince(nowMs: number, maxEventAgeSec: number): number {
|
|
73
|
+
return Math.floor((nowMs - maxEventAgeSec * 1000) / 1000);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isExpired(event: SignalingEventLike, nowSec: number, maxEventAgeSec: number): boolean {
|
|
77
|
+
const createdAt = event.created_at ?? 0;
|
|
78
|
+
if (nowSec - createdAt > maxEventAgeSec) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const expirationTag = event.tags.find((tag) => tag[0] === 'expiration');
|
|
83
|
+
if (expirationTag?.[1]) {
|
|
84
|
+
const expiration = Number.parseInt(expirationTag[1], 10);
|
|
85
|
+
if (Number.isFinite(expiration) && expiration < nowSec) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalizePeerEndpoint(value: string, senderPubkey: string): string {
|
|
94
|
+
const trimmed = value.trim();
|
|
95
|
+
if (!trimmed) return senderPubkey;
|
|
96
|
+
return trimmed.includes(':') ? senderPubkey : trimmed;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function normalizeSignalingMessage(raw: unknown, senderPubkey: string): SignalingMessage | null {
|
|
100
|
+
if (!raw || typeof raw !== 'object' || !('type' in raw)) return null;
|
|
101
|
+
const msg = raw as Record<string, unknown>;
|
|
102
|
+
if (typeof msg.type !== 'string') return null;
|
|
103
|
+
|
|
104
|
+
if (typeof msg.peerId === 'string' && msg.type === 'hello') {
|
|
105
|
+
return {
|
|
106
|
+
...(msg as unknown as Extract<SignalingMessage, { type: 'hello' }>),
|
|
107
|
+
peerId: senderPubkey,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
'targetPeerId' in msg &&
|
|
113
|
+
typeof msg.targetPeerId === 'string' &&
|
|
114
|
+
typeof msg.peerId === 'string'
|
|
115
|
+
) {
|
|
116
|
+
return {
|
|
117
|
+
...(msg as unknown as DirectedSignalingMessage),
|
|
118
|
+
peerId: senderPubkey,
|
|
119
|
+
targetPeerId: normalizePeerEndpoint(msg.targetPeerId, senderPubkey),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!('recipient' in msg) || typeof msg.recipient !== 'string' || typeof msg.peerId !== 'string') {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const senderPeerId = senderPubkey;
|
|
128
|
+
const targetPeerId = normalizePeerEndpoint(msg.recipient, senderPubkey);
|
|
129
|
+
|
|
130
|
+
switch (msg.type) {
|
|
131
|
+
case 'offer': {
|
|
132
|
+
const offer = msg.offer as { sdp?: string } | string | undefined;
|
|
133
|
+
const sdp = typeof offer === 'string' ? offer : offer?.sdp;
|
|
134
|
+
return sdp ? { type: 'offer', peerId: senderPeerId, targetPeerId, sdp } : null;
|
|
135
|
+
}
|
|
136
|
+
case 'answer': {
|
|
137
|
+
const answer = msg.answer as { sdp?: string } | string | undefined;
|
|
138
|
+
const sdp = typeof answer === 'string' ? answer : answer?.sdp;
|
|
139
|
+
return sdp ? { type: 'answer', peerId: senderPeerId, targetPeerId, sdp } : null;
|
|
140
|
+
}
|
|
141
|
+
case 'candidate': {
|
|
142
|
+
const candidateObj = msg.candidate as { candidate?: string; sdpMLineIndex?: number; sdpMid?: string } | string | undefined;
|
|
143
|
+
const candidate = typeof candidateObj === 'string' ? candidateObj : candidateObj?.candidate;
|
|
144
|
+
return candidate
|
|
145
|
+
? {
|
|
146
|
+
type: 'candidate',
|
|
147
|
+
peerId: senderPeerId,
|
|
148
|
+
targetPeerId,
|
|
149
|
+
candidate,
|
|
150
|
+
sdpMLineIndex: typeof candidateObj === 'object' ? candidateObj?.sdpMLineIndex : undefined,
|
|
151
|
+
sdpMid: typeof candidateObj === 'object' ? candidateObj?.sdpMid : undefined,
|
|
152
|
+
}
|
|
153
|
+
: null;
|
|
154
|
+
}
|
|
155
|
+
case 'candidates': {
|
|
156
|
+
const candidates = Array.isArray(msg.candidates)
|
|
157
|
+
? msg.candidates
|
|
158
|
+
.map((entry) => {
|
|
159
|
+
if (typeof entry === 'string') {
|
|
160
|
+
return { candidate: entry };
|
|
161
|
+
}
|
|
162
|
+
if (entry && typeof entry === 'object') {
|
|
163
|
+
const candidateEntry = entry as { candidate?: string; sdpMLineIndex?: number; sdpMid?: string };
|
|
164
|
+
if (typeof candidateEntry.candidate === 'string') {
|
|
165
|
+
return {
|
|
166
|
+
candidate: candidateEntry.candidate,
|
|
167
|
+
sdpMLineIndex: candidateEntry.sdpMLineIndex,
|
|
168
|
+
sdpMid: candidateEntry.sdpMid,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
})
|
|
174
|
+
.filter((entry): entry is { candidate: string; sdpMLineIndex?: number; sdpMid?: string } => !!entry)
|
|
175
|
+
: [];
|
|
176
|
+
|
|
177
|
+
return { type: 'candidates', peerId: senderPeerId, targetPeerId, candidates };
|
|
178
|
+
}
|
|
179
|
+
default:
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function createSignalingFilters(
|
|
185
|
+
myPubkey: string,
|
|
186
|
+
nowMs = Date.now(),
|
|
187
|
+
maxEventAgeSec = MAX_EVENT_AGE_SEC
|
|
188
|
+
): SignalingFilters {
|
|
189
|
+
const since = getSince(nowMs, maxEventAgeSec);
|
|
190
|
+
return {
|
|
191
|
+
since,
|
|
192
|
+
helloFilter: {
|
|
193
|
+
kinds: [SIGNALING_KIND],
|
|
194
|
+
'#l': [HELLO_TAG],
|
|
195
|
+
since,
|
|
196
|
+
},
|
|
197
|
+
directedFilter: {
|
|
198
|
+
kinds: [SIGNALING_KIND],
|
|
199
|
+
'#p': [myPubkey],
|
|
200
|
+
since,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export async function sendSignalingMessage<TEvent extends SignalingEventLike>({
|
|
206
|
+
msg,
|
|
207
|
+
recipientPubkey,
|
|
208
|
+
signEvent,
|
|
209
|
+
giftWrap,
|
|
210
|
+
publish,
|
|
211
|
+
nowMs = () => Date.now(),
|
|
212
|
+
}: SendSignalingMessageOptions<TEvent>): Promise<void> {
|
|
213
|
+
if (recipientPubkey) {
|
|
214
|
+
const wrappedEvent = await giftWrap(
|
|
215
|
+
{
|
|
216
|
+
kind: SIGNALING_KIND,
|
|
217
|
+
content: JSON.stringify(msg),
|
|
218
|
+
tags: [],
|
|
219
|
+
},
|
|
220
|
+
recipientPubkey
|
|
221
|
+
);
|
|
222
|
+
await publish(wrappedEvent);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const createdAt = Math.floor(nowMs() / 1000);
|
|
227
|
+
const event = await signEvent({
|
|
228
|
+
kind: SIGNALING_KIND,
|
|
229
|
+
created_at: createdAt,
|
|
230
|
+
tags: [
|
|
231
|
+
['l', HELLO_TAG],
|
|
232
|
+
['peerId', msg.peerId],
|
|
233
|
+
['expiration', String(createdAt + HELLO_EXPIRATION_SEC)],
|
|
234
|
+
],
|
|
235
|
+
content: '',
|
|
236
|
+
});
|
|
237
|
+
await publish(event);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export async function decodeSignalingEvent<TEvent extends SignalingEventLike>({
|
|
241
|
+
event,
|
|
242
|
+
giftUnwrap,
|
|
243
|
+
nowMs = () => Date.now(),
|
|
244
|
+
maxEventAgeSec = MAX_EVENT_AGE_SEC,
|
|
245
|
+
}: DecodeSignalingEventOptions<TEvent>): Promise<DecodedSignalingEvent | null> {
|
|
246
|
+
const nowSec = nowMs() / 1000;
|
|
247
|
+
if (isExpired(event, nowSec, maxEventAgeSec)) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const isHello = event.tags.some((tag) => tag[0] === 'l' && tag[1] === HELLO_TAG);
|
|
252
|
+
if (isHello) {
|
|
253
|
+
const peerIdTag = event.tags.find((tag) => tag[0] === 'peerId');
|
|
254
|
+
if (!peerIdTag?.[1]) return null;
|
|
255
|
+
const senderPeerId = normalizePeerEndpoint(event.pubkey, event.pubkey);
|
|
256
|
+
if (normalizePeerEndpoint(peerIdTag[1], event.pubkey) !== senderPeerId) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
senderPubkey: event.pubkey,
|
|
261
|
+
message: {
|
|
262
|
+
type: 'hello',
|
|
263
|
+
peerId: senderPeerId,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const seal = await giftUnwrap(event);
|
|
269
|
+
if (!seal?.content) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
const raw = JSON.parse(seal.content);
|
|
275
|
+
const message = normalizeSignalingMessage(raw, seal.pubkey);
|
|
276
|
+
if (!message) return null;
|
|
277
|
+
return {
|
|
278
|
+
senderPubkey: seal.pubkey,
|
|
279
|
+
message,
|
|
280
|
+
};
|
|
281
|
+
} catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
type UploadRateLimiterConfig = {
|
|
2
|
+
bytesPerSecond?: number | null;
|
|
3
|
+
now?: () => number;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type UploadReservation = {
|
|
7
|
+
allowed: boolean;
|
|
8
|
+
delayMs: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function normalizeBytesPerSecond(value?: number | null): number | null {
|
|
12
|
+
if (!Number.isFinite(value) || !value || value <= 0) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return Math.floor(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class UploadRateLimiter {
|
|
19
|
+
private bytesPerSecond: number | null;
|
|
20
|
+
private availableBytes: number;
|
|
21
|
+
private lastRefillMs: number;
|
|
22
|
+
private readonly now: () => number;
|
|
23
|
+
|
|
24
|
+
constructor(config: UploadRateLimiterConfig = {}) {
|
|
25
|
+
this.now = config.now ?? (() => performance.now());
|
|
26
|
+
this.bytesPerSecond = normalizeBytesPerSecond(config.bytesPerSecond);
|
|
27
|
+
this.availableBytes = this.bytesPerSecond ?? Number.POSITIVE_INFINITY;
|
|
28
|
+
this.lastRefillMs = this.now();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setBytesPerSecond(bytesPerSecond?: number | null): void {
|
|
32
|
+
const nowMs = this.now();
|
|
33
|
+
this.refill(nowMs);
|
|
34
|
+
this.bytesPerSecond = normalizeBytesPerSecond(bytesPerSecond);
|
|
35
|
+
this.availableBytes = this.bytesPerSecond
|
|
36
|
+
? Math.min(this.availableBytes, this.bytesPerSecond)
|
|
37
|
+
: Number.POSITIVE_INFINITY;
|
|
38
|
+
this.lastRefillMs = nowMs;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getBytesPerSecond(): number | null {
|
|
42
|
+
return this.bytesPerSecond;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
reserve(byteLength: number): UploadReservation {
|
|
46
|
+
if (byteLength <= 0) {
|
|
47
|
+
return { allowed: true, delayMs: 0 };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const limit = this.bytesPerSecond;
|
|
51
|
+
if (!limit) {
|
|
52
|
+
return { allowed: true, delayMs: 0 };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const nowMs = this.now();
|
|
56
|
+
this.refill(nowMs);
|
|
57
|
+
|
|
58
|
+
if (this.availableBytes >= byteLength) {
|
|
59
|
+
this.availableBytes = Math.max(0, this.availableBytes - byteLength);
|
|
60
|
+
return { allowed: true, delayMs: 0 };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const missingBytes = byteLength - this.availableBytes;
|
|
64
|
+
return {
|
|
65
|
+
allowed: false,
|
|
66
|
+
delayMs: Math.max(4, Math.ceil((missingBytes / limit) * 1000)),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private refill(nowMs: number): void {
|
|
71
|
+
const limit = this.bytesPerSecond;
|
|
72
|
+
if (!limit) {
|
|
73
|
+
this.availableBytes = Number.POSITIVE_INFINITY;
|
|
74
|
+
this.lastRefillMs = nowMs;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const elapsedMs = Math.max(0, nowMs - this.lastRefillMs);
|
|
79
|
+
this.lastRefillMs = nowMs;
|
|
80
|
+
this.availableBytes = Math.min(
|
|
81
|
+
limit,
|
|
82
|
+
this.availableBytes + (elapsedMs * limit) / 1000,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|