@trystero-p2p/torrent 0.23.0-beta.1 → 0.23.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;KAgDY,iBAAA,GAAoB,cAAA,GAAiB,WAAA;AAAA,cAuRpC,QAAA,EAAU,QAAA,CAAS,iBAAA;AAAA,cAWnB,eAAA;AAAA,cAIA,gBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;KA+CY,iBAAA,GAAoB,cAAA,GAAiB,WAAA;AAAA,cA6VpC,QAAA,EAAU,QAAA,CAAS,iBAAA;AAAA,cAcnB,eAAA;AAAA,cAIA,gBAAA"}
package/dist/index.mjs CHANGED
@@ -1,17 +1,20 @@
1
- import { createStrategy, entries, fromJson, genId, getRelays, libName, makeSocket, pauseRelayReconnection, resumeRelayReconnection, selfId, sha1, socketGetter, toJson } from "@trystero-p2p/core";
1
+ import { createRelayManager, createStrategy, entries, fromJson, genId, getRelays, keys, libName, makeSocket, pauseRelayReconnection, resumeRelayReconnection, selfId, sha1, toJson } from "@trystero-p2p/core";
2
2
  //#region src/index.ts
3
- const clients = {};
3
+ const relayManager = createRelayManager((client) => client.socket);
4
4
  const topicToInfoHash = {};
5
5
  const infoHashToTopic = {};
6
- const announceIntervals = {};
7
- const announceFns = {};
8
- const subscriptionTokens = {};
6
+ const announceIntervals = relayManager.scoped();
7
+ const announceFns = relayManager.scoped();
8
+ const subscriptionTokens = relayManager.scoped();
9
9
  const trackerAnnounceMs = {};
10
10
  const handledSignals = {};
11
- const msgHandlers = {};
11
+ const msgHandlers = relayManager.scoped();
12
+ const roomOutstandingOffers = {};
13
+ const roomOfferGenerationPromises = {};
14
+ const roomSubscriberCounts = {};
12
15
  const trackerAction = "announce";
13
16
  const hashLimit = 20;
14
- const offerPoolSize = 10;
17
+ const offerPoolSize = 3;
15
18
  const defaultAnnounceMs = 1e4;
16
19
  const maxAnnounceMs = 2e4;
17
20
  const offerRetentionMs = 12e4;
@@ -24,18 +27,68 @@ const getInfoHash = async (topic) => {
24
27
  infoHashToTopic[hash] = topic;
25
28
  return hash;
26
29
  };
27
- const send = async (client, topic, payload) => {
28
- client.send(toJson({
29
- action: trackerAction,
30
- info_hash: await getInfoHash(topic),
31
- peer_id: selfId,
32
- ...payload
33
- }));
34
- };
30
+ const send = async (client, topic, payload) => client.send(toJson({
31
+ action: trackerAction,
32
+ info_hash: await getInfoHash(topic),
33
+ peer_id: selfId,
34
+ ...payload
35
+ }));
35
36
  const warn = (url, msg, didFail = false) => console.warn(`${libName}: torrent tracker ${didFail ? "failure" : "warning"} from ${url} - ${msg}`);
