@hashtree/worker 0.2.0 → 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/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/{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/relay/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/{dist/transferableBytes.js → src/transferableBytes.ts} +2 -3
- package/src/tree-root.ts +851 -0
- package/src/types.ts +8 -0
- package/src/worker.ts +975 -0
- 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 -139
- 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 -392
- 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/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 -57
- package/dist/p2p/meshQueryRouter.d.ts.map +0 -1
- package/dist/p2p/meshQueryRouter.js +0 -264
- 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 -176
- package/dist/p2p/webrtcController.d.ts.map +0 -1
- package/dist/p2p/webrtcController.js +0 -938
- 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/relay/identity.d.ts +0 -36
- package/dist/relay/identity.d.ts.map +0 -1
- package/dist/relay/identity.js +0 -78
- package/dist/relay/identity.js.map +0 -1
- package/dist/relay/mediaHandler.d.ts +0 -64
- package/dist/relay/mediaHandler.d.ts.map +0 -1
- package/dist/relay/mediaHandler.js +0 -1285
- package/dist/relay/mediaHandler.js.map +0 -1
- package/dist/relay/ndk.d.ts +0 -96
- package/dist/relay/ndk.d.ts.map +0 -1
- package/dist/relay/ndk.js +0 -502
- package/dist/relay/ndk.js.map +0 -1
- package/dist/relay/nostr-wasm.d.ts +0 -14
- package/dist/relay/nostr-wasm.d.ts.map +0 -1
- package/dist/relay/nostr-wasm.js.map +0 -1
- package/dist/relay/nostr.d.ts +0 -60
- package/dist/relay/nostr.d.ts.map +0 -1
- package/dist/relay/nostr.js +0 -207
- package/dist/relay/nostr.js.map +0 -1
- package/dist/relay/protocol.d.ts +0 -592
- package/dist/relay/protocol.d.ts.map +0 -1
- package/dist/relay/protocol.js +0 -16
- package/dist/relay/protocol.js.map +0 -1
- package/dist/relay/publicAssetUrl.d.ts +0 -6
- package/dist/relay/publicAssetUrl.d.ts.map +0 -1
- package/dist/relay/publicAssetUrl.js +0 -14
- package/dist/relay/publicAssetUrl.js.map +0 -1
- package/dist/relay/rootPathResolver.d.ts +0 -9
- package/dist/relay/rootPathResolver.d.ts.map +0 -1
- package/dist/relay/rootPathResolver.js +0 -32
- package/dist/relay/rootPathResolver.js.map +0 -1
- package/dist/relay/signing.d.ts +0 -50
- package/dist/relay/signing.d.ts.map +0 -1
- package/dist/relay/signing.js +0 -299
- package/dist/relay/signing.js.map +0 -1
- package/dist/relay/treeRootCache.d.ts +0 -86
- package/dist/relay/treeRootCache.d.ts.map +0 -1
- package/dist/relay/treeRootCache.js +0 -269
- package/dist/relay/treeRootCache.js.map +0 -1
- package/dist/relay/treeRootSubscription.d.ts +0 -55
- package/dist/relay/treeRootSubscription.d.ts.map +0 -1
- package/dist/relay/treeRootSubscription.js +0 -478
- package/dist/relay/treeRootSubscription.js.map +0 -1
- package/dist/relay/utils/constants.d.ts +0 -76
- package/dist/relay/utils/constants.d.ts.map +0 -1
- package/dist/relay/utils/constants.js +0 -113
- package/dist/relay/utils/constants.js.map +0 -1
- package/dist/relay/utils/errorMessage.d.ts +0 -5
- package/dist/relay/utils/errorMessage.d.ts.map +0 -1
- package/dist/relay/utils/errorMessage.js +0 -8
- package/dist/relay/utils/errorMessage.js.map +0 -1
- package/dist/relay/utils/lruCache.d.ts +0 -26
- package/dist/relay/utils/lruCache.d.ts.map +0 -1
- package/dist/relay/utils/lruCache.js +0 -66
- package/dist/relay/utils/lruCache.js.map +0 -1
- package/dist/relay/webrtc.d.ts +0 -2
- package/dist/relay/webrtc.d.ts.map +0 -1
- package/dist/relay/webrtc.js +0 -3
- package/dist/relay/webrtc.js.map +0 -1
- package/dist/relay/webrtcSignaling.d.ts +0 -37
- package/dist/relay/webrtcSignaling.d.ts.map +0 -1
- package/dist/relay/webrtcSignaling.js +0 -86
- package/dist/relay/webrtcSignaling.js.map +0 -1
- package/dist/relay/worker.d.ts +0 -12
- package/dist/relay/worker.d.ts.map +0 -1
- package/dist/relay/worker.js +0 -1540
- package/dist/relay/worker.js.map +0 -1
- package/dist/relay-client.d.ts +0 -31
- package/dist/relay-client.d.ts.map +0 -1
- package/dist/relay-client.js +0 -197
- package/dist/relay-client.js.map +0 -1
- package/dist/relay-entry.d.ts +0 -2
- package/dist/relay-entry.d.ts.map +0 -1
- package/dist/relay-entry.js +0 -2
- package/dist/relay-entry.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 -24
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -126
- package/dist/runtime.js.map +0 -1
- package/dist/transferableBytes.d.ts +0 -2
- package/dist/transferableBytes.d.ts.map +0 -1
- package/dist/transferableBytes.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 -792
- 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
|
+
}
|