@trystero-p2p/torrent 0.23.0-beta.1 → 0.23.0

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,cAoVpC,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,59 +120,31 @@ 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
138
  offer: data.offer,
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
142
  answer: fromJson(signal)["answer"],
116
143
  offer_id: data.offer_id,
117
144
  to_peer_id: data.peer_id
118
145
  }));
119
146
  else if (data.answer && data.offer_id && data.peer_id) {
120
- const offer = claimOutstandingOffer(data.offer_id);
147
+ const offer = claimOutstandingOffer(rootTopic, data.offer_id);
121
148
  if (offer) onMessage(rootTopic, {
122
149
  answer: data.answer,
123
150
  peerId: data.peer_id,
@@ -128,30 +155,23 @@ const joinRoomStrategy = createStrategy({
128
155
  handlers[rootTopic] = topicHandler;
129
156
  const announce = async () => {
130
157
  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
158
  send(client, rootTopic, {
141
159
  numwant: offerPoolSize,
142
- offers: entries(outstandingOffers).map(([id, { offer }]) => ({
160
+ offers: entries(await ensureOutstandingOffers(rootTopic, getOffers)).map(([id, { offer }]) => ({
143
161
  offer_id: id,
144
162
  offer
145
163
  }))
146
164
  });
147
165
  };
148
- trackerAnnounceMs[url] = defaultAnnounceMs;
166
+ trackerAnnounceMs[client.url] = defaultAnnounceMs;
149
167
  relayFns[rootTopic] = announce;
150
- relayIntervals[rootTopic] = setInterval(announce, trackerAnnounceMs[url]);
168
+ relayIntervals[rootTopic] = setInterval(announce, trackerAnnounceMs[client.url]);
151
169
  announce();
152
170
  return () => {
171
+ roomSubscriberCounts[rootTopic] = Math.max(0, (roomSubscriberCounts[rootTopic] ?? 1) - 1);
172
+ if (!roomSubscriberCounts[rootTopic]) delete roomSubscriberCounts[rootTopic];
153
173
  if (activeTokens[rootTopic] !== subscriptionToken) {
154
- reclaimAllOutstandingOffers();
174
+ if (!roomSubscriberCounts[rootTopic]) reclaimAllOutstandingOffers(rootTopic);
155
175
  return;
156
176
  }
157
177
  const interval = relayIntervals[rootTopic];
@@ -162,7 +182,7 @@ const joinRoomStrategy = createStrategy({
162
182
  if (handlers[rootTopic] === topicHandler) delete handlers[rootTopic];
163
183
  if (relayFns[rootTopic] === announce) delete relayFns[rootTopic];
164
184
  delete activeTokens[rootTopic];
165
- reclaimAllOutstandingOffers();
185
+ if (!roomSubscriberCounts[rootTopic]) reclaimAllOutstandingOffers(rootTopic);
166
186
  };
167
187
  },
168
188
  announce: (client) => trackerAnnounceMs[client.url]
@@ -171,7 +191,7 @@ const joinRoom = (config, roomId, callbacks) => joinRoomStrategy({
171
191
  ...config,
172
192
  trickleIce: config.trickleIce ?? false
173
193
  }, roomId, callbacks);
174
- const getRelaySockets = socketGetter(clients);
194
+ const getRelaySockets = relayManager.getSockets;
175
195
  const defaultRelayUrls = [
176
196
  "tracker.webtorrent.dev",
177
197
  "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?: 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\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,\n peerId: data.peer_id,\n hasOutgoingOffer:\n keys(getRoomOutstandingOffers(rootTopic)).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(rootTopic, 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 const outstandingOffers = await ensureOutstandingOffers(\n rootTopic,\n getOffers\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[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.0",
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.0"
38
38
  }
39
39
  }