@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.
Files changed (258) hide show
  1. package/README.md +16 -16
  2. package/package.json +23 -19
  3. package/src/app-runtime.ts +393 -0
  4. package/src/capabilities/blossomBandwidthTracker.ts +74 -0
  5. package/src/capabilities/blossomTransport.ts +179 -0
  6. package/src/capabilities/connectivity.ts +54 -0
  7. package/src/capabilities/idbStorage.ts +94 -0
  8. package/src/capabilities/meshRouterStore.ts +426 -0
  9. package/src/capabilities/rootResolver.ts +497 -0
  10. package/src/client-id.ts +137 -0
  11. package/src/client.ts +501 -0
  12. package/{dist/entry.js → src/entry.ts} +1 -1
  13. package/src/htree-path.ts +53 -0
  14. package/src/htree-url.ts +156 -0
  15. package/src/index.ts +76 -0
  16. package/src/mediaStreaming.ts +64 -0
  17. package/src/p2p/boundedQueue.ts +168 -0
  18. package/src/p2p/errorMessage.ts +6 -0
  19. package/src/p2p/index.ts +48 -0
  20. package/src/p2p/lruCache.ts +78 -0
  21. package/src/p2p/meshQueryRouter.ts +361 -0
  22. package/src/p2p/protocol.ts +11 -0
  23. package/src/p2p/queryForwardingMachine.ts +197 -0
  24. package/src/p2p/signaling.ts +284 -0
  25. package/src/p2p/uploadRateLimiter.ts +85 -0
  26. package/src/p2p/webrtcController.ts +1168 -0
  27. package/src/p2p/webrtcProxy.ts +519 -0
  28. package/src/privacyGuards.ts +31 -0
  29. package/src/protocol.ts +124 -0
  30. package/src/relay/identity.ts +86 -0
  31. package/src/relay/mediaHandler.ts +1633 -0
  32. package/src/relay/ndk.ts +590 -0
  33. package/{dist/iris/nostr-wasm.js → src/relay/nostr-wasm.ts} +4 -1
  34. package/src/relay/nostr.ts +249 -0
  35. package/src/relay/protocol.ts +361 -0
  36. package/src/relay/publicAssetUrl.ts +25 -0
  37. package/src/relay/rootPathResolver.ts +50 -0
  38. package/src/relay/shims.d.ts +17 -0
  39. package/src/relay/signing.ts +332 -0
  40. package/src/relay/treeRootCache.ts +354 -0
  41. package/src/relay/treeRootSubscription.ts +577 -0
  42. package/src/relay/utils/constants.ts +139 -0
  43. package/src/relay/utils/errorMessage.ts +7 -0
  44. package/src/relay/utils/lruCache.ts +79 -0
  45. package/src/relay/webrtc.ts +5 -0
  46. package/src/relay/webrtcSignaling.ts +108 -0
  47. package/src/relay/worker.ts +1787 -0
  48. package/src/relay-client.ts +265 -0
  49. package/src/relay-entry.ts +1 -0
  50. package/src/runtime-network.ts +134 -0
  51. package/src/runtime.ts +153 -0
  52. package/src/transferableBytes.ts +5 -0
  53. package/src/tree-root.ts +851 -0
  54. package/src/types.ts +8 -0
  55. package/src/worker.ts +975 -0
  56. package/LICENSE +0 -21
  57. package/dist/app-runtime.d.ts +0 -60
  58. package/dist/app-runtime.d.ts.map +0 -1
  59. package/dist/app-runtime.js +0 -271
  60. package/dist/app-runtime.js.map +0 -1
  61. package/dist/capabilities/blossomBandwidthTracker.d.ts +0 -26
  62. package/dist/capabilities/blossomBandwidthTracker.d.ts.map +0 -1
  63. package/dist/capabilities/blossomBandwidthTracker.js +0 -53
  64. package/dist/capabilities/blossomBandwidthTracker.js.map +0 -1
  65. package/dist/capabilities/blossomTransport.d.ts +0 -22
  66. package/dist/capabilities/blossomTransport.d.ts.map +0 -1
  67. package/dist/capabilities/blossomTransport.js +0 -144
  68. package/dist/capabilities/blossomTransport.js.map +0 -1
  69. package/dist/capabilities/connectivity.d.ts +0 -3
  70. package/dist/capabilities/connectivity.d.ts.map +0 -1
  71. package/dist/capabilities/connectivity.js +0 -49
  72. package/dist/capabilities/connectivity.js.map +0 -1
  73. package/dist/capabilities/idbStorage.d.ts +0 -25
  74. package/dist/capabilities/idbStorage.d.ts.map +0 -1
  75. package/dist/capabilities/idbStorage.js +0 -73
  76. package/dist/capabilities/idbStorage.js.map +0 -1
  77. package/dist/capabilities/meshRouterStore.d.ts +0 -71
  78. package/dist/capabilities/meshRouterStore.d.ts.map +0 -1
  79. package/dist/capabilities/meshRouterStore.js +0 -316
  80. package/dist/capabilities/meshRouterStore.js.map +0 -1
  81. package/dist/capabilities/rootResolver.d.ts +0 -10
  82. package/dist/capabilities/rootResolver.d.ts.map +0 -1
  83. package/dist/capabilities/rootResolver.js +0 -393
  84. package/dist/capabilities/rootResolver.js.map +0 -1
  85. package/dist/client-id.d.ts +0 -18
  86. package/dist/client-id.d.ts.map +0 -1
  87. package/dist/client-id.js +0 -98
  88. package/dist/client-id.js.map +0 -1
  89. package/dist/client.d.ts +0 -61
  90. package/dist/client.d.ts.map +0 -1
  91. package/dist/client.js +0 -417
  92. package/dist/client.js.map +0 -1
  93. package/dist/entry.d.ts +0 -2
  94. package/dist/entry.d.ts.map +0 -1
  95. package/dist/entry.js.map +0 -1
  96. package/dist/htree-path.d.ts +0 -13
  97. package/dist/htree-path.d.ts.map +0 -1
  98. package/dist/htree-path.js +0 -38
  99. package/dist/htree-path.js.map +0 -1
  100. package/dist/htree-url.d.ts +0 -22
  101. package/dist/htree-url.d.ts.map +0 -1
  102. package/dist/htree-url.js +0 -118
  103. package/dist/htree-url.js.map +0 -1
  104. package/dist/index.d.ts +0 -17
  105. package/dist/index.d.ts.map +0 -1
  106. package/dist/index.js +0 -8
  107. package/dist/index.js.map +0 -1
  108. package/dist/iris/identity.d.ts +0 -36
  109. package/dist/iris/identity.d.ts.map +0 -1
  110. package/dist/iris/identity.js +0 -78
  111. package/dist/iris/identity.js.map +0 -1
  112. package/dist/iris/mediaHandler.d.ts +0 -64
  113. package/dist/iris/mediaHandler.d.ts.map +0 -1
  114. package/dist/iris/mediaHandler.js +0 -1285
  115. package/dist/iris/mediaHandler.js.map +0 -1
  116. package/dist/iris/ndk.d.ts +0 -96
  117. package/dist/iris/ndk.d.ts.map +0 -1
  118. package/dist/iris/ndk.js +0 -502
  119. package/dist/iris/ndk.js.map +0 -1
  120. package/dist/iris/nostr-wasm.d.ts +0 -14
  121. package/dist/iris/nostr-wasm.d.ts.map +0 -1
  122. package/dist/iris/nostr-wasm.js.map +0 -1
  123. package/dist/iris/nostr.d.ts +0 -60
  124. package/dist/iris/nostr.d.ts.map +0 -1
  125. package/dist/iris/nostr.js +0 -207
  126. package/dist/iris/nostr.js.map +0 -1
  127. package/dist/iris/protocol.d.ts +0 -583
  128. package/dist/iris/protocol.d.ts.map +0 -1
  129. package/dist/iris/protocol.js +0 -16
  130. package/dist/iris/protocol.js.map +0 -1
  131. package/dist/iris/publicAssetUrl.d.ts +0 -6
  132. package/dist/iris/publicAssetUrl.d.ts.map +0 -1
  133. package/dist/iris/publicAssetUrl.js +0 -14
  134. package/dist/iris/publicAssetUrl.js.map +0 -1
  135. package/dist/iris/rootPathResolver.d.ts +0 -9
  136. package/dist/iris/rootPathResolver.d.ts.map +0 -1
  137. package/dist/iris/rootPathResolver.js +0 -32
  138. package/dist/iris/rootPathResolver.js.map +0 -1
  139. package/dist/iris/signing.d.ts +0 -50
  140. package/dist/iris/signing.d.ts.map +0 -1
  141. package/dist/iris/signing.js +0 -299
  142. package/dist/iris/signing.js.map +0 -1
  143. package/dist/iris/treeRootCache.d.ts +0 -86
  144. package/dist/iris/treeRootCache.d.ts.map +0 -1
  145. package/dist/iris/treeRootCache.js +0 -269
  146. package/dist/iris/treeRootCache.js.map +0 -1
  147. package/dist/iris/treeRootSubscription.d.ts +0 -55
  148. package/dist/iris/treeRootSubscription.d.ts.map +0 -1
  149. package/dist/iris/treeRootSubscription.js +0 -479
  150. package/dist/iris/treeRootSubscription.js.map +0 -1
  151. package/dist/iris/utils/constants.d.ts +0 -76
  152. package/dist/iris/utils/constants.d.ts.map +0 -1
  153. package/dist/iris/utils/constants.js +0 -113
  154. package/dist/iris/utils/constants.js.map +0 -1
  155. package/dist/iris/utils/errorMessage.d.ts +0 -5
  156. package/dist/iris/utils/errorMessage.d.ts.map +0 -1
  157. package/dist/iris/utils/errorMessage.js +0 -8
  158. package/dist/iris/utils/errorMessage.js.map +0 -1
  159. package/dist/iris/utils/lruCache.d.ts +0 -26
  160. package/dist/iris/utils/lruCache.d.ts.map +0 -1
  161. package/dist/iris/utils/lruCache.js +0 -66
  162. package/dist/iris/utils/lruCache.js.map +0 -1
  163. package/dist/iris/webrtc.d.ts +0 -2
  164. package/dist/iris/webrtc.d.ts.map +0 -1
  165. package/dist/iris/webrtc.js +0 -3
  166. package/dist/iris/webrtc.js.map +0 -1
  167. package/dist/iris/webrtcSignaling.d.ts +0 -37
  168. package/dist/iris/webrtcSignaling.d.ts.map +0 -1
  169. package/dist/iris/webrtcSignaling.js +0 -86
  170. package/dist/iris/webrtcSignaling.js.map +0 -1
  171. package/dist/iris/worker.d.ts +0 -12
  172. package/dist/iris/worker.d.ts.map +0 -1
  173. package/dist/iris/worker.js +0 -1529
  174. package/dist/iris/worker.js.map +0 -1
  175. package/dist/iris-client.d.ts +0 -31
  176. package/dist/iris-client.d.ts.map +0 -1
  177. package/dist/iris-client.js +0 -197
  178. package/dist/iris-client.js.map +0 -1
  179. package/dist/iris-entry.d.ts +0 -2
  180. package/dist/iris-entry.d.ts.map +0 -1
  181. package/dist/iris-entry.js +0 -2
  182. package/dist/iris-entry.js.map +0 -1
  183. package/dist/mediaStreaming.d.ts +0 -7
  184. package/dist/mediaStreaming.d.ts.map +0 -1
  185. package/dist/mediaStreaming.js +0 -48
  186. package/dist/mediaStreaming.js.map +0 -1
  187. package/dist/p2p/boundedQueue.d.ts +0 -79
  188. package/dist/p2p/boundedQueue.d.ts.map +0 -1
  189. package/dist/p2p/boundedQueue.js +0 -134
  190. package/dist/p2p/boundedQueue.js.map +0 -1
  191. package/dist/p2p/errorMessage.d.ts +0 -5
  192. package/dist/p2p/errorMessage.d.ts.map +0 -1
  193. package/dist/p2p/errorMessage.js +0 -7
  194. package/dist/p2p/errorMessage.js.map +0 -1
  195. package/dist/p2p/index.d.ts +0 -8
  196. package/dist/p2p/index.d.ts.map +0 -1
  197. package/dist/p2p/index.js +0 -6
  198. package/dist/p2p/index.js.map +0 -1
  199. package/dist/p2p/lruCache.d.ts +0 -26
  200. package/dist/p2p/lruCache.d.ts.map +0 -1
  201. package/dist/p2p/lruCache.js +0 -65
  202. package/dist/p2p/lruCache.js.map +0 -1
  203. package/dist/p2p/meshQueryRouter.d.ts +0 -44
  204. package/dist/p2p/meshQueryRouter.d.ts.map +0 -1
  205. package/dist/p2p/meshQueryRouter.js +0 -228
  206. package/dist/p2p/meshQueryRouter.js.map +0 -1
  207. package/dist/p2p/protocol.d.ts +0 -10
  208. package/dist/p2p/protocol.d.ts.map +0 -1
  209. package/dist/p2p/protocol.js +0 -2
  210. package/dist/p2p/protocol.js.map +0 -1
  211. package/dist/p2p/queryForwardingMachine.d.ts +0 -46
  212. package/dist/p2p/queryForwardingMachine.d.ts.map +0 -1
  213. package/dist/p2p/queryForwardingMachine.js +0 -144
  214. package/dist/p2p/queryForwardingMachine.js.map +0 -1
  215. package/dist/p2p/signaling.d.ts +0 -63
  216. package/dist/p2p/signaling.d.ts.map +0 -1
  217. package/dist/p2p/signaling.js +0 -185
  218. package/dist/p2p/signaling.js.map +0 -1
  219. package/dist/p2p/uploadRateLimiter.d.ts +0 -21
  220. package/dist/p2p/uploadRateLimiter.d.ts.map +0 -1
  221. package/dist/p2p/uploadRateLimiter.js +0 -62
  222. package/dist/p2p/uploadRateLimiter.js.map +0 -1
  223. package/dist/p2p/webrtcController.d.ts +0 -168
  224. package/dist/p2p/webrtcController.d.ts.map +0 -1
  225. package/dist/p2p/webrtcController.js +0 -902
  226. package/dist/p2p/webrtcController.js.map +0 -1
  227. package/dist/p2p/webrtcProxy.d.ts +0 -62
  228. package/dist/p2p/webrtcProxy.d.ts.map +0 -1
  229. package/dist/p2p/webrtcProxy.js +0 -447
  230. package/dist/p2p/webrtcProxy.js.map +0 -1
  231. package/dist/privacyGuards.d.ts +0 -14
  232. package/dist/privacyGuards.d.ts.map +0 -1
  233. package/dist/privacyGuards.js +0 -27
  234. package/dist/privacyGuards.js.map +0 -1
  235. package/dist/protocol.d.ts +0 -225
  236. package/dist/protocol.d.ts.map +0 -1
  237. package/dist/protocol.js +0 -2
  238. package/dist/protocol.js.map +0 -1
  239. package/dist/runtime-network.d.ts +0 -23
  240. package/dist/runtime-network.d.ts.map +0 -1
  241. package/dist/runtime-network.js +0 -105
  242. package/dist/runtime-network.js.map +0 -1
  243. package/dist/runtime.d.ts +0 -23
  244. package/dist/runtime.d.ts.map +0 -1
  245. package/dist/runtime.js +0 -122
  246. package/dist/runtime.js.map +0 -1
  247. package/dist/tree-root.d.ts +0 -201
  248. package/dist/tree-root.d.ts.map +0 -1
  249. package/dist/tree-root.js +0 -632
  250. package/dist/tree-root.js.map +0 -1
  251. package/dist/types.d.ts +0 -2
  252. package/dist/types.d.ts.map +0 -1
  253. package/dist/types.js +0 -2
  254. package/dist/types.js.map +0 -1
  255. package/dist/worker.d.ts +0 -9
  256. package/dist/worker.d.ts.map +0 -1
  257. package/dist/worker.js +0 -797
  258. package/dist/worker.js.map +0 -1
@@ -0,0 +1,249 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Nostr Relay Manager for Worker
4
+ *
5
+ * Manages WebSocket connections to Nostr relays using nostr-tools.
6
+ * Provides subscribe/publish functionality for the worker.
7
+ *
8
+ * Used for:
9
+ * - WebRTC signaling (kind 25050 ephemeral)
10
+ * - Tree root resolution (kind 30078)
11
+ */
12
+
13
+ import { SimplePool, type Filter, type Event } from 'nostr-tools';
14
+ import type { NostrFilter, SignedEvent, RelayStats } from './protocol';
15
+
16
+ // Subscription entry
17
+ interface Subscription {
18
+ id: string;
19
+ filters: NostrFilter[];
20
+ subs: ReturnType<SimplePool['subscribe']>[];
21
+ }
22
+
23
+ // Relay connection stats
24
+ interface RelayStatsInternal {
25
+ url: string;
26
+ connected: boolean;
27
+ eventsReceived: number;
28
+ eventsSent: number;
29
+ }
30
+
31
+ export class NostrManager {
32
+ private pool: SimplePool;
33
+ private relays: string[] = [];
34
+ private subscriptions = new Map<string, Subscription>();
35
+ private relayStats = new Map<string, RelayStatsInternal>();
36
+ private onEvent: ((subId: string, event: SignedEvent) => void) | null = null;
37
+ private onEose: ((subId: string) => void) | null = null;
38
+
39
+ constructor() {
40
+ this.pool = new SimplePool();
41
+ }
42
+
43
+ /**
44
+ * Initialize with relay URLs
45
+ */
46
+ init(relays: string[]): void {
47
+ this.relays = relays;
48
+
49
+ // Initialize stats for each relay
50
+ for (const url of relays) {
51
+ this.relayStats.set(url, {
52
+ url,
53
+ connected: false,
54
+ eventsReceived: 0,
55
+ eventsSent: 0,
56
+ });
57
+ }
58
+
59
+ console.log('[NostrManager] Initialized with relays:', relays);
60
+ }
61
+
62
+ /**
63
+ * Set event callback
64
+ */
65
+ setOnEvent(callback: (subId: string, event: SignedEvent) => void): void {
66
+ this.onEvent = callback;
67
+ }
68
+
69
+ /**
70
+ * Set EOSE callback
71
+ */
72
+ setOnEose(callback: (subId: string) => void): void {
73
+ this.onEose = callback;
74
+ }
75
+
76
+ /**
77
+ * Subscribe to events matching filters
78
+ */
79
+ subscribe(subId: string, filters: NostrFilter[]): void {
80
+ // Close existing subscription with same ID if any
81
+ this.unsubscribe(subId);
82
+
83
+ console.log('[NostrManager] Creating subscription:', subId, 'to relays:', this.relays, 'filters:', filters);
84
+
85
+ // Convert our NostrFilter to nostr-tools Filter
86
+ // Subscribe to each filter separately and track them
87
+ const subs: ReturnType<SimplePool['subscribe']>[] = [];
88
+
89
+ for (const f of filters) {
90
+ // Build filter with any tag filters (e.g., #e, #p, #d, #l)
91
+ const poolFilter: Filter = {
92
+ ids: f.ids,
93
+ authors: f.authors,
94
+ kinds: f.kinds,
95
+ since: f.since,
96
+ until: f.until,
97
+ limit: f.limit,
98
+ };
99
+
100
+ // Copy any tag filters (keys starting with #)
101
+ for (const key of Object.keys(f)) {
102
+ if (key.startsWith('#') && f[key]) {
103
+ (poolFilter as Record<string, unknown>)[key] = f[key];
104
+ }
105
+ }
106
+
107
+ try {
108
+ console.log('[NostrManager] pool.subscribe called with filter:', poolFilter);
109
+ const sub = this.pool.subscribe(this.relays, poolFilter, {
110
+ onevent: (event: Event) => {
111
+ console.log('[NostrManager] Received event:', event.kind, 'from:', event.pubkey?.slice(0, 8), 'id:', event.id?.slice(0, 8));
112
+ // Convert to SignedEvent
113
+ const signedEvent: SignedEvent = {
114
+ id: event.id,
115
+ pubkey: event.pubkey,
116
+ kind: event.kind,
117
+ content: event.content,
118
+ tags: event.tags,
119
+ created_at: event.created_at,
120
+ sig: event.sig,
121
+ };
122
+
123
+ this.onEvent?.(subId, signedEvent);
124
+ },
125
+ oneose: () => {
126
+ console.log('[NostrManager] EOSE for sub:', subId);
127
+ this.onEose?.(subId);
128
+ },
129
+ onerror: (err: Error) => {
130
+ console.error('[NostrManager] Subscription error:', subId, err);
131
+ },
132
+ });
133
+ console.log('[NostrManager] Subscription object:', typeof sub, sub);
134
+ subs.push(sub);
135
+ } catch (err) {
136
+ console.error('[NostrManager] Error creating subscription:', subId, err);
137
+ }
138
+ }
139
+
140
+ // Store all subs for this subscription ID
141
+ this.subscriptions.set(subId, { id: subId, filters, subs });
142
+ console.log('[NostrManager] Subscribed:', subId, filters);
143
+ }
144
+
145
+ /**
146
+ * Unsubscribe from a subscription
147
+ */
148
+ unsubscribe(subId: string): void {
149
+ const sub = this.subscriptions.get(subId);
150
+ if (sub) {
151
+ for (const s of sub.subs) {
152
+ s.close();
153
+ }
154
+ this.subscriptions.delete(subId);
155
+ console.log('[NostrManager] Unsubscribed:', subId);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Publish an event to all relays
161
+ */
162
+ async publish(event: SignedEvent): Promise<void> {
163
+ // Convert to nostr-tools Event
164
+ const poolEvent: Event = {
165
+ id: event.id,
166
+ pubkey: event.pubkey,
167
+ kind: event.kind,
168
+ content: event.content,
169
+ tags: event.tags,
170
+ created_at: event.created_at,
171
+ sig: event.sig,
172
+ };
173
+
174
+ try {
175
+ await Promise.any(
176
+ this.pool.publish(this.relays, poolEvent)
177
+ );
178
+
179
+ // Update stats for successful publish
180
+ for (const [, stats] of this.relayStats) {
181
+ stats.eventsSent++;
182
+ }
183
+
184
+ console.log('[NostrManager] Published event:', event.id);
185
+ } catch (err) {
186
+ console.error('[NostrManager] Failed to publish:', err);
187
+ throw err;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Get relay connection stats
193
+ */
194
+ getRelayStats(): RelayStats[] {
195
+ const result: RelayStats[] = [];
196
+ for (const [url, stats] of this.relayStats) {
197
+ result.push({
198
+ url,
199
+ connected: stats.connected,
200
+ eventsReceived: stats.eventsReceived,
201
+ eventsSent: stats.eventsSent,
202
+ });
203
+ }
204
+ return result;
205
+ }
206
+
207
+ /**
208
+ * Update relay connection status
209
+ * Called when connection state changes
210
+ */
211
+ setRelayConnected(url: string, connected: boolean): void {
212
+ const stats = this.relayStats.get(url);
213
+ if (stats) {
214
+ stats.connected = connected;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Close all subscriptions and connections
220
+ */
221
+ close(): void {
222
+ for (const [subId, sub] of this.subscriptions) {
223
+ for (const s of sub.subs) {
224
+ s.close();
225
+ }
226
+ console.log('[NostrManager] Closed subscription:', subId);
227
+ }
228
+ this.subscriptions.clear();
229
+ this.pool.close(this.relays);
230
+ console.log('[NostrManager] Closed');
231
+ }
232
+ }
233
+
234
+ // Singleton instance for the worker
235
+ let instance: NostrManager | null = null;
236
+
237
+ export function getNostrManager(): NostrManager {
238
+ if (!instance) {
239
+ instance = new NostrManager();
240
+ }
241
+ return instance;
242
+ }
243
+
244
+ export function closeNostrManager(): void {
245
+ if (instance) {
246
+ instance.close();
247
+ instance = null;
248
+ }
249
+ }
@@ -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
+ }