@hashtree/worker 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -3
- 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/src/entry.ts +3 -0
- 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/src/relay/nostr-wasm.ts +249 -0
- 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
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Worker Protocol Types
|
|
4
|
+
*
|
|
5
|
+
* Message types for communication between main thread and hashtree worker.
|
|
6
|
+
* Worker owns: HashTree, WebRTC, Nostr (via nostr-tools)
|
|
7
|
+
* Main thread owns: UI, NIP-07 extension access (signing/encryption)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { CID } from '@hashtree/core';
|
|
11
|
+
import type { BlossomBandwidthStats } from '@hashtree/core/worker';
|
|
12
|
+
|
|
13
|
+
// Re-export common types from hashtree's worker protocol
|
|
14
|
+
export type {
|
|
15
|
+
NostrFilter,
|
|
16
|
+
UnsignedEvent,
|
|
17
|
+
SignedEvent,
|
|
18
|
+
SocialGraphEvent,
|
|
19
|
+
BlossomBandwidthStats,
|
|
20
|
+
BlossomBandwidthServerStats,
|
|
21
|
+
} from '@hashtree/core/worker';
|
|
22
|
+
|
|
23
|
+
// Tree visibility levels
|
|
24
|
+
export type TreeVisibility = 'public' | 'link-visible' | 'private';
|
|
25
|
+
|
|
26
|
+
export interface TreeRootInfo {
|
|
27
|
+
hash: Uint8Array;
|
|
28
|
+
key?: Uint8Array;
|
|
29
|
+
visibility: TreeVisibility;
|
|
30
|
+
labels?: string[];
|
|
31
|
+
updatedAt: number;
|
|
32
|
+
snapshotNhash?: string;
|
|
33
|
+
encryptedKey?: string;
|
|
34
|
+
keyId?: string;
|
|
35
|
+
selfEncryptedKey?: string;
|
|
36
|
+
selfEncryptedLinkKey?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Extended peer stats with pool classification for apps that group peers.
|
|
40
|
+
export interface PeerStats {
|
|
41
|
+
peerId: string;
|
|
42
|
+
pubkey: string;
|
|
43
|
+
connected: boolean;
|
|
44
|
+
pool: 'follows' | 'other';
|
|
45
|
+
requestsSent: number;
|
|
46
|
+
requestsReceived: number;
|
|
47
|
+
responsesSent: number;
|
|
48
|
+
responsesReceived: number;
|
|
49
|
+
bytesSent: number;
|
|
50
|
+
bytesReceived: number;
|
|
51
|
+
forwardedRequests: number;
|
|
52
|
+
forwardedResolved: number;
|
|
53
|
+
forwardedSuppressed: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Main Thread → Worker Messages
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
export type WorkerRequest =
|
|
61
|
+
// Lifecycle
|
|
62
|
+
| { type: 'init'; id: string; config: WorkerConfig }
|
|
63
|
+
| { type: 'close'; id: string }
|
|
64
|
+
| { type: 'setIdentity'; id: string; pubkey: string; nsec?: string }
|
|
65
|
+
|
|
66
|
+
// Store operations (low-level hash-based)
|
|
67
|
+
| { type: 'get'; id: string; hash: Uint8Array }
|
|
68
|
+
| { type: 'put'; id: string; hash: Uint8Array; data: Uint8Array }
|
|
69
|
+
| { type: 'has'; id: string; hash: Uint8Array }
|
|
70
|
+
| { type: 'delete'; id: string; hash: Uint8Array }
|
|
71
|
+
|
|
72
|
+
// Tree operations (high-level CID-based)
|
|
73
|
+
| { type: 'readFile'; id: string; cid: CID }
|
|
74
|
+
| { type: 'readFileRange'; id: string; cid: CID; start: number; end?: number }
|
|
75
|
+
| { type: 'readFileStream'; id: string; cid: CID }
|
|
76
|
+
| { type: 'writeFile'; id: string; parentCid: CID | null; path: string; data: Uint8Array }
|
|
77
|
+
| { type: 'deleteFile'; id: string; parentCid: CID; path: string }
|
|
78
|
+
| { type: 'listDir'; id: string; cid: CID }
|
|
79
|
+
| { type: 'resolveRoot'; id: string; npub: string; path?: string }
|
|
80
|
+
|
|
81
|
+
// Tree root cache and subscriptions
|
|
82
|
+
| {
|
|
83
|
+
type: 'setTreeRootCache';
|
|
84
|
+
id: string;
|
|
85
|
+
npub: string;
|
|
86
|
+
treeName: string;
|
|
87
|
+
hash: Uint8Array;
|
|
88
|
+
key?: Uint8Array;
|
|
89
|
+
visibility: TreeVisibility;
|
|
90
|
+
labels?: string[];
|
|
91
|
+
encryptedKey?: string;
|
|
92
|
+
keyId?: string;
|
|
93
|
+
selfEncryptedKey?: string;
|
|
94
|
+
selfEncryptedLinkKey?: string;
|
|
95
|
+
}
|
|
96
|
+
| { type: 'getTreeRootInfo'; id: string; npub: string; treeName: string }
|
|
97
|
+
| { type: 'mergeTreeRootKey'; id: string; npub: string; treeName: string; hash: Uint8Array; key: Uint8Array }
|
|
98
|
+
| { type: 'subscribeTreeRoots'; id: string; pubkey: string }
|
|
99
|
+
| { type: 'unsubscribeTreeRoots'; id: string; pubkey: string }
|
|
100
|
+
|
|
101
|
+
// Nostr subscriptions
|
|
102
|
+
| { type: 'subscribe'; id: string; filters: NostrFilter[] }
|
|
103
|
+
| { type: 'unsubscribe'; id: string; subId: string }
|
|
104
|
+
| { type: 'publish'; id: string; event: SignedEvent }
|
|
105
|
+
|
|
106
|
+
// Media streaming (service worker registers a MessagePort)
|
|
107
|
+
| { type: 'registerMediaPort'; port: MessagePort; debug?: boolean }
|
|
108
|
+
|
|
109
|
+
// Stats
|
|
110
|
+
| { type: 'getPeerStats'; id: string }
|
|
111
|
+
| { type: 'getRelayStats'; id: string }
|
|
112
|
+
| { type: 'getStorageStats'; id: string }
|
|
113
|
+
|
|
114
|
+
// WebRTC pool configuration
|
|
115
|
+
| { type: 'setWebRTCPools'; id: string; pools: { follows: { max: number; satisfied: number }; other: { max: number; satisfied: number } } }
|
|
116
|
+
| { type: 'setWebRTCForwardRateLimit'; id: string; forwardRateLimit?: ForwardRateLimitConfig }
|
|
117
|
+
| { type: 'sendWebRTCHello'; id: string }
|
|
118
|
+
| { type: 'setFollows'; id: string; follows: string[] }
|
|
119
|
+
|
|
120
|
+
// Blossom configuration
|
|
121
|
+
| { type: 'setBlossomServers'; id: string; servers: BlossomServerConfig[] }
|
|
122
|
+
|
|
123
|
+
// Storage configuration
|
|
124
|
+
| { type: 'setStorageMaxBytes'; id: string; maxBytes: number }
|
|
125
|
+
|
|
126
|
+
// Blossom upload
|
|
127
|
+
| { type: 'pushToBlossom'; id: string; cidHash: Uint8Array; cidKey?: Uint8Array; treeName?: string }
|
|
128
|
+
| { type: 'startBlossomSession'; id: string; sessionId: string; totalChunks: number }
|
|
129
|
+
| { type: 'endBlossomSession'; id: string }
|
|
130
|
+
|
|
131
|
+
// Republish cached events
|
|
132
|
+
| { type: 'republishTrees'; id: string }
|
|
133
|
+
| { type: 'republishTree'; id: string; pubkey: string; treeName: string }
|
|
134
|
+
|
|
135
|
+
// Heartbeat
|
|
136
|
+
| { type: 'ping'; id: string }
|
|
137
|
+
|
|
138
|
+
// SocialGraph
|
|
139
|
+
| { type: 'initSocialGraph'; id: string; rootPubkey?: string }
|
|
140
|
+
| { type: 'setSocialGraphRoot'; id: string; pubkey: string }
|
|
141
|
+
| { type: 'handleSocialGraphEvents'; id: string; events: SocialGraphEvent[] }
|
|
142
|
+
| { type: 'getFollowDistance'; id: string; pubkey: string }
|
|
143
|
+
| { type: 'isFollowing'; id: string; follower: string; followed: string }
|
|
144
|
+
| { type: 'getFollows'; id: string; pubkey: string }
|
|
145
|
+
| { type: 'getFollowers'; id: string; pubkey: string }
|
|
146
|
+
| { type: 'getFollowedByFriends'; id: string; pubkey: string }
|
|
147
|
+
| { type: 'getSocialGraphSize'; id: string }
|
|
148
|
+
|
|
149
|
+
// NIP-07 responses (main thread → worker, after signing/encryption)
|
|
150
|
+
| { type: 'signed'; id: string; event?: SignedEvent; error?: string }
|
|
151
|
+
| { type: 'encrypted'; id: string; ciphertext?: string; error?: string }
|
|
152
|
+
| { type: 'decrypted'; id: string; plaintext?: string; error?: string }
|
|
153
|
+
|
|
154
|
+
// WebRTC proxy events (main thread reports to worker)
|
|
155
|
+
| WebRTCEvent;
|
|
156
|
+
|
|
157
|
+
/** Blossom server configuration */
|
|
158
|
+
export interface BlossomServerConfig {
|
|
159
|
+
url: string;
|
|
160
|
+
read?: boolean; // Whether to read from this server (default true)
|
|
161
|
+
write?: boolean; // Whether to write to this server
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface ForwardRateLimitConfig {
|
|
165
|
+
maxForwardsPerPeerWindow?: number;
|
|
166
|
+
windowMs?: number;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface WorkerConfig {
|
|
170
|
+
relays: string[];
|
|
171
|
+
blossomServers?: BlossomServerConfig[]; // Blossom servers with read/write config
|
|
172
|
+
pubkey: string; // User's pubkey (required - user always logged in)
|
|
173
|
+
nsec?: string; // Hex-encoded secret key (only for nsec login, not extension)
|
|
174
|
+
storeName?: string; // IndexedDB database name, defaults to 'hashtree-worker'
|
|
175
|
+
forwardRateLimit?: ForwardRateLimitConfig;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// Worker → Main Thread Messages
|
|
180
|
+
// ============================================================================
|
|
181
|
+
|
|
182
|
+
export type WorkerResponse =
|
|
183
|
+
// Lifecycle
|
|
184
|
+
| { type: 'ready' }
|
|
185
|
+
| { type: 'pong'; id: string }
|
|
186
|
+
| { type: 'error'; id?: string; error: string }
|
|
187
|
+
|
|
188
|
+
// Generic responses
|
|
189
|
+
| { type: 'result'; id: string; data?: Uint8Array; error?: string }
|
|
190
|
+
| { type: 'bool'; id: string; value: boolean; error?: string }
|
|
191
|
+
| { type: 'cid'; id: string; cid?: CID; error?: string }
|
|
192
|
+
| { type: 'void'; id: string; error?: string }
|
|
193
|
+
|
|
194
|
+
// Tree operations
|
|
195
|
+
| { type: 'dirListing'; id: string; entries?: DirEntry[]; error?: string }
|
|
196
|
+
| { type: 'streamChunk'; id: string; chunk: Uint8Array; done: boolean }
|
|
197
|
+
|
|
198
|
+
// Nostr events
|
|
199
|
+
| { type: 'event'; subId: string; event: SignedEvent }
|
|
200
|
+
| { type: 'eose'; subId: string }
|
|
201
|
+
|
|
202
|
+
// Stats
|
|
203
|
+
| { type: 'peerStats'; id: string; stats: PeerStats[] }
|
|
204
|
+
| { type: 'relayStats'; id: string; stats: RelayStats[] }
|
|
205
|
+
| { type: 'storageStats'; id: string; items: number; bytes: number }
|
|
206
|
+
|
|
207
|
+
// Blossom notifications
|
|
208
|
+
| { type: 'blossomBandwidth'; stats: BlossomBandwidthStats }
|
|
209
|
+
| { type: 'blossomUploadError'; hash: string; error: string }
|
|
210
|
+
| { type: 'blossomUploadProgress'; progress: BlossomUploadProgress }
|
|
211
|
+
| { type: 'blossomPushResult'; id: string; pushed: number; skipped: number; failed: number; error?: string; errors?: string[] }
|
|
212
|
+
|
|
213
|
+
// Republish result
|
|
214
|
+
| { type: 'republishResult'; id: string; count: number; error?: string; encryptionErrors?: string[] }
|
|
215
|
+
|
|
216
|
+
// Background Blossom push progress (for automatic pushes)
|
|
217
|
+
| { type: 'blossomPushStarted'; treeName: string; totalChunks: number }
|
|
218
|
+
| { type: 'blossomPushProgress'; treeName: string; current: number; total: number }
|
|
219
|
+
| { type: 'blossomPushComplete'; treeName: string; pushed: number; skipped: number; failed: number }
|
|
220
|
+
|
|
221
|
+
// Tree root updates (worker → main thread notification)
|
|
222
|
+
| { type: 'treeRootUpdate'; npub: string; treeName: string; hash: Uint8Array; key?: Uint8Array; visibility: TreeVisibility; labels?: string[]; updatedAt: number; snapshotNhash?: string; encryptedKey?: string; keyId?: string; selfEncryptedKey?: string; selfEncryptedLinkKey?: string }
|
|
223
|
+
| { type: 'treeRootInfo'; id: string; record?: TreeRootInfo; error?: string }
|
|
224
|
+
|
|
225
|
+
// SocialGraph responses
|
|
226
|
+
| { type: 'socialGraphInit'; id: string; version: number; size: number; error?: string }
|
|
227
|
+
| { type: 'socialGraphVersion'; version: number }
|
|
228
|
+
| { type: 'followDistance'; id: string; distance: number; error?: string }
|
|
229
|
+
| { type: 'isFollowingResult'; id: string; result: boolean; error?: string }
|
|
230
|
+
| { type: 'pubkeyList'; id: string; pubkeys: string[]; error?: string }
|
|
231
|
+
| { type: 'socialGraphSize'; id: string; size: number; error?: string }
|
|
232
|
+
|
|
233
|
+
// NIP-07 requests (worker → main thread, needs extension)
|
|
234
|
+
| { type: 'signEvent'; id: string; event: UnsignedEvent }
|
|
235
|
+
| { type: 'nip44Encrypt'; id: string; pubkey: string; plaintext: string }
|
|
236
|
+
| { type: 'nip44Decrypt'; id: string; pubkey: string; ciphertext: string }
|
|
237
|
+
|
|
238
|
+
// WebRTC proxy commands (worker tells main thread what to do)
|
|
239
|
+
| WebRTCCommand;
|
|
240
|
+
|
|
241
|
+
export interface DirEntry {
|
|
242
|
+
name: string;
|
|
243
|
+
isDir: boolean;
|
|
244
|
+
size?: number;
|
|
245
|
+
cid?: CID;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface RelayStats {
|
|
249
|
+
url: string;
|
|
250
|
+
connected: boolean;
|
|
251
|
+
eventsReceived: number;
|
|
252
|
+
eventsSent: number;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Per-server upload status */
|
|
256
|
+
export interface BlossomServerStatus {
|
|
257
|
+
url: string;
|
|
258
|
+
uploaded: number; // Number of chunks uploaded to this server
|
|
259
|
+
failed: number; // Number of chunks failed on this server
|
|
260
|
+
skipped: number; // Number of chunks already existed on this server
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Overall blossom upload progress */
|
|
264
|
+
export interface BlossomUploadProgress {
|
|
265
|
+
sessionId: string; // Unique session identifier
|
|
266
|
+
totalChunks: number; // Total chunks to upload
|
|
267
|
+
processedChunks: number; // Chunks processed (uploaded + skipped + failed)
|
|
268
|
+
servers: BlossomServerStatus[]; // Per-server status
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ============================================================================
|
|
272
|
+
// Service Worker ↔ Worker Messages (via MessagePort)
|
|
273
|
+
// ============================================================================
|
|
274
|
+
|
|
275
|
+
// Request by direct CID (for cached/known content)
|
|
276
|
+
export interface MediaRequestByCid {
|
|
277
|
+
type: 'media';
|
|
278
|
+
requestId: string;
|
|
279
|
+
cid: string; // hex encoded CID hash
|
|
280
|
+
start: number;
|
|
281
|
+
end?: number;
|
|
282
|
+
mimeType?: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Request by npub/path (supports live streaming via tree root updates)
|
|
286
|
+
export interface MediaRequestByPath {
|
|
287
|
+
type: 'mediaByPath';
|
|
288
|
+
requestId: string;
|
|
289
|
+
npub: string;
|
|
290
|
+
path: string; // e.g., "public/video.webm"
|
|
291
|
+
start: number;
|
|
292
|
+
end?: number;
|
|
293
|
+
mimeType?: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export type MediaRequest = MediaRequestByCid | MediaRequestByPath;
|
|
297
|
+
|
|
298
|
+
export type MediaResponse =
|
|
299
|
+
| { type: 'headers'; requestId: string; totalSize: number; mimeType: string; isLive?: boolean }
|
|
300
|
+
| { type: 'chunk'; requestId: string; data: Uint8Array }
|
|
301
|
+
| { type: 'done'; requestId: string }
|
|
302
|
+
| { type: 'error'; requestId: string; message: string };
|
|
303
|
+
|
|
304
|
+
// ============================================================================
|
|
305
|
+
// WebRTC Proxy Protocol (Worker ↔ Main Thread)
|
|
306
|
+
// Worker controls logic, main thread owns RTCPeerConnection
|
|
307
|
+
// ============================================================================
|
|
308
|
+
|
|
309
|
+
/** Worker → Main: Commands to control WebRTC connections */
|
|
310
|
+
export type WebRTCCommand =
|
|
311
|
+
// Connection lifecycle
|
|
312
|
+
| { type: 'rtc:createPeer'; peerId: string; pubkey: string }
|
|
313
|
+
| { type: 'rtc:closePeer'; peerId: string }
|
|
314
|
+
|
|
315
|
+
// SDP handling
|
|
316
|
+
| { type: 'rtc:createOffer'; peerId: string }
|
|
317
|
+
| { type: 'rtc:createAnswer'; peerId: string }
|
|
318
|
+
| { type: 'rtc:setLocalDescription'; peerId: string; sdp: RTCSessionDescriptionInit }
|
|
319
|
+
| { type: 'rtc:setRemoteDescription'; peerId: string; sdp: RTCSessionDescriptionInit }
|
|
320
|
+
|
|
321
|
+
// ICE handling
|
|
322
|
+
| { type: 'rtc:addIceCandidate'; peerId: string; candidate: RTCIceCandidateInit }
|
|
323
|
+
|
|
324
|
+
// Data channel
|
|
325
|
+
| { type: 'rtc:sendData'; peerId: string; data: Uint8Array };
|
|
326
|
+
|
|
327
|
+
/** Main → Worker: Events from WebRTC connections */
|
|
328
|
+
export type WebRTCEvent =
|
|
329
|
+
// Connection state
|
|
330
|
+
| { type: 'rtc:peerCreated'; peerId: string }
|
|
331
|
+
| { type: 'rtc:peerStateChange'; peerId: string; state: RTCPeerConnectionState }
|
|
332
|
+
| { type: 'rtc:peerClosed'; peerId: string }
|
|
333
|
+
|
|
334
|
+
// SDP results
|
|
335
|
+
| { type: 'rtc:offerCreated'; peerId: string; sdp: RTCSessionDescriptionInit }
|
|
336
|
+
| { type: 'rtc:answerCreated'; peerId: string; sdp: RTCSessionDescriptionInit }
|
|
337
|
+
| { type: 'rtc:descriptionSet'; peerId: string; error?: string }
|
|
338
|
+
|
|
339
|
+
// ICE events
|
|
340
|
+
| { type: 'rtc:iceCandidate'; peerId: string; candidate: RTCIceCandidateInit | null }
|
|
341
|
+
| { type: 'rtc:iceGatheringComplete'; peerId: string }
|
|
342
|
+
|
|
343
|
+
// Data channel
|
|
344
|
+
| { type: 'rtc:dataChannelOpen'; peerId: string }
|
|
345
|
+
| { type: 'rtc:dataChannelMessage'; peerId: string; data: Uint8Array }
|
|
346
|
+
| { type: 'rtc:dataChannelClose'; peerId: string }
|
|
347
|
+
| { type: 'rtc:dataChannelError'; peerId: string; error: string }
|
|
348
|
+
|
|
349
|
+
// Backpressure signals (main thread buffer state)
|
|
350
|
+
| { type: 'rtc:bufferHigh'; peerId: string }
|
|
351
|
+
| { type: 'rtc:bufferLow'; peerId: string };
|
|
352
|
+
|
|
353
|
+
// ============================================================================
|
|
354
|
+
// Helper functions
|
|
355
|
+
// ============================================================================
|
|
356
|
+
|
|
357
|
+
let requestIdCounter = 0;
|
|
358
|
+
|
|
359
|
+
export function generateRequestId(): string {
|
|
360
|
+
return `req_${Date.now()}_${++requestIdCounter}`;
|
|
361
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ResolveWorkerPublicAssetUrlOptions {
|
|
2
|
+
importMetaUrl: string;
|
|
3
|
+
origin: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function resolveWorkerPublicAssetUrl(
|
|
7
|
+
baseUrl: string | undefined,
|
|
8
|
+
assetPath: string,
|
|
9
|
+
options: ResolveWorkerPublicAssetUrlOptions,
|
|
10
|
+
): string {
|
|
11
|
+
const normalizedAssetPath = assetPath.replace(/^\/+/, '');
|
|
12
|
+
const normalizedBaseUrl = typeof baseUrl === 'string' && baseUrl.trim() ? baseUrl.trim() : '/';
|
|
13
|
+
|
|
14
|
+
if (/^https?:\/\//i.test(normalizedBaseUrl)) {
|
|
15
|
+
return new URL(normalizedAssetPath, normalizedBaseUrl).toString();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (normalizedBaseUrl === '.' || normalizedBaseUrl === './') {
|
|
19
|
+
return new URL(`../${normalizedAssetPath}`, options.importMetaUrl).toString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const rootedBaseUrl = normalizedBaseUrl.startsWith('/') ? normalizedBaseUrl : `/${normalizedBaseUrl}`;
|
|
23
|
+
const basePath = rootedBaseUrl.endsWith('/') ? rootedBaseUrl : `${rootedBaseUrl}/`;
|
|
24
|
+
return new URL(`${basePath}${normalizedAssetPath}`, options.origin).toString();
|
|
25
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CID, HashTree } from '@hashtree/core';
|
|
2
|
+
import { resolveTreeRootNow } from './treeRootSubscription';
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_ROOT_PATH_RESOLVE_TIMEOUT_MS = 15_000;
|
|
5
|
+
|
|
6
|
+
export interface ParsedRootPath {
|
|
7
|
+
treeName: string;
|
|
8
|
+
subPath: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function parseRootPath(path?: string): ParsedRootPath {
|
|
12
|
+
const pathParts = path?.split('/').filter(Boolean) ?? [];
|
|
13
|
+
return {
|
|
14
|
+
treeName: pathParts[0] || 'public',
|
|
15
|
+
subPath: pathParts.slice(1),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function resolveRootPath(
|
|
20
|
+
tree: Pick<HashTree, 'resolvePath'> | null,
|
|
21
|
+
npub: string,
|
|
22
|
+
path?: string,
|
|
23
|
+
timeoutMs: number = DEFAULT_ROOT_PATH_RESOLVE_TIMEOUT_MS,
|
|
24
|
+
): Promise<CID | null> {
|
|
25
|
+
const exactTreeName = path?.split('/').filter(Boolean).join('/') || 'public';
|
|
26
|
+
const exactRootCid = await resolveTreeRootNow(npub, exactTreeName, timeoutMs);
|
|
27
|
+
if (exactRootCid) {
|
|
28
|
+
return exactRootCid;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { treeName, subPath } = parseRootPath(path);
|
|
32
|
+
if (subPath.length === 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const rootCid = await resolveTreeRootNow(npub, treeName, timeoutMs);
|
|
37
|
+
if (!rootCid) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (subPath.length === 0) {
|
|
42
|
+
return rootCid;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!tree) {
|
|
46
|
+
throw new Error('Tree not initialized');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (await tree.resolvePath(rootCid, subPath))?.cid ?? null;
|
|
50
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare module 'ndk' {
|
|
2
|
+
const NDK: any;
|
|
3
|
+
export default NDK;
|
|
4
|
+
export const NDKEvent: any;
|
|
5
|
+
export const NDKPrivateKeySigner: any;
|
|
6
|
+
export type NDKFilter = any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module 'ndk-cache' {
|
|
10
|
+
const NDKCacheAdapterDexie: any;
|
|
11
|
+
export default NDKCacheAdapterDexie;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module 'nostr-social-graph' {
|
|
15
|
+
export const SocialGraph: any;
|
|
16
|
+
export type NostrEvent = any;
|
|
17
|
+
}
|