bun-torrent 0.0.2-beta → 0.0.4-beta
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/README.md +22 -3
- package/dist/client.d.ts +67 -0
- package/dist/client.error.d.ts +8 -0
- package/dist/client.error.js +12 -0
- package/dist/client.error.js.map +1 -0
- package/dist/client.js +130 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/peer/availability.d.ts +50 -0
- package/{src/peer/availability.ts → dist/peer/availability.js} +14 -31
- package/dist/peer/availability.js.map +1 -0
- package/dist/peer/consts.d.ts +8 -0
- package/{src/peer/consts.ts → dist/peer/consts.js} +1 -3
- package/dist/peer/consts.js.map +1 -0
- package/dist/peer/handshake/handshake.error.d.ts +11 -0
- package/dist/peer/handshake/handshake.error.js +15 -0
- package/dist/peer/handshake/handshake.error.js.map +1 -0
- package/dist/peer/handshake/index.d.ts +7 -0
- package/{src/peer/handshake/index.ts → dist/peer/handshake/index.js} +8 -42
- package/dist/peer/handshake/index.js.map +1 -0
- package/{src/peer/index.ts → dist/peer/index.d.ts} +2 -21
- package/dist/peer/index.js +8 -0
- package/dist/peer/index.js.map +1 -0
- package/dist/peer/messages/error.d.ts +12 -0
- package/dist/peer/messages/error.js +16 -0
- package/dist/peer/messages/error.js.map +1 -0
- package/dist/peer/messages/helpers.d.ts +5 -0
- package/{src/peer/messages/helpers.ts → dist/peer/messages/helpers.js} +7 -18
- package/dist/peer/messages/helpers.js.map +1 -0
- package/dist/peer/messages/index.d.ts +6 -0
- package/{src/peer/messages/index.ts → dist/peer/messages/index.js} +30 -80
- package/dist/peer/messages/index.js.map +1 -0
- package/{src/peer/messages/types.ts → dist/peer/messages/types.d.ts} +3 -25
- package/dist/peer/messages/types.js +14 -0
- package/dist/peer/messages/types.js.map +1 -0
- package/dist/peer/peer-id.d.ts +1 -0
- package/{src/peer/peer-id.ts → dist/peer/peer-id.js} +2 -3
- package/dist/peer/peer-id.js.map +1 -0
- package/dist/peer/pool/index.d.ts +65 -0
- package/dist/peer/pool/index.js +207 -0
- package/dist/peer/pool/index.js.map +1 -0
- package/dist/peer/pool/pool.error.d.ts +10 -0
- package/dist/peer/pool/pool.error.js +15 -0
- package/dist/peer/pool/pool.error.js.map +1 -0
- package/dist/peer/session/index.d.ts +35 -0
- package/dist/peer/session/index.js +195 -0
- package/dist/peer/session/index.js.map +1 -0
- package/dist/peer/session/session.error.d.ts +12 -0
- package/dist/peer/session/session.error.js +17 -0
- package/dist/peer/session/session.error.js.map +1 -0
- package/dist/peer/types.js +2 -0
- package/dist/peer/types.js.map +1 -0
- package/dist/torrent/bencode/decoder.d.ts +2 -0
- package/dist/torrent/bencode/decoder.error.d.ts +24 -0
- package/dist/torrent/bencode/decoder.error.js +34 -0
- package/dist/torrent/bencode/decoder.error.js.map +1 -0
- package/{src/torrent/bencode/decoder.ts → dist/torrent/bencode/decoder.js} +44 -92
- package/dist/torrent/bencode/decoder.js.map +1 -0
- package/dist/torrent/bencode/encoder.d.ts +2 -0
- package/dist/torrent/bencode/encoder.error.d.ts +10 -0
- package/dist/torrent/bencode/encoder.error.js +14 -0
- package/dist/torrent/bencode/encoder.error.js.map +1 -0
- package/{src/torrent/bencode/encoder.ts → dist/torrent/bencode/encoder.js} +24 -38
- package/dist/torrent/bencode/encoder.js.map +1 -0
- package/{src/torrent/bencode/index.ts → dist/torrent/bencode/index.d.ts} +0 -2
- package/dist/torrent/bencode/index.js +6 -0
- package/dist/torrent/bencode/index.js.map +1 -0
- package/{src/torrent/bencode/types.ts → dist/torrent/bencode/types.d.ts} +7 -9
- package/dist/torrent/bencode/types.js +10 -0
- package/dist/torrent/bencode/types.js.map +1 -0
- package/dist/torrent/bencode/utils.d.ts +5 -0
- package/dist/torrent/bencode/utils.js +12 -0
- package/dist/torrent/bencode/utils.js.map +1 -0
- package/dist/torrent/download/PeerScorer.d.ts +15 -0
- package/dist/torrent/download/PeerScorer.js +24 -0
- package/dist/torrent/download/PeerScorer.js.map +1 -0
- package/dist/torrent/download/index.d.ts +4 -0
- package/dist/torrent/download/index.js +3 -0
- package/dist/torrent/download/index.js.map +1 -0
- package/dist/torrent/download/manager.d.ts +81 -0
- package/dist/torrent/download/manager.js +357 -0
- package/dist/torrent/download/manager.js.map +1 -0
- package/dist/torrent/file-selection.d.ts +6 -0
- package/dist/torrent/file-selection.js +32 -0
- package/dist/torrent/file-selection.js.map +1 -0
- package/dist/torrent/index.d.ts +17 -0
- package/dist/torrent/index.js +11 -0
- package/dist/torrent/index.js.map +1 -0
- package/dist/torrent/parser/helpers.d.ts +8 -0
- package/{src/torrent/parser/helpers.ts → dist/torrent/parser/helpers.js} +13 -24
- package/dist/torrent/parser/helpers.js.map +1 -0
- package/dist/torrent/parser/index.d.ts +2 -0
- package/{src/torrent/parser/index.ts → dist/torrent/parser/index.js} +25 -103
- package/dist/torrent/parser/index.js.map +1 -0
- package/dist/torrent/parser/info-hash.d.ts +2 -0
- package/dist/torrent/parser/info-hash.js +6 -0
- package/dist/torrent/parser/info-hash.js.map +1 -0
- package/dist/torrent/parser/parser.error.d.ts +14 -0
- package/dist/torrent/parser/parser.error.js +19 -0
- package/dist/torrent/parser/parser.error.js.map +1 -0
- package/dist/torrent/pieces/DefaultPlanner.d.ts +98 -0
- package/{src/torrent/pieces/DefaultPlanner.ts → dist/torrent/pieces/DefaultPlanner.js} +49 -93
- package/dist/torrent/pieces/DefaultPlanner.js.map +1 -0
- package/dist/torrent/pieces/index.d.ts +5 -0
- package/dist/torrent/pieces/index.js +5 -0
- package/dist/torrent/pieces/index.js.map +1 -0
- package/{src/torrent/pieces/planner.ts → dist/torrent/pieces/planner.d.ts} +2 -7
- package/dist/torrent/pieces/planner.error.d.ts +11 -0
- package/dist/torrent/pieces/planner.error.js +16 -0
- package/dist/torrent/pieces/planner.error.js.map +1 -0
- package/dist/torrent/pieces/planner.js +11 -0
- package/dist/torrent/pieces/planner.js.map +1 -0
- package/{src/torrent/pieces/types.ts → dist/torrent/pieces/types.d.ts} +1 -13
- package/dist/torrent/pieces/types.js +2 -0
- package/dist/torrent/pieces/types.js.map +1 -0
- package/dist/torrent/pieces/utils.d.ts +34 -0
- package/{src/torrent/pieces/utils.ts → dist/torrent/pieces/utils.js} +8 -37
- package/dist/torrent/pieces/utils.js.map +1 -0
- package/dist/torrent/pieces/validation.d.ts +14 -0
- package/{src/torrent/pieces/validation.ts → dist/torrent/pieces/validation.js} +4 -13
- package/dist/torrent/pieces/validation.js.map +1 -0
- package/dist/torrent/session/index.d.ts +77 -0
- package/dist/torrent/session/index.js +126 -0
- package/dist/torrent/session/index.js.map +1 -0
- package/dist/torrent/storage/index.d.ts +35 -0
- package/{src/torrent/storage/index.ts → dist/torrent/storage/index.js} +33 -94
- package/dist/torrent/storage/index.js.map +1 -0
- package/dist/torrent/storage/storage.error.d.ts +11 -0
- package/dist/torrent/storage/storage.error.js +16 -0
- package/dist/torrent/storage/storage.error.js.map +1 -0
- package/{src/torrent/storage/types.ts → dist/torrent/storage/types.d.ts} +0 -2
- package/dist/torrent/storage/types.js +2 -0
- package/dist/torrent/storage/types.js.map +1 -0
- package/{src/torrent/types.ts → dist/torrent/types.d.ts} +0 -1
- package/dist/torrent/types.js +2 -0
- package/dist/torrent/types.js.map +1 -0
- package/dist/tracker/http.d.ts +4 -0
- package/{src/tracker/http.ts → dist/tracker/http.js} +24 -74
- package/dist/tracker/http.js.map +1 -0
- package/dist/tracker/index.d.ts +12 -0
- package/dist/tracker/index.js +49 -0
- package/dist/tracker/index.js.map +1 -0
- package/dist/tracker/tracker.error.d.ts +19 -0
- package/dist/tracker/tracker.error.js +24 -0
- package/dist/tracker/tracker.error.js.map +1 -0
- package/dist/tracker/types.d.ts +11 -0
- package/dist/tracker/types.js +2 -0
- package/dist/tracker/types.js.map +1 -0
- package/dist/tracker/udp.d.ts +3 -0
- package/{src/tracker/udp.ts → dist/tracker/udp.js} +27 -96
- package/dist/tracker/udp.js.map +1 -0
- package/dist/utils/buffers.d.ts +5 -0
- package/{src/utils/buffers.ts → dist/utils/buffers.js} +7 -14
- package/dist/utils/buffers.js.map +1 -0
- package/dist/utils/errors.d.ts +4 -0
- package/{src/utils/errors.ts → dist/utils/errors.js} +4 -4
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/formats.d.ts +1 -0
- package/{src/utils/formats.ts → dist/utils/formats.js} +4 -3
- package/dist/utils/formats.js.map +1 -0
- package/dist/utils/sha1.d.ts +1 -0
- package/dist/utils/sha1.js +3 -0
- package/dist/utils/sha1.js.map +1 -0
- package/package.json +10 -6
- package/src/app.ts +0 -65
- package/src/client.error.ts +0 -12
- package/src/client.test.ts +0 -121
- package/src/client.ts +0 -185
- package/src/index.test.ts +0 -7
- package/src/index.ts +0 -76
- package/src/peer/__tests__/peer-id.test.ts +0 -24
- package/src/peer/availability.test.ts +0 -50
- package/src/peer/handshake/handshake.error.ts +0 -15
- package/src/peer/handshake/handshake.test.ts +0 -125
- package/src/peer/messages/error.ts +0 -16
- package/src/peer/messages/messages.test.ts +0 -231
- package/src/peer/pool/index.ts +0 -301
- package/src/peer/pool/pool.error.ts +0 -17
- package/src/peer/pool/pool.test.ts +0 -305
- package/src/peer/session/index.ts +0 -276
- package/src/peer/session/session.error.ts +0 -19
- package/src/peer/session/session.test.ts +0 -110
- package/src/torrent/bencode/__tests__/decoder.test.ts +0 -212
- package/src/torrent/bencode/__tests__/encoder.test.ts +0 -138
- package/src/torrent/bencode/__tests__/encoder.unit.test.ts +0 -24
- package/src/torrent/bencode/__tests__/integration.test.ts +0 -64
- package/src/torrent/bencode/decoder.error.ts +0 -36
- package/src/torrent/bencode/encoder.error.ts +0 -14
- package/src/torrent/bencode/utils.ts +0 -17
- package/src/torrent/download/index.ts +0 -9
- package/src/torrent/download/manager.test.ts +0 -393
- package/src/torrent/download/manager.ts +0 -376
- package/src/torrent/file-selection.ts +0 -51
- package/src/torrent/index.ts +0 -61
- package/src/torrent/parser/info-hash.test.ts +0 -39
- package/src/torrent/parser/info-hash.ts +0 -7
- package/src/torrent/parser/parser.error.ts +0 -21
- package/src/torrent/parser/parser.test.ts +0 -286
- package/src/torrent/pieces/index.ts +0 -20
- package/src/torrent/pieces/planner.error.ts +0 -18
- package/src/torrent/pieces/planner.test.ts +0 -303
- package/src/torrent/pieces/validation.test.ts +0 -32
- package/src/torrent/session/index.ts +0 -195
- package/src/torrent/session/session.test.ts +0 -279
- package/src/torrent/storage/storage.error.ts +0 -18
- package/src/torrent/storage/storage.test.ts +0 -326
- package/src/tracker/http.test.ts +0 -66
- package/src/tracker/index.ts +0 -93
- package/src/tracker/tracker.error.ts +0 -26
- package/src/tracker/types.ts +0 -17
- package/src/tracker/udp.test.ts +0 -155
- package/src/utils/__tests__/sha1.test.ts +0 -16
- package/src/utils/sha1.ts +0 -4
- /package/{src/peer/types.ts → dist/peer/types.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -66,8 +66,13 @@ import { Client } from 'bun-torrent';
|
|
|
66
66
|
|
|
67
67
|
const client = new Client({
|
|
68
68
|
outputDirectory: './downloads',
|
|
69
|
+
targetConnections: 20,
|
|
70
|
+
maxConnecting: 30,
|
|
69
71
|
maxInFlightRequestsPerPeer: 20,
|
|
72
|
+
peerConnectTimeoutMs: 5_000,
|
|
73
|
+
trackerTimeoutMs: 5_000,
|
|
70
74
|
requestTimeoutMs: 15_000,
|
|
75
|
+
seed: false,
|
|
71
76
|
progressEvents: 'piece',
|
|
72
77
|
speedSampleIntervalMs: 500,
|
|
73
78
|
});
|
|
@@ -77,8 +82,14 @@ Client options:
|
|
|
77
82
|
|
|
78
83
|
- `outputDirectory`: directory where downloaded files are written. Defaults to `process.cwd()`.
|
|
79
84
|
- `files`: optional default file selection for downloads.
|
|
85
|
+
- `targetConnections`: preferred number of connected peers. Defaults to `20`.
|
|
86
|
+
- `minConnections`: minimum connectable peer count before downloading starts. Defaults to `0`.
|
|
87
|
+
- `maxConnecting`: maximum simultaneous peer connection attempts. Defaults to `30`.
|
|
88
|
+
- `peerConnectTimeoutMs`: timeout for connecting to one peer. Defaults to `5000`.
|
|
89
|
+
- `trackerTimeoutMs`: timeout for tracker announces. Defaults to `5000`.
|
|
80
90
|
- `maxInFlightRequestsPerPeer`: maximum active block requests per peer. Defaults to `20`.
|
|
81
91
|
- `requestTimeoutMs`: timeout for an individual block request. Defaults to `15000`.
|
|
92
|
+
- `seed`: reserved for future seeding support. Currently only `false` is supported.
|
|
82
93
|
- `progressEvents`: `'piece'` emits progress when a piece completes, `'block'` emits for every received block. Defaults to `'piece'`.
|
|
83
94
|
- `speedSampleIntervalMs`: minimum interval used to refresh speed calculations. Defaults to `500`.
|
|
84
95
|
|
|
@@ -109,8 +120,11 @@ const torrent = await client.download(
|
|
|
109
120
|
},
|
|
110
121
|
{
|
|
111
122
|
outputDirectory: './downloads',
|
|
123
|
+
targetConnections: 50,
|
|
124
|
+
maxConnecting: 100,
|
|
112
125
|
minConnections: 5,
|
|
113
126
|
announcePort: 6881,
|
|
127
|
+
seed: false,
|
|
114
128
|
progressEvents: 'block',
|
|
115
129
|
onChangeState: (state) => {
|
|
116
130
|
console.log('client state:', state);
|
|
@@ -123,10 +137,15 @@ Download options:
|
|
|
123
137
|
|
|
124
138
|
- `outputDirectory`: override the output directory for this download.
|
|
125
139
|
- `files`: download only selected files.
|
|
140
|
+
- `targetConnections`: override the preferred peer connection count.
|
|
126
141
|
- `minConnections`: minimum connectable peer count requested before downloading starts.
|
|
142
|
+
- `maxConnecting`: override simultaneous peer connection attempts.
|
|
143
|
+
- `peerConnectTimeoutMs`: override peer connection timeout.
|
|
144
|
+
- `trackerTimeoutMs`: override tracker announce timeout.
|
|
127
145
|
- `announcePort`: port sent to trackers in announce requests.
|
|
128
146
|
- `maxInFlightRequestsPerPeer`: override request concurrency per peer.
|
|
129
147
|
- `requestTimeoutMs`: override block request timeout.
|
|
148
|
+
- `seed`: reserved for future seeding support. Currently only `false` is supported.
|
|
130
149
|
- `progressEvents`: `'piece'` or `'block'`.
|
|
131
150
|
- `speedSampleIntervalMs`: override speed sample interval.
|
|
132
151
|
- `onChangeState`: receives client setup states: `parsing`, `tracking`, `connecting`, `downloading`.
|
|
@@ -185,8 +204,8 @@ torrent.on('progress', (progress) => {
|
|
|
185
204
|
console.log(progress.percent, progress.speed);
|
|
186
205
|
});
|
|
187
206
|
|
|
188
|
-
torrent.on('peer', (
|
|
189
|
-
console.log('peer connected',
|
|
207
|
+
torrent.on('peer', (stats) => {
|
|
208
|
+
console.log('peer connected', stats.connections, '/', stats.targetConnections);
|
|
190
209
|
});
|
|
191
210
|
|
|
192
211
|
torrent.on('done', () => {
|
|
@@ -209,7 +228,7 @@ The current torrent state is also available through `torrent.state`. Possible st
|
|
|
209
228
|
- `failed`
|
|
210
229
|
- `closed`
|
|
211
230
|
|
|
212
|
-
Call `torrent.close()` to stop
|
|
231
|
+
When a download completes, `torrent.done` resolves, the state becomes `completed`, and internal peer connections are closed automatically because seeding is not implemented yet. Call `torrent.close()` to stop an active download early or to mark a completed torrent as explicitly closed.
|
|
213
232
|
|
|
214
233
|
## Progress Shape
|
|
215
234
|
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type DownloadProgressEventMode } from './torrent/download';
|
|
2
|
+
import { Torrent } from './torrent/session/index';
|
|
3
|
+
import type { TorrentMetadata } from './torrent/types';
|
|
4
|
+
import type { TorrentFileSelection } from './torrent/file-selection';
|
|
5
|
+
export declare enum DownloadState {
|
|
6
|
+
PARSING = "parsing",
|
|
7
|
+
TRACKING = "tracking",
|
|
8
|
+
CONNECTING = "connecting",
|
|
9
|
+
DOWNLOADING = "downloading"
|
|
10
|
+
}
|
|
11
|
+
export type ClientConfig = {
|
|
12
|
+
files?: TorrentFileSelection;
|
|
13
|
+
maxInFlightRequestsPerPeer?: number;
|
|
14
|
+
maxConnecting?: number;
|
|
15
|
+
minConnections?: number;
|
|
16
|
+
outputDirectory?: string;
|
|
17
|
+
peerConnectTimeoutMs?: number;
|
|
18
|
+
progressEvents?: DownloadProgressEventMode;
|
|
19
|
+
requestTimeoutMs?: number;
|
|
20
|
+
seed?: false;
|
|
21
|
+
speedSampleIntervalMs?: number;
|
|
22
|
+
targetConnections?: number;
|
|
23
|
+
trackerTimeoutMs?: number;
|
|
24
|
+
};
|
|
25
|
+
export declare const DEFAULT_CLIENT_CONFIG: {
|
|
26
|
+
readonly maxInFlightRequestsPerPeer: 20;
|
|
27
|
+
readonly maxConnecting: 30;
|
|
28
|
+
readonly minConnections: 0;
|
|
29
|
+
readonly peerConnectTimeoutMs: 5000;
|
|
30
|
+
readonly progressEvents: "piece";
|
|
31
|
+
readonly requestTimeoutMs: 15000;
|
|
32
|
+
readonly seed: false;
|
|
33
|
+
readonly speedSampleIntervalMs: 500;
|
|
34
|
+
readonly targetConnections: 20;
|
|
35
|
+
readonly trackerTimeoutMs: 5000;
|
|
36
|
+
};
|
|
37
|
+
export declare class Client {
|
|
38
|
+
private readonly config;
|
|
39
|
+
private readonly peerId;
|
|
40
|
+
constructor(config?: ClientConfig);
|
|
41
|
+
download(input: {
|
|
42
|
+
torrentFile: string | Uint8Array | ArrayBuffer;
|
|
43
|
+
}, options?: DownloadOptions): Promise<Torrent>;
|
|
44
|
+
inspect(input: {
|
|
45
|
+
torrentFile: string | Uint8Array | ArrayBuffer;
|
|
46
|
+
}): Promise<TorrentMetadata>;
|
|
47
|
+
private trackPeers;
|
|
48
|
+
}
|
|
49
|
+
export type DownloadOptions = {
|
|
50
|
+
announcePort?: number;
|
|
51
|
+
files?: TorrentFileSelection;
|
|
52
|
+
maxInFlightRequestsPerPeer?: number;
|
|
53
|
+
maxConnecting?: number;
|
|
54
|
+
minConnections?: number;
|
|
55
|
+
onChangeState?: (state: DownloadState) => unknown;
|
|
56
|
+
outputDirectory?: string;
|
|
57
|
+
peerConnectTimeoutMs?: number;
|
|
58
|
+
progressEvents?: DownloadProgressEventMode;
|
|
59
|
+
requestTimeoutMs?: number;
|
|
60
|
+
seed?: false;
|
|
61
|
+
speedSampleIntervalMs?: number;
|
|
62
|
+
targetConnections?: number;
|
|
63
|
+
trackerTimeoutMs?: number;
|
|
64
|
+
};
|
|
65
|
+
export type TorrentFileInput = string | Uint8Array | ArrayBuffer;
|
|
66
|
+
export declare const readTorrentFile: (input: TorrentFileInput) => Promise<Uint8Array>;
|
|
67
|
+
export { Client as TorrentClient };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BunTorrentError } from './utils/errors';
|
|
2
|
+
export declare enum ClientErrorCode {
|
|
3
|
+
INVALID_FILE_SELECTION = "CLIENT_INVALID_FILE_SELECTION",
|
|
4
|
+
UNSUPPORTED_TORRENT_FILE_INPUT = "CLIENT_UNSUPPORTED_TORRENT_FILE_INPUT"
|
|
5
|
+
}
|
|
6
|
+
export declare class ClientError extends BunTorrentError {
|
|
7
|
+
constructor(code: ClientErrorCode, message: string);
|
|
8
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BunTorrentError } from './utils/errors';
|
|
2
|
+
export var ClientErrorCode;
|
|
3
|
+
(function (ClientErrorCode) {
|
|
4
|
+
ClientErrorCode["INVALID_FILE_SELECTION"] = "CLIENT_INVALID_FILE_SELECTION";
|
|
5
|
+
ClientErrorCode["UNSUPPORTED_TORRENT_FILE_INPUT"] = "CLIENT_UNSUPPORTED_TORRENT_FILE_INPUT";
|
|
6
|
+
})(ClientErrorCode || (ClientErrorCode = {}));
|
|
7
|
+
export class ClientError extends BunTorrentError {
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super(message, code);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=client.error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.error.js","sourceRoot":"","sources":["../src/client.error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,CAAN,IAAY,eAGX;AAHD,WAAY,eAAe;IACvB,2EAAwD,CAAA;IACxD,2FAAwE,CAAA;AAC5E,CAAC,EAHW,eAAe,KAAf,eAAe,QAG1B;AAED,MAAM,OAAO,WAAY,SAAQ,eAAe;IAC5C,YAAY,IAAqB,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC;CACJ"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { createPeerId } from './peer/peer-id';
|
|
2
|
+
import { openPeerPool } from './peer/pool';
|
|
3
|
+
import { DownloadManager } from './torrent/download';
|
|
4
|
+
import { parseTorrent } from './torrent/parser';
|
|
5
|
+
import { Torrent } from './torrent/session/index';
|
|
6
|
+
import { ClientError, ClientErrorCode } from './client.error';
|
|
7
|
+
import { trackPeers, TrackerError, TrackerErrorCode } from './tracker';
|
|
8
|
+
import { getUnknownSelectedFiles, normalizeTorrentFileSelection } from './torrent/file-selection';
|
|
9
|
+
export var DownloadState;
|
|
10
|
+
(function (DownloadState) {
|
|
11
|
+
DownloadState["PARSING"] = "parsing";
|
|
12
|
+
DownloadState["TRACKING"] = "tracking";
|
|
13
|
+
DownloadState["CONNECTING"] = "connecting";
|
|
14
|
+
DownloadState["DOWNLOADING"] = "downloading";
|
|
15
|
+
})(DownloadState || (DownloadState = {}));
|
|
16
|
+
export const DEFAULT_CLIENT_CONFIG = {
|
|
17
|
+
maxInFlightRequestsPerPeer: 20,
|
|
18
|
+
maxConnecting: 30,
|
|
19
|
+
minConnections: 0,
|
|
20
|
+
peerConnectTimeoutMs: 5_000,
|
|
21
|
+
progressEvents: 'piece',
|
|
22
|
+
requestTimeoutMs: 15_000,
|
|
23
|
+
seed: false,
|
|
24
|
+
speedSampleIntervalMs: 500,
|
|
25
|
+
targetConnections: 20,
|
|
26
|
+
trackerTimeoutMs: 5_000,
|
|
27
|
+
};
|
|
28
|
+
export class Client {
|
|
29
|
+
config;
|
|
30
|
+
peerId;
|
|
31
|
+
constructor(config = {}) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.peerId = createPeerId();
|
|
34
|
+
}
|
|
35
|
+
async download(input, options = {}) {
|
|
36
|
+
options.onChangeState?.(DownloadState.PARSING);
|
|
37
|
+
const meta = await this.inspect(input);
|
|
38
|
+
const downloadConfig = resolveDownloadConfig(this.config, options);
|
|
39
|
+
assertValidFileSelection(meta, downloadConfig.files);
|
|
40
|
+
options.onChangeState?.(DownloadState.TRACKING);
|
|
41
|
+
const peers = await this.trackPeers(meta, options, downloadConfig);
|
|
42
|
+
options.onChangeState?.(DownloadState.CONNECTING);
|
|
43
|
+
const pool = await openPeerPool(peers, {
|
|
44
|
+
infoHash: meta.infoHash,
|
|
45
|
+
peerId: this.peerId,
|
|
46
|
+
targetConnections: downloadConfig.targetConnections,
|
|
47
|
+
totalPieces: meta.pieces.length,
|
|
48
|
+
minConnections: downloadConfig.minConnections,
|
|
49
|
+
maxConnecting: downloadConfig.maxConnecting,
|
|
50
|
+
timeoutMs: downloadConfig.peerConnectTimeoutMs,
|
|
51
|
+
});
|
|
52
|
+
options.onChangeState?.(DownloadState.DOWNLOADING);
|
|
53
|
+
return new Torrent(meta, pool, new DownloadManager({
|
|
54
|
+
metadata: meta,
|
|
55
|
+
files: downloadConfig.files,
|
|
56
|
+
outputDirectory: downloadConfig.outputDirectory,
|
|
57
|
+
peerPool: pool,
|
|
58
|
+
maxInFlightRequestsPerPeer: downloadConfig.maxInFlightRequestsPerPeer,
|
|
59
|
+
progressEvents: downloadConfig.progressEvents,
|
|
60
|
+
requestTimeoutMs: downloadConfig.requestTimeoutMs,
|
|
61
|
+
speedSampleIntervalMs: downloadConfig.speedSampleIntervalMs,
|
|
62
|
+
}), normalizeTorrentFileSelection(downloadConfig.files), downloadConfig.seed);
|
|
63
|
+
}
|
|
64
|
+
async inspect(input) {
|
|
65
|
+
const bytes = await readTorrentFile(input.torrentFile);
|
|
66
|
+
return parseTorrent(bytes);
|
|
67
|
+
}
|
|
68
|
+
async trackPeers(meta, options, config) {
|
|
69
|
+
try {
|
|
70
|
+
return await trackPeers({
|
|
71
|
+
meta,
|
|
72
|
+
peerId: this.peerId,
|
|
73
|
+
announcePort: options.announcePort,
|
|
74
|
+
timeoutMs: config.trackerTimeoutMs,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (isNonFatalTrackerError(error))
|
|
79
|
+
return [];
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const isNonFatalTrackerError = (error) => error instanceof TrackerError &&
|
|
85
|
+
(error.code === TrackerErrorCode.ANNOUNCE_FAILED ||
|
|
86
|
+
error.code === TrackerErrorCode.NO_PEERS ||
|
|
87
|
+
error.code === TrackerErrorCode.NO_SUPPORTED_TRACKERS);
|
|
88
|
+
const resolveDownloadConfig = (config, options) => ({
|
|
89
|
+
files: options.files ?? config.files ?? null,
|
|
90
|
+
maxInFlightRequestsPerPeer: options.maxInFlightRequestsPerPeer ??
|
|
91
|
+
config.maxInFlightRequestsPerPeer ??
|
|
92
|
+
DEFAULT_CLIENT_CONFIG.maxInFlightRequestsPerPeer,
|
|
93
|
+
maxConnecting: options.maxConnecting ?? config.maxConnecting ?? DEFAULT_CLIENT_CONFIG.maxConnecting,
|
|
94
|
+
minConnections: options.minConnections ?? config.minConnections ?? DEFAULT_CLIENT_CONFIG.minConnections,
|
|
95
|
+
outputDirectory: options.outputDirectory ?? config.outputDirectory ?? process.cwd(),
|
|
96
|
+
peerConnectTimeoutMs: options.peerConnectTimeoutMs ??
|
|
97
|
+
config.peerConnectTimeoutMs ??
|
|
98
|
+
DEFAULT_CLIENT_CONFIG.peerConnectTimeoutMs,
|
|
99
|
+
progressEvents: options.progressEvents ?? config.progressEvents ?? DEFAULT_CLIENT_CONFIG.progressEvents,
|
|
100
|
+
requestTimeoutMs: options.requestTimeoutMs ??
|
|
101
|
+
config.requestTimeoutMs ??
|
|
102
|
+
DEFAULT_CLIENT_CONFIG.requestTimeoutMs,
|
|
103
|
+
seed: options.seed ?? config.seed ?? DEFAULT_CLIENT_CONFIG.seed,
|
|
104
|
+
speedSampleIntervalMs: options.speedSampleIntervalMs ??
|
|
105
|
+
config.speedSampleIntervalMs ??
|
|
106
|
+
DEFAULT_CLIENT_CONFIG.speedSampleIntervalMs,
|
|
107
|
+
targetConnections: options.targetConnections ??
|
|
108
|
+
config.targetConnections ??
|
|
109
|
+
DEFAULT_CLIENT_CONFIG.targetConnections,
|
|
110
|
+
trackerTimeoutMs: options.trackerTimeoutMs ??
|
|
111
|
+
config.trackerTimeoutMs ??
|
|
112
|
+
DEFAULT_CLIENT_CONFIG.trackerTimeoutMs,
|
|
113
|
+
});
|
|
114
|
+
const assertValidFileSelection = (metadata, files) => {
|
|
115
|
+
const unknownFiles = getUnknownSelectedFiles(metadata, files);
|
|
116
|
+
if (unknownFiles.length === 0)
|
|
117
|
+
return;
|
|
118
|
+
throw new ClientError(ClientErrorCode.INVALID_FILE_SELECTION, `Unknown torrent file selection: ${unknownFiles.join(', ')}`);
|
|
119
|
+
};
|
|
120
|
+
export const readTorrentFile = async (input) => {
|
|
121
|
+
if (typeof input === 'string')
|
|
122
|
+
return await Bun.file(input).bytes();
|
|
123
|
+
if (input instanceof Uint8Array)
|
|
124
|
+
return input;
|
|
125
|
+
if (input instanceof ArrayBuffer)
|
|
126
|
+
return new Uint8Array(input);
|
|
127
|
+
throw new ClientError(ClientErrorCode.UNSUPPORTED_TORRENT_FILE_INPUT, 'Unsupported torrent file input');
|
|
128
|
+
};
|
|
129
|
+
export { Client as TorrentClient };
|
|
130
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Nikola Nedeljkovic
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
export { Client, DEFAULT_CLIENT_CONFIG, DownloadState, TorrentClient } from './client';
|
|
6
|
+
export { ClientError, ClientErrorCode } from './client.error';
|
|
7
|
+
export type { ClientConfig, DownloadOptions, TorrentFileInput } from './client';
|
|
8
|
+
export { Torrent, TorrentState, TorrentParseError, TorrentParseErrorCode, TorrentStorageError, TorrentStorageErrorCode, PiecePlannerError, PiecePlannerErrorCode, } from './torrent/index';
|
|
9
|
+
export { PeerPoolError, PeerPoolErrorCode, PeerSessionError, PeerSessionErrorCode } from './peer';
|
|
10
|
+
export { TrackerError, TrackerErrorCode } from './tracker';
|
|
11
|
+
export type { DownloadProgress, DownloadProgressEventMode, TorrentFileSelection, TorrentFiles, TorrentMetadata, TorrentStats, TorrentStateChange, } from './torrent/index';
|
|
12
|
+
export type { TorrentFile } from './torrent/types';
|
|
13
|
+
export { BunTorrentError } from './utils/errors';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Nikola Nedeljkovic
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
export { Client, DEFAULT_CLIENT_CONFIG, DownloadState, TorrentClient } from './client';
|
|
6
|
+
export { ClientError, ClientErrorCode } from './client.error';
|
|
7
|
+
export { Torrent, TorrentState, TorrentParseError, TorrentParseErrorCode, TorrentStorageError, TorrentStorageErrorCode, PiecePlannerError, PiecePlannerErrorCode, } from './torrent/index';
|
|
8
|
+
export { PeerPoolError, PeerPoolErrorCode, PeerSessionError, PeerSessionErrorCode } from './peer';
|
|
9
|
+
export { TrackerError, TrackerErrorCode } from './tracker';
|
|
10
|
+
export { BunTorrentError } from './utils/errors';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG9D,OAAO,EACH,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAClG,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAa3D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type PeerPieceAvailabilityOptions = {
|
|
2
|
+
bitfield?: Uint8Array;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Mutable view of which torrent pieces a peer claims to have.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PeerPieceAvailability {
|
|
8
|
+
readonly totalPieces: number;
|
|
9
|
+
private readonly pieces;
|
|
10
|
+
/**
|
|
11
|
+
* Create availability state from an optional peer bitfield.
|
|
12
|
+
*
|
|
13
|
+
* @param totalPieces - Number of pieces in the torrent.
|
|
14
|
+
* @param options - Optional initial bitfield from the peer.
|
|
15
|
+
*/
|
|
16
|
+
constructor(totalPieces: number, options?: PeerPieceAvailabilityOptions);
|
|
17
|
+
/**
|
|
18
|
+
* Return true when the peer claims to have a piece.
|
|
19
|
+
*
|
|
20
|
+
* @param pieceIndex - Zero-based piece index.
|
|
21
|
+
* @returns Whether the peer has the piece. Invalid indexes return false.
|
|
22
|
+
*/
|
|
23
|
+
hasPiece(pieceIndex: number): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Mark one piece as available after receiving a peer `have` message.
|
|
26
|
+
*
|
|
27
|
+
* @param pieceIndex - Zero-based piece index.
|
|
28
|
+
*/
|
|
29
|
+
markHave(pieceIndex: number): void;
|
|
30
|
+
/**
|
|
31
|
+
* Replace availability from a peer `bitfield` message.
|
|
32
|
+
*
|
|
33
|
+
* @param bitfield - Raw bitfield payload from the peer.
|
|
34
|
+
*/
|
|
35
|
+
setBitfield(bitfield: Uint8Array): void;
|
|
36
|
+
/**
|
|
37
|
+
* Return available piece indexes in ascending order.
|
|
38
|
+
*
|
|
39
|
+
* @returns Piece indexes currently marked as available.
|
|
40
|
+
*/
|
|
41
|
+
toPieceIndexes(): number[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create mutable availability state from an optional peer bitfield.
|
|
45
|
+
*
|
|
46
|
+
* @param totalPieces - Number of pieces in the torrent.
|
|
47
|
+
* @param bitfield - Optional raw bitfield payload from the peer.
|
|
48
|
+
* @returns Peer piece availability state.
|
|
49
|
+
*/
|
|
50
|
+
export declare const createPeerPieceAvailability: (totalPieces: number, bitfield?: Uint8Array) => PeerPieceAvailability;
|
|
@@ -1,72 +1,58 @@
|
|
|
1
|
-
export type PeerPieceAvailabilityOptions = {
|
|
2
|
-
bitfield?: Uint8Array;
|
|
3
|
-
};
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
2
|
* Mutable view of which torrent pieces a peer claims to have.
|
|
7
3
|
*/
|
|
8
4
|
export class PeerPieceAvailability {
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
totalPieces;
|
|
6
|
+
pieces;
|
|
11
7
|
/**
|
|
12
8
|
* Create availability state from an optional peer bitfield.
|
|
13
9
|
*
|
|
14
10
|
* @param totalPieces - Number of pieces in the torrent.
|
|
15
11
|
* @param options - Optional initial bitfield from the peer.
|
|
16
12
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
) {
|
|
21
|
-
this.pieces = Array.from({ length: totalPieces }, (_, pieceIndex) =>
|
|
22
|
-
options.bitfield ? hasBitfieldPiece(options.bitfield, pieceIndex) : false,
|
|
23
|
-
);
|
|
13
|
+
constructor(totalPieces, options = {}) {
|
|
14
|
+
this.totalPieces = totalPieces;
|
|
15
|
+
this.pieces = Array.from({ length: totalPieces }, (_, pieceIndex) => options.bitfield ? hasBitfieldPiece(options.bitfield, pieceIndex) : false);
|
|
24
16
|
}
|
|
25
|
-
|
|
26
17
|
/**
|
|
27
18
|
* Return true when the peer claims to have a piece.
|
|
28
19
|
*
|
|
29
20
|
* @param pieceIndex - Zero-based piece index.
|
|
30
21
|
* @returns Whether the peer has the piece. Invalid indexes return false.
|
|
31
22
|
*/
|
|
32
|
-
|
|
23
|
+
hasPiece(pieceIndex) {
|
|
33
24
|
return this.pieces[pieceIndex] ?? false;
|
|
34
25
|
}
|
|
35
|
-
|
|
36
26
|
/**
|
|
37
27
|
* Mark one piece as available after receiving a peer `have` message.
|
|
38
28
|
*
|
|
39
29
|
* @param pieceIndex - Zero-based piece index.
|
|
40
30
|
*/
|
|
41
|
-
|
|
31
|
+
markHave(pieceIndex) {
|
|
42
32
|
if (!Number.isInteger(pieceIndex) || pieceIndex < 0 || pieceIndex >= this.totalPieces) {
|
|
43
33
|
return;
|
|
44
34
|
}
|
|
45
|
-
|
|
46
35
|
this.pieces[pieceIndex] = true;
|
|
47
36
|
}
|
|
48
|
-
|
|
49
37
|
/**
|
|
50
38
|
* Replace availability from a peer `bitfield` message.
|
|
51
39
|
*
|
|
52
40
|
* @param bitfield - Raw bitfield payload from the peer.
|
|
53
41
|
*/
|
|
54
|
-
|
|
42
|
+
setBitfield(bitfield) {
|
|
55
43
|
for (let pieceIndex = 0; pieceIndex < this.totalPieces; pieceIndex += 1) {
|
|
56
44
|
this.pieces[pieceIndex] = hasBitfieldPiece(bitfield, pieceIndex);
|
|
57
45
|
}
|
|
58
46
|
}
|
|
59
|
-
|
|
60
47
|
/**
|
|
61
48
|
* Return available piece indexes in ascending order.
|
|
62
49
|
*
|
|
63
50
|
* @returns Piece indexes currently marked as available.
|
|
64
51
|
*/
|
|
65
|
-
|
|
52
|
+
toPieceIndexes() {
|
|
66
53
|
return this.pieces.flatMap((available, pieceIndex) => (available ? [pieceIndex] : []));
|
|
67
54
|
}
|
|
68
55
|
}
|
|
69
|
-
|
|
70
56
|
/**
|
|
71
57
|
* Create mutable availability state from an optional peer bitfield.
|
|
72
58
|
*
|
|
@@ -74,15 +60,12 @@ export class PeerPieceAvailability {
|
|
|
74
60
|
* @param bitfield - Optional raw bitfield payload from the peer.
|
|
75
61
|
* @returns Peer piece availability state.
|
|
76
62
|
*/
|
|
77
|
-
export const createPeerPieceAvailability = (
|
|
78
|
-
|
|
79
|
-
bitfield?: Uint8Array,
|
|
80
|
-
): PeerPieceAvailability => new PeerPieceAvailability(totalPieces, { bitfield });
|
|
81
|
-
|
|
82
|
-
const hasBitfieldPiece = (bitfield: Uint8Array, pieceIndex: number): boolean => {
|
|
63
|
+
export const createPeerPieceAvailability = (totalPieces, bitfield) => new PeerPieceAvailability(totalPieces, { bitfield });
|
|
64
|
+
const hasBitfieldPiece = (bitfield, pieceIndex) => {
|
|
83
65
|
const byte = bitfield[Math.floor(pieceIndex / 8)];
|
|
84
|
-
if (byte === undefined)
|
|
85
|
-
|
|
66
|
+
if (byte === undefined)
|
|
67
|
+
return false;
|
|
86
68
|
const mask = 0x80 >> (pieceIndex % 8);
|
|
87
69
|
return (byte & mask) !== 0;
|
|
88
70
|
};
|
|
71
|
+
//# sourceMappingURL=availability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability.js","sourceRoot":"","sources":["../../src/peer/availability.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,OAAO,qBAAqB;IAUV;IATH,MAAM,CAAY;IAEnC;;;;;OAKG;IACH,YACoB,WAAmB,EACnC,UAAwC,EAAE;QAD1B,gBAAW,GAAX,WAAW,CAAQ;QAGnC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,CAChE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAC5E,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACI,QAAQ,CAAC,UAAkB;QAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAC,UAAkB;QAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACpF,OAAO;QACX,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACI,WAAW,CAAC,QAAoB;QACnC,KAAK,IAAI,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,IAAI,CAAC,EAAE,CAAC;YACtE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACrE,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,cAAc;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3F,CAAC;CACJ;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACvC,WAAmB,EACnB,QAAqB,EACA,EAAE,CAAC,IAAI,qBAAqB,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;AAEjF,MAAM,gBAAgB,GAAG,CAAC,QAAoB,EAAE,UAAkB,EAAW,EAAE;IAC3E,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAErC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const PROTOCOL = "BitTorrent protocol";
|
|
2
|
+
export declare const PROTOCOL_BYTES: NodeJS.NonSharedUint8Array;
|
|
3
|
+
export declare const PEER_ID_PREFIX = "-BT0001-";
|
|
4
|
+
export declare const HANDSHAKE_LENGTH = 68;
|
|
5
|
+
export declare const RESERVED_LENGTH = 8;
|
|
6
|
+
export declare const INFO_HASH_LENGTH = 20;
|
|
7
|
+
export declare const PEER_ID_LENGTH = 20;
|
|
8
|
+
export declare const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
export const PROTOCOL = 'BitTorrent protocol';
|
|
2
2
|
export const PROTOCOL_BYTES = new TextEncoder().encode(PROTOCOL);
|
|
3
|
-
|
|
4
3
|
export const PEER_ID_PREFIX = '-BT0001-';
|
|
5
|
-
|
|
6
4
|
export const HANDSHAKE_LENGTH = 68;
|
|
7
5
|
export const RESERVED_LENGTH = 8;
|
|
8
6
|
export const INFO_HASH_LENGTH = 20;
|
|
9
7
|
export const PEER_ID_LENGTH = 20;
|
|
10
|
-
|
|
11
8
|
export const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
9
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../../src/peer/consts.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,qBAAqB,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAEjE,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,MAAM,CAAC,MAAM,QAAQ,GAAG,gEAAgE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BunTorrentError } from '../../utils/errors';
|
|
2
|
+
export declare enum HandshakeErrorCode {
|
|
3
|
+
INFOHASH_INVALID_LENGTH = "HANDSHK_INFOHASH_INVALID_LENGTH",
|
|
4
|
+
INVALID_LENGTH = "HANDSHK_INVALID_LENGTH",
|
|
5
|
+
INVALID_PROTOCOL = "HANDSHK_INVALID_PROTOCOL",
|
|
6
|
+
PEERID_INVALID_LENGTH = "HANDSHK_PEERID_INVALID_LENGTH",
|
|
7
|
+
RESERVED_INVALID_LENGTH = "HANDSHK_RESERVED_INVALID_LENGTH"
|
|
8
|
+
}
|
|
9
|
+
export declare class PeerHandshakeError extends BunTorrentError {
|
|
10
|
+
constructor(code: HandshakeErrorCode, message: string);
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BunTorrentError } from '../../utils/errors';
|
|
2
|
+
export var HandshakeErrorCode;
|
|
3
|
+
(function (HandshakeErrorCode) {
|
|
4
|
+
HandshakeErrorCode["INFOHASH_INVALID_LENGTH"] = "HANDSHK_INFOHASH_INVALID_LENGTH";
|
|
5
|
+
HandshakeErrorCode["INVALID_LENGTH"] = "HANDSHK_INVALID_LENGTH";
|
|
6
|
+
HandshakeErrorCode["INVALID_PROTOCOL"] = "HANDSHK_INVALID_PROTOCOL";
|
|
7
|
+
HandshakeErrorCode["PEERID_INVALID_LENGTH"] = "HANDSHK_PEERID_INVALID_LENGTH";
|
|
8
|
+
HandshakeErrorCode["RESERVED_INVALID_LENGTH"] = "HANDSHK_RESERVED_INVALID_LENGTH";
|
|
9
|
+
})(HandshakeErrorCode || (HandshakeErrorCode = {}));
|
|
10
|
+
export class PeerHandshakeError extends BunTorrentError {
|
|
11
|
+
constructor(code, message) {
|
|
12
|
+
super(message, code);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=handshake.error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake.error.js","sourceRoot":"","sources":["../../../src/peer/handshake/handshake.error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,CAAN,IAAY,kBAMX;AAND,WAAY,kBAAkB;IAC1B,iFAA2D,CAAA;IAC3D,+DAAyC,CAAA;IACzC,mEAA6C,CAAA;IAC7C,6EAAuD,CAAA;IACvD,iFAA2D,CAAA;AAC/D,CAAC,EANW,kBAAkB,KAAlB,kBAAkB,QAM7B;AAED,MAAM,OAAO,kBAAmB,SAAQ,eAAe;IACnD,YAAY,IAAwB,EAAE,OAAe;QACjD,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC;CACJ"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PeerHandshake } from '../types';
|
|
2
|
+
export declare const encodeHandshake: (input: {
|
|
3
|
+
infoHash: Uint8Array;
|
|
4
|
+
peerId: Uint8Array;
|
|
5
|
+
reserved?: Uint8Array;
|
|
6
|
+
}) => Uint8Array;
|
|
7
|
+
export declare const decodeHandshake: (handshake: Uint8Array) => PeerHandshake;
|