37
+ const getRoomOutstandingOffers = (rootTopic) => roomOutstandingOffers[rootTopic] ??= {};
38
+ const deleteRoomOfferBookkeeping = (rootTopic) => {
39
+ delete roomOutstandingOffers[rootTopic];
40
+ delete roomOfferGenerationPromises[rootTopic];
41
+ };
42
+ const claimOutstandingOffer = (rootTopic, offerId) => {
43
+ const outstandingOffers = roomOutstandingOffers[rootTopic];
44
+ const offer = outstandingOffers?.[offerId];
45
+ if (!offer) return;
46
+ delete outstandingOffers[offerId];
47
+ offer.claim?.();
48
+ if (!keys(outstandingOffers).length && !roomSubscriberCounts[rootTopic]) deleteRoomOfferBookkeeping(rootTopic);
49
+ return offer;
50
+ };
51
+ const reclaimOutstandingOffer = (rootTopic, offerId) => {
52
+ const outstandingOffers = roomOutstandingOffers[rootTopic];
53
+ const offer = outstandingOffers?.[offerId];
54
+ if (!offer) return;
55
+ delete outstandingOffers[offerId];
56
+ offer.reclaim?.();
57
+ if (!keys(outstandingOffers).length && !roomSubscriberCounts[rootTopic]) deleteRoomOfferBookkeeping(rootTopic);
58
+ };
59
+ const reclaimAllOutstandingOffers = (rootTopic) => {
60
+ keys(getRoomOutstandingOffers(rootTopic)).forEach((offerId) => reclaimOutstandingOffer(rootTopic, offerId));
61
+ deleteRoomOfferBookkeeping(rootTopic);
62
+ };
63
+ const pruneOutstandingOffers = (rootTopic) => {
64
+ const now = Date.now();
65
+ entries(getRoomOutstandingOffers(rootTopic)).forEach(([offerId, offer]) => {
66
+ if (now - offer.createdAt > offerRetentionMs) reclaimOutstandingOffer(rootTopic, offerId);
67
+ });
68
+ };
69
+ const ensureOutstandingOffers = async (rootTopic, getOffers) => {
70
+ while (roomOfferGenerationPromises[rootTopic]) await roomOfferGenerationPromises[rootTopic];
71
+ const nextPromise = (async () => {
72
+ pruneOutstandingOffers(rootTopic);
73
+ const outstandingOffers = getRoomOutstandingOffers(rootTopic);
74
+ const outstandingCount = keys(outstandingOffers).length;
75
+ const missingOffers = Math.max(0, offerPoolSize - outstandingCount);
76
+ if (missingOffers > 0) (await getOffers(missingOffers)).forEach((peerAndOffer) => {
77
+ outstandingOffers[genId(hashLimit)] = {
78
+ ...peerAndOffer,
79
+ createdAt: Date.now()
80
+ };
81
+ });
82
+ })().finally(() => {
83
+ if (roomOfferGenerationPromises[rootTopic] === nextPromise) delete roomOfferGenerationPromises[rootTopic];
84
+ });
85
+ roomOfferGenerationPromises[rootTopic] = nextPromise;
86
+ await nextPromise;
87
+ return getRoomOutstandingOffers(rootTopic);
88
+ };
36
89
  const joinRoomStrategy = createStrategy({
37
90
  init: (config) => getRelays(config, defaultRelayUrls, defaultRedundancy).map((rawUrl) => {
38
- const client = makeSocket(rawUrl, (rawData) => {
91
+ const client = relayManager.register(rawUrl, makeSocket(rawUrl, (rawData) => {
39
92
  const data = fromJson(rawData);
40
93
  const errMsg = data["failure reason"];
41
94
  const warnMsg = data["warning message"];
@@ -46,14 +99,16 @@ const joinRoomStrategy = createStrategy({
46
99
  return;
47
100
  }
48
101
  if (warnMsg) warn(client.url, warnMsg);
49
- if (interval && interval * 1e3 > (trackerAnnounceMs[client.url] ?? defaultAnnounceMs) && topic && announceFns[client.url]?.[topic]) {
102
+ if (interval && interval * 1e3 > (trackerAnnounceMs[client.url] ?? defaultAnnounceMs) && topic && announceFns.forKey(rawUrl)[topic]) {
50
103
  const nextInterval = Math.min(interval * 1e3, maxAnnounceMs);
51
- const relayIntervals = announceIntervals[client.url] ??= {};
52
- const relayFns = announceFns[client.url] ??= {};
104
+ const relayIntervals = announceIntervals.forKey(rawUrl);
105
+ const relayFns = announceFns.forKey(rawUrl);
53
106
  if (relayIntervals[topic]) clearInterval(relayIntervals[topic]);
54
107
  trackerAnnounceMs[client.url] = nextInterval;
55
108
  const relayFn = relayFns[topic];
56
- if (relayFn) relayIntervals[topic] = setInterval(relayFn, nextInterval);
109
+ if (relayFn) relayIntervals[topic] = setInterval(() => {
110
+ relayFn();
111
+ }, nextInterval);
57
112
  }
58
113
  if ((data.offer || data.answer) && topic && data.offer_id) {
59
114
  if (data.peer_id === selfId) return;
@@ -65,61 +120,36 @@ const joinRoomStrategy = createStrategy({
65
120
  entries(handledSignals).forEach(([key, handledAtMs]) => {
66
121
  if (nowMs - handledAtMs > signalDedupeWindowMs * 6) delete handledSignals[key];
67
122
  });
68
- msgHandlers[client.url]?.[topic]?.(data);
123
+ msgHandlers.forKey(rawUrl)[topic]?.(data);
69
124
  }
70
- });
71
- const { url } = client;
72
- clients[url] = client;
73
- msgHandlers[url] = {};
125
+ }));
74
126
  return client.ready;
75
127
  }),
76
128
  subscribe: (client, rootTopic, _, onMessage, getOffers) => {
77
- const { url } = client;
78
- const handlers = msgHandlers[url] ??= {};
79
- const relayFns = announceFns[url] ??= {};
80
- const relayIntervals = announceIntervals[url] ??= {};
81
- const activeTokens = subscriptionTokens[url] ??= {};
129
+ const handlers = msgHandlers.forRelay(client);
130
+ const relayFns = announceFns.forRelay(client);
131
+ const relayIntervals = announceIntervals.forRelay(client);
132
+ const activeTokens = subscriptionTokens.forRelay(client);
82
133
  const subscriptionToken = Symbol(rootTopic);
83
- const outstandingOffers = {};
84
134
  activeTokens[rootTopic] = subscriptionToken;
85
- const claimOutstandingOffer = (offerId) => {
86
- const offer = outstandingOffers[offerId];
87
- if (!offer) return;
88
- delete outstandingOffers[offerId];
89
- offer.claim?.();
90
- return offer;
91
- };
92
- const reclaimOutstandingOffer = (offerId) => {
93
- const offer = outstandingOffers[offerId];
94
- if (!offer) return;
95
- delete outstandingOffers[offerId];
96
- offer.reclaim?.();
97
- };
98
- const reclaimAllOutstandingOffers = () => {
99
- entries(outstandingOffers).forEach(([offerId]) => {
100
- reclaimOutstandingOffer(offerId);
101
- });
102
- };
103
- const pruneOutstandingOffers = () => {
104
- const now = Date.now();
105
- entries(outstandingOffers).forEach(([offerId, offer]) => {
106
- if (now - offer.createdAt > offerRetentionMs) reclaimOutstandingOffer(offerId);
107
- });
108
- };
135
+ roomSubscriberCounts[rootTopic] = (roomSubscriberCounts[rootTopic] ?? 0) + 1;
109
136
  const topicHandler = (data) => {
110
137
  if (data.offer && data.peer_id && data.offer_id) onMessage(rootTopic, {
111
- offer: data.offer,
138
+ offer: data.offer.sdp,
112
139
  peerId: data.peer_id,
113
- hasOutgoingOffer: entries(outstandingOffers).length > 0
140
+ hasOutgoingOffer: keys(getRoomOutstandingOffers(rootTopic)).length > 0
114
141
  }, (_, signal) => void send(client, rootTopic, {
115
- answer: fromJson(signal)["answer"],
142
+ answer: {
143
+ type: "answer",
144
+ sdp: fromJson(signal).answer
145
+ },
116
146
  offer_id: data.offer_id,
117
147
  to_peer_id: data.peer_id
118
148
  }));
119
149
  else if (data.answer && data.offer_id && data.peer_id) {
120
- const offer = claimOutstandingOffer(data.offer_id);
150
+ const offer = claimOutstandingOffer(rootTopic, data.offer_id);
121
151
  if (offer) onMessage(rootTopic, {
122
- answer: data.answer,
152
+ answer: data.answer.sdp,
123
153
  peerId: data.peer_id,
124
154
  peer: offer.peer
125
155
  }, () => {});
@@ -128,30 +158,26 @@ const joinRoomStrategy = createStrategy({
128
158
  handlers[rootTopic] = topicHandler;
129
159
  const announce = async () => {
130
160
  if (activeTokens[rootTopic] !== subscriptionToken) return;
131
- pruneOutstandingOffers();
132
- const outstandingCount = entries(outstandingOffers).length;
133
- const missingOffers = Math.max(0, offerPoolSize - outstandingCount);
134
- if (missingOffers > 0) (await getOffers(missingOffers)).forEach((peerAndOffer) => {
135
- outstandingOffers[genId(hashLimit)] = {
136
- ...peerAndOffer,
137
- createdAt: Date.now()
138
- };
139
- });
140
161
  send(client, rootTopic, {
141
162
  numwant: offerPoolSize,
142
- offers: entries(outstandingOffers).map(([id, { offer }]) => ({
163
+ offers: entries(await ensureOutstandingOffers(rootTopic, getOffers)).map(([id, { offer }]) => ({
143
164
  offer_id: id,
144
- offer
165
+ offer: {
166
+ type: "offer",
167
+ sdp: offer
168
+ }
145
169
  }))
146
170
  });
147
171
  };
148
- trackerAnnounceMs[url] = defaultAnnounceMs;
172
+ trackerAnnounceMs[client.url] = defaultAnnounceMs;
149
173
  relayFns[rootTopic] = announce;
150
- relayIntervals[rootTopic] = setInterval(announce, trackerAnnounceMs[url]);
174
+ relayIntervals[rootTopic] = setInterval(announce, trackerAnnounceMs[client.url]);
151
175
  announce();
152
176
  return () => {
177
+ roomSubscriberCounts[rootTopic] = Math.max(0, (roomSubscriberCounts[rootTopic] ?? 1) - 1);
178
+ if (!roomSubscriberCounts[rootTopic]) delete roomSubscriberCounts[rootTopic];
153
179
  if (activeTokens[rootTopic] !== subscriptionToken) {
154
- reclaimAllOutstandingOffers();
180
+ if (!roomSubscriberCounts[rootTopic]) reclaimAllOutstandingOffers(rootTopic);
155
181
  return;
156
182
  }
157
183
  const interval = relayIntervals[rootTopic];
@@ -162,7 +188,7 @@ const joinRoomStrategy = createStrategy({
162
188
  if (handlers[rootTopic] === topicHandler) delete handlers[rootTopic];
163
189
  if (relayFns[rootTopic] === announce) delete relayFns[rootTopic];
164
190
  delete activeTokens[rootTopic];
165
- reclaimAllOutstandingOffers();
191
+ if (!roomSubscriberCounts[rootTopic]) reclaimAllOutstandingOffers(rootTopic);
166
192
  };
167
193
  },
168
194
  announce: (client) => trackerAnnounceMs[client.url]
@@ -171,7 +197,7 @@ const joinRoom = (config, roomId, callbacks) => joinRoomStrategy({
171
197
  ...config,
172
198
  trickleIce: config.trickleIce ?? false
173
199
  }, roomId, callbacks);
174
- const getRelaySockets = socketGetter(clients);
200
+ const getRelaySockets = relayManager.getSockets;
175
201
  const defaultRelayUrls = [
176
202
  "tracker.webtorrent.dev",
177
203
  "tracker.openwebtorrent.com",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n createStrategy,\n entries,\n fromJson,\n genId,\n getRelays,\n libName,\n makeSocket,\n pauseRelayReconnection,\n resumeRelayReconnection,\n selfId,\n sha1,\n socketGetter,\n toJson,\n type BaseRoomConfig,\n type JoinRoom,\n type OfferRecord,\n type RelayConfig,\n type SocketClient\n} from '@trystero-p2p/core'\n\nconst clients: Record<string, SocketClient> = {}\nconst topicToInfoHash: Record<string, string> = {}\nconst infoHashToTopic: Record<string, string> = {}\nconst announceIntervals: Record<\n string,\n Record<string, ReturnType<typeof setInterval>>\n> = {}\nconst announceFns: Record<\n string,\n Record<string, () => void | Promise<void>>\n> = {}\nconst subscriptionTokens: Record<string, Record<string, symbol>> = {}\nconst trackerAnnounceMs: Record<string, number> = {}\nconst handledSignals: Record<string, number> = {}\nconst msgHandlers: Record<\n string,\n Record<string, ((data: TrackerMessage) => void) | undefined>\n> = {}\nconst trackerAction = 'announce'\nconst hashLimit = 20\nconst offerPoolSize = 10\nconst defaultAnnounceMs = 10_000\nconst maxAnnounceMs = 20_000\nconst offerRetentionMs = 120_000\nconst signalDedupeWindowMs = 4_000\nconst defaultRedundancy = 3\n\nexport type TorrentRoomConfig = BaseRoomConfig & RelayConfig\n\ntype TrackerMessage = {\n offer?: unknown\n answer?: unknown\n offer_id?: string\n peer_id?: string\n info_hash?: string\n interval?: number\n ['failure reason']?: string\n ['warning message']?: string\n}\n\nconst getInfoHash = async (topic: string): Promise<string> => {\n if (topicToInfoHash[topic]) {\n return topicToInfoHash[topic]\n }\n\n const hash = (await sha1(topic)).slice(0, hashLimit)\n topicToInfoHash[topic] = hash\n infoHashToTopic[hash] = topic\n\n return hash\n}\n\nconst send = async (\n client: SocketClient,\n topic: string,\n payload: Record<string, unknown>\n): Promise<void> => {\n client.send(\n toJson({\n action: trackerAction,\n info_hash: await getInfoHash(topic),\n peer_id: selfId,\n ...payload\n })\n )\n}\n\nconst warn = (url: string, msg: string, didFail = false): void =>\n console.warn(\n `${libName}: torrent tracker ${didFail ? 'failure' : 'warning'} from ${url} - ${msg}`\n )\n\nconst joinRoomStrategy: JoinRoom<TorrentRoomConfig> = createStrategy({\n init: config =>\n getRelays(config, defaultRelayUrls, defaultRedundancy).map(rawUrl => {\n const client = makeSocket(rawUrl, rawData => {\n const data = fromJson<TrackerMessage>(rawData)\n const errMsg = data['failure reason']\n const warnMsg = data['warning message']\n const {interval} = data\n const topic = data.info_hash\n ? infoHashToTopic[data.info_hash]\n : undefined\n\n if (errMsg) {\n warn(client.url, errMsg, true)\n return\n }\n\n if (warnMsg) {\n warn(client.url, warnMsg)\n }\n\n if (\n interval &&\n interval * 1000 >\n (trackerAnnounceMs[client.url] ?? defaultAnnounceMs) &&\n topic &&\n announceFns[client.url]?.[topic]\n ) {\n const nextInterval = Math.min(interval * 1000, maxAnnounceMs)\n const relayIntervals = (announceIntervals[client.url] ??= {})\n const relayFns = (announceFns[client.url] ??= {})\n\n if (relayIntervals[topic]) {\n clearInterval(relayIntervals[topic])\n }\n trackerAnnounceMs[client.url] = nextInterval\n const relayFn = relayFns[topic]\n\n if (relayFn) {\n relayIntervals[topic] = setInterval(relayFn, nextInterval)\n }\n }\n\n if ((data.offer || data.answer) && topic && data.offer_id) {\n if (data.peer_id === selfId) {\n return\n }\n\n const signalType = data.offer ? 'offer' : 'answer'\n const signalKey = `${topic}:${signalType}:${data.offer_id}:${data.peer_id ?? ''}`\n const nowMs = Date.now()\n const lastHandledMs = handledSignals[signalKey]\n\n if (\n typeof lastHandledMs === 'number' &&\n nowMs - lastHandledMs < signalDedupeWindowMs\n ) {\n return\n }\n\n handledSignals[signalKey] = nowMs\n\n entries(handledSignals).forEach(([key, handledAtMs]) => {\n if (nowMs - handledAtMs > signalDedupeWindowMs * 6) {\n delete handledSignals[key]\n }\n })\n\n msgHandlers[client.url]?.[topic]?.(data)\n }\n })\n\n const {url} = client\n\n clients[url] = client\n msgHandlers[url] = {}\n\n return client.ready\n }),\n\n subscribe: (client, rootTopic, _, onMessage, getOffers) => {\n const {url} = client\n const handlers = (msgHandlers[url] ??= {})\n const relayFns = (announceFns[url] ??= {})\n const relayIntervals = (announceIntervals[url] ??= {})\n const activeTokens = (subscriptionTokens[url] ??= {})\n const subscriptionToken = Symbol(rootTopic)\n const outstandingOffers: Record<string, OfferRecord & {createdAt: number}> =\n {}\n\n activeTokens[rootTopic] = subscriptionToken\n\n const claimOutstandingOffer = (\n offerId: string\n ): OfferRecord | undefined => {\n const offer = outstandingOffers[offerId]\n\n if (!offer) {\n return\n }\n\n delete outstandingOffers[offerId]\n offer.claim?.()\n\n return offer\n }\n\n const reclaimOutstandingOffer = (offerId: string): void => {\n const offer = outstandingOffers[offerId]\n\n if (!offer) {\n return\n }\n\n delete outstandingOffers[offerId]\n offer.reclaim?.()\n }\n\n const reclaimAllOutstandingOffers = (): void => {\n entries(outstandingOffers).forEach(([offerId]) => {\n reclaimOutstandingOffer(offerId)\n })\n }\n\n const pruneOutstandingOffers = (): void => {\n const now = Date.now()\n\n entries(outstandingOffers).forEach(([offerId, offer]) => {\n if (now - offer.createdAt > offerRetentionMs) {\n reclaimOutstandingOffer(offerId)\n }\n })\n }\n\n const topicHandler = (data: TrackerMessage): void => {\n if (data.offer && data.peer_id && data.offer_id) {\n void onMessage(\n rootTopic,\n {\n offer: data.offer,\n peerId: data.peer_id,\n hasOutgoingOffer: entries(outstandingOffers).length > 0\n },\n (_, signal) =>\n void send(client, rootTopic, {\n answer: fromJson<Record<string, unknown>>(signal)['answer'],\n offer_id: data.offer_id,\n to_peer_id: data.peer_id\n })\n )\n } else if (data.answer && data.offer_id && data.peer_id) {\n const offer = claimOutstandingOffer(data.offer_id)\n\n if (offer) {\n void onMessage(\n rootTopic,\n {\n answer: data.answer,\n peerId: data.peer_id,\n peer: offer.peer\n },\n () => {}\n )\n }\n }\n }\n\n handlers[rootTopic] = topicHandler\n\n const announce = async (): Promise<void> => {\n if (activeTokens[rootTopic] !== subscriptionToken) {\n return\n }\n\n pruneOutstandingOffers()\n\n const outstandingCount = entries(outstandingOffers).length\n const missingOffers = Math.max(0, offerPoolSize - outstandingCount)\n\n if (missingOffers > 0) {\n ;(await getOffers(missingOffers)).forEach(peerAndOffer => {\n outstandingOffers[genId(hashLimit)] = {\n ...peerAndOffer,\n createdAt: Date.now()\n }\n })\n }\n\n const offers = entries(outstandingOffers).map(([id, {offer}]) => ({\n offer_id: id,\n offer\n }))\n\n void send(client, rootTopic, {\n numwant: offerPoolSize,\n offers\n })\n }\n\n trackerAnnounceMs[url] = defaultAnnounceMs\n relayFns[rootTopic] = announce\n relayIntervals[rootTopic] = setInterval(announce, trackerAnnounceMs[url])\n void announce()\n\n return () => {\n if (activeTokens[rootTopic] !== subscriptionToken) {\n reclaimAllOutstandingOffers()\n\n return\n }\n\n const interval = relayIntervals[rootTopic]\n if (interval) {\n clearInterval(interval)\n delete relayIntervals[rootTopic]\n }\n\n if (handlers[rootTopic] === topicHandler) {\n delete handlers[rootTopic]\n }\n\n if (relayFns[rootTopic] === announce) {\n delete relayFns[rootTopic]\n }\n\n delete activeTokens[rootTopic]\n\n reclaimAllOutstandingOffers()\n }\n },\n\n announce: client => trackerAnnounceMs[client.url]\n})\n\nexport const joinRoom: JoinRoom<TorrentRoomConfig> = (\n config,\n roomId,\n callbacks\n) =>\n joinRoomStrategy(\n {...config, trickleIce: config.trickleIce ?? false},\n roomId,\n callbacks\n )\n\nexport const getRelaySockets = socketGetter(clients)\n\nexport {pauseRelayReconnection, resumeRelayReconnection, selfId}\n\nexport const defaultRelayUrls = [\n 'tracker.webtorrent.dev',\n 'tracker.openwebtorrent.com',\n 'tracker.btorrent.xyz',\n 'tracker.files.fm:7073/announce'\n].map(url => 'wss://' + url)\n\nexport type * from '@trystero-p2p/core'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n createRelayManager,\n createStrategy,\n entries,\n fromJson,\n genId,\n getRelays,\n keys,\n libName,\n makeSocket,\n pauseRelayReconnection,\n resumeRelayReconnection,\n selfId,\n sha1,\n toJson,\n type BaseRoomConfig,\n type JoinRoom,\n type OfferRecord,\n type RelayConfig,\n type SocketClient\n} from '@trystero-p2p/core'\n\nconst relayManager = createRelayManager<SocketClient>(client => client.socket)\nconst topicToInfoHash: Record<string, string> = {}\nconst infoHashToTopic: Record<string, string> = {}\nconst announceIntervals = relayManager.scoped<ReturnType<typeof setInterval>>()\nconst announceFns = relayManager.scoped<() => void | Promise<void>>()\nconst subscriptionTokens = relayManager.scoped<symbol>()\nconst trackerAnnounceMs: Record<string, number> = {}\nconst handledSignals: Record<string, number> = {}\nconst msgHandlers = relayManager.scoped<(data: TrackerMessage) => void>()\nconst roomOutstandingOffers: Record<\n string,\n Record<string, OfferRecord & {createdAt: number}>\n> = {}\nconst roomOfferGenerationPromises: Record<string, Promise<void> | undefined> =\n {}\nconst roomSubscriberCounts: Record<string, number> = {}\nconst trackerAction = 'announce'\nconst hashLimit = 20\nconst offerPoolSize = 3\nconst defaultAnnounceMs = 10_000\nconst maxAnnounceMs = 20_000\nconst offerRetentionMs = 120_000\nconst signalDedupeWindowMs = 4_000\nconst defaultRedundancy = 3\n\nexport type TorrentRoomConfig = BaseRoomConfig & RelayConfig\n\ntype TrackerMessage = {\n offer?: {\n type: 'offer'\n sdp: string\n }\n answer?: {\n type: 'answer'\n sdp: string\n }\n offer_id?: string\n peer_id?: string\n info_hash?: string\n interval?: number\n ['failure reason']?: string\n ['warning message']?: string\n}\n\nconst getInfoHash = async (topic: string): Promise<string> => {\n if (topicToInfoHash[topic]) {\n return topicToInfoHash[topic]\n }\n\n const hash = (await sha1(topic)).slice(0, hashLimit)\n topicToInfoHash[topic] = hash\n infoHashToTopic[hash] = topic\n\n return hash\n}\n\nconst send = async (\n client: SocketClient,\n topic: string,\n payload: Record<string, unknown>\n): Promise<void> =>\n client.send(\n toJson({\n action: trackerAction,\n info_hash: await getInfoHash(topic),\n peer_id: selfId,\n ...payload\n })\n )\n\nconst warn = (url: string, msg: string, didFail = false): void =>\n console.warn(\n `${libName}: torrent tracker ${didFail ? 'failure' : 'warning'} from ${url} - ${msg}`\n )\n\nconst getRoomOutstandingOffers = (\n rootTopic: string\n): Record<string, OfferRecord & {createdAt: number}> =>\n (roomOutstandingOffers[rootTopic] ??= {})\n\nconst deleteRoomOfferBookkeeping = (rootTopic: string): void => {\n delete roomOutstandingOffers[rootTopic]\n delete roomOfferGenerationPromises[rootTopic]\n}\n\nconst claimOutstandingOffer = (\n rootTopic: string,\n offerId: string\n): OfferRecord | undefined => {\n const outstandingOffers = roomOutstandingOffers[rootTopic]\n const offer = outstandingOffers?.[offerId]\n\n if (!offer) {\n return\n }\n\n delete outstandingOffers[offerId]\n offer.claim?.()\n\n if (!keys(outstandingOffers).length && !roomSubscriberCounts[rootTopic]) {\n deleteRoomOfferBookkeeping(rootTopic)\n }\n\n return offer\n}\n\nconst reclaimOutstandingOffer = (rootTopic: string, offerId: string): void => {\n const outstandingOffers = roomOutstandingOffers[rootTopic]\n const offer = outstandingOffers?.[offerId]\n\n if (!offer) {\n return\n }\n\n delete outstandingOffers[offerId]\n offer.reclaim?.()\n\n if (!keys(outstandingOffers).length && !roomSubscriberCounts[rootTopic]) {\n deleteRoomOfferBookkeeping(rootTopic)\n }\n}\n\nconst reclaimAllOutstandingOffers = (rootTopic: string): void => {\n keys(getRoomOutstandingOffers(rootTopic)).forEach(offerId =>\n reclaimOutstandingOffer(rootTopic, offerId)\n )\n deleteRoomOfferBookkeeping(rootTopic)\n}\n\nconst pruneOutstandingOffers = (rootTopic: string): void => {\n const now = Date.now()\n\n entries(getRoomOutstandingOffers(rootTopic)).forEach(([offerId, offer]) => {\n if (now - offer.createdAt > offerRetentionMs) {\n reclaimOutstandingOffer(rootTopic, offerId)\n }\n })\n}\n\nconst ensureOutstandingOffers = async (\n rootTopic: string,\n getOffers: (n: number) => Promise<OfferRecord[]>\n): Promise<Record<string, OfferRecord & {createdAt: number}>> => {\n while (roomOfferGenerationPromises[rootTopic]) {\n await roomOfferGenerationPromises[rootTopic]\n }\n\n const nextPromise = (async () => {\n pruneOutstandingOffers(rootTopic)\n\n const outstandingOffers = getRoomOutstandingOffers(rootTopic)\n const outstandingCount = keys(outstandingOffers).length\n const missingOffers = Math.max(0, offerPoolSize - outstandingCount)\n\n if (missingOffers > 0) {\n ;(await getOffers(missingOffers)).forEach(peerAndOffer => {\n outstandingOffers[genId(hashLimit)] = {\n ...peerAndOffer,\n createdAt: Date.now()\n }\n })\n }\n })().finally(() => {\n if (roomOfferGenerationPromises[rootTopic] === nextPromise) {\n delete roomOfferGenerationPromises[rootTopic]\n }\n })\n\n roomOfferGenerationPromises[rootTopic] = nextPromise\n await nextPromise\n\n return getRoomOutstandingOffers(rootTopic)\n}\n\nconst joinRoomStrategy: JoinRoom<TorrentRoomConfig> = createStrategy({\n init: config =>\n getRelays(config, defaultRelayUrls, defaultRedundancy).map(rawUrl => {\n const client = relayManager.register(\n rawUrl,\n makeSocket(rawUrl, rawData => {\n const data = fromJson<TrackerMessage>(rawData)\n const errMsg = data['failure reason']\n const warnMsg = data['warning message']\n const {interval} = data\n const topic = data.info_hash\n ? infoHashToTopic[data.info_hash]\n : undefined\n\n if (errMsg) {\n warn(client.url, errMsg, true)\n return\n }\n\n if (warnMsg) {\n warn(client.url, warnMsg)\n }\n\n if (\n interval &&\n interval * 1000 >\n (trackerAnnounceMs[client.url] ?? defaultAnnounceMs) &&\n topic &&\n announceFns.forKey(rawUrl)[topic]\n ) {\n const nextInterval = Math.min(interval * 1000, maxAnnounceMs)\n const relayIntervals = announceIntervals.forKey(rawUrl)\n const relayFns = announceFns.forKey(rawUrl)\n\n if (relayIntervals[topic]) {\n clearInterval(relayIntervals[topic])\n }\n trackerAnnounceMs[client.url] = nextInterval\n const relayFn = relayFns[topic]\n\n if (relayFn) {\n relayIntervals[topic] = setInterval(() => {\n void relayFn()\n }, nextInterval)\n }\n }\n\n if ((data.offer || data.answer) && topic && data.offer_id) {\n if (data.peer_id === selfId) {\n return\n }\n\n const signalType = data.offer ? 'offer' : 'answer'\n const signalKey = `${topic}:${signalType}:${data.offer_id}:${data.peer_id ?? ''}`\n const nowMs = Date.now()\n const lastHandledMs = handledSignals[signalKey]\n\n if (\n typeof lastHandledMs === 'number' &&\n nowMs - lastHandledMs < signalDedupeWindowMs\n ) {\n return\n }\n\n handledSignals[signalKey] = nowMs\n\n entries(handledSignals).forEach(([key, handledAtMs]) => {\n if (nowMs - handledAtMs > signalDedupeWindowMs * 6) {\n delete handledSignals[key]\n }\n })\n\n msgHandlers.forKey(rawUrl)[topic]?.(data)\n }\n })\n )\n\n return client.ready\n }),\n\n subscribe: (client, rootTopic, _, onMessage, getOffers) => {\n const handlers = msgHandlers.forRelay(client)\n const relayFns = announceFns.forRelay(client)\n const relayIntervals = announceIntervals.forRelay(client)\n const activeTokens = subscriptionTokens.forRelay(client)\n const subscriptionToken = Symbol(rootTopic)\n\n activeTokens[rootTopic] = subscriptionToken\n roomSubscriberCounts[rootTopic] = (roomSubscriberCounts[rootTopic] ?? 0) + 1\n\n const topicHandler = (data: TrackerMessage): void => {\n if (data.offer && data.peer_id && data.offer_id) {\n void onMessage(\n rootTopic,\n {\n offer: data.offer.sdp,\n peerId: data.peer_id,\n hasOutgoingOffer:\n keys(getRoomOutstandingOffers(rootTopic)).length > 0\n },\n (_, signal) =>\n void send(client, rootTopic, {\n answer: {\n type: 'answer',\n sdp: fromJson<{answer: string}>(signal).answer\n },\n offer_id: data.offer_id,\n to_peer_id: data.peer_id\n })\n )\n } else if (data.answer && data.offer_id && data.peer_id) {\n const offer = claimOutstandingOffer(rootTopic, data.offer_id)\n\n if (offer) {\n void onMessage(\n rootTopic,\n {\n answer: data.answer.sdp,\n peerId: data.peer_id,\n peer: offer.peer\n },\n () => {}\n )\n }\n }\n }\n\n handlers[rootTopic] = topicHandler\n\n const announce = async (): Promise<void> => {\n if (activeTokens[rootTopic] !== subscriptionToken) {\n return\n }\n\n const outstandingOffers = await ensureOutstandingOffers(\n rootTopic,\n getOffers\n )\n const offers = entries(outstandingOffers).map(([id, {offer}]) => ({\n offer_id: id,\n offer: {type: 'offer', sdp: offer}\n }))\n\n void send(client, rootTopic, {\n numwant: offerPoolSize,\n offers\n })\n }\n\n trackerAnnounceMs[client.url] = defaultAnnounceMs\n relayFns[rootTopic] = announce\n relayIntervals[rootTopic] = setInterval(\n announce,\n trackerAnnounceMs[client.url]\n )\n void announce()\n\n return () => {\n roomSubscriberCounts[rootTopic] = Math.max(\n 0,\n (roomSubscriberCounts[rootTopic] ?? 1) - 1\n )\n\n if (!roomSubscriberCounts[rootTopic]) {\n delete roomSubscriberCounts[rootTopic]\n }\n\n if (activeTokens[rootTopic] !== subscriptionToken) {\n if (!roomSubscriberCounts[rootTopic]) {\n reclaimAllOutstandingOffers(rootTopic)\n }\n\n return\n }\n\n const interval = relayIntervals[rootTopic]\n if (interval) {\n clearInterval(interval)\n delete relayIntervals[rootTopic]\n }\n\n if (handlers[rootTopic] === topicHandler) {\n delete handlers[rootTopic]\n }\n\n if (relayFns[rootTopic] === announce) {\n delete relayFns[rootTopic]\n }\n\n delete activeTokens[rootTopic]\n\n if (!roomSubscriberCounts[rootTopic]) {\n reclaimAllOutstandingOffers(rootTopic)\n }\n }\n },\n\n announce: client => trackerAnnounceMs[client.url]\n})\n\nexport const joinRoom: JoinRoom<TorrentRoomConfig> = (\n config,\n roomId,\n callbacks\n) =>\n joinRoomStrategy(\n {\n ...config,\n trickleIce: config.trickleIce ?? false\n },\n roomId,\n callbacks\n )\n\nexport const getRelaySockets = relayManager.getSockets\n\nexport {pauseRelayReconnection, resumeRelayReconnection, selfId}\n\nexport const defaultRelayUrls = [\n 'tracker.webtorrent.dev',\n 'tracker.openwebtorrent.com',\n 'tracker.btorrent.xyz',\n 'tracker.files.fm:7073/announce'\n].map(url => 'wss://' + url)\n\nexport type * from '@trystero-p2p/core'\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trystero-p2p/torrent",
3
- "version": "0.23.0-beta.1",
3
+ "version": "0.23.1",
4
4
  "description": "Torrent tracker strategy for Trystero",
5
5
  "keywords": [
6
6
  "bittorrent",
@@ -28,12 +28,12 @@
28
28
  "dist"
29
29
  ],
30
30
  "type": "module",
31
- "types": "./dist/index.d.ts",
31
+ "types": "./dist/index.d.mts",
32
32
  "exports": {
33
33
  ".": "./dist/index.mjs",
34
34
  "./package.json": "./package.json"
35
35
  },
36
36
  "dependencies": {
37
- "@trystero-p2p/core": "0.23.0-beta.1"
37
+ "@trystero-p2p/core": "0.23.1"
38
38
  }
39
39
  }