├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── docs
├── .nojekyll
├── README.md
├── classes
│ ├── BlockHasher.md
│ ├── ChunkSplitter.md
│ ├── MerkleRootCalculator.md
│ ├── MerkleTreeBalancer.md
│ ├── PieceHasher.md
│ └── Tokenizer.md
├── enums
│ ├── CommonPieceLength.md
│ ├── TokenType.md
│ └── TorrentType.md
├── interfaces
│ ├── BObjectLoose.md
│ ├── BObjectStrict.md
│ ├── ByteStringToken.md
│ ├── DictionaryEndToken.md
│ ├── DictionaryStartToken.md
│ ├── FilePropsBase.md
│ ├── FilePropsV1.md
│ ├── FilePropsV2.md
│ ├── FileTreeFileNode.md
│ ├── InfoBase.md
│ ├── InfoBaseV1.md
│ ├── InfoMultiFileHybrid.md
│ ├── InfoMultiFileV1.md
│ ├── InfoSingleFileHybrid.md
│ ├── InfoSingleFileV1.md
│ ├── InfoV2.md
│ ├── IntegerToken.md
│ ├── ListEndToken.md
│ ├── ListStartToken.md
│ ├── MetaInfoBase.md
│ ├── MetaInfoHybrid.md
│ ├── MetaInfoV1.md
│ ├── MetaInfoV2.md
│ ├── TorrentOptionsBase.md
│ ├── TorrentOptionsHybrid.md
│ ├── TorrentOptionsV1.md
│ └── TorrentOptionsV2.md
└── modules.md
├── package.json
├── src
├── create.ts
├── decode.ts
├── encode.ts
├── index.ts
├── transformers
│ ├── blockHasher.ts
│ ├── chunkSplitter.ts
│ ├── index.ts
│ ├── merkleRootCalculator.ts
│ ├── merkleTreeBalancer.ts
│ ├── pieceHasher.ts
│ └── tokenizer.ts
└── utils
│ ├── codec.ts
│ ├── fileDirLike.ts
│ ├── fileTree.ts
│ ├── index.ts
│ └── misc.ts
├── tsconfig.json
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "parserOptions": {
4 | "project": ["./tsconfig.json"]
5 | },
6 | "plugins": ["@typescript-eslint"],
7 | "root": true,
8 | "extends": [
9 | "eslint:recommended",
10 | "plugin:@typescript-eslint/recommended",
11 | "plugin:@typescript-eslint/recommended-requiring-type-checking"
12 | ],
13 | "ignorePatterns": ["dist/**/*", "node_modules/**/*", "webpack.config.js"]
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .vscode
3 | dist
4 | package-lock.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Ze-Zheng Wu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
torrefy
4 |
5 |
6 |
7 |
8 | An ESM package that uses Web Streams API to create v1, v2 or hybrid torrents in your web browser.
9 |
10 |
11 | 🏗This package is under active development.🏗
12 |
13 |
14 |
15 | ## Install
16 |
17 | ```bash
18 | npm i torrefy # or yarn add torrefy
19 | ```
20 |
21 | ## Basic usage
22 |
23 | ```ts
24 | import { create, encode, decode } from "torrefy";
25 |
26 | // create a test file
27 | const testFile = new File(
28 | ["Hello world. This is the test file content."],
29 | "testfile.txt"
30 | );
31 |
32 | // calculate (hash) the meta info of the test file
33 | const metaInfo = await create([testFile]);
34 |
35 | // bencode meta info into a readable stream
36 | const torrentStream = encode(metaInfo);
37 |
38 | // tee the readable stream into two readable streams
39 | const [torrentStream1, torrentStream2] = torrentStream.tee();
40 |
41 | // consume the first readable stream as an array buffer
42 | const torrentBinary = await new Response(torrentStream1).arrayBuffer();
43 |
44 | // decode the second readable stream into meta info
45 | const decodedMetaInfo = await decode(torrentStream2);
46 | ```
47 |
48 | ## Features
49 |
50 | ### Supports Creating V1, V2 or Hybrid Torrents
51 |
52 | This package supports creating [v1](http://bittorrent.org/beps/bep_0003.html), [v2](https://www.bittorrent.org/beps/bep_0052.html) ([introduction blog](https://blog.libtorrent.org/2020/09/bittorrent-v2/)) or [hybrid](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path) ([introduction blog](https://blog.libtorrent.org/2020/09/bittorrent-v2/#:~:text=for%20backwards%20compatibility.-,backwards%20compatibility,-All%20new%20features)) torrents.
53 |
54 | ### Covers Various Web File APIs
55 |
56 | This package can handle input files or directories acquired from [File API](https://developer.mozilla.org/docs/Web/API/File), [File and Directory Entries API](https://developer.mozilla.org/docs/Web/API/File_and_Directory_Entries_API) or [File System Access API](https://developer.mozilla.org/docs/Web/API/File_System_Access_API).
57 |
58 | ### Supports Comprehensive Options
59 |
60 | TBD
61 |
62 | ### Supports Handling Progress
63 |
64 | TBD
65 |
66 | ### Exposes Stream-Based APIs
67 |
68 | The `create` function consumes an [iterable](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) of input files as [`ReadableStream`](https://developer.mozilla.org/docs/Web/API/ReadableStream)s with options and populates a `MetaInfo` object. This function internally uses several [`TransformStream`](https://developer.mozilla.org/docs/Web/API/TransformStream)s to chop the files into pieces and hash them.
69 |
70 | The `encode` function consumes any bcodec friendly entity (e.g. `MetaInfo` object) and [bencode](http://bittorrent.org/beps/bep_0003.html#bencoding)s it into a `ReadableStream`.
71 |
72 | The `decode` function consumes any bcodec friendly `ReadableStream` (e.g. torrent `ReadableStream`) and bdecodes it into the corresponding entity. This function internally uses a `TransformStream` called `Tokenizer` to tokenize the input `ReadableStream` and then calls `parse` function to parse the `Tokens`.
73 |
74 | All `TransformStream`s used in this package are also exported.
75 |
76 | ### Supports a Comprehensive Set of Bcodec Friendly Javascript Types
77 |
78 | Bcodec friendly Javascript types includes (for the time being):
79 |
80 | | Bcodec Type \ Javascript Type | `Strict` | `Loose` |
81 | | :---------------------------: | :---------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------: |
82 | | `ByteString` | [`string`](https://developer.mozilla.org/docs/Glossary/String) | `string` [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) |
83 | | `Integer` | [`number`](https://developer.mozilla.org/docs/Glossary/Number) [`bigint`](https://developer.mozilla.org/docs/Glossary/BigInt) | `number` `bigint` [`boolean`](https://developer.mozilla.org/docs/Glossary/Boolean) |
84 | | `List` | [`Strict[]`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) | `Loose[]` |
85 | | `Dictionary` | [`{[key: string]: Strict}`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) | ` {[key: string]: Loose}`
[`Map`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map) |
86 | | ignored | - | `undefined` `null` |
87 |
88 | `encode` function supports all `Loose` type inputs and `decode` function always returns `Strict` type results.
89 |
90 | ### Supports Hooks in Bencoding
91 |
92 | You can register encoder hooks when using the `encode` function. A common use case is extracting the bencoded `info` dictionary and calculating the [`infohash`](http://bittorrent.org/beps/bep_0052.html#infohash). (This package doesn't provide an out-of-box function to calculate `infohash` for now)
93 |
94 | To use encoder hooks, you will have to install the peer dependency [`@sec-ant/trie-map`](https://www.npmjs.com/package/@sec-ant/trie-map), which acts as an encoder hook system and allows you to register encoder hooks with iterable paths as keys in. Refer to its [README](https://github.com/Sec-ant/trie-map) to learn more about the package.
95 |
96 | This package provides several helper functions to help you register hooks in a hook system and consume their results as you please: `useUint8ArrayStreamHook`, `useArrayBufferPromiseHook`, `useTextPromiseHook`. You can also define your own functions to register and use hooks.
97 |
98 | Here is probably how you should use this feature:
99 |
100 | ```ts
101 | import { encode, EncoderHookSystem, useArrayBufferPromiseHook } from "torrefy";
102 | import { TrieMap } from "@sec-ant/trie-map";
103 |
104 | // create a dummy object to encode
105 | const dummyObject = {
106 | a: "b",
107 | c: 1,
108 | info: {
109 | foo: "bar",
110 | },
111 | s: ["t"],
112 | };
113 |
114 | // initialize an encoder hook system
115 | const hookSystem: EncoderHookSystem = new TrieMap();
116 |
117 | // register an encoder hook under dummyObject.info path in the hook system
118 | // and consume the result as an array buffer promise
119 | const infoArrayBufferPromise = useArrayBufferPromiseHook(["info"], hookSystem);
120 |
121 | // pass the hook system as an input argument to the encode function
122 | const bencodedReadableStream = encode(dummyObject, hookSystem);
123 |
124 | // consume the result of the hook
125 | const infoArrayBuffer = await infoArrayBufferPromise; // => ArrayBuffer(12)
126 | ```
127 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | torrefy / [Exports](modules.md)
2 |
3 |
4 |

5 |
torrefy
6 |
7 |
8 |
9 |
10 | An ESM package that uses Web Streams API to create v1, v2 or hybrid torrents in your web browser.
11 |
12 |
13 | 🏗This package is under active development.🏗
14 |
15 |
16 |
17 | ## Install
18 |
19 | ```bash
20 | npm i torrefy # or yarn add torrefy
21 | ```
22 |
23 | ## Basic usage
24 |
25 | ```ts
26 | import { create, encode, decode } from "torrefy";
27 |
28 | // create a test file
29 | const testFile = new File(
30 | ["Hello world. This is the test file content."],
31 | "testfile.txt"
32 | );
33 |
34 | // calculate (hash) the meta info of the test file
35 | const metaInfo = await create([testFile]);
36 |
37 | // bencode meta info into a readable stream
38 | const torrentStream = encode(metaInfo);
39 |
40 | // tee the readable stream into two readable streams
41 | const [torrentStream1, torrentStream2] = torrentStream.tee();
42 |
43 | // consume the first readable stream as an array buffer
44 | const torrentBinary = await new Response(torrentStream1).arrayBuffer();
45 |
46 | // decode the second readable stream into meta info
47 | const decodedMetaInfo = await decode(torrentStream2);
48 | ```
49 |
50 | ## Features
51 |
52 | ### Supports Creating V1, V2 or Hybrid Torrents
53 |
54 | This package supports creating [v1](http://bittorrent.org/beps/bep_0003.html), [v2](https://www.bittorrent.org/beps/bep_0052.html) ([introduction blog](https://blog.libtorrent.org/2020/09/bittorrent-v2/)) or [hybrid](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path) ([introduction blog](https://blog.libtorrent.org/2020/09/bittorrent-v2/#:~:text=for%20backwards%20compatibility.-,backwards%20compatibility,-All%20new%20features)) torrents.
55 |
56 | ### Covers Various Web File APIs
57 |
58 | This package can handle input files or directories acquired from [File API](https://developer.mozilla.org/docs/Web/API/File), [File and Directory Entries API](https://developer.mozilla.org/docs/Web/API/File_and_Directory_Entries_API) or [File System Access API](https://developer.mozilla.org/docs/Web/API/File_System_Access_API).
59 |
60 | ### Supports Comprehensive Options
61 |
62 | TBD
63 |
64 | ### Supports Handling Progress
65 |
66 | TBD
67 |
68 | ### Exposes Stream-Based APIs
69 |
70 | The `create` function consumes an [iterable](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) of input files as [`ReadableStream`](https://developer.mozilla.org/docs/Web/API/ReadableStream)s with options and populates a `MetaInfo` object. This function internally uses several [`TransformStream`](https://developer.mozilla.org/docs/Web/API/TransformStream)s to chop the files into pieces and hash them.
71 |
72 | The `encode` function consumes any bcodec friendly entity (e.g. `MetaInfo` object) and [bencode](http://bittorrent.org/beps/bep_0003.html#bencoding)s it into a `ReadableStream`.
73 |
74 | The `decode` function consumes any bcodec friendly `ReadableStream` (e.g. torrent `ReadableStream`) and bdecodes it into the corresponding entity. This function internally uses a `TransformStream` called `Tokenizer` to tokenize the input `ReadableStream` and then calls `parse` function to parse the `Tokens`.
75 |
76 | All `TransformStream`s used in this package are also exported.
77 |
78 | ### Supports a Comprehensive Set of Bcodec Friendly Javascript Types
79 |
80 | Bcodec friendly Javascript types includes (for the time being):
81 |
82 | | Bcodec Type \ Javascript Type | `Strict` | `Loose` |
83 | | :---------------------------: | :---------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------: |
84 | | `ByteString` | [`string`](https://developer.mozilla.org/docs/Glossary/String) | `string` [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) |
85 | | `Integer` | [`number`](https://developer.mozilla.org/docs/Glossary/Number) [`bigint`](https://developer.mozilla.org/docs/Glossary/BigInt) | `number` `bigint` [`boolean`](https://developer.mozilla.org/docs/Glossary/Boolean) |
86 | | `List` | [`Strict[]`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) | `Loose[]` |
87 | | `Dictionary` | [`{[key: string]: Strict}`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) | ` {[key: string]: Loose}`
[`Map`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map) |
88 | | ignored | - | `undefined` `null` |
89 |
90 | `encode` function supports all `Loose` type inputs and `decode` function always returns `Strict` type results.
91 |
92 | ### Supports Hooks in Bencoding
93 |
94 | You can register encoder hooks when using the `encode` function. A common use case is extracting the bencoded `info` dictionary and calculating the [`infohash`](http://bittorrent.org/beps/bep_0052.html#infohash). (This package doesn't provide an out-of-box function to calculate `infohash` for now)
95 |
96 | To use encoder hooks, you will have to install the peer dependency [`@sec-ant/trie-map`](https://www.npmjs.com/package/@sec-ant/trie-map), which acts as an encoder hook system and allows you to register encoder hooks with iterable paths as keys in. Refer to its [README](https://github.com/Sec-ant/trie-map) to learn more about the package.
97 |
98 | This package provides several helper functions to help you register hooks in a hook system and consume their results as you please: `useUint8ArrayStreamHook`, `useArrayBufferPromiseHook`, `useTextPromiseHook`. You can also define your own functions to register and use hooks.
99 |
100 | Here is probably how you should use this feature:
101 |
102 | ```ts
103 | import { encode, EncoderHookSystem, useArrayBufferPromiseHook } from "torrefy";
104 | import { TrieMap } from "@sec-ant/trie-map";
105 |
106 | // create a dummy object to encode
107 | const dummyObject = {
108 | a: "b",
109 | c: 1,
110 | info: {
111 | foo: "bar",
112 | },
113 | s: ["t"],
114 | };
115 |
116 | // initialize an encoder hook system
117 | const hookSystem: EncoderHookSystem = new TrieMap();
118 |
119 | // register an encoder hook under dummyObject.info path in the hook system
120 | // and consume the result as an array buffer promise
121 | const infoArrayBufferPromise = useArrayBufferPromiseHook(["info"], hookSystem);
122 |
123 | // pass the hook system as an input argument to the encode function
124 | const bencodedReadableStream = encode(dummyObject, hookSystem);
125 |
126 | // consume the result of the hook
127 | const infoArrayBuffer = await infoArrayBufferPromise; // => ArrayBuffer(12)
128 | ```
129 |
--------------------------------------------------------------------------------
/docs/classes/BlockHasher.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / BlockHasher
2 |
3 | # Class: BlockHasher
4 |
5 | ## Hierarchy
6 |
7 | - `TransformStream`<`Uint8Array`, `Uint8Array`[]\>
8 |
9 | ↳ **`BlockHasher`**
10 |
11 | ## Table of contents
12 |
13 | ### Constructors
14 |
15 | - [constructor](BlockHasher.md#constructor)
16 |
17 | ### Properties
18 |
19 | - [readable](BlockHasher.md#readable)
20 | - [writable](BlockHasher.md#writable)
21 |
22 | ## Constructors
23 |
24 | ### constructor
25 |
26 | • **new BlockHasher**(`blocksPerPiece`)
27 |
28 | #### Parameters
29 |
30 | | Name | Type |
31 | | :------ | :------ |
32 | | `blocksPerPiece` | `number` |
33 |
34 | #### Overrides
35 |
36 | TransformStream<Uint8Array, Uint8Array[]\>.constructor
37 |
38 | #### Defined in
39 |
40 | [src/transformers/blockHasher.ts:67](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/blockHasher.ts#L67)
41 |
42 | ## Properties
43 |
44 | ### readable
45 |
46 | • `Readonly` **readable**: `ReadableStream`<`Uint8Array`[]\>
47 |
48 | #### Inherited from
49 |
50 | TransformStream.readable
51 |
52 | #### Defined in
53 |
54 | node_modules/typescript/lib/lib.dom.d.ts:14430
55 |
56 | ___
57 |
58 | ### writable
59 |
60 | • `Readonly` **writable**: `WritableStream`<`Uint8Array`\>
61 |
62 | #### Inherited from
63 |
64 | TransformStream.writable
65 |
66 | #### Defined in
67 |
68 | node_modules/typescript/lib/lib.dom.d.ts:14431
69 |
--------------------------------------------------------------------------------
/docs/classes/ChunkSplitter.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / ChunkSplitter
2 |
3 | # Class: ChunkSplitter
4 |
5 | ## Hierarchy
6 |
7 | - `TransformStream`<`Uint8Array`, `Uint8Array`\>
8 |
9 | ↳ **`ChunkSplitter`**
10 |
11 | ## Table of contents
12 |
13 | ### Constructors
14 |
15 | - [constructor](ChunkSplitter.md#constructor)
16 |
17 | ### Properties
18 |
19 | - [readable](ChunkSplitter.md#readable)
20 | - [writable](ChunkSplitter.md#writable)
21 |
22 | ## Constructors
23 |
24 | ### constructor
25 |
26 | • **new ChunkSplitter**(`chunkLength`, `opts?`)
27 |
28 | #### Parameters
29 |
30 | | Name | Type | Default value |
31 | | :------ | :------ | :------ |
32 | | `chunkLength` | `number` | `undefined` |
33 | | `opts` | `Object` | `undefined` |
34 | | `opts.padding` | `boolean` | `false` |
35 |
36 | #### Overrides
37 |
38 | TransformStream<Uint8Array, Uint8Array\>.constructor
39 |
40 | #### Defined in
41 |
42 | [src/transformers/chunkSplitter.ts:45](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/chunkSplitter.ts#L45)
43 |
44 | ## Properties
45 |
46 | ### readable
47 |
48 | • `Readonly` **readable**: `ReadableStream`<`Uint8Array`\>
49 |
50 | #### Inherited from
51 |
52 | TransformStream.readable
53 |
54 | #### Defined in
55 |
56 | node_modules/typescript/lib/lib.dom.d.ts:14430
57 |
58 | ___
59 |
60 | ### writable
61 |
62 | • `Readonly` **writable**: `WritableStream`<`Uint8Array`\>
63 |
64 | #### Inherited from
65 |
66 | TransformStream.writable
67 |
68 | #### Defined in
69 |
70 | node_modules/typescript/lib/lib.dom.d.ts:14431
71 |
--------------------------------------------------------------------------------
/docs/classes/MerkleRootCalculator.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / MerkleRootCalculator
2 |
3 | # Class: MerkleRootCalculator
4 |
5 | ## Hierarchy
6 |
7 | - `TransformStream`<`Uint8Array`[], `Uint8Array`\>
8 |
9 | ↳ **`MerkleRootCalculator`**
10 |
11 | ## Table of contents
12 |
13 | ### Constructors
14 |
15 | - [constructor](MerkleRootCalculator.md#constructor)
16 |
17 | ### Properties
18 |
19 | - [readable](MerkleRootCalculator.md#readable)
20 | - [writable](MerkleRootCalculator.md#writable)
21 |
22 | ## Constructors
23 |
24 | ### constructor
25 |
26 | • **new MerkleRootCalculator**(`updateProgress?`)
27 |
28 | #### Parameters
29 |
30 | | Name | Type |
31 | | :------ | :------ |
32 | | `updateProgress?` | [`UpdateProgress`](../modules.md#updateprogress) |
33 |
34 | #### Overrides
35 |
36 | TransformStream<
37 | Uint8Array[],
38 | Uint8Array
39 | \>.constructor
40 |
41 | #### Defined in
42 |
43 | [src/transformers/merkleRootCalculator.ts:28](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/merkleRootCalculator.ts#L28)
44 |
45 | ## Properties
46 |
47 | ### readable
48 |
49 | • `Readonly` **readable**: `ReadableStream`<`Uint8Array`\>
50 |
51 | #### Inherited from
52 |
53 | TransformStream.readable
54 |
55 | #### Defined in
56 |
57 | node_modules/typescript/lib/lib.dom.d.ts:14430
58 |
59 | ___
60 |
61 | ### writable
62 |
63 | • `Readonly` **writable**: `WritableStream`<`Uint8Array`[]\>
64 |
65 | #### Inherited from
66 |
67 | TransformStream.writable
68 |
69 | #### Defined in
70 |
71 | node_modules/typescript/lib/lib.dom.d.ts:14431
72 |
--------------------------------------------------------------------------------
/docs/classes/MerkleTreeBalancer.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / MerkleTreeBalancer
2 |
3 | # Class: MerkleTreeBalancer
4 |
5 | ## Hierarchy
6 |
7 | - `TransformStream`<`Uint8Array`, `Uint8Array`[]\>
8 |
9 | ↳ **`MerkleTreeBalancer`**
10 |
11 | ## Table of contents
12 |
13 | ### Constructors
14 |
15 | - [constructor](MerkleTreeBalancer.md#constructor)
16 |
17 | ### Properties
18 |
19 | - [readable](MerkleTreeBalancer.md#readable)
20 | - [writable](MerkleTreeBalancer.md#writable)
21 |
22 | ## Constructors
23 |
24 | ### constructor
25 |
26 | • **new MerkleTreeBalancer**(`blocksPerPiece`)
27 |
28 | #### Parameters
29 |
30 | | Name | Type |
31 | | :------ | :------ |
32 | | `blocksPerPiece` | `number` |
33 |
34 | #### Overrides
35 |
36 | TransformStream<
37 | Uint8Array,
38 | Uint8Array[]
39 | \>.constructor
40 |
41 | #### Defined in
42 |
43 | [src/transformers/merkleTreeBalancer.ts:40](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/merkleTreeBalancer.ts#L40)
44 |
45 | ## Properties
46 |
47 | ### readable
48 |
49 | • `Readonly` **readable**: `ReadableStream`<`Uint8Array`[]\>
50 |
51 | #### Inherited from
52 |
53 | TransformStream.readable
54 |
55 | #### Defined in
56 |
57 | node_modules/typescript/lib/lib.dom.d.ts:14430
58 |
59 | ___
60 |
61 | ### writable
62 |
63 | • `Readonly` **writable**: `WritableStream`<`Uint8Array`\>
64 |
65 | #### Inherited from
66 |
67 | TransformStream.writable
68 |
69 | #### Defined in
70 |
71 | node_modules/typescript/lib/lib.dom.d.ts:14431
72 |
--------------------------------------------------------------------------------
/docs/classes/PieceHasher.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / PieceHasher
2 |
3 | # Class: PieceHasher
4 |
5 | ## Hierarchy
6 |
7 | - `TransformStream`<`Uint8Array`, `Uint8Array`\>
8 |
9 | ↳ **`PieceHasher`**
10 |
11 | ## Table of contents
12 |
13 | ### Constructors
14 |
15 | - [constructor](PieceHasher.md#constructor)
16 |
17 | ### Properties
18 |
19 | - [readable](PieceHasher.md#readable)
20 | - [writable](PieceHasher.md#writable)
21 |
22 | ## Constructors
23 |
24 | ### constructor
25 |
26 | • **new PieceHasher**(`updateProgress?`)
27 |
28 | #### Parameters
29 |
30 | | Name | Type |
31 | | :------ | :------ |
32 | | `updateProgress?` | [`UpdateProgress`](../modules.md#updateprogress) |
33 |
34 | #### Overrides
35 |
36 | TransformStream<Uint8Array, Uint8Array\>.constructor
37 |
38 | #### Defined in
39 |
40 | [src/transformers/pieceHasher.ts:32](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/pieceHasher.ts#L32)
41 |
42 | ## Properties
43 |
44 | ### readable
45 |
46 | • `Readonly` **readable**: `ReadableStream`<`Uint8Array`\>
47 |
48 | #### Inherited from
49 |
50 | TransformStream.readable
51 |
52 | #### Defined in
53 |
54 | node_modules/typescript/lib/lib.dom.d.ts:14430
55 |
56 | ___
57 |
58 | ### writable
59 |
60 | • `Readonly` **writable**: `WritableStream`<`Uint8Array`\>
61 |
62 | #### Inherited from
63 |
64 | TransformStream.writable
65 |
66 | #### Defined in
67 |
68 | node_modules/typescript/lib/lib.dom.d.ts:14431
69 |
--------------------------------------------------------------------------------
/docs/classes/Tokenizer.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / Tokenizer
2 |
3 | # Class: Tokenizer
4 |
5 | ## Hierarchy
6 |
7 | - `TransformStream`<`Uint8Array`, [`Token`](../modules.md#token)\>
8 |
9 | ↳ **`Tokenizer`**
10 |
11 | ## Table of contents
12 |
13 | ### Constructors
14 |
15 | - [constructor](Tokenizer.md#constructor)
16 |
17 | ### Properties
18 |
19 | - [readable](Tokenizer.md#readable)
20 | - [writable](Tokenizer.md#writable)
21 |
22 | ## Constructors
23 |
24 | ### constructor
25 |
26 | • **new Tokenizer**()
27 |
28 | #### Overrides
29 |
30 | TransformStream<Uint8Array, Token\>.constructor
31 |
32 | #### Defined in
33 |
34 | [src/transformers/tokenizer.ts:248](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L248)
35 |
36 | ## Properties
37 |
38 | ### readable
39 |
40 | • `Readonly` **readable**: `ReadableStream`<[`IntegerToken`](../interfaces/IntegerToken.md) \| [`ByteStringToken`](../interfaces/ByteStringToken.md) \| [`ListStartToken`](../interfaces/ListStartToken.md) \| [`ListEndToken`](../interfaces/ListEndToken.md) \| [`DictionaryStartToken`](../interfaces/DictionaryStartToken.md) \| [`DictionaryEndToken`](../interfaces/DictionaryEndToken.md)\>
41 |
42 | #### Inherited from
43 |
44 | TransformStream.readable
45 |
46 | #### Defined in
47 |
48 | node_modules/typescript/lib/lib.dom.d.ts:14430
49 |
50 | ___
51 |
52 | ### writable
53 |
54 | • `Readonly` **writable**: `WritableStream`<`Uint8Array`\>
55 |
56 | #### Inherited from
57 |
58 | TransformStream.writable
59 |
60 | #### Defined in
61 |
62 | node_modules/typescript/lib/lib.dom.d.ts:14431
63 |
--------------------------------------------------------------------------------
/docs/enums/CommonPieceLength.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / CommonPieceLength
2 |
3 | # Enumeration: CommonPieceLength
4 |
5 | common piece lengths
6 |
7 | ## Table of contents
8 |
9 | ### Enumeration Members
10 |
11 | - [128KB](CommonPieceLength.md#128kb)
12 | - [16KB](CommonPieceLength.md#16kb)
13 | - [16MB](CommonPieceLength.md#16mb)
14 | - [1MB](CommonPieceLength.md#1mb)
15 | - [256KB](CommonPieceLength.md#256kb)
16 | - [2MB](CommonPieceLength.md#2mb)
17 | - [32KB](CommonPieceLength.md#32kb)
18 | - [32MB](CommonPieceLength.md#32mb)
19 | - [4MB](CommonPieceLength.md#4mb)
20 | - [512KB](CommonPieceLength.md#512kb)
21 | - [64KB](CommonPieceLength.md#64kb)
22 | - [8MB](CommonPieceLength.md#8mb)
23 |
24 | ## Enumeration Members
25 |
26 | ### 128KB
27 |
28 | • **128KB** = ``131072``
29 |
30 | #### Defined in
31 |
32 | [src/create.ts:410](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L410)
33 |
34 | ___
35 |
36 | ### 16KB
37 |
38 | • **16KB** = ``16384``
39 |
40 | #### Defined in
41 |
42 | [src/create.ts:407](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L407)
43 |
44 | ___
45 |
46 | ### 16MB
47 |
48 | • **16MB** = ``16777216``
49 |
50 | #### Defined in
51 |
52 | [src/create.ts:417](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L417)
53 |
54 | ___
55 |
56 | ### 1MB
57 |
58 | • **1MB** = ``1048576``
59 |
60 | #### Defined in
61 |
62 | [src/create.ts:413](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L413)
63 |
64 | ___
65 |
66 | ### 256KB
67 |
68 | • **256KB** = ``262144``
69 |
70 | #### Defined in
71 |
72 | [src/create.ts:411](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L411)
73 |
74 | ___
75 |
76 | ### 2MB
77 |
78 | • **2MB** = ``2097152``
79 |
80 | #### Defined in
81 |
82 | [src/create.ts:414](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L414)
83 |
84 | ___
85 |
86 | ### 32KB
87 |
88 | • **32KB** = ``32768``
89 |
90 | #### Defined in
91 |
92 | [src/create.ts:408](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L408)
93 |
94 | ___
95 |
96 | ### 32MB
97 |
98 | • **32MB** = ``33554432``
99 |
100 | #### Defined in
101 |
102 | [src/create.ts:418](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L418)
103 |
104 | ___
105 |
106 | ### 4MB
107 |
108 | • **4MB** = ``4194304``
109 |
110 | #### Defined in
111 |
112 | [src/create.ts:415](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L415)
113 |
114 | ___
115 |
116 | ### 512KB
117 |
118 | • **512KB** = ``524288``
119 |
120 | #### Defined in
121 |
122 | [src/create.ts:412](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L412)
123 |
124 | ___
125 |
126 | ### 64KB
127 |
128 | • **64KB** = ``65536``
129 |
130 | #### Defined in
131 |
132 | [src/create.ts:409](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L409)
133 |
134 | ___
135 |
136 | ### 8MB
137 |
138 | • **8MB** = ``8388608``
139 |
140 | #### Defined in
141 |
142 | [src/create.ts:416](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L416)
143 |
--------------------------------------------------------------------------------
/docs/enums/TokenType.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / TokenType
2 |
3 | # Enumeration: TokenType
4 |
5 | ## Table of contents
6 |
7 | ### Enumeration Members
8 |
9 | - [ByteString](TokenType.md#bytestring)
10 | - [DictionaryEnd](TokenType.md#dictionaryend)
11 | - [DictionaryStart](TokenType.md#dictionarystart)
12 | - [Integer](TokenType.md#integer)
13 | - [ListEnd](TokenType.md#listend)
14 | - [ListStart](TokenType.md#liststart)
15 |
16 | ## Enumeration Members
17 |
18 | ### ByteString
19 |
20 | • **ByteString** = ``"ByteString"``
21 |
22 | #### Defined in
23 |
24 | [src/transformers/tokenizer.ts:14](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L14)
25 |
26 | ___
27 |
28 | ### DictionaryEnd
29 |
30 | • **DictionaryEnd** = ``"DictionaryEnd"``
31 |
32 | #### Defined in
33 |
34 | [src/transformers/tokenizer.ts:18](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L18)
35 |
36 | ___
37 |
38 | ### DictionaryStart
39 |
40 | • **DictionaryStart** = ``"DictionaryStart"``
41 |
42 | #### Defined in
43 |
44 | [src/transformers/tokenizer.ts:17](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L17)
45 |
46 | ___
47 |
48 | ### Integer
49 |
50 | • **Integer** = ``"Integer"``
51 |
52 | #### Defined in
53 |
54 | [src/transformers/tokenizer.ts:13](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L13)
55 |
56 | ___
57 |
58 | ### ListEnd
59 |
60 | • **ListEnd** = ``"ListEnd"``
61 |
62 | #### Defined in
63 |
64 | [src/transformers/tokenizer.ts:16](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L16)
65 |
66 | ___
67 |
68 | ### ListStart
69 |
70 | • **ListStart** = ``"ListStart"``
71 |
72 | #### Defined in
73 |
74 | [src/transformers/tokenizer.ts:15](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L15)
75 |
--------------------------------------------------------------------------------
/docs/enums/TorrentType.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / TorrentType
2 |
3 | # Enumeration: TorrentType
4 |
5 | torrent type: v1, v2, hybrid
6 |
7 | ## Table of contents
8 |
9 | ### Enumeration Members
10 |
11 | - [HYBRID](TorrentType.md#hybrid)
12 | - [V1](TorrentType.md#v1)
13 | - [V2](TorrentType.md#v2)
14 |
15 | ## Enumeration Members
16 |
17 | ### HYBRID
18 |
19 | • **HYBRID** = ``"hybrid"``
20 |
21 | v1 + v2 hybrid torrent
22 |
23 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path)
24 |
25 | #### Defined in
26 |
27 | [src/create.ts:61](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L61)
28 |
29 | ___
30 |
31 | ### V1
32 |
33 | • **V1** = ``"v1"``
34 |
35 | v1 torrent
36 |
37 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html)
38 |
39 | #### Defined in
40 |
41 | [src/create.ts:49](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L49)
42 |
43 | ___
44 |
45 | ### V2
46 |
47 | • **V2** = ``"v2"``
48 |
49 | v2 torrent
50 |
51 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html)
52 |
53 | #### Defined in
54 |
55 | [src/create.ts:55](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L55)
56 |
--------------------------------------------------------------------------------
/docs/interfaces/BObjectLoose.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / BObjectLoose
2 |
3 | # Interface: BObjectLoose
4 |
5 | ## Indexable
6 |
7 | ▪ [key: [`BByteString`](../modules.md#bbytestring)<``true``\>]: [`BData`](../modules.md#bdata)<``false``\>
8 |
--------------------------------------------------------------------------------
/docs/interfaces/BObjectStrict.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / BObjectStrict
2 |
3 | # Interface: BObjectStrict
4 |
5 | ## Indexable
6 |
7 | ▪ [key: [`BByteString`](../modules.md#bbytestring)<``true``\>]: [`BData`](../modules.md#bdata)<``true``\>
8 |
--------------------------------------------------------------------------------
/docs/interfaces/ByteStringToken.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / ByteStringToken
2 |
3 | # Interface: ByteStringToken
4 |
5 | ## Table of contents
6 |
7 | ### Properties
8 |
9 | - [type](ByteStringToken.md#type)
10 | - [value](ByteStringToken.md#value)
11 |
12 | ## Properties
13 |
14 | ### type
15 |
16 | • **type**: [`ByteString`](../enums/TokenType.md#bytestring)
17 |
18 | #### Defined in
19 |
20 | [src/transformers/tokenizer.ts:27](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L27)
21 |
22 | ___
23 |
24 | ### value
25 |
26 | • **value**: `Uint8Array`
27 |
28 | #### Defined in
29 |
30 | [src/transformers/tokenizer.ts:28](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L28)
31 |
--------------------------------------------------------------------------------
/docs/interfaces/DictionaryEndToken.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / DictionaryEndToken
2 |
3 | # Interface: DictionaryEndToken
4 |
5 | ## Table of contents
6 |
7 | ### Properties
8 |
9 | - [type](DictionaryEndToken.md#type)
10 |
11 | ## Properties
12 |
13 | ### type
14 |
15 | • **type**: [`DictionaryEnd`](../enums/TokenType.md#dictionaryend)
16 |
17 | #### Defined in
18 |
19 | [src/transformers/tokenizer.ts:44](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L44)
20 |
--------------------------------------------------------------------------------
/docs/interfaces/DictionaryStartToken.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / DictionaryStartToken
2 |
3 | # Interface: DictionaryStartToken
4 |
5 | ## Table of contents
6 |
7 | ### Properties
8 |
9 | - [type](DictionaryStartToken.md#type)
10 |
11 | ## Properties
12 |
13 | ### type
14 |
15 | • **type**: [`DictionaryStart`](../enums/TokenType.md#dictionarystart)
16 |
17 | #### Defined in
18 |
19 | [src/transformers/tokenizer.ts:40](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L40)
20 |
--------------------------------------------------------------------------------
/docs/interfaces/FilePropsBase.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / FilePropsBase
2 |
3 | # Interface: FilePropsBase
4 |
5 | base file props
6 |
7 | ## Hierarchy
8 |
9 | - [`BObject`](../modules.md#bobject)<``false``\>
10 |
11 | ↳ **`FilePropsBase`**
12 |
13 | ↳↳ [`FilePropsV1`](FilePropsV1.md)
14 |
15 | ↳↳ [`FilePropsV2`](FilePropsV2.md)
16 |
17 | ## Table of contents
18 |
19 | ### Properties
20 |
21 | - [attr](FilePropsBase.md#attr)
22 | - [length](FilePropsBase.md#length)
23 |
24 | ## Properties
25 |
26 | ### attr
27 |
28 | • `Optional` **attr**: [`FileAttrs`](../modules.md#fileattrs)
29 |
30 | A variable-length string. When present,
31 | the characters each represent a file attribute:
32 | ```
33 | l = symlink
34 | x = executable
35 | h = hidden
36 | p = padding file
37 | ```
38 | [BEP 47](https://www.bittorrent.org/beps/bep_0047.html#:~:text=20%20bytes%3E%2C%0A%20%20%20%20...%0A%20%20%7D%2C%0A%20%20...%0A%7D-,attr,-A%20variable%2Dlength)
39 |
40 | #### Defined in
41 |
42 | [src/utils/fileTree.ts:76](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L76)
43 |
44 | ___
45 |
46 | ### length
47 |
48 | • **length**: `number`
49 |
50 | Length of the file in bytes
51 |
52 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,length,-%2D%20The%20length%20of)
53 | |
54 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=pieces%20root32%3Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeeeeeee-,length,-Length%20of%20the)
55 |
56 | #### Defined in
57 |
58 | [src/utils/fileTree.ts:64](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L64)
59 |
--------------------------------------------------------------------------------
/docs/interfaces/FilePropsV1.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / FilePropsV1
2 |
3 | # Interface: FilePropsV1
4 |
5 | v1 file props
6 |
7 | ## Hierarchy
8 |
9 | - [`FilePropsBase`](FilePropsBase.md)
10 |
11 | ↳ **`FilePropsV1`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [attr](FilePropsV1.md#attr)
18 | - [length](FilePropsV1.md#length)
19 | - [path](FilePropsV1.md#path)
20 |
21 | ## Properties
22 |
23 | ### attr
24 |
25 | • `Optional` **attr**: [`FileAttrs`](../modules.md#fileattrs)
26 |
27 | A variable-length string. When present,
28 | the characters each represent a file attribute:
29 | ```
30 | l = symlink
31 | x = executable
32 | h = hidden
33 | p = padding file
34 | ```
35 | [BEP 47](https://www.bittorrent.org/beps/bep_0047.html#:~:text=20%20bytes%3E%2C%0A%20%20%20%20...%0A%20%20%7D%2C%0A%20%20...%0A%7D-,attr,-A%20variable%2Dlength)
36 |
37 | #### Inherited from
38 |
39 | [FilePropsBase](FilePropsBase.md).[attr](FilePropsBase.md#attr)
40 |
41 | #### Defined in
42 |
43 | [src/utils/fileTree.ts:76](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L76)
44 |
45 | ___
46 |
47 | ### length
48 |
49 | • **length**: `number`
50 |
51 | Length of the file in bytes
52 |
53 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,length,-%2D%20The%20length%20of)
54 | |
55 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=pieces%20root32%3Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeeeeeee-,length,-Length%20of%20the)
56 |
57 | #### Inherited from
58 |
59 | [FilePropsBase](FilePropsBase.md).[length](FilePropsBase.md#length)
60 |
61 | #### Defined in
62 |
63 | [src/utils/fileTree.ts:64](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L64)
64 |
65 | ___
66 |
67 | ### path
68 |
69 | • **path**: `string`[]
70 |
71 | A list of UTF-8 encoded strings
72 | corresponding to **subdirectory** names
73 |
74 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=file%2C%20in%20bytes.-,path,-%2D%20A%20list%20of)
75 |
76 | #### Defined in
77 |
78 | [src/utils/fileTree.ts:89](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L89)
79 |
--------------------------------------------------------------------------------
/docs/interfaces/FilePropsV2.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / FilePropsV2
2 |
3 | # Interface: FilePropsV2
4 |
5 | v2 file props
6 |
7 | ## Hierarchy
8 |
9 | - [`FilePropsBase`](FilePropsBase.md)
10 |
11 | ↳ **`FilePropsV2`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [attr](FilePropsV2.md#attr)
18 | - [length](FilePropsV2.md#length)
19 | - [pieces root](FilePropsV2.md#pieces root)
20 |
21 | ## Properties
22 |
23 | ### attr
24 |
25 | • `Optional` **attr**: [`FileAttrs`](../modules.md#fileattrs)
26 |
27 | A variable-length string. When present,
28 | the characters each represent a file attribute:
29 | ```
30 | l = symlink
31 | x = executable
32 | h = hidden
33 | p = padding file
34 | ```
35 | [BEP 47](https://www.bittorrent.org/beps/bep_0047.html#:~:text=20%20bytes%3E%2C%0A%20%20%20%20...%0A%20%20%7D%2C%0A%20%20...%0A%7D-,attr,-A%20variable%2Dlength)
36 |
37 | #### Inherited from
38 |
39 | [FilePropsBase](FilePropsBase.md).[attr](FilePropsBase.md#attr)
40 |
41 | #### Defined in
42 |
43 | [src/utils/fileTree.ts:76](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L76)
44 |
45 | ___
46 |
47 | ### length
48 |
49 | • **length**: `number`
50 |
51 | Length of the file in bytes
52 |
53 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,length,-%2D%20The%20length%20of)
54 | |
55 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=pieces%20root32%3Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeeeeeee-,length,-Length%20of%20the)
56 |
57 | #### Inherited from
58 |
59 | [FilePropsBase](FilePropsBase.md).[length](FilePropsBase.md#length)
60 |
61 | #### Defined in
62 |
63 | [src/utils/fileTree.ts:64](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L64)
64 |
65 | ___
66 |
67 | ### pieces root
68 |
69 | • `Optional` **pieces root**: `ArrayBuffer`
70 |
71 | For **non-empty** files this is the the root hash
72 | of a merkle tree with a branching factor of 2,
73 | constructed from 16KiB blocks of the file
74 |
75 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=any%20sibling%20entries.-,pieces%20root,-For%20non%2Dempty)
76 |
77 | #### Defined in
78 |
79 | [src/utils/fileTree.ts:103](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L103)
80 |
--------------------------------------------------------------------------------
/docs/interfaces/FileTreeFileNode.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / FileTreeFileNode
2 |
3 | # Interface: FileTreeFileNode
4 |
5 | v2 file tree file node
6 |
7 | ## Hierarchy
8 |
9 | - [`BObject`](../modules.md#bobject)<``false``\>
10 |
11 | ↳ **`FileTreeFileNode`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [](FileTreeFileNode.md#)
18 |
19 | ## Properties
20 |
21 | • ****: [`FilePropsV2`](FilePropsV2.md)
22 |
23 | Entries with zero-length keys describe the properties
24 | of the composed path at that point
25 |
26 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=Entries%20with%20zero%2Dlength%20keys%20describe%20the%20properties%20of%20the%20composed%20path%20at%20that%20point)
27 |
28 | #### Defined in
29 |
30 | [src/utils/fileTree.ts:121](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/utils/fileTree.ts#L121)
31 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoBase.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoBase
2 |
3 | # Interface: InfoBase
4 |
5 | info base
6 |
7 | ## Hierarchy
8 |
9 | - [`BObject`](../modules.md#bobject)<``false``\>
10 |
11 | ↳ **`InfoBase`**
12 |
13 | ↳↳ [`InfoBaseV1`](InfoBaseV1.md)
14 |
15 | ↳↳ [`InfoV2`](InfoV2.md)
16 |
17 | ## Table of contents
18 |
19 | ### Properties
20 |
21 | - [name](InfoBase.md#name)
22 | - [piece length](InfoBase.md#piece length)
23 | - [private](InfoBase.md#private)
24 | - [source](InfoBase.md#source)
25 |
26 | ## Properties
27 |
28 | ### name
29 |
30 | • **name**: `string`
31 |
32 | The suggested name to save the file (or directory) as.
33 | It is purely advisory
34 |
35 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
36 | |
37 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
38 |
39 | #### Defined in
40 |
41 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
42 |
43 | ___
44 |
45 | ### piece length
46 |
47 | • **piece length**: `number`
48 |
49 | The number of bytes that each logical piece
50 | in the peer protocol refers to
51 |
52 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
53 | |
54 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
55 |
56 | #### Defined in
57 |
58 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
59 |
60 | ___
61 |
62 | ### private
63 |
64 | • `Optional` **private**: `boolean`
65 |
66 | is private torrent
67 |
68 | #### Defined in
69 |
70 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
71 |
72 | ___
73 |
74 | ### source
75 |
76 | • `Optional` **source**: `string`
77 |
78 | source
79 |
80 | #### Defined in
81 |
82 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
83 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoBaseV1.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoBaseV1
2 |
3 | # Interface: InfoBaseV1
4 |
5 | v1 info base
6 |
7 | ## Hierarchy
8 |
9 | - [`InfoBase`](InfoBase.md)
10 |
11 | ↳ **`InfoBaseV1`**
12 |
13 | ↳↳ [`InfoSingleFileV1`](InfoSingleFileV1.md)
14 |
15 | ↳↳ [`InfoMultiFileV1`](InfoMultiFileV1.md)
16 |
17 | ## Table of contents
18 |
19 | ### Properties
20 |
21 | - [name](InfoBaseV1.md#name)
22 | - [piece length](InfoBaseV1.md#piece length)
23 | - [pieces](InfoBaseV1.md#pieces)
24 | - [private](InfoBaseV1.md#private)
25 | - [source](InfoBaseV1.md#source)
26 |
27 | ## Properties
28 |
29 | ### name
30 |
31 | • **name**: `string`
32 |
33 | The suggested name to save the file (or directory) as.
34 | It is purely advisory
35 |
36 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
37 | |
38 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
39 |
40 | #### Inherited from
41 |
42 | [InfoBase](InfoBase.md).[name](InfoBase.md#name)
43 |
44 | #### Defined in
45 |
46 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
47 |
48 | ___
49 |
50 | ### piece length
51 |
52 | • **piece length**: `number`
53 |
54 | The number of bytes that each logical piece
55 | in the peer protocol refers to
56 |
57 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
58 | |
59 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
60 |
61 | #### Inherited from
62 |
63 | [InfoBase](InfoBase.md).[piece length](InfoBase.md#piece length)
64 |
65 | #### Defined in
66 |
67 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
68 |
69 | ___
70 |
71 | ### pieces
72 |
73 | • **pieces**: `string` \| `ArrayBuffer`
74 |
75 | Pieces maps to a string whose length is a multiple of 20
76 |
77 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=M%20as%20default%29.-,pieces,-maps%20to%20a)
78 |
79 | #### Defined in
80 |
81 | [src/create.ts:252](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L252)
82 |
83 | ___
84 |
85 | ### private
86 |
87 | • `Optional` **private**: `boolean`
88 |
89 | is private torrent
90 |
91 | #### Inherited from
92 |
93 | [InfoBase](InfoBase.md).[private](InfoBase.md#private)
94 |
95 | #### Defined in
96 |
97 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
98 |
99 | ___
100 |
101 | ### source
102 |
103 | • `Optional` **source**: `string`
104 |
105 | source
106 |
107 | #### Inherited from
108 |
109 | [InfoBase](InfoBase.md).[source](InfoBase.md#source)
110 |
111 | #### Defined in
112 |
113 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
114 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoMultiFileHybrid.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoMultiFileHybrid
2 |
3 | # Interface: InfoMultiFileHybrid
4 |
5 | hybrid multi file info
6 |
7 | ## Hierarchy
8 |
9 | - [`InfoMultiFileV1`](InfoMultiFileV1.md)
10 |
11 | - [`InfoV2`](InfoV2.md)
12 |
13 | ↳ **`InfoMultiFileHybrid`**
14 |
15 | ## Table of contents
16 |
17 | ### Properties
18 |
19 | - [file tree](InfoMultiFileHybrid.md#file tree)
20 | - [files](InfoMultiFileHybrid.md#files)
21 | - [meta version](InfoMultiFileHybrid.md#meta version)
22 | - [name](InfoMultiFileHybrid.md#name)
23 | - [piece length](InfoMultiFileHybrid.md#piece length)
24 | - [pieces](InfoMultiFileHybrid.md#pieces)
25 | - [private](InfoMultiFileHybrid.md#private)
26 | - [source](InfoMultiFileHybrid.md#source)
27 |
28 | ## Properties
29 |
30 | ### file tree
31 |
32 | • **file tree**: [`FileTreeDirNode`](../modules.md#filetreedirnode)
33 |
34 | A tree of dictionaries where dictionary keys
35 | represent UTF-8 encoded path elements
36 |
37 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=about%20invalid%20files.-,file%20tree,-A%20tree%20of)
38 |
39 | #### Inherited from
40 |
41 | [InfoV2](InfoV2.md).[file tree](InfoV2.md#file tree)
42 |
43 | #### Defined in
44 |
45 | [src/create.ts:284](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L284)
46 |
47 | ___
48 |
49 | ### files
50 |
51 | • **files**: [`FileListV1`](../modules.md#filelistv1)
52 |
53 | #### Inherited from
54 |
55 | [InfoMultiFileV1](InfoMultiFileV1.md).[files](InfoMultiFileV1.md#files)
56 |
57 | #### Defined in
58 |
59 | [src/create.ts:266](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L266)
60 |
61 | ___
62 |
63 | ### meta version
64 |
65 | • **meta version**: `number`
66 |
67 | An integer value, set to 2 to indicate compatibility
68 | with the current revision of this specification
69 |
70 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=an%20alignment%20gap.-,meta%20version,-An%20integer%20value)
71 |
72 | #### Inherited from
73 |
74 | [InfoV2](InfoV2.md).[meta version](InfoV2.md#meta version)
75 |
76 | #### Defined in
77 |
78 | [src/create.ts:291](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L291)
79 |
80 | ___
81 |
82 | ### name
83 |
84 | • **name**: `string`
85 |
86 | The suggested name to save the file (or directory) as.
87 | It is purely advisory
88 |
89 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
90 | |
91 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
92 |
93 | #### Inherited from
94 |
95 | [InfoV2](InfoV2.md).[name](InfoV2.md#name)
96 |
97 | #### Defined in
98 |
99 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
100 |
101 | ___
102 |
103 | ### piece length
104 |
105 | • **piece length**: `number`
106 |
107 | The number of bytes that each logical piece
108 | in the peer protocol refers to
109 |
110 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
111 | |
112 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
113 |
114 | #### Inherited from
115 |
116 | [InfoV2](InfoV2.md).[piece length](InfoV2.md#piece length)
117 |
118 | #### Defined in
119 |
120 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
121 |
122 | ___
123 |
124 | ### pieces
125 |
126 | • **pieces**: `string` \| `ArrayBuffer`
127 |
128 | Pieces maps to a string whose length is a multiple of 20
129 |
130 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=M%20as%20default%29.-,pieces,-maps%20to%20a)
131 |
132 | #### Inherited from
133 |
134 | [InfoMultiFileV1](InfoMultiFileV1.md).[pieces](InfoMultiFileV1.md#pieces)
135 |
136 | #### Defined in
137 |
138 | [src/create.ts:252](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L252)
139 |
140 | ___
141 |
142 | ### private
143 |
144 | • `Optional` **private**: `boolean`
145 |
146 | is private torrent
147 |
148 | #### Inherited from
149 |
150 | [InfoV2](InfoV2.md).[private](InfoV2.md#private)
151 |
152 | #### Defined in
153 |
154 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
155 |
156 | ___
157 |
158 | ### source
159 |
160 | • `Optional` **source**: `string`
161 |
162 | source
163 |
164 | #### Inherited from
165 |
166 | [InfoV2](InfoV2.md).[source](InfoV2.md#source)
167 |
168 | #### Defined in
169 |
170 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
171 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoMultiFileV1.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoMultiFileV1
2 |
3 | # Interface: InfoMultiFileV1
4 |
5 | v1 multi file info
6 |
7 | ## Hierarchy
8 |
9 | - [`InfoBaseV1`](InfoBaseV1.md)
10 |
11 | ↳ **`InfoMultiFileV1`**
12 |
13 | ↳↳ [`InfoMultiFileHybrid`](InfoMultiFileHybrid.md)
14 |
15 | ## Table of contents
16 |
17 | ### Properties
18 |
19 | - [files](InfoMultiFileV1.md#files)
20 | - [name](InfoMultiFileV1.md#name)
21 | - [piece length](InfoMultiFileV1.md#piece length)
22 | - [pieces](InfoMultiFileV1.md#pieces)
23 | - [private](InfoMultiFileV1.md#private)
24 | - [source](InfoMultiFileV1.md#source)
25 |
26 | ## Properties
27 |
28 | ### files
29 |
30 | • **files**: [`FileListV1`](../modules.md#filelistv1)
31 |
32 | #### Defined in
33 |
34 | [src/create.ts:266](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L266)
35 |
36 | ___
37 |
38 | ### name
39 |
40 | • **name**: `string`
41 |
42 | The suggested name to save the file (or directory) as.
43 | It is purely advisory
44 |
45 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
46 | |
47 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
48 |
49 | #### Inherited from
50 |
51 | [InfoBaseV1](InfoBaseV1.md).[name](InfoBaseV1.md#name)
52 |
53 | #### Defined in
54 |
55 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
56 |
57 | ___
58 |
59 | ### piece length
60 |
61 | • **piece length**: `number`
62 |
63 | The number of bytes that each logical piece
64 | in the peer protocol refers to
65 |
66 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
67 | |
68 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
69 |
70 | #### Inherited from
71 |
72 | [InfoBaseV1](InfoBaseV1.md).[piece length](InfoBaseV1.md#piece length)
73 |
74 | #### Defined in
75 |
76 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
77 |
78 | ___
79 |
80 | ### pieces
81 |
82 | • **pieces**: `string` \| `ArrayBuffer`
83 |
84 | Pieces maps to a string whose length is a multiple of 20
85 |
86 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=M%20as%20default%29.-,pieces,-maps%20to%20a)
87 |
88 | #### Inherited from
89 |
90 | [InfoBaseV1](InfoBaseV1.md).[pieces](InfoBaseV1.md#pieces)
91 |
92 | #### Defined in
93 |
94 | [src/create.ts:252](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L252)
95 |
96 | ___
97 |
98 | ### private
99 |
100 | • `Optional` **private**: `boolean`
101 |
102 | is private torrent
103 |
104 | #### Inherited from
105 |
106 | [InfoBaseV1](InfoBaseV1.md).[private](InfoBaseV1.md#private)
107 |
108 | #### Defined in
109 |
110 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
111 |
112 | ___
113 |
114 | ### source
115 |
116 | • `Optional` **source**: `string`
117 |
118 | source
119 |
120 | #### Inherited from
121 |
122 | [InfoBaseV1](InfoBaseV1.md).[source](InfoBaseV1.md#source)
123 |
124 | #### Defined in
125 |
126 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
127 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoSingleFileHybrid.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoSingleFileHybrid
2 |
3 | # Interface: InfoSingleFileHybrid
4 |
5 | hybrid single file info
6 |
7 | ## Hierarchy
8 |
9 | - [`InfoSingleFileV1`](InfoSingleFileV1.md)
10 |
11 | - [`InfoV2`](InfoV2.md)
12 |
13 | ↳ **`InfoSingleFileHybrid`**
14 |
15 | ## Table of contents
16 |
17 | ### Properties
18 |
19 | - [file tree](InfoSingleFileHybrid.md#file tree)
20 | - [length](InfoSingleFileHybrid.md#length)
21 | - [meta version](InfoSingleFileHybrid.md#meta version)
22 | - [name](InfoSingleFileHybrid.md#name)
23 | - [piece length](InfoSingleFileHybrid.md#piece length)
24 | - [pieces](InfoSingleFileHybrid.md#pieces)
25 | - [private](InfoSingleFileHybrid.md#private)
26 | - [source](InfoSingleFileHybrid.md#source)
27 |
28 | ## Properties
29 |
30 | ### file tree
31 |
32 | • **file tree**: [`FileTreeDirNode`](../modules.md#filetreedirnode)
33 |
34 | A tree of dictionaries where dictionary keys
35 | represent UTF-8 encoded path elements
36 |
37 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=about%20invalid%20files.-,file%20tree,-A%20tree%20of)
38 |
39 | #### Inherited from
40 |
41 | [InfoV2](InfoV2.md).[file tree](InfoV2.md#file tree)
42 |
43 | #### Defined in
44 |
45 | [src/create.ts:284](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L284)
46 |
47 | ___
48 |
49 | ### length
50 |
51 | • **length**: `number`
52 |
53 | #### Inherited from
54 |
55 | [InfoSingleFileV1](InfoSingleFileV1.md).[length](InfoSingleFileV1.md#length)
56 |
57 | #### Defined in
58 |
59 | [src/create.ts:259](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L259)
60 |
61 | ___
62 |
63 | ### meta version
64 |
65 | • **meta version**: `number`
66 |
67 | An integer value, set to 2 to indicate compatibility
68 | with the current revision of this specification
69 |
70 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=an%20alignment%20gap.-,meta%20version,-An%20integer%20value)
71 |
72 | #### Inherited from
73 |
74 | [InfoV2](InfoV2.md).[meta version](InfoV2.md#meta version)
75 |
76 | #### Defined in
77 |
78 | [src/create.ts:291](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L291)
79 |
80 | ___
81 |
82 | ### name
83 |
84 | • **name**: `string`
85 |
86 | The suggested name to save the file (or directory) as.
87 | It is purely advisory
88 |
89 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
90 | |
91 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
92 |
93 | #### Inherited from
94 |
95 | [InfoV2](InfoV2.md).[name](InfoV2.md#name)
96 |
97 | #### Defined in
98 |
99 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
100 |
101 | ___
102 |
103 | ### piece length
104 |
105 | • **piece length**: `number`
106 |
107 | The number of bytes that each logical piece
108 | in the peer protocol refers to
109 |
110 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
111 | |
112 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
113 |
114 | #### Inherited from
115 |
116 | [InfoV2](InfoV2.md).[piece length](InfoV2.md#piece length)
117 |
118 | #### Defined in
119 |
120 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
121 |
122 | ___
123 |
124 | ### pieces
125 |
126 | • **pieces**: `string` \| `ArrayBuffer`
127 |
128 | Pieces maps to a string whose length is a multiple of 20
129 |
130 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=M%20as%20default%29.-,pieces,-maps%20to%20a)
131 |
132 | #### Inherited from
133 |
134 | [InfoSingleFileV1](InfoSingleFileV1.md).[pieces](InfoSingleFileV1.md#pieces)
135 |
136 | #### Defined in
137 |
138 | [src/create.ts:252](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L252)
139 |
140 | ___
141 |
142 | ### private
143 |
144 | • `Optional` **private**: `boolean`
145 |
146 | is private torrent
147 |
148 | #### Inherited from
149 |
150 | [InfoV2](InfoV2.md).[private](InfoV2.md#private)
151 |
152 | #### Defined in
153 |
154 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
155 |
156 | ___
157 |
158 | ### source
159 |
160 | • `Optional` **source**: `string`
161 |
162 | source
163 |
164 | #### Inherited from
165 |
166 | [InfoV2](InfoV2.md).[source](InfoV2.md#source)
167 |
168 | #### Defined in
169 |
170 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
171 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoSingleFileV1.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoSingleFileV1
2 |
3 | # Interface: InfoSingleFileV1
4 |
5 | v1 single file info
6 |
7 | ## Hierarchy
8 |
9 | - [`InfoBaseV1`](InfoBaseV1.md)
10 |
11 | ↳ **`InfoSingleFileV1`**
12 |
13 | ↳↳ [`InfoSingleFileHybrid`](InfoSingleFileHybrid.md)
14 |
15 | ## Table of contents
16 |
17 | ### Properties
18 |
19 | - [length](InfoSingleFileV1.md#length)
20 | - [name](InfoSingleFileV1.md#name)
21 | - [piece length](InfoSingleFileV1.md#piece length)
22 | - [pieces](InfoSingleFileV1.md#pieces)
23 | - [private](InfoSingleFileV1.md#private)
24 | - [source](InfoSingleFileV1.md#source)
25 |
26 | ## Properties
27 |
28 | ### length
29 |
30 | • **length**: `number`
31 |
32 | #### Defined in
33 |
34 | [src/create.ts:259](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L259)
35 |
36 | ___
37 |
38 | ### name
39 |
40 | • **name**: `string`
41 |
42 | The suggested name to save the file (or directory) as.
43 | It is purely advisory
44 |
45 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
46 | |
47 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
48 |
49 | #### Inherited from
50 |
51 | [InfoBaseV1](InfoBaseV1.md).[name](InfoBaseV1.md#name)
52 |
53 | #### Defined in
54 |
55 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
56 |
57 | ___
58 |
59 | ### piece length
60 |
61 | • **piece length**: `number`
62 |
63 | The number of bytes that each logical piece
64 | in the peer protocol refers to
65 |
66 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
67 | |
68 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
69 |
70 | #### Inherited from
71 |
72 | [InfoBaseV1](InfoBaseV1.md).[piece length](InfoBaseV1.md#piece length)
73 |
74 | #### Defined in
75 |
76 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
77 |
78 | ___
79 |
80 | ### pieces
81 |
82 | • **pieces**: `string` \| `ArrayBuffer`
83 |
84 | Pieces maps to a string whose length is a multiple of 20
85 |
86 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=M%20as%20default%29.-,pieces,-maps%20to%20a)
87 |
88 | #### Inherited from
89 |
90 | [InfoBaseV1](InfoBaseV1.md).[pieces](InfoBaseV1.md#pieces)
91 |
92 | #### Defined in
93 |
94 | [src/create.ts:252](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L252)
95 |
96 | ___
97 |
98 | ### private
99 |
100 | • `Optional` **private**: `boolean`
101 |
102 | is private torrent
103 |
104 | #### Inherited from
105 |
106 | [InfoBaseV1](InfoBaseV1.md).[private](InfoBaseV1.md#private)
107 |
108 | #### Defined in
109 |
110 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
111 |
112 | ___
113 |
114 | ### source
115 |
116 | • `Optional` **source**: `string`
117 |
118 | source
119 |
120 | #### Inherited from
121 |
122 | [InfoBaseV1](InfoBaseV1.md).[source](InfoBaseV1.md#source)
123 |
124 | #### Defined in
125 |
126 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
127 |
--------------------------------------------------------------------------------
/docs/interfaces/InfoV2.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / InfoV2
2 |
3 | # Interface: InfoV2
4 |
5 | v2 info
6 |
7 | ## Hierarchy
8 |
9 | - [`InfoBase`](InfoBase.md)
10 |
11 | ↳ **`InfoV2`**
12 |
13 | ↳↳ [`InfoSingleFileHybrid`](InfoSingleFileHybrid.md)
14 |
15 | ↳↳ [`InfoMultiFileHybrid`](InfoMultiFileHybrid.md)
16 |
17 | ## Table of contents
18 |
19 | ### Properties
20 |
21 | - [file tree](InfoV2.md#file tree)
22 | - [meta version](InfoV2.md#meta version)
23 | - [name](InfoV2.md#name)
24 | - [piece length](InfoV2.md#piece length)
25 | - [private](InfoV2.md#private)
26 | - [source](InfoV2.md#source)
27 |
28 | ## Properties
29 |
30 | ### file tree
31 |
32 | • **file tree**: [`FileTreeDirNode`](../modules.md#filetreedirnode)
33 |
34 | A tree of dictionaries where dictionary keys
35 | represent UTF-8 encoded path elements
36 |
37 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=about%20invalid%20files.-,file%20tree,-A%20tree%20of)
38 |
39 | #### Defined in
40 |
41 | [src/create.ts:284](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L284)
42 |
43 | ___
44 |
45 | ### meta version
46 |
47 | • **meta version**: `number`
48 |
49 | An integer value, set to 2 to indicate compatibility
50 | with the current revision of this specification
51 |
52 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=an%20alignment%20gap.-,meta%20version,-An%20integer%20value)
53 |
54 | #### Defined in
55 |
56 | [src/create.ts:291](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L291)
57 |
58 | ___
59 |
60 | ### name
61 |
62 | • **name**: `string`
63 |
64 | The suggested name to save the file (or directory) as.
65 | It is purely advisory
66 |
67 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
68 | |
69 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
70 |
71 | #### Inherited from
72 |
73 | [InfoBase](InfoBase.md).[name](InfoBase.md#name)
74 |
75 | #### Defined in
76 |
77 | [src/create.ts:223](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L223)
78 |
79 | ___
80 |
81 | ### piece length
82 |
83 | • **piece length**: `number`
84 |
85 | The number of bytes that each logical piece
86 | in the peer protocol refers to
87 |
88 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
89 | |
90 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
91 |
92 | #### Inherited from
93 |
94 | [InfoBase](InfoBase.md).[piece length](InfoBase.md#piece length)
95 |
96 | #### Defined in
97 |
98 | [src/create.ts:232](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L232)
99 |
100 | ___
101 |
102 | ### private
103 |
104 | • `Optional` **private**: `boolean`
105 |
106 | is private torrent
107 |
108 | #### Inherited from
109 |
110 | [InfoBase](InfoBase.md).[private](InfoBase.md#private)
111 |
112 | #### Defined in
113 |
114 | [src/create.ts:236](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L236)
115 |
116 | ___
117 |
118 | ### source
119 |
120 | • `Optional` **source**: `string`
121 |
122 | source
123 |
124 | #### Inherited from
125 |
126 | [InfoBase](InfoBase.md).[source](InfoBase.md#source)
127 |
128 | #### Defined in
129 |
130 | [src/create.ts:240](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L240)
131 |
--------------------------------------------------------------------------------
/docs/interfaces/IntegerToken.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / IntegerToken
2 |
3 | # Interface: IntegerToken
4 |
5 | ## Table of contents
6 |
7 | ### Properties
8 |
9 | - [type](IntegerToken.md#type)
10 | - [value](IntegerToken.md#value)
11 |
12 | ## Properties
13 |
14 | ### type
15 |
16 | • **type**: [`Integer`](../enums/TokenType.md#integer)
17 |
18 | #### Defined in
19 |
20 | [src/transformers/tokenizer.ts:22](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L22)
21 |
22 | ___
23 |
24 | ### value
25 |
26 | • **value**: `Uint8Array`
27 |
28 | #### Defined in
29 |
30 | [src/transformers/tokenizer.ts:23](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L23)
31 |
--------------------------------------------------------------------------------
/docs/interfaces/ListEndToken.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / ListEndToken
2 |
3 | # Interface: ListEndToken
4 |
5 | ## Table of contents
6 |
7 | ### Properties
8 |
9 | - [type](ListEndToken.md#type)
10 |
11 | ## Properties
12 |
13 | ### type
14 |
15 | • **type**: [`ListEnd`](../enums/TokenType.md#listend)
16 |
17 | #### Defined in
18 |
19 | [src/transformers/tokenizer.ts:36](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L36)
20 |
--------------------------------------------------------------------------------
/docs/interfaces/ListStartToken.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / ListStartToken
2 |
3 | # Interface: ListStartToken
4 |
5 | ## Table of contents
6 |
7 | ### Properties
8 |
9 | - [type](ListStartToken.md#type)
10 |
11 | ## Properties
12 |
13 | ### type
14 |
15 | • **type**: [`ListStart`](../enums/TokenType.md#liststart)
16 |
17 | #### Defined in
18 |
19 | [src/transformers/tokenizer.ts:32](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/transformers/tokenizer.ts#L32)
20 |
--------------------------------------------------------------------------------
/docs/interfaces/MetaInfoBase.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / MetaInfoBase
2 |
3 | # Interface: MetaInfoBase
4 |
5 | base meta info
6 |
7 | ## Hierarchy
8 |
9 | - [`BObject`](../modules.md#bobject)<``false``\>
10 |
11 | ↳ **`MetaInfoBase`**
12 |
13 | ↳↳ [`MetaInfoV1`](MetaInfoV1.md)
14 |
15 | ↳↳ [`MetaInfoV2`](MetaInfoV2.md)
16 |
17 | ↳↳ [`MetaInfoHybrid`](MetaInfoHybrid.md)
18 |
19 | ## Table of contents
20 |
21 | ### Properties
22 |
23 | - [announce](MetaInfoBase.md#announce)
24 | - [announce-list](MetaInfoBase.md#announce-list)
25 | - [comment](MetaInfoBase.md#comment)
26 | - [created by](MetaInfoBase.md#created by)
27 | - [creation date](MetaInfoBase.md#creation date)
28 |
29 | ## Properties
30 |
31 | ### announce
32 |
33 | • `Optional` **announce**: `string`
34 |
35 | The URL of the tracker
36 |
37 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
38 | |
39 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
40 |
41 | #### Defined in
42 |
43 | [src/create.ts:338](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L338)
44 |
45 | ___
46 |
47 | ### announce-list
48 |
49 | • `Optional` **announce-list**: `string`[][]
50 |
51 | This key will refer to a list of lists of URLs,
52 | and will contain a list of tiers of announces
53 |
54 | [BEP 12](http://bittorrent.org/beps/bep_0012.html#:~:text=This%20key%20will%20refer%20to%20a%20list%20of%20lists%20of%20URLs%2C%20and%20will%20contain%20a%20list%20of%20tiers%20of%20announces)
55 |
56 | #### Defined in
57 |
58 | [src/create.ts:345](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L345)
59 |
60 | ___
61 |
62 | ### comment
63 |
64 | • `Optional` **comment**: `string`
65 |
66 | Free-form textual comments of the author
67 |
68 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=00%3A00%20UTC%29-,comment,-%3A%20%28optional%29%20free%2Dform)
69 |
70 | #### Defined in
71 |
72 | [src/create.ts:351](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L351)
73 |
74 | ___
75 |
76 | ### created by
77 |
78 | • `Optional` **created by**: `string`
79 |
80 | Name and version of the program used to create the .torrent
81 |
82 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=the%20author%20%28string%29-,created%20by,-%3A%20%28optional%29%20name%20and)
83 |
84 | #### Defined in
85 |
86 | [src/create.ts:357](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L357)
87 |
88 | ___
89 |
90 | ### creation date
91 |
92 | • `Optional` **creation date**: `number`
93 |
94 | The creation time of the torrent,
95 | in standard UNIX epoch format
96 |
97 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=is%20here.-,creation%20date,-%3A%20%28optional%29%20the%20creation)
98 |
99 | #### Defined in
100 |
101 | [src/create.ts:364](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L364)
102 |
--------------------------------------------------------------------------------
/docs/interfaces/MetaInfoHybrid.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / MetaInfoHybrid
2 |
3 | # Interface: MetaInfoHybrid
4 |
5 | hybrid meta info
6 |
7 | ## Hierarchy
8 |
9 | - [`MetaInfoBase`](MetaInfoBase.md)
10 |
11 | ↳ **`MetaInfoHybrid`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [announce](MetaInfoHybrid.md#announce)
18 | - [announce-list](MetaInfoHybrid.md#announce-list)
19 | - [comment](MetaInfoHybrid.md#comment)
20 | - [created by](MetaInfoHybrid.md#created by)
21 | - [creation date](MetaInfoHybrid.md#creation date)
22 | - [info](MetaInfoHybrid.md#info)
23 | - [piece layers](MetaInfoHybrid.md#piece layers)
24 |
25 | ## Properties
26 |
27 | ### announce
28 |
29 | • `Optional` **announce**: `string`
30 |
31 | The URL of the tracker
32 |
33 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
34 | |
35 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
36 |
37 | #### Inherited from
38 |
39 | [MetaInfoBase](MetaInfoBase.md).[announce](MetaInfoBase.md#announce)
40 |
41 | #### Defined in
42 |
43 | [src/create.ts:338](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L338)
44 |
45 | ___
46 |
47 | ### announce-list
48 |
49 | • `Optional` **announce-list**: `string`[][]
50 |
51 | This key will refer to a list of lists of URLs,
52 | and will contain a list of tiers of announces
53 |
54 | [BEP 12](http://bittorrent.org/beps/bep_0012.html#:~:text=This%20key%20will%20refer%20to%20a%20list%20of%20lists%20of%20URLs%2C%20and%20will%20contain%20a%20list%20of%20tiers%20of%20announces)
55 |
56 | #### Inherited from
57 |
58 | [MetaInfoBase](MetaInfoBase.md).[announce-list](MetaInfoBase.md#announce-list)
59 |
60 | #### Defined in
61 |
62 | [src/create.ts:345](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L345)
63 |
64 | ___
65 |
66 | ### comment
67 |
68 | • `Optional` **comment**: `string`
69 |
70 | Free-form textual comments of the author
71 |
72 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=00%3A00%20UTC%29-,comment,-%3A%20%28optional%29%20free%2Dform)
73 |
74 | #### Inherited from
75 |
76 | [MetaInfoBase](MetaInfoBase.md).[comment](MetaInfoBase.md#comment)
77 |
78 | #### Defined in
79 |
80 | [src/create.ts:351](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L351)
81 |
82 | ___
83 |
84 | ### created by
85 |
86 | • `Optional` **created by**: `string`
87 |
88 | Name and version of the program used to create the .torrent
89 |
90 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=the%20author%20%28string%29-,created%20by,-%3A%20%28optional%29%20name%20and)
91 |
92 | #### Inherited from
93 |
94 | [MetaInfoBase](MetaInfoBase.md).[created by](MetaInfoBase.md#created by)
95 |
96 | #### Defined in
97 |
98 | [src/create.ts:357](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L357)
99 |
100 | ___
101 |
102 | ### creation date
103 |
104 | • `Optional` **creation date**: `number`
105 |
106 | The creation time of the torrent,
107 | in standard UNIX epoch format
108 |
109 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=is%20here.-,creation%20date,-%3A%20%28optional%29%20the%20creation)
110 |
111 | #### Inherited from
112 |
113 | [MetaInfoBase](MetaInfoBase.md).[creation date](MetaInfoBase.md#creation date)
114 |
115 | #### Defined in
116 |
117 | [src/create.ts:364](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L364)
118 |
119 | ___
120 |
121 | ### info
122 |
123 | • **info**: [`InfoHybrid`](../modules.md#infohybrid)
124 |
125 | #### Defined in
126 |
127 | [src/create.ts:386](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L386)
128 |
129 | ___
130 |
131 | ### piece layers
132 |
133 | • `Optional` **piece layers**: [`PieceLayers`](../modules.md#piecelayers)
134 |
135 | #### Defined in
136 |
137 | [src/create.ts:387](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L387)
138 |
--------------------------------------------------------------------------------
/docs/interfaces/MetaInfoV1.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / MetaInfoV1
2 |
3 | # Interface: MetaInfoV1
4 |
5 | v1 meta info
6 |
7 | ## Hierarchy
8 |
9 | - [`MetaInfoBase`](MetaInfoBase.md)
10 |
11 | ↳ **`MetaInfoV1`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [announce](MetaInfoV1.md#announce)
18 | - [announce-list](MetaInfoV1.md#announce-list)
19 | - [comment](MetaInfoV1.md#comment)
20 | - [created by](MetaInfoV1.md#created by)
21 | - [creation date](MetaInfoV1.md#creation date)
22 | - [info](MetaInfoV1.md#info)
23 |
24 | ## Properties
25 |
26 | ### announce
27 |
28 | • `Optional` **announce**: `string`
29 |
30 | The URL of the tracker
31 |
32 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
33 | |
34 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
35 |
36 | #### Inherited from
37 |
38 | [MetaInfoBase](MetaInfoBase.md).[announce](MetaInfoBase.md#announce)
39 |
40 | #### Defined in
41 |
42 | [src/create.ts:338](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L338)
43 |
44 | ___
45 |
46 | ### announce-list
47 |
48 | • `Optional` **announce-list**: `string`[][]
49 |
50 | This key will refer to a list of lists of URLs,
51 | and will contain a list of tiers of announces
52 |
53 | [BEP 12](http://bittorrent.org/beps/bep_0012.html#:~:text=This%20key%20will%20refer%20to%20a%20list%20of%20lists%20of%20URLs%2C%20and%20will%20contain%20a%20list%20of%20tiers%20of%20announces)
54 |
55 | #### Inherited from
56 |
57 | [MetaInfoBase](MetaInfoBase.md).[announce-list](MetaInfoBase.md#announce-list)
58 |
59 | #### Defined in
60 |
61 | [src/create.ts:345](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L345)
62 |
63 | ___
64 |
65 | ### comment
66 |
67 | • `Optional` **comment**: `string`
68 |
69 | Free-form textual comments of the author
70 |
71 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=00%3A00%20UTC%29-,comment,-%3A%20%28optional%29%20free%2Dform)
72 |
73 | #### Inherited from
74 |
75 | [MetaInfoBase](MetaInfoBase.md).[comment](MetaInfoBase.md#comment)
76 |
77 | #### Defined in
78 |
79 | [src/create.ts:351](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L351)
80 |
81 | ___
82 |
83 | ### created by
84 |
85 | • `Optional` **created by**: `string`
86 |
87 | Name and version of the program used to create the .torrent
88 |
89 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=the%20author%20%28string%29-,created%20by,-%3A%20%28optional%29%20name%20and)
90 |
91 | #### Inherited from
92 |
93 | [MetaInfoBase](MetaInfoBase.md).[created by](MetaInfoBase.md#created by)
94 |
95 | #### Defined in
96 |
97 | [src/create.ts:357](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L357)
98 |
99 | ___
100 |
101 | ### creation date
102 |
103 | • `Optional` **creation date**: `number`
104 |
105 | The creation time of the torrent,
106 | in standard UNIX epoch format
107 |
108 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=is%20here.-,creation%20date,-%3A%20%28optional%29%20the%20creation)
109 |
110 | #### Inherited from
111 |
112 | [MetaInfoBase](MetaInfoBase.md).[creation date](MetaInfoBase.md#creation date)
113 |
114 | #### Defined in
115 |
116 | [src/create.ts:364](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L364)
117 |
118 | ___
119 |
120 | ### info
121 |
122 | • **info**: [`InfoV1`](../modules.md#infov1)
123 |
124 | #### Defined in
125 |
126 | [src/create.ts:371](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L371)
127 |
--------------------------------------------------------------------------------
/docs/interfaces/MetaInfoV2.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / MetaInfoV2
2 |
3 | # Interface: MetaInfoV2
4 |
5 | v2 meta info
6 |
7 | ## Hierarchy
8 |
9 | - [`MetaInfoBase`](MetaInfoBase.md)
10 |
11 | ↳ **`MetaInfoV2`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [announce](MetaInfoV2.md#announce)
18 | - [announce-list](MetaInfoV2.md#announce-list)
19 | - [comment](MetaInfoV2.md#comment)
20 | - [created by](MetaInfoV2.md#created by)
21 | - [creation date](MetaInfoV2.md#creation date)
22 | - [info](MetaInfoV2.md#info)
23 | - [piece layers](MetaInfoV2.md#piece layers)
24 |
25 | ## Properties
26 |
27 | ### announce
28 |
29 | • `Optional` **announce**: `string`
30 |
31 | The URL of the tracker
32 |
33 | [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
34 | |
35 | [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
36 |
37 | #### Inherited from
38 |
39 | [MetaInfoBase](MetaInfoBase.md).[announce](MetaInfoBase.md#announce)
40 |
41 | #### Defined in
42 |
43 | [src/create.ts:338](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L338)
44 |
45 | ___
46 |
47 | ### announce-list
48 |
49 | • `Optional` **announce-list**: `string`[][]
50 |
51 | This key will refer to a list of lists of URLs,
52 | and will contain a list of tiers of announces
53 |
54 | [BEP 12](http://bittorrent.org/beps/bep_0012.html#:~:text=This%20key%20will%20refer%20to%20a%20list%20of%20lists%20of%20URLs%2C%20and%20will%20contain%20a%20list%20of%20tiers%20of%20announces)
55 |
56 | #### Inherited from
57 |
58 | [MetaInfoBase](MetaInfoBase.md).[announce-list](MetaInfoBase.md#announce-list)
59 |
60 | #### Defined in
61 |
62 | [src/create.ts:345](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L345)
63 |
64 | ___
65 |
66 | ### comment
67 |
68 | • `Optional` **comment**: `string`
69 |
70 | Free-form textual comments of the author
71 |
72 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=00%3A00%20UTC%29-,comment,-%3A%20%28optional%29%20free%2Dform)
73 |
74 | #### Inherited from
75 |
76 | [MetaInfoBase](MetaInfoBase.md).[comment](MetaInfoBase.md#comment)
77 |
78 | #### Defined in
79 |
80 | [src/create.ts:351](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L351)
81 |
82 | ___
83 |
84 | ### created by
85 |
86 | • `Optional` **created by**: `string`
87 |
88 | Name and version of the program used to create the .torrent
89 |
90 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=the%20author%20%28string%29-,created%20by,-%3A%20%28optional%29%20name%20and)
91 |
92 | #### Inherited from
93 |
94 | [MetaInfoBase](MetaInfoBase.md).[created by](MetaInfoBase.md#created by)
95 |
96 | #### Defined in
97 |
98 | [src/create.ts:357](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L357)
99 |
100 | ___
101 |
102 | ### creation date
103 |
104 | • `Optional` **creation date**: `number`
105 |
106 | The creation time of the torrent,
107 | in standard UNIX epoch format
108 |
109 | [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=is%20here.-,creation%20date,-%3A%20%28optional%29%20the%20creation)
110 |
111 | #### Inherited from
112 |
113 | [MetaInfoBase](MetaInfoBase.md).[creation date](MetaInfoBase.md#creation date)
114 |
115 | #### Defined in
116 |
117 | [src/create.ts:364](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L364)
118 |
119 | ___
120 |
121 | ### info
122 |
123 | • **info**: [`InfoV2`](InfoV2.md)
124 |
125 | #### Defined in
126 |
127 | [src/create.ts:378](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L378)
128 |
129 | ___
130 |
131 | ### piece layers
132 |
133 | • `Optional` **piece layers**: [`PieceLayers`](../modules.md#piecelayers)
134 |
135 | #### Defined in
136 |
137 | [src/create.ts:379](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L379)
138 |
--------------------------------------------------------------------------------
/docs/interfaces/TorrentOptionsBase.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / TorrentOptionsBase
2 |
3 | # Interface: TorrentOptionsBase
4 |
5 | base torrent options
6 |
7 | ## Hierarchy
8 |
9 | - **`TorrentOptionsBase`**
10 |
11 | ↳ [`TorrentOptionsV1`](TorrentOptionsV1.md)
12 |
13 | ↳ [`TorrentOptionsV2`](TorrentOptionsV2.md)
14 |
15 | ↳ [`TorrentOptionsHybrid`](TorrentOptionsHybrid.md)
16 |
17 | ## Table of contents
18 |
19 | ### Properties
20 |
21 | - [addCreatedBy](TorrentOptionsBase.md#addcreatedby)
22 | - [addCreationDate](TorrentOptionsBase.md#addcreationdate)
23 | - [announce](TorrentOptionsBase.md#announce)
24 | - [announceList](TorrentOptionsBase.md#announcelist)
25 | - [blockLength](TorrentOptionsBase.md#blocklength)
26 | - [comment](TorrentOptionsBase.md#comment)
27 | - [isPrivate](TorrentOptionsBase.md#isprivate)
28 | - [name](TorrentOptionsBase.md#name)
29 | - [pieceLength](TorrentOptionsBase.md#piecelength)
30 | - [source](TorrentOptionsBase.md#source)
31 | - [urlList](TorrentOptionsBase.md#urllist)
32 |
33 | ## Properties
34 |
35 | ### addCreatedBy
36 |
37 | • `Optional` **addCreatedBy**: `boolean`
38 |
39 | add created by whom
40 |
41 | #### Defined in
42 |
43 | [src/create.ts:71](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L71)
44 |
45 | ___
46 |
47 | ### addCreationDate
48 |
49 | • `Optional` **addCreationDate**: `boolean`
50 |
51 | add creation date
52 |
53 | #### Defined in
54 |
55 | [src/create.ts:75](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L75)
56 |
57 | ___
58 |
59 | ### announce
60 |
61 | • `Optional` **announce**: `string`
62 |
63 | announce url
64 |
65 | #### Defined in
66 |
67 | [src/create.ts:79](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L79)
68 |
69 | ___
70 |
71 | ### announceList
72 |
73 | • `Optional` **announceList**: `string`[][]
74 |
75 | announce url list
76 |
77 | #### Defined in
78 |
79 | [src/create.ts:83](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L83)
80 |
81 | ___
82 |
83 | ### blockLength
84 |
85 | • `Optional` **blockLength**: `number`
86 |
87 | block length: 16384 (16 KiB) by default
88 | (do not alter this value)
89 |
90 | #### Defined in
91 |
92 | [src/create.ts:88](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L88)
93 |
94 | ___
95 |
96 | ### comment
97 |
98 | • `Optional` **comment**: `string`
99 |
100 | comment
101 |
102 | #### Defined in
103 |
104 | [src/create.ts:92](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L92)
105 |
106 | ___
107 |
108 | ### isPrivate
109 |
110 | • `Optional` **isPrivate**: `boolean`
111 |
112 | is private torrent
113 |
114 | #### Defined in
115 |
116 | [src/create.ts:96](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L96)
117 |
118 | ___
119 |
120 | ### name
121 |
122 | • `Optional` **name**: `string`
123 |
124 | torrent name
125 |
126 | #### Defined in
127 |
128 | [src/create.ts:100](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L100)
129 |
130 | ___
131 |
132 | ### pieceLength
133 |
134 | • `Optional` **pieceLength**: `number`
135 |
136 | piece length: a power of 2 number,
137 | will automatically calculate when this value is missing
138 |
139 | #### Defined in
140 |
141 | [src/create.ts:105](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L105)
142 |
143 | ___
144 |
145 | ### source
146 |
147 | • `Optional` **source**: `string`
148 |
149 | source
150 |
151 | #### Defined in
152 |
153 | [src/create.ts:109](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L109)
154 |
155 | ___
156 |
157 | ### urlList
158 |
159 | • `Optional` **urlList**: `string`[]
160 |
161 | url list
162 |
163 | [BEP 9](http://www.bittorrent.org/beps/bep_0019.html#metadata-extension)
164 |
165 | #### Defined in
166 |
167 | [src/create.ts:115](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L115)
168 |
--------------------------------------------------------------------------------
/docs/interfaces/TorrentOptionsHybrid.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / TorrentOptionsHybrid
2 |
3 | # Interface: TorrentOptionsHybrid
4 |
5 | v1 + v2 hybrid torrent options
6 |
7 | ## Hierarchy
8 |
9 | - [`TorrentOptionsBase`](TorrentOptionsBase.md)
10 |
11 | ↳ **`TorrentOptionsHybrid`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [addCreatedBy](TorrentOptionsHybrid.md#addcreatedby)
18 | - [addCreationDate](TorrentOptionsHybrid.md#addcreationdate)
19 | - [announce](TorrentOptionsHybrid.md#announce)
20 | - [announceList](TorrentOptionsHybrid.md#announcelist)
21 | - [blockLength](TorrentOptionsHybrid.md#blocklength)
22 | - [comment](TorrentOptionsHybrid.md#comment)
23 | - [isPrivate](TorrentOptionsHybrid.md#isprivate)
24 | - [metaVersion](TorrentOptionsHybrid.md#metaversion)
25 | - [name](TorrentOptionsHybrid.md#name)
26 | - [pieceLength](TorrentOptionsHybrid.md#piecelength)
27 | - [source](TorrentOptionsHybrid.md#source)
28 | - [type](TorrentOptionsHybrid.md#type)
29 | - [urlList](TorrentOptionsHybrid.md#urllist)
30 |
31 | ## Properties
32 |
33 | ### addCreatedBy
34 |
35 | • `Optional` **addCreatedBy**: `boolean`
36 |
37 | add created by whom
38 |
39 | #### Inherited from
40 |
41 | [TorrentOptionsBase](TorrentOptionsBase.md).[addCreatedBy](TorrentOptionsBase.md#addcreatedby)
42 |
43 | #### Defined in
44 |
45 | [src/create.ts:71](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L71)
46 |
47 | ___
48 |
49 | ### addCreationDate
50 |
51 | • `Optional` **addCreationDate**: `boolean`
52 |
53 | add creation date
54 |
55 | #### Inherited from
56 |
57 | [TorrentOptionsBase](TorrentOptionsBase.md).[addCreationDate](TorrentOptionsBase.md#addcreationdate)
58 |
59 | #### Defined in
60 |
61 | [src/create.ts:75](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L75)
62 |
63 | ___
64 |
65 | ### announce
66 |
67 | • `Optional` **announce**: `string`
68 |
69 | announce url
70 |
71 | #### Inherited from
72 |
73 | [TorrentOptionsBase](TorrentOptionsBase.md).[announce](TorrentOptionsBase.md#announce)
74 |
75 | #### Defined in
76 |
77 | [src/create.ts:79](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L79)
78 |
79 | ___
80 |
81 | ### announceList
82 |
83 | • `Optional` **announceList**: `string`[][]
84 |
85 | announce url list
86 |
87 | #### Inherited from
88 |
89 | [TorrentOptionsBase](TorrentOptionsBase.md).[announceList](TorrentOptionsBase.md#announcelist)
90 |
91 | #### Defined in
92 |
93 | [src/create.ts:83](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L83)
94 |
95 | ___
96 |
97 | ### blockLength
98 |
99 | • `Optional` **blockLength**: `number`
100 |
101 | block length: 16384 (16 KiB) by default
102 | (do not alter this value)
103 |
104 | #### Inherited from
105 |
106 | [TorrentOptionsBase](TorrentOptionsBase.md).[blockLength](TorrentOptionsBase.md#blocklength)
107 |
108 | #### Defined in
109 |
110 | [src/create.ts:88](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L88)
111 |
112 | ___
113 |
114 | ### comment
115 |
116 | • `Optional` **comment**: `string`
117 |
118 | comment
119 |
120 | #### Inherited from
121 |
122 | [TorrentOptionsBase](TorrentOptionsBase.md).[comment](TorrentOptionsBase.md#comment)
123 |
124 | #### Defined in
125 |
126 | [src/create.ts:92](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L92)
127 |
128 | ___
129 |
130 | ### isPrivate
131 |
132 | • `Optional` **isPrivate**: `boolean`
133 |
134 | is private torrent
135 |
136 | #### Inherited from
137 |
138 | [TorrentOptionsBase](TorrentOptionsBase.md).[isPrivate](TorrentOptionsBase.md#isprivate)
139 |
140 | #### Defined in
141 |
142 | [src/create.ts:96](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L96)
143 |
144 | ___
145 |
146 | ### metaVersion
147 |
148 | • `Optional` **metaVersion**: ``2``
149 |
150 | meta version
151 |
152 | #### Defined in
153 |
154 | [src/create.ts:161](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L161)
155 |
156 | ___
157 |
158 | ### name
159 |
160 | • `Optional` **name**: `string`
161 |
162 | torrent name
163 |
164 | #### Inherited from
165 |
166 | [TorrentOptionsBase](TorrentOptionsBase.md).[name](TorrentOptionsBase.md#name)
167 |
168 | #### Defined in
169 |
170 | [src/create.ts:100](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L100)
171 |
172 | ___
173 |
174 | ### pieceLength
175 |
176 | • `Optional` **pieceLength**: `number`
177 |
178 | piece length: a power of 2 number,
179 | will automatically calculate when this value is missing
180 |
181 | #### Inherited from
182 |
183 | [TorrentOptionsBase](TorrentOptionsBase.md).[pieceLength](TorrentOptionsBase.md#piecelength)
184 |
185 | #### Defined in
186 |
187 | [src/create.ts:105](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L105)
188 |
189 | ___
190 |
191 | ### source
192 |
193 | • `Optional` **source**: `string`
194 |
195 | source
196 |
197 | #### Inherited from
198 |
199 | [TorrentOptionsBase](TorrentOptionsBase.md).[source](TorrentOptionsBase.md#source)
200 |
201 | #### Defined in
202 |
203 | [src/create.ts:109](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L109)
204 |
205 | ___
206 |
207 | ### type
208 |
209 | • **type**: [`HYBRID`](../enums/TorrentType.md#hybrid)
210 |
211 | torrent type: HYBRID
212 |
213 | #### Defined in
214 |
215 | [src/create.ts:165](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L165)
216 |
217 | ___
218 |
219 | ### urlList
220 |
221 | • `Optional` **urlList**: `string`[]
222 |
223 | url list
224 |
225 | [BEP 9](http://www.bittorrent.org/beps/bep_0019.html#metadata-extension)
226 |
227 | #### Inherited from
228 |
229 | [TorrentOptionsBase](TorrentOptionsBase.md).[urlList](TorrentOptionsBase.md#urllist)
230 |
231 | #### Defined in
232 |
233 | [src/create.ts:115](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L115)
234 |
--------------------------------------------------------------------------------
/docs/interfaces/TorrentOptionsV1.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / TorrentOptionsV1
2 |
3 | # Interface: TorrentOptionsV1
4 |
5 | v1 torrent options
6 |
7 | ## Hierarchy
8 |
9 | - [`TorrentOptionsBase`](TorrentOptionsBase.md)
10 |
11 | ↳ **`TorrentOptionsV1`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [addCreatedBy](TorrentOptionsV1.md#addcreatedby)
18 | - [addCreationDate](TorrentOptionsV1.md#addcreationdate)
19 | - [addPaddingFiles](TorrentOptionsV1.md#addpaddingfiles)
20 | - [announce](TorrentOptionsV1.md#announce)
21 | - [announceList](TorrentOptionsV1.md#announcelist)
22 | - [blockLength](TorrentOptionsV1.md#blocklength)
23 | - [comment](TorrentOptionsV1.md#comment)
24 | - [isPrivate](TorrentOptionsV1.md#isprivate)
25 | - [name](TorrentOptionsV1.md#name)
26 | - [pieceLength](TorrentOptionsV1.md#piecelength)
27 | - [sortFiles](TorrentOptionsV1.md#sortfiles)
28 | - [source](TorrentOptionsV1.md#source)
29 | - [type](TorrentOptionsV1.md#type)
30 | - [urlList](TorrentOptionsV1.md#urllist)
31 |
32 | ## Properties
33 |
34 | ### addCreatedBy
35 |
36 | • `Optional` **addCreatedBy**: `boolean`
37 |
38 | add created by whom
39 |
40 | #### Inherited from
41 |
42 | [TorrentOptionsBase](TorrentOptionsBase.md).[addCreatedBy](TorrentOptionsBase.md#addcreatedby)
43 |
44 | #### Defined in
45 |
46 | [src/create.ts:71](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L71)
47 |
48 | ___
49 |
50 | ### addCreationDate
51 |
52 | • `Optional` **addCreationDate**: `boolean`
53 |
54 | add creation date
55 |
56 | #### Inherited from
57 |
58 | [TorrentOptionsBase](TorrentOptionsBase.md).[addCreationDate](TorrentOptionsBase.md#addcreationdate)
59 |
60 | #### Defined in
61 |
62 | [src/create.ts:75](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L75)
63 |
64 | ___
65 |
66 | ### addPaddingFiles
67 |
68 | • `Optional` **addPaddingFiles**: `boolean`
69 |
70 | add padding files
71 | this option is only available in V1 type torrents
72 | because files are forcibly padded in HYBRID type
73 | and don't need padding in V2 type
74 |
75 | #### Defined in
76 |
77 | [src/create.ts:128](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L128)
78 |
79 | ___
80 |
81 | ### announce
82 |
83 | • `Optional` **announce**: `string`
84 |
85 | announce url
86 |
87 | #### Inherited from
88 |
89 | [TorrentOptionsBase](TorrentOptionsBase.md).[announce](TorrentOptionsBase.md#announce)
90 |
91 | #### Defined in
92 |
93 | [src/create.ts:79](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L79)
94 |
95 | ___
96 |
97 | ### announceList
98 |
99 | • `Optional` **announceList**: `string`[][]
100 |
101 | announce url list
102 |
103 | #### Inherited from
104 |
105 | [TorrentOptionsBase](TorrentOptionsBase.md).[announceList](TorrentOptionsBase.md#announcelist)
106 |
107 | #### Defined in
108 |
109 | [src/create.ts:83](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L83)
110 |
111 | ___
112 |
113 | ### blockLength
114 |
115 | • `Optional` **blockLength**: `number`
116 |
117 | block length: 16384 (16 KiB) by default
118 | (do not alter this value)
119 |
120 | #### Inherited from
121 |
122 | [TorrentOptionsBase](TorrentOptionsBase.md).[blockLength](TorrentOptionsBase.md#blocklength)
123 |
124 | #### Defined in
125 |
126 | [src/create.ts:88](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L88)
127 |
128 | ___
129 |
130 | ### comment
131 |
132 | • `Optional` **comment**: `string`
133 |
134 | comment
135 |
136 | #### Inherited from
137 |
138 | [TorrentOptionsBase](TorrentOptionsBase.md).[comment](TorrentOptionsBase.md#comment)
139 |
140 | #### Defined in
141 |
142 | [src/create.ts:92](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L92)
143 |
144 | ___
145 |
146 | ### isPrivate
147 |
148 | • `Optional` **isPrivate**: `boolean`
149 |
150 | is private torrent
151 |
152 | #### Inherited from
153 |
154 | [TorrentOptionsBase](TorrentOptionsBase.md).[isPrivate](TorrentOptionsBase.md#isprivate)
155 |
156 | #### Defined in
157 |
158 | [src/create.ts:96](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L96)
159 |
160 | ___
161 |
162 | ### name
163 |
164 | • `Optional` **name**: `string`
165 |
166 | torrent name
167 |
168 | #### Inherited from
169 |
170 | [TorrentOptionsBase](TorrentOptionsBase.md).[name](TorrentOptionsBase.md#name)
171 |
172 | #### Defined in
173 |
174 | [src/create.ts:100](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L100)
175 |
176 | ___
177 |
178 | ### pieceLength
179 |
180 | • `Optional` **pieceLength**: `number`
181 |
182 | piece length: a power of 2 number,
183 | will automatically calculate when this value is missing
184 |
185 | #### Inherited from
186 |
187 | [TorrentOptionsBase](TorrentOptionsBase.md).[pieceLength](TorrentOptionsBase.md#piecelength)
188 |
189 | #### Defined in
190 |
191 | [src/create.ts:105](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L105)
192 |
193 | ___
194 |
195 | ### sortFiles
196 |
197 | • `Optional` **sortFiles**: `boolean`
198 |
199 | sort file: only available in V1 type
200 | files are forcibly sorted in V2 and HYBRID type
201 |
202 | #### Defined in
203 |
204 | [src/create.ts:133](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L133)
205 |
206 | ___
207 |
208 | ### source
209 |
210 | • `Optional` **source**: `string`
211 |
212 | source
213 |
214 | #### Inherited from
215 |
216 | [TorrentOptionsBase](TorrentOptionsBase.md).[source](TorrentOptionsBase.md#source)
217 |
218 | #### Defined in
219 |
220 | [src/create.ts:109](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L109)
221 |
222 | ___
223 |
224 | ### type
225 |
226 | • **type**: [`V1`](../enums/TorrentType.md#v1)
227 |
228 | torrent type: V1
229 |
230 | #### Defined in
231 |
232 | [src/create.ts:137](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L137)
233 |
234 | ___
235 |
236 | ### urlList
237 |
238 | • `Optional` **urlList**: `string`[]
239 |
240 | url list
241 |
242 | [BEP 9](http://www.bittorrent.org/beps/bep_0019.html#metadata-extension)
243 |
244 | #### Inherited from
245 |
246 | [TorrentOptionsBase](TorrentOptionsBase.md).[urlList](TorrentOptionsBase.md#urllist)
247 |
248 | #### Defined in
249 |
250 | [src/create.ts:115](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L115)
251 |
--------------------------------------------------------------------------------
/docs/interfaces/TorrentOptionsV2.md:
--------------------------------------------------------------------------------
1 | [torrefy](../README.md) / [Exports](../modules.md) / TorrentOptionsV2
2 |
3 | # Interface: TorrentOptionsV2
4 |
5 | v2 torrent options
6 |
7 | ## Hierarchy
8 |
9 | - [`TorrentOptionsBase`](TorrentOptionsBase.md)
10 |
11 | ↳ **`TorrentOptionsV2`**
12 |
13 | ## Table of contents
14 |
15 | ### Properties
16 |
17 | - [addCreatedBy](TorrentOptionsV2.md#addcreatedby)
18 | - [addCreationDate](TorrentOptionsV2.md#addcreationdate)
19 | - [announce](TorrentOptionsV2.md#announce)
20 | - [announceList](TorrentOptionsV2.md#announcelist)
21 | - [blockLength](TorrentOptionsV2.md#blocklength)
22 | - [comment](TorrentOptionsV2.md#comment)
23 | - [isPrivate](TorrentOptionsV2.md#isprivate)
24 | - [metaVersion](TorrentOptionsV2.md#metaversion)
25 | - [name](TorrentOptionsV2.md#name)
26 | - [pieceLength](TorrentOptionsV2.md#piecelength)
27 | - [source](TorrentOptionsV2.md#source)
28 | - [type](TorrentOptionsV2.md#type)
29 | - [urlList](TorrentOptionsV2.md#urllist)
30 |
31 | ## Properties
32 |
33 | ### addCreatedBy
34 |
35 | • `Optional` **addCreatedBy**: `boolean`
36 |
37 | add created by whom
38 |
39 | #### Inherited from
40 |
41 | [TorrentOptionsBase](TorrentOptionsBase.md).[addCreatedBy](TorrentOptionsBase.md#addcreatedby)
42 |
43 | #### Defined in
44 |
45 | [src/create.ts:71](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L71)
46 |
47 | ___
48 |
49 | ### addCreationDate
50 |
51 | • `Optional` **addCreationDate**: `boolean`
52 |
53 | add creation date
54 |
55 | #### Inherited from
56 |
57 | [TorrentOptionsBase](TorrentOptionsBase.md).[addCreationDate](TorrentOptionsBase.md#addcreationdate)
58 |
59 | #### Defined in
60 |
61 | [src/create.ts:75](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L75)
62 |
63 | ___
64 |
65 | ### announce
66 |
67 | • `Optional` **announce**: `string`
68 |
69 | announce url
70 |
71 | #### Inherited from
72 |
73 | [TorrentOptionsBase](TorrentOptionsBase.md).[announce](TorrentOptionsBase.md#announce)
74 |
75 | #### Defined in
76 |
77 | [src/create.ts:79](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L79)
78 |
79 | ___
80 |
81 | ### announceList
82 |
83 | • `Optional` **announceList**: `string`[][]
84 |
85 | announce url list
86 |
87 | #### Inherited from
88 |
89 | [TorrentOptionsBase](TorrentOptionsBase.md).[announceList](TorrentOptionsBase.md#announcelist)
90 |
91 | #### Defined in
92 |
93 | [src/create.ts:83](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L83)
94 |
95 | ___
96 |
97 | ### blockLength
98 |
99 | • `Optional` **blockLength**: `number`
100 |
101 | block length: 16384 (16 KiB) by default
102 | (do not alter this value)
103 |
104 | #### Inherited from
105 |
106 | [TorrentOptionsBase](TorrentOptionsBase.md).[blockLength](TorrentOptionsBase.md#blocklength)
107 |
108 | #### Defined in
109 |
110 | [src/create.ts:88](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L88)
111 |
112 | ___
113 |
114 | ### comment
115 |
116 | • `Optional` **comment**: `string`
117 |
118 | comment
119 |
120 | #### Inherited from
121 |
122 | [TorrentOptionsBase](TorrentOptionsBase.md).[comment](TorrentOptionsBase.md#comment)
123 |
124 | #### Defined in
125 |
126 | [src/create.ts:92](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L92)
127 |
128 | ___
129 |
130 | ### isPrivate
131 |
132 | • `Optional` **isPrivate**: `boolean`
133 |
134 | is private torrent
135 |
136 | #### Inherited from
137 |
138 | [TorrentOptionsBase](TorrentOptionsBase.md).[isPrivate](TorrentOptionsBase.md#isprivate)
139 |
140 | #### Defined in
141 |
142 | [src/create.ts:96](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L96)
143 |
144 | ___
145 |
146 | ### metaVersion
147 |
148 | • `Optional` **metaVersion**: ``2``
149 |
150 | meta version
151 |
152 | #### Defined in
153 |
154 | [src/create.ts:147](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L147)
155 |
156 | ___
157 |
158 | ### name
159 |
160 | • `Optional` **name**: `string`
161 |
162 | torrent name
163 |
164 | #### Inherited from
165 |
166 | [TorrentOptionsBase](TorrentOptionsBase.md).[name](TorrentOptionsBase.md#name)
167 |
168 | #### Defined in
169 |
170 | [src/create.ts:100](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L100)
171 |
172 | ___
173 |
174 | ### pieceLength
175 |
176 | • `Optional` **pieceLength**: `number`
177 |
178 | piece length: a power of 2 number,
179 | will automatically calculate when this value is missing
180 |
181 | #### Inherited from
182 |
183 | [TorrentOptionsBase](TorrentOptionsBase.md).[pieceLength](TorrentOptionsBase.md#piecelength)
184 |
185 | #### Defined in
186 |
187 | [src/create.ts:105](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L105)
188 |
189 | ___
190 |
191 | ### source
192 |
193 | • `Optional` **source**: `string`
194 |
195 | source
196 |
197 | #### Inherited from
198 |
199 | [TorrentOptionsBase](TorrentOptionsBase.md).[source](TorrentOptionsBase.md#source)
200 |
201 | #### Defined in
202 |
203 | [src/create.ts:109](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L109)
204 |
205 | ___
206 |
207 | ### type
208 |
209 | • **type**: [`V2`](../enums/TorrentType.md#v2)
210 |
211 | torrent type: V2
212 |
213 | #### Defined in
214 |
215 | [src/create.ts:151](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L151)
216 |
217 | ___
218 |
219 | ### urlList
220 |
221 | • `Optional` **urlList**: `string`[]
222 |
223 | url list
224 |
225 | [BEP 9](http://www.bittorrent.org/beps/bep_0019.html#metadata-extension)
226 |
227 | #### Inherited from
228 |
229 | [TorrentOptionsBase](TorrentOptionsBase.md).[urlList](TorrentOptionsBase.md#urllist)
230 |
231 | #### Defined in
232 |
233 | [src/create.ts:115](https://github.com/Sec-ant/bepjs/blob/f9eb2df/src/create.ts#L115)
234 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "torrefy",
3 | "version": "2.0.8",
4 | "description": "An ESM package that uses Web Streams API to create v1, v2 or hybrid torrents in your web browser.",
5 | "type": "module",
6 | "main": "./dist/index.js",
7 | "types": "./dist/index.d.ts",
8 | "files": [
9 | "./dist/**/*.ts",
10 | "./dist/**/*.js"
11 | ],
12 | "exports": {
13 | "import": "./dist/index.js",
14 | "browser": "./dist/index.umd.js",
15 | "default": "./dist/index.js"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/Sec-ant/torrefy.git"
20 | },
21 | "homepage": "https://github.com/Sec-ant/torrefy",
22 | "bugs": {
23 | "url": "https://github.com/Sec-ant/torrefy/issues"
24 | },
25 | "keywords": [
26 | "torrent",
27 | "bittorrent",
28 | "bencode",
29 | "bdecode",
30 | "metainfo",
31 | "infohash",
32 | "webtorrent",
33 | "esm",
34 | "webstream"
35 | ],
36 | "author": "Ze-Zheng Wu",
37 | "license": "MIT",
38 | "scripts": {
39 | "clean": "rm -fr ./dist/*.*s",
40 | "lint": "eslint ./src",
41 | "build": "npm run lint && npm run clean && webpack",
42 | "serve": "http-server -c-1 dist",
43 | "docs": "typedoc --plugin typedoc-plugin-markdown ./src/index.ts"
44 | },
45 | "devDependencies": {
46 | "@types/uuid": "^9.0.0",
47 | "@types/wicg-file-system-access": "^2020.9.5",
48 | "@typescript-eslint/eslint-plugin": "^5.45.1",
49 | "@typescript-eslint/parser": "^5.45.1",
50 | "eslint": "^8.29.0",
51 | "http-server": "^14.1.1",
52 | "mocha": "^10.1.0",
53 | "ts-loader": "^9.4.2",
54 | "tslib": "^2.4.1",
55 | "typedoc": "^0.23.21",
56 | "typedoc-plugin-markdown": "^3.14.0",
57 | "typescript": "^4.9.3",
58 | "webpack": "^5.75.0",
59 | "webpack-cli": "^5.0.1"
60 | },
61 | "dependencies": {
62 | "jssha": "github:Sec-ant/jsSHA#dd69d437ee912b95007a330f9afec7635f83271e",
63 | "uuid": "^9.0.0",
64 | "workbox-streams": "^6.5.4"
65 | },
66 | "peerDependencies": {
67 | "@sec-ant/trie-map": "^1.1.5"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/create.ts:
--------------------------------------------------------------------------------
1 | import { concatenate as concatenateStreams } from "workbox-streams";
2 |
3 | import { BlockHasher } from "./transformers/blockHasher.js";
4 | import { ChunkSplitter } from "./transformers/chunkSplitter.js";
5 | import { MerkleRootCalculator } from "./transformers/merkleRootCalculator.js";
6 | import { MerkleTreeBalancer } from "./transformers/merkleTreeBalancer.js";
7 | import { PieceHasher } from "./transformers/pieceHasher.js";
8 | import { BObject } from "./utils/codec.js";
9 | import {
10 | FileAttrs,
11 | FileListV1,
12 | FileTreeDirNode,
13 | FileTreeFileNode,
14 | populateFileTree,
15 | resolveCommonDirAndTorrentName,
16 | } from "./utils/fileTree.js";
17 | import { FileDirLikes } from "./utils/fileDirLike.js";
18 | import { nextPowerOfTwo, getTimeStampSecondsNow } from "./utils/misc.js";
19 |
20 | /**
21 | * support padding attribute on file
22 | */
23 | declare global {
24 | interface File {
25 | readonly padding?: boolean;
26 | }
27 | }
28 |
29 | /**
30 | * created by: name + version
31 | * inject value when build
32 | */
33 | declare const CREATED_BY: string;
34 |
35 | /**
36 | * meta version can only be 2 at the time being
37 | */
38 | export type MetaVersion = 2;
39 |
40 | /**
41 | * torrent type: v1, v2, hybrid
42 | */
43 | export enum TorrentType {
44 | /**
45 | * v1 torrent
46 | *
47 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html)
48 | */
49 | V1 = "v1",
50 | /**
51 | * v2 torrent
52 | *
53 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html)
54 | */
55 | V2 = "v2",
56 | /**
57 | * v1 + v2 hybrid torrent
58 | *
59 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path)
60 | */
61 | HYBRID = "hybrid",
62 | }
63 |
64 | /**
65 | * base torrent options
66 | */
67 | export interface TorrentOptionsBase {
68 | /**
69 | * add created by whom
70 | */
71 | addCreatedBy?: boolean;
72 | /**
73 | * add creation date
74 | */
75 | addCreationDate?: boolean;
76 | /**
77 | * announce url
78 | */
79 | announce?: string;
80 | /**
81 | * announce url list
82 | */
83 | announceList?: string[][];
84 | /**
85 | * block length: 16384 (16 KiB) by default
86 | * (do not alter this value)
87 | */
88 | blockLength?: number;
89 | /**
90 | * comment
91 | */
92 | comment?: string;
93 | /**
94 | * is private torrent
95 | */
96 | isPrivate?: boolean;
97 | /**
98 | * torrent name
99 | */
100 | name?: string;
101 | /**
102 | * piece length: a power of 2 number,
103 | * will automatically calculate when this value is missing
104 | */
105 | pieceLength?: number;
106 | /**
107 | * source
108 | */
109 | source?: string;
110 | /**
111 | * url list
112 | *
113 | * [BEP 9](http://www.bittorrent.org/beps/bep_0019.html#metadata-extension)
114 | */
115 | urlList?: string[];
116 | }
117 |
118 | /**
119 | * v1 torrent options
120 | */
121 | export interface TorrentOptionsV1 extends TorrentOptionsBase {
122 | /**
123 | * add padding files
124 | * this option is only available in V1 type torrents
125 | * because files are forcibly padded in HYBRID type
126 | * and don't need padding in V2 type
127 | */
128 | addPaddingFiles?: boolean;
129 | /**
130 | * sort file: only available in V1 type
131 | * files are forcibly sorted in V2 and HYBRID type
132 | */
133 | sortFiles?: boolean;
134 | /**
135 | * torrent type: V1
136 | */
137 | type: TorrentType.V1;
138 | }
139 |
140 | /**
141 | * v2 torrent options
142 | */
143 | export interface TorrentOptionsV2 extends TorrentOptionsBase {
144 | /**
145 | * meta version
146 | */
147 | metaVersion?: MetaVersion;
148 | /**
149 | * torrent type: V2
150 | */
151 | type: TorrentType.V2;
152 | }
153 |
154 | /**
155 | * v1 + v2 hybrid torrent options
156 | */
157 | export interface TorrentOptionsHybrid extends TorrentOptionsBase {
158 | /**
159 | * meta version
160 | */
161 | metaVersion?: MetaVersion;
162 | /**
163 | * torrent type: HYBRID
164 | */
165 | type: TorrentType.HYBRID;
166 | }
167 |
168 | /**
169 | * torrent options
170 | */
171 | export type TorrentOptions =
172 | T extends TorrentType.V1
173 | ? TorrentOptionsV1
174 | : T extends TorrentType.V2
175 | ? TorrentOptionsV2
176 | : T extends TorrentType.HYBRID
177 | ? TorrentOptionsHybrid
178 | : never;
179 |
180 | type UnrequiredOptions =
181 | | "announce"
182 | | "announceList"
183 | | "comment"
184 | | "name"
185 | | "source"
186 | | "urlList";
187 |
188 | type InternalTorrentOptionsV1 = TorrentOptionsV1 &
189 | Required>;
190 |
191 | type InternalTorrentOptionsV2 = TorrentOptionsV2 &
192 | Required>;
193 |
194 | type InternalTorrentOptionsHybrid = TorrentOptionsHybrid &
195 | Required>;
196 |
197 | /**
198 | * internal torrent options
199 | */
200 | type InternalTorrentOptions =
201 | T extends TorrentType.V1
202 | ? InternalTorrentOptionsV1
203 | : T extends TorrentType.V2
204 | ? InternalTorrentOptionsV2
205 | : T extends TorrentType.HYBRID
206 | ? InternalTorrentOptionsHybrid
207 | : never;
208 |
209 | //===================================================================================
210 |
211 | /**
212 | * info base
213 | */
214 | export interface InfoBase extends BObject {
215 | /**
216 | * The suggested name to save the file (or directory) as.
217 | * It is purely advisory
218 | *
219 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=The-,name,-key%20maps%20to)
220 | * |
221 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=info%20dictionary-,name,-A%20display%20name)
222 | */
223 | name: string;
224 | /**
225 | * The number of bytes that each logical piece
226 | * in the peer protocol refers to
227 | *
228 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=is%20purely%20advisory.-,piece%20length,-maps%20to%20the)
229 | * |
230 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=is%20purely%20advisory.-,piece%20length,-The%20number%20of)
231 | */
232 | ["piece length"]: number;
233 | /**
234 | * is private torrent
235 | */
236 | private?: boolean;
237 | /**
238 | * source
239 | */
240 | source?: string;
241 | }
242 |
243 | /**
244 | * v1 info base
245 | */
246 | export interface InfoBaseV1 extends InfoBase {
247 | /**
248 | * Pieces maps to a string whose length is a multiple of 20
249 | *
250 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=M%20as%20default%29.-,pieces,-maps%20to%20a)
251 | */
252 | pieces: ArrayBuffer | string;
253 | }
254 |
255 | /**
256 | * v1 single file info
257 | */
258 | export interface InfoSingleFileV1 extends InfoBaseV1 {
259 | length: number;
260 | }
261 |
262 | /**
263 | * v1 multi file info
264 | */
265 | export interface InfoMultiFileV1 extends InfoBaseV1 {
266 | files: FileListV1;
267 | }
268 |
269 | /**
270 | * v1 info
271 | */
272 | export type InfoV1 = InfoSingleFileV1 | InfoMultiFileV1;
273 |
274 | /**
275 | * v2 info
276 | */
277 | export interface InfoV2 extends InfoBase {
278 | /**
279 | * A tree of dictionaries where dictionary keys
280 | * represent UTF-8 encoded path elements
281 | *
282 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=about%20invalid%20files.-,file%20tree,-A%20tree%20of)
283 | */
284 | ["file tree"]: FileTreeDirNode;
285 | /**
286 | * An integer value, set to 2 to indicate compatibility
287 | * with the current revision of this specification
288 | *
289 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=an%20alignment%20gap.-,meta%20version,-An%20integer%20value)
290 | */
291 | ["meta version"]: number;
292 | }
293 |
294 | /**
295 | * hybrid single file info
296 | */
297 | export interface InfoSingleFileHybrid extends InfoSingleFileV1, InfoV2 {}
298 |
299 | /**
300 | * hybrid multi file info
301 | */
302 | export interface InfoMultiFileHybrid extends InfoMultiFileV1, InfoV2 {}
303 |
304 | /**
305 | * hybrid info
306 | */
307 | export type InfoHybrid = InfoSingleFileHybrid | InfoMultiFileHybrid;
308 |
309 | /**
310 | * info
311 | */
312 | export type Info = T extends TorrentType.V1
313 | ? InfoV1
314 | : T extends TorrentType.V2
315 | ? InfoV2
316 | : T extends TorrentType.HYBRID
317 | ? InfoHybrid
318 | : never;
319 |
320 | //===================================================
321 |
322 | /**
323 | * v2 piece layers
324 | */
325 | export type PieceLayers = Map;
326 |
327 | /**
328 | * base meta info
329 | */
330 | export interface MetaInfoBase extends BObject {
331 | /**
332 | * The URL of the tracker
333 | *
334 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
335 | * |
336 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=the%20following%20keys%3A-,announce,-The%20URL%20of)
337 | */
338 | announce?: string;
339 | /**
340 | * This key will refer to a list of lists of URLs,
341 | * and will contain a list of tiers of announces
342 | *
343 | * [BEP 12](http://bittorrent.org/beps/bep_0012.html#:~:text=This%20key%20will%20refer%20to%20a%20list%20of%20lists%20of%20URLs%2C%20and%20will%20contain%20a%20list%20of%20tiers%20of%20announces)
344 | */
345 | ["announce-list"]?: string[][];
346 | /**
347 | * Free-form textual comments of the author
348 | *
349 | * [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=00%3A00%20UTC%29-,comment,-%3A%20%28optional%29%20free%2Dform)
350 | */
351 | comment?: string;
352 | /**
353 | * Name and version of the program used to create the .torrent
354 | *
355 | * [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=the%20author%20%28string%29-,created%20by,-%3A%20%28optional%29%20name%20and)
356 | */
357 | ["created by"]?: string;
358 | /**
359 | * The creation time of the torrent,
360 | * in standard UNIX epoch format
361 | *
362 | * [BitTorrent Specification](https://courses.edsa-project.eu/pluginfile.php/1514/mod_resource/content/0/bitTorrent_part2.htm#:~:text=is%20here.-,creation%20date,-%3A%20%28optional%29%20the%20creation)
363 | */
364 | ["creation date"]?: number;
365 | }
366 |
367 | /**
368 | * v1 meta info
369 | */
370 | export interface MetaInfoV1 extends MetaInfoBase {
371 | info: Info;
372 | }
373 |
374 | /**
375 | * v2 meta info
376 | */
377 | export interface MetaInfoV2 extends MetaInfoBase {
378 | info: Info;
379 | ["piece layers"]?: PieceLayers;
380 | }
381 |
382 | /**
383 | * hybrid meta info
384 | */
385 | export interface MetaInfoHybrid extends MetaInfoBase {
386 | info: Info;
387 | ["piece layers"]?: PieceLayers;
388 | }
389 |
390 | /**
391 | * meta info
392 | */
393 | export type MetaInfo =
394 | T extends TorrentType.V1
395 | ? MetaInfoV1
396 | : T extends TorrentType.V2
397 | ? MetaInfoV2
398 | : T extends TorrentType.HYBRID
399 | ? MetaInfoHybrid
400 | : never;
401 |
402 | /**
403 | * common piece lengths
404 | */
405 |
406 | export enum CommonPieceLength {
407 | "16KB" = 1 << 14,
408 | "32KB" = 1 << 15,
409 | "64KB" = 1 << 16,
410 | "128KB" = 1 << 17,
411 | "256KB" = 1 << 18,
412 | "512KB" = 1 << 19,
413 | "1MB" = 1 << 20,
414 | "2MB" = 1 << 21,
415 | "4MB" = 1 << 22,
416 | "8MB" = 1 << 23,
417 | "16MB" = 1 << 24,
418 | "32MB" = 1 << 25,
419 | }
420 |
421 | /**
422 | * default block length 1 << 14 = 16384
423 | */
424 | export const BLOCK_LENGTH = 1 << 14;
425 |
426 | /**
427 | * default meta version = 2
428 | */
429 | export const META_VERSION: MetaVersion = 2;
430 |
431 | /**
432 | * default v1 torrent options
433 | */
434 | const defaultTorrentOptionsV1: InternalTorrentOptions = {
435 | type: TorrentType.V1,
436 | addCreatedBy: true,
437 | addCreationDate: true,
438 | addPaddingFiles: false,
439 | blockLength: BLOCK_LENGTH,
440 | pieceLength: NaN,
441 | sortFiles: true,
442 | isPrivate: false,
443 | };
444 |
445 | /**
446 | * default v2 torrent options
447 | */
448 | const defaultTorrentOptionsV2: InternalTorrentOptions = {
449 | type: TorrentType.V2,
450 | addCreatedBy: true,
451 | addCreationDate: true,
452 | blockLength: BLOCK_LENGTH,
453 | pieceLength: NaN,
454 | metaVersion: META_VERSION,
455 | isPrivate: false,
456 | };
457 |
458 | /**
459 | * default hybrid torrent options
460 | */
461 | const defaultTorrentOptionsHybrid: InternalTorrentOptions =
462 | {
463 | type: TorrentType.HYBRID,
464 | addCreatedBy: true,
465 | addCreationDate: true,
466 | blockLength: BLOCK_LENGTH,
467 | pieceLength: NaN,
468 | metaVersion: META_VERSION,
469 | isPrivate: false,
470 | };
471 |
472 | export type OnProgress = (
473 | current: number,
474 | total: number
475 | ) => void | Promise;
476 |
477 | function getTotalPieces(iterableFiles: Iterable, pieceLength: number) {
478 | let totalPieces = 0;
479 | for (const file of iterableFiles) {
480 | totalPieces += Math.ceil(file.size / pieceLength);
481 | }
482 | return totalPieces;
483 | }
484 |
485 | async function createV1(
486 | fileDirLikes: FileDirLikes,
487 | opts: TorrentOptions,
488 | onProgress?: OnProgress
489 | ) {
490 | // assign options
491 | const iOpts: InternalTorrentOptions = {
492 | ...defaultTorrentOptionsV1,
493 | ...opts,
494 | };
495 | // build file tree
496 | const { fileTree, traverseTree, totalFileCount, totalFileSize } =
497 | await populateFileTree(fileDirLikes, {
498 | sort: iOpts.sortFiles,
499 | });
500 | // early exit
501 | if (totalFileCount === 0) {
502 | return;
503 | }
504 | // get all files
505 | const files: File[] = [];
506 | for (const [, file] of traverseTree(fileTree)) {
507 | files.push(file);
508 | }
509 | // auto calculate piece length
510 | if (isNaN(iOpts.pieceLength)) {
511 | // auto calculate piece length if not assigned
512 | iOpts.pieceLength = calculatePieceLength(totalFileSize, iOpts.blockLength);
513 | }
514 | // Piece length is almost always a power of two in v1 torrents
515 | // https://www.bittorrent.org/beps/bep_0003.html#:~:text=piece%20length%20is%20almost%20always%20a%20power%20of%20two
516 | if ((iOpts.pieceLength & (iOpts.pieceLength - 1)) !== 0) {
517 | console.warn(`piece length ${iOpts.pieceLength} is not a power of 2`);
518 | }
519 | // collapse announce list
520 | iOpts.announceList = collapseAnnounceList(iOpts.announceList);
521 | // auto assign announce if possible
522 | if (
523 | typeof iOpts.announce === "undefined" &&
524 | typeof iOpts.announceList !== "undefined"
525 | ) {
526 | iOpts.announce = iOpts.announceList[0]?.[0];
527 | }
528 | // sanitize url list
529 | iOpts.urlList = sanitizeUrlList(iOpts.urlList);
530 | // progress hook
531 | const [updateProgress, setProgressTotal] = useProgress(0, onProgress);
532 | // torrent name and common dir
533 | const { commonDir, name } = resolveCommonDirAndTorrentName(
534 | iOpts.name,
535 | fileTree
536 | );
537 | iOpts.name = name;
538 | // declare v1 piece readable stream
539 | let v1PiecesReadableStream: ReadableStream;
540 | // add padding files
541 | if (iOpts.addPaddingFiles) {
542 | // assign progress total (in piece unit)
543 | const totalPieces = getTotalPieces(files, iOpts.pieceLength);
544 | await setProgressTotal(totalPieces);
545 |
546 | const pieceLayerReadableStreamPromises: Promise<
547 | ReadableStream
548 | >[] = [];
549 | const lastFileIndex = totalFileCount - 1;
550 | for (let fileIndex = lastFileIndex; fileIndex >= 0; --fileIndex) {
551 | const file = files[fileIndex] as File;
552 | pieceLayerReadableStreamPromises.unshift(
553 | Promise.resolve(
554 | getPieceLayerReadableStream(file.stream(), {
555 | pieceLength: iOpts.pieceLength,
556 | padding: fileIndex !== lastFileIndex,
557 | updateProgress,
558 | })
559 | )
560 | );
561 | if (fileIndex === lastFileIndex) {
562 | continue;
563 | }
564 | const remainderSize = file.size % iOpts.pieceLength;
565 | if (remainderSize === 0) {
566 | continue;
567 | }
568 | const paddingSize = iOpts.pieceLength - remainderSize;
569 | const paddingFile = createPaddingFile(paddingSize, commonDir);
570 | files.splice(fileIndex + 1, 0, paddingFile);
571 | }
572 | v1PiecesReadableStream = concatenateStreams(
573 | pieceLayerReadableStreamPromises
574 | ).stream as ReadableStream;
575 | }
576 | // no padding files
577 | else {
578 | // assign progress total (in piece unit)
579 | const totalPieces = Math.ceil(totalFileSize / iOpts.pieceLength);
580 | await setProgressTotal(totalPieces);
581 |
582 | // concatenate all files into a single stream first
583 | const {
584 | stream: concatenatedFileReadableStream,
585 | }: { stream: ReadableStream } = concatenateStreams(
586 | files.map((file) => Promise.resolve(file.stream()))
587 | );
588 | // and then hash it
589 | v1PiecesReadableStream = getPieceLayerReadableStream(
590 | concatenatedFileReadableStream,
591 | {
592 | pieceLength: iOpts.pieceLength,
593 | padding: false,
594 | updateProgress,
595 | }
596 | );
597 | }
598 |
599 | const metaInfo: MetaInfo = {
600 | ...(typeof iOpts.announce === "undefined"
601 | ? {}
602 | : { announce: iOpts.announce }),
603 | ...(iOpts.announceList ? { "announce-list": iOpts.announceList } : {}),
604 | ...(typeof iOpts.comment === "undefined" ? {} : { comment: iOpts.comment }),
605 | ...(iOpts.addCreatedBy ? { "created by": CREATED_BY } : {}),
606 | ...(iOpts.addCreationDate
607 | ? { "creation date": getTimeStampSecondsNow() }
608 | : {}),
609 | info: {
610 | ...(totalFileCount > 1
611 | ? {
612 | files: files.map((file) => {
613 | // get file path segments
614 | const filePath = (file.webkitRelativePath || file.name).split(
615 | "/"
616 | );
617 | // remove common dir
618 | commonDir && filePath.shift();
619 | // emit
620 | return {
621 | ...(file.padding ? { attr: "p" as FileAttrs } : {}),
622 | length: file.size,
623 | path: filePath,
624 | };
625 | }),
626 | }
627 | : {
628 | length: totalFileSize,
629 | }),
630 | name: iOpts.name,
631 | "piece length": iOpts.pieceLength,
632 | pieces: await new Response(v1PiecesReadableStream).arrayBuffer(),
633 | ...(iOpts.isPrivate ? { private: true } : {}),
634 | ...(typeof iOpts.source === "undefined" ? {} : { source: iOpts.source }),
635 | },
636 | ...(iOpts.urlList ? { "url-list": iOpts.urlList } : {}),
637 | };
638 |
639 | return metaInfo;
640 | }
641 |
642 | async function createV2(
643 | fileDirLikes: FileDirLikes,
644 | opts: TorrentOptions,
645 | onProgress?: OnProgress
646 | ) {
647 | // assign options
648 | const iOpts: InternalTorrentOptions = {
649 | ...defaultTorrentOptionsV2,
650 | ...opts,
651 | };
652 | // build file tree
653 | const { fileTree, traverseTree, totalFileCount, totalFileSize } =
654 | await populateFileTree(fileDirLikes);
655 | // early exit
656 | if (totalFileCount === 0) {
657 | return;
658 | }
659 | // get all files
660 | const files: File[] = [];
661 | const fileNodeToFileEntries: [FileTreeFileNode, File][] = [];
662 | for (const [fileNode, file] of traverseTree(fileTree)) {
663 | files.push(file);
664 | fileNodeToFileEntries.push([fileNode, file]);
665 | }
666 | // auto calculate piece length
667 | if (isNaN(iOpts.pieceLength)) {
668 | // auto calculate piece length if not assigned
669 | iOpts.pieceLength = calculatePieceLength(totalFileSize, iOpts.blockLength);
670 | }
671 | // Piece length cannot be smaller than block length in v2 torrents
672 | // https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=of%20two%20and-,at%20least%2016KiB,-.
673 | if (iOpts.pieceLength < iOpts.blockLength) {
674 | throw new Error(
675 | `piece length ${iOpts.pieceLength} is smaller than block length ${iOpts.blockLength}`
676 | );
677 | }
678 | // Piece length must be a power of two in v2 torrents.
679 | // https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=It%20must%20be%20a%20power%20of%20two
680 | if ((iOpts.pieceLength & (iOpts.pieceLength - 1)) !== 0) {
681 | throw new Error(`piece length ${iOpts.pieceLength} is not a power of 2`);
682 | }
683 | // calculate blocks per piece
684 | const blocksPerPiece = iOpts.pieceLength / iOpts.blockLength;
685 | // collapse announce list
686 | iOpts.announceList = collapseAnnounceList(iOpts.announceList);
687 | // auto assign announce if possible
688 | if (
689 | typeof iOpts.announce === "undefined" &&
690 | typeof iOpts.announceList !== "undefined"
691 | ) {
692 | iOpts.announce = iOpts.announceList[0]?.[0];
693 | }
694 | // sanitize url list
695 | iOpts.urlList = sanitizeUrlList(iOpts.urlList);
696 | // assign progress total (in piece unit)
697 | const totalPieces = getTotalPieces(files, iOpts.pieceLength);
698 | // progress hook
699 | const [updateProgress] = useProgress(totalPieces, onProgress);
700 | // torrent name
701 | const { name } = resolveCommonDirAndTorrentName(iOpts.name, fileTree);
702 | iOpts.name = name;
703 | // init piece layers
704 | const pieceLayers: PieceLayers = new Map();
705 | // populate piece layers and file nodes
706 | await Promise.all(
707 | fileNodeToFileEntries.map(async ([fileNode, file]) => {
708 | await populatePieceLayersAndFileNodes(
709 | file.stream(),
710 | pieceLayers,
711 | fileNode,
712 | {
713 | blockLength: iOpts.blockLength,
714 | pieceLength: iOpts.pieceLength,
715 | blocksPerPiece,
716 | updateProgress,
717 | }
718 | );
719 | })
720 | );
721 |
722 | const metaInfo: MetaInfo = {
723 | ...(typeof iOpts.announce === "undefined"
724 | ? {}
725 | : { announce: iOpts.announce }),
726 | ...(iOpts.announceList ? { "announce-list": iOpts.announceList } : {}),
727 | ...(typeof iOpts.comment === "undefined" ? {} : { comment: iOpts.comment }),
728 | ...(iOpts.addCreatedBy ? { "created by": CREATED_BY } : {}),
729 | ...(iOpts.addCreationDate
730 | ? { "creation date": getTimeStampSecondsNow() }
731 | : {}),
732 | info: {
733 | ["file tree"]: fileTree,
734 | "meta version": iOpts.metaVersion,
735 | name: iOpts.name,
736 | "piece length": iOpts.pieceLength,
737 | // only add private field when it is private
738 | ...(iOpts.isPrivate ? { private: true } : {}),
739 | ...(typeof iOpts.source === "undefined" ? {} : { source: iOpts.source }),
740 | },
741 | // piece layers must not be abscent
742 | "piece layers": pieceLayers,
743 | ...(iOpts.urlList ? { "url-list": iOpts.urlList } : {}),
744 | };
745 |
746 | return metaInfo;
747 | }
748 |
749 | async function createHybrid(
750 | fileDirLikes: FileDirLikes,
751 | opts: TorrentOptions,
752 | onProgress?: OnProgress
753 | ) {
754 | // assign options
755 | const iOpts: InternalTorrentOptions = {
756 | ...defaultTorrentOptionsHybrid,
757 | ...opts,
758 | };
759 | // build file tree
760 | const { fileTree, traverseTree, totalFileCount, totalFileSize } =
761 | await populateFileTree(fileDirLikes);
762 | // early exit
763 | if (totalFileCount === 0) {
764 | return;
765 | }
766 | // get all files
767 | const files: File[] = [];
768 | const fileNodeToFileEntries: [FileTreeFileNode, File][] = [];
769 | for (const [fileNode, file] of traverseTree(fileTree)) {
770 | files.push(file);
771 | fileNodeToFileEntries.push([fileNode, file]);
772 | }
773 | // auto calculate piece length
774 | if (isNaN(iOpts.pieceLength)) {
775 | // auto calculate piece length if not assigned
776 | iOpts.pieceLength = calculatePieceLength(totalFileSize, iOpts.blockLength);
777 | }
778 | // Piece length cannot be smaller than block length in hybrid torrents
779 | // https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=of%20two%20and-,at%20least%2016KiB,-.
780 | if (iOpts.pieceLength < iOpts.blockLength) {
781 | throw new Error(
782 | `piece length ${iOpts.pieceLength} is smaller than block length ${iOpts.blockLength}`
783 | );
784 | }
785 | // Piece length must be a power of two in hybrid torrents.
786 | // https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=It%20must%20be%20a%20power%20of%20two
787 | if ((iOpts.pieceLength & (iOpts.pieceLength - 1)) !== 0) {
788 | throw new Error(`piece length ${iOpts.pieceLength} is not a power of 2`);
789 | }
790 | // calculate blocks per piece
791 | const blocksPerPiece = iOpts.pieceLength / iOpts.blockLength;
792 | // collapse announce list
793 | iOpts.announceList = collapseAnnounceList(iOpts.announceList);
794 | // auto assign announce if possible
795 | if (
796 | typeof iOpts.announce === "undefined" &&
797 | typeof iOpts.announceList !== "undefined"
798 | ) {
799 | iOpts.announce = iOpts.announceList[0]?.[0];
800 | }
801 | // sanitize url list
802 | iOpts.urlList = sanitizeUrlList(iOpts.urlList);
803 | // progress hook
804 | const [updateProgress] = useProgress(
805 | getTotalPieces(files, iOpts.pieceLength) * 2,
806 | onProgress
807 | );
808 | // torrent name
809 | const { commonDir, name } = resolveCommonDirAndTorrentName(
810 | iOpts.name,
811 | fileTree
812 | );
813 | iOpts.name = name;
814 | // init piece layers
815 | const pieceLayers: PieceLayers = new Map();
816 |
817 | const pieceLayerReadableStreamPromise: Promise>[] =
818 | [];
819 | let lastFileIndex = totalFileCount - 1;
820 | let fileIndex = -1;
821 | for (const [fileNode, file] of fileNodeToFileEntries) {
822 | ++fileIndex;
823 | // we need to tee one stream into two streams for v1 and v2
824 | const [v1FileStream, v2FileStream] = file.stream().tee();
825 | pieceLayerReadableStreamPromise.unshift(
826 | Promise.resolve(
827 | getPieceLayerReadableStream(v1FileStream, {
828 | pieceLength: iOpts.pieceLength,
829 | padding: fileIndex !== lastFileIndex,
830 | updateProgress,
831 | })
832 | )
833 | );
834 | // v2
835 | await populatePieceLayersAndFileNodes(v2FileStream, pieceLayers, fileNode, {
836 | blockLength: iOpts.blockLength,
837 | pieceLength: iOpts.pieceLength,
838 | blocksPerPiece,
839 | updateProgress,
840 | });
841 | if (fileIndex === lastFileIndex) {
842 | break;
843 | }
844 | const remainderSize = file.size % iOpts.pieceLength;
845 | if (remainderSize === 0) {
846 | continue;
847 | }
848 | const paddingSize = iOpts.pieceLength - remainderSize;
849 | const paddingFile = createPaddingFile(paddingSize, commonDir);
850 | files.splice(++fileIndex, 0, paddingFile);
851 | ++lastFileIndex;
852 | }
853 | const v1PiecesReadableStream = concatenateStreams(
854 | pieceLayerReadableStreamPromise
855 | ).stream as ReadableStream;
856 |
857 | const metaInfo: MetaInfo = {
858 | ...(typeof iOpts.announce === "undefined"
859 | ? {}
860 | : { announce: iOpts.announce }),
861 | ...(iOpts.announceList ? { "announce-list": iOpts.announceList } : {}),
862 | ...(typeof iOpts.comment === "undefined" ? {} : { comment: iOpts.comment }),
863 | ...(iOpts.addCreatedBy ? { "created by": CREATED_BY } : {}),
864 | ...(iOpts.addCreationDate
865 | ? { "creation date": getTimeStampSecondsNow() }
866 | : {}),
867 | info: {
868 | "file tree": fileTree,
869 | ...(totalFileCount > 1
870 | ? {
871 | files: files.map((file) => {
872 | // get file path segments
873 | const filePath = (file.webkitRelativePath || file.name).split(
874 | "/"
875 | );
876 | // remove common dir
877 | commonDir && filePath.shift();
878 | // emit
879 | return {
880 | ...(file.padding ? { attr: "p" as FileAttrs } : {}),
881 | length: file.size,
882 | path: filePath,
883 | };
884 | }),
885 | }
886 | : {
887 | length: totalFileSize,
888 | }),
889 | "meta version": iOpts.metaVersion,
890 | name: iOpts.name,
891 | "piece length": iOpts.pieceLength,
892 | // stream to array buffer
893 | pieces: await new Response(v1PiecesReadableStream).arrayBuffer(),
894 | // only add private field when it is private
895 | ...(iOpts.isPrivate ? { private: true } : {}),
896 | ...(typeof iOpts.source === "undefined" ? {} : { source: iOpts.source }),
897 | },
898 | // piece layers must not be abscent
899 | "piece layers": pieceLayers,
900 | ...(iOpts.urlList ? { "url-list": iOpts.urlList } : {}),
901 | };
902 |
903 | return metaInfo;
904 | }
905 |
906 | export async function create(
907 | fileDirLikes: FileDirLikes,
908 | opts: TorrentOptions = { type: TorrentType.V1 },
909 | onProgress?: OnProgress
910 | ) {
911 | switch (opts.type) {
912 | case TorrentType.V1:
913 | return await createV1(fileDirLikes, opts, onProgress);
914 | case TorrentType.V2:
915 | return await createV2(fileDirLikes, opts, onProgress);
916 | case TorrentType.HYBRID:
917 | return await createHybrid(fileDirLikes, opts, onProgress);
918 | }
919 | }
920 |
921 | /**
922 | * Populate piece layers and file node in a v2 torrent
923 | * @param fileStream
924 | * @param pieceLayers
925 | * @param fileNode
926 | * @param opts
927 | */
928 | async function populatePieceLayersAndFileNodes(
929 | fileStream: ReadableStream,
930 | pieceLayers: PieceLayers,
931 | fileNode: FileTreeFileNode,
932 | opts: {
933 | blockLength: number;
934 | pieceLength: number;
935 | blocksPerPiece: number;
936 | updateProgress?: UpdateProgress;
937 | }
938 | ) {
939 | // get pieces root and piece layer readable streams
940 | const { piecesRootReadableStream, pieceLayerReadableStream } =
941 | getPiecesRootAndPieceLayerReadableStreams(fileStream, opts);
942 | // get buffer promise from pieces root
943 | const piecesRootArrayBufferPromise = new Response(
944 | piecesRootReadableStream
945 | ).arrayBuffer();
946 | // only files that are larger than the piece size have piece layer entries
947 | // https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=For%20each%20file%20in%20the%20file%20tree%20that%20is%20larger%20than%20the%20piece%20size%20it%20contains%20one%20string%20value
948 | if (fileNode[""].length > opts.pieceLength) {
949 | const pieceLayerArrayBufferPromise = new Response(
950 | pieceLayerReadableStream
951 | ).arrayBuffer();
952 | pieceLayers.set(
953 | await piecesRootArrayBufferPromise,
954 | await pieceLayerArrayBufferPromise
955 | );
956 | }
957 | // only non-empty files have ["pieces root"] property
958 | // https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=For-,non%2Dempty,-files%20this%20is
959 | if (fileNode[""].length > 0) {
960 | fileNode[""]["pieces root"] = await piecesRootArrayBufferPromise;
961 | }
962 | }
963 |
964 | /**
965 | * Returns the piece layer of file(s) in a v1 type torrent
966 | * @param stream file stream
967 | * @param opts options
968 | * @returns piece layer
969 | */
970 | function getPieceLayerReadableStream(
971 | stream: ReadableStream,
972 | opts: {
973 | pieceLength: number;
974 | padding: boolean;
975 | updateProgress?: UpdateProgress;
976 | }
977 | ): ReadableStream {
978 | const pieceLayerReadableStream = stream
979 | .pipeThrough(
980 | new ChunkSplitter(opts.pieceLength, {
981 | padding: opts.padding,
982 | })
983 | )
984 | .pipeThrough(new PieceHasher(opts.updateProgress));
985 | return pieceLayerReadableStream;
986 | }
987 |
988 | /**
989 | * Returns the pieces root and piece layer of a file in a v2 type torrent
990 | * @param stream
991 | * @param opts
992 | * @returns
993 | */
994 | function getPiecesRootAndPieceLayerReadableStreams(
995 | stream: ReadableStream,
996 | opts: {
997 | blockLength: number;
998 | blocksPerPiece: number;
999 | updateProgress?: UpdateProgress;
1000 | }
1001 | ) {
1002 | const pieceLayerReadableStream: ReadableStream = stream
1003 | .pipeThrough(new ChunkSplitter(opts.blockLength))
1004 | .pipeThrough(new BlockHasher(opts.blocksPerPiece))
1005 | .pipeThrough(new MerkleRootCalculator(opts.updateProgress));
1006 |
1007 | const [pl1ReadableStream, pl2ReadableStream] = pieceLayerReadableStream.tee();
1008 |
1009 | return {
1010 | piecesRootReadableStream: pl2ReadableStream
1011 | .pipeThrough(new MerkleTreeBalancer(opts.blocksPerPiece))
1012 | .pipeThrough(new MerkleRootCalculator()),
1013 | pieceLayerReadableStream: pl1ReadableStream,
1014 | };
1015 | }
1016 |
1017 | function createPaddingFile(paddingSize: number, commonDir: string | undefined) {
1018 | const paddingFile = new File(
1019 | [new ArrayBuffer(paddingSize)],
1020 | `${paddingSize}`,
1021 | {
1022 | type: "application/octet-stream",
1023 | }
1024 | );
1025 | const paddingFilePath = `.pad/${paddingSize}`;
1026 | const nestedPath =
1027 | typeof commonDir === "undefined"
1028 | ? paddingFilePath
1029 | : `${commonDir}/${paddingFilePath}`;
1030 | Object.defineProperties(paddingFile, {
1031 | webkitRelativePath: {
1032 | configurable: true,
1033 | enumerable: true,
1034 | get: () => nestedPath,
1035 | },
1036 | padding: {
1037 | configurable: true,
1038 | enumerable: false,
1039 | get: () => true,
1040 | },
1041 | });
1042 | return paddingFile;
1043 | }
1044 |
1045 | export type SetProgressTotal = (
1046 | totalNumberOrFunction:
1047 | | number
1048 | | ((total: number, current?: number) => number | Promise)
1049 | ) => Promise;
1050 |
1051 | export type UpdateProgress = () => Promise;
1052 |
1053 | function useProgress(
1054 | initTotal: number,
1055 | onProgress?: OnProgress
1056 | ): [UpdateProgress, SetProgressTotal] {
1057 | // init progress parameters
1058 | const progressRef = {
1059 | // progress current (in piece unit)
1060 | current: 0,
1061 | // progress total (in piece unit)
1062 | total: initTotal,
1063 | };
1064 | // update progress
1065 | const updateProgress: UpdateProgress = async () => {
1066 | if (onProgress) {
1067 | await onProgress(++progressRef.current, progressRef.total);
1068 | }
1069 | };
1070 | // set progress total
1071 | const setProgressTotal: SetProgressTotal = async (totalNumberOrFunction) => {
1072 | if (typeof totalNumberOrFunction === "number") {
1073 | progressRef.total = totalNumberOrFunction;
1074 | } else {
1075 | progressRef.total = await totalNumberOrFunction(
1076 | progressRef.total,
1077 | progressRef.current
1078 | );
1079 | }
1080 | };
1081 | return [updateProgress, setProgressTotal];
1082 | }
1083 |
1084 | /**
1085 | * Collapse announce list
1086 | * @param announceList
1087 | * @returns collapsed announce list
1088 | */
1089 | function collapseAnnounceList(
1090 | announceList: string[][] | undefined
1091 | ): string[][] | undefined {
1092 | if (typeof announceList === "undefined") {
1093 | return undefined;
1094 | }
1095 | const collapsedAnnounceList: string[][] = [];
1096 | for (const tier of announceList) {
1097 | if (tier.length === 0) {
1098 | continue;
1099 | }
1100 | collapsedAnnounceList.push(tier);
1101 | }
1102 | if (collapsedAnnounceList.length === 0) {
1103 | return undefined;
1104 | }
1105 | return collapsedAnnounceList;
1106 | }
1107 |
1108 | /**
1109 | * Sanitize url list
1110 | * @param urlList
1111 | * @returns sanitized url list
1112 | */
1113 | function sanitizeUrlList(urlList: string[] | undefined): string[] | undefined {
1114 | if (typeof urlList === "undefined") {
1115 | return undefined;
1116 | }
1117 | if (urlList.length === 0) {
1118 | return undefined;
1119 | }
1120 | return urlList;
1121 | }
1122 |
1123 | /**
1124 | * Calculate piece length from file size
1125 | * @param fileSize
1126 | * @returns
1127 | */
1128 | function calculatePieceLength(fileSize: number, blockLength: number) {
1129 | return Math.max(blockLength, nextPowerOfTwo(fileSize >>> 10));
1130 | }
1131 |
--------------------------------------------------------------------------------
/src/decode.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BData,
3 | BList,
4 | BObject,
5 | BYTE_0,
6 | BYTE_MINUS,
7 | isDigitByte,
8 | } from "./utils/codec.js";
9 | import { Token, TokenType, Tokenizer } from "./transformers/tokenizer.js";
10 |
11 | export async function parse(
12 | tokenReadableStream: ReadableStream
13 | ): Promise {
14 | let parsedResult: BData | undefined;
15 | const contextStack: (BObject | BList)[] = [];
16 | const tokenStreamReader = tokenReadableStream.getReader();
17 | let dictionaryKey: string | undefined;
18 | // eslint-disable-next-line no-constant-condition
19 | while (true) {
20 | const { done, value: token } = await tokenStreamReader.read();
21 | if (done) {
22 | break;
23 | }
24 | const currentContext = contextStack.at(-1);
25 | // current context: global
26 | if (!currentContext) {
27 | if (typeof parsedResult !== "undefined") {
28 | throw new SyntaxError(`Unexpected token: ${JSON.stringify(token)}`);
29 | }
30 | switch (token.type) {
31 | case TokenType.Integer:
32 | parsedResult = parseInteger(token.value);
33 | break;
34 | case TokenType.ByteString:
35 | parsedResult = parseByteString(token.value);
36 | break;
37 | case TokenType.DictionaryStart: {
38 | const nextContext = Object.create(null) as BObject;
39 | contextStack.push(nextContext);
40 | parsedResult = nextContext;
41 | break;
42 | }
43 | case TokenType.ListStart: {
44 | const nextContext: BList = [];
45 | contextStack.push(nextContext);
46 | parsedResult = nextContext;
47 | break;
48 | }
49 | default:
50 | throw new SyntaxError(`Unexpected token: ${JSON.stringify(token)}`);
51 | }
52 | }
53 | // current context: list
54 | else if (Array.isArray(currentContext)) {
55 | switch (token.type) {
56 | case TokenType.Integer:
57 | currentContext.push(parseInteger(token.value));
58 | break;
59 | case TokenType.ByteString:
60 | currentContext.push(parseByteString(token.value));
61 | break;
62 | case TokenType.DictionaryStart: {
63 | const nextContext = Object.create(null) as BObject;
64 | currentContext.push(nextContext);
65 | contextStack.push(nextContext);
66 | break;
67 | }
68 | case TokenType.ListStart: {
69 | const nextContext: BList = [];
70 | currentContext.push(nextContext);
71 | contextStack.push(nextContext);
72 | break;
73 | }
74 | case TokenType.ListEnd:
75 | contextStack.pop();
76 | break;
77 | default:
78 | throw new SyntaxError(`Unexpected token: ${JSON.stringify(token)}`);
79 | }
80 | }
81 | // current context: dictionary
82 | else {
83 | // dictionary key
84 | if (typeof dictionaryKey === "undefined") {
85 | switch (token.type) {
86 | case TokenType.ByteString:
87 | dictionaryKey = parseByteString(token.value);
88 | break;
89 | case TokenType.DictionaryEnd:
90 | contextStack.pop();
91 | break;
92 | default:
93 | throw new SyntaxError(`Unexpected token: ${JSON.stringify(token)}`);
94 | }
95 | }
96 | // dictionary value
97 | else {
98 | switch (token.type) {
99 | case TokenType.Integer:
100 | currentContext[dictionaryKey] = parseInteger(token.value);
101 | break;
102 | case TokenType.ByteString:
103 | currentContext[dictionaryKey] = parseByteString(token.value);
104 | break;
105 | case TokenType.DictionaryStart: {
106 | const nextContext = Object.create(null) as BObject;
107 | currentContext[dictionaryKey] = nextContext;
108 | contextStack.push(nextContext);
109 | break;
110 | }
111 | case TokenType.ListStart: {
112 | const nextContext: BList = [];
113 | currentContext[dictionaryKey] = nextContext;
114 | contextStack.push(nextContext);
115 | break;
116 | }
117 | default:
118 | throw new SyntaxError(`Unexpected token: ${JSON.stringify(token)}`);
119 | }
120 | dictionaryKey = undefined;
121 | }
122 | }
123 | }
124 | if (contextStack.length) {
125 | throw new Error(`Unexpected end of token stream`);
126 | }
127 | return parsedResult;
128 | }
129 |
130 | export async function decode(
131 | torrentReadableStream: ReadableStream
132 | ): Promise {
133 | const tokenizer = new Tokenizer();
134 | const tokenReadableStream = torrentReadableStream.pipeThrough(tokenizer);
135 | return await parse(tokenReadableStream);
136 | }
137 |
138 | function parseInteger(tokenValue: Uint8Array) {
139 | let integer: number | bigint = 0;
140 | let isPositive = true;
141 | for (const [index, byte] of tokenValue.entries()) {
142 | // branch order is important!
143 | // handle negative sign
144 | if (index === 0 && byte === BYTE_MINUS) {
145 | isPositive = false;
146 | continue;
147 | }
148 | // negative zero is not allowed
149 | if (index === 1 && !isPositive && byte === BYTE_0) {
150 | throw new SyntaxError("Negative zero is not a valid integer");
151 | }
152 | // leading zeros are not allowed
153 | if (index === 1 && integer === 0) {
154 | throw new SyntaxError("Leading zeros are not allowed");
155 | }
156 | // not a digit or negative sign
157 | if (!isDigitByte(byte)) {
158 | throw new SyntaxError(`Unexpected byte: ${byte}`);
159 | }
160 | const byteNumber = byte - BYTE_0;
161 | // handle immediate overflow
162 | if (
163 | isPositive &&
164 | typeof integer === "number" &&
165 | integer > (Number.MAX_SAFE_INTEGER - byteNumber) / 10
166 | ) {
167 | integer = BigInt(integer);
168 | }
169 | // handle immediate underflow
170 | else if (
171 | !isPositive &&
172 | typeof integer === "number" &&
173 | integer < (Number.MIN_SAFE_INTEGER + byteNumber) / 10
174 | ) {
175 | integer = BigInt(integer);
176 | }
177 | // handle number
178 | if (typeof integer === "number") {
179 | if (isPositive) {
180 | integer = integer * 10 + byteNumber;
181 | } else {
182 | integer = integer * 10 - byteNumber;
183 | }
184 | }
185 | // handle big int
186 | else {
187 | if (isPositive) {
188 | integer = integer * 10n + BigInt(byteNumber);
189 | } else {
190 | integer = integer * 10n - BigInt(byteNumber);
191 | }
192 | }
193 | }
194 | return integer;
195 | }
196 |
197 | function parseByteString(tokenValue: Uint8Array) {
198 | const textDecoder = new TextDecoder();
199 | return textDecoder.decode(tokenValue);
200 | }
201 |
--------------------------------------------------------------------------------
/src/encode.ts:
--------------------------------------------------------------------------------
1 | import { TrieMap } from "@sec-ant/trie-map";
2 | import { BData, BUFF_L, BUFF_E, BUFF_D } from "./utils/codec.js";
3 | import { iterableSort } from "./utils/misc.js";
4 |
5 | /**
6 | * encoder hook
7 | */
8 | export type EncoderHook = (
9 | result: IteratorResult
10 | ) => void;
11 |
12 | /**
13 | * encoder hook path
14 | */
15 | export type EncoderHookPath = Iterable;
16 |
17 | /**
18 | * encoder hook system
19 | */
20 | export type EncoderHookSystem = TrieMap;
21 |
22 | /**
23 | * a global weakmap that keeps all consumed hook systems, for internal use
24 | */
25 | const consumedHookSystems = new WeakMap();
26 |
27 | /**
28 | * bencode readable stream underlying source
29 | */
30 | class EncoderUnderlyingSource implements UnderlyingSource {
31 | textEncoder = new TextEncoder();
32 | textDecoder = new TextDecoder();
33 | data: BData;
34 | path: (EncoderHookPath extends Iterable
35 | ? PathElement
36 | : never)[] = [];
37 | hookSystem?: EncoderHookSystem;
38 | attachedHooks = new WeakMap();
39 | constructor(data: BData, hookSystem?: EncoderHookSystem) {
40 | this.data = data;
41 | this.hookSystem = hookSystem;
42 | if (hookSystem) {
43 | consumedHookSystems.set(hookSystem, true);
44 | }
45 | }
46 | start(controller: ReadableStreamController) {
47 | this.encode(this.data, controller);
48 | // wind up
49 | if (this.hookSystem) {
50 | for (const hook of this.hookSystem.values()) {
51 | // only done once, in case of closing controller twice
52 | if (!this.attachedHooks.get(hook)) {
53 | hook({ value: undefined, done: true });
54 | }
55 | }
56 | }
57 | controller.close();
58 | }
59 | encode(data: BData, controller: ReadableStreamController) {
60 | // undefined or null: return
61 | if (typeof data === "undefined" || data === null) {
62 | return;
63 | }
64 | // boolean: integer
65 | if (typeof data === "boolean") {
66 | this.encode(data ? 1 : 0, controller);
67 | }
68 | // number: integer
69 | else if (typeof data === "number") {
70 | const integer = Math.round(data);
71 | controller.enqueue(this.textEncoder.encode(`i${integer}e`));
72 | if (integer !== data) {
73 | console.warn(
74 | `WARNING: Possible data corruption detected with value "${data}":`,
75 | `Bencoding only defines support for integers, value was converted to "${integer}"`
76 | );
77 | console.trace();
78 | }
79 | }
80 | // bigint: integer
81 | else if (typeof data === "bigint") {
82 | controller.enqueue(this.textEncoder.encode(`i${data}e`));
83 | }
84 | // string: byte string
85 | else if (typeof data === "string") {
86 | const byteData = this.textEncoder.encode(data);
87 | const byteLength = byteData.byteLength;
88 | controller.enqueue(this.textEncoder.encode(`${byteLength}:`));
89 | controller.enqueue(byteData);
90 | }
91 | // array buffer: byte string
92 | else if (data instanceof ArrayBuffer) {
93 | const byteData = new Uint8Array(data);
94 | const byteLength = byteData.byteLength;
95 | controller.enqueue(this.textEncoder.encode(`${byteLength}:`));
96 | controller.enqueue(byteData);
97 | }
98 | // array: list
99 | else if (Array.isArray(data)) {
100 | controller.enqueue(BUFF_L);
101 | let counter = 0;
102 | for (const member of data) {
103 | // push path
104 | this.path.push(counter);
105 | // if hook system is provided
106 | let hook: EncoderHook | undefined;
107 | if (this.hookSystem && (hook = this.hookSystem.get(this.path))) {
108 | const newController = attachHook(controller, hook);
109 | this.encode(member, newController);
110 | hook({ value: undefined, done: true });
111 | this.attachedHooks.set(hook, true);
112 | } else {
113 | this.encode(member, controller);
114 | }
115 | // pop path
116 | this.path.pop();
117 | ++counter;
118 | }
119 | controller.enqueue(BUFF_E);
120 | }
121 | // map: dictionary
122 | else if (data instanceof Map) {
123 | controller.enqueue(BUFF_D);
124 | // sort keys order
125 | const keys = iterableSort(data.keys(), (a, b) => {
126 | a = typeof a === "string" ? a : this.textDecoder.decode(a);
127 | b = typeof b === "string" ? b : this.textDecoder.decode(b);
128 | return a < b ? -1 : a > b ? 1 : 0;
129 | });
130 | // iterate keys
131 | for (const key of keys) {
132 | const value = data.get(key);
133 | // ignore nullables
134 | if (typeof value === "undefined" || data === null) {
135 | continue;
136 | }
137 | if (key instanceof ArrayBuffer) {
138 | this.encode(key, controller);
139 | this.encode(value, controller);
140 | } else {
141 | // push path
142 | this.path.push(key);
143 | // encode key
144 | this.encode(key, controller);
145 | // if hook system is provided
146 | let hook: EncoderHook | undefined;
147 | if (this.hookSystem && (hook = this.hookSystem.get(this.path))) {
148 | const newController = attachHook(controller, hook);
149 | this.encode(value, newController);
150 | hook({ value: undefined, done: true });
151 | this.attachedHooks.set(hook, true);
152 | } else {
153 | this.encode(value, controller);
154 | }
155 | // pop path
156 | this.path.pop();
157 | }
158 | }
159 | controller.enqueue(BUFF_E);
160 | }
161 | // object: dictionary
162 | else {
163 | controller.enqueue(BUFF_D);
164 | const keys = Object.keys(data).sort();
165 | for (const key of keys) {
166 | const value = data[key];
167 | if (typeof value === "undefined" || data === null) {
168 | continue;
169 | }
170 | // push path
171 | this.path.push(key);
172 | // encode key
173 | this.encode(key, controller);
174 | // if hook system is provided
175 | let hook: EncoderHook | undefined;
176 | if (this.hookSystem && (hook = this.hookSystem.get(this.path))) {
177 | const newController = attachHook(controller, hook);
178 | this.encode(value, newController);
179 | hook({ value: undefined, done: true });
180 | this.attachedHooks.set(hook, true);
181 | } else {
182 | this.encode(value, controller);
183 | }
184 | // pop path
185 | this.path.pop();
186 | }
187 | controller.enqueue(BUFF_E);
188 | }
189 | }
190 | }
191 |
192 | /**
193 | * Bencode
194 | * @param data
195 | * @param hookSystem
196 | * @returns readable stream of the bencoded data
197 | */
198 | export function encode(data: BData, hookSystem?: EncoderHookSystem) {
199 | return new ReadableStream(new EncoderUnderlyingSource(data, hookSystem));
200 | }
201 |
202 | /**
203 | * Attach hook to controller
204 | * @param controller
205 | * @param hook
206 | * @returns
207 | */
208 | function attachHook(
209 | controller: ReadableStreamController,
210 | hook: EncoderHook
211 | ) {
212 | const newController = new Proxy(controller, {
213 | get: function (target, prop, receiver) {
214 | switch (prop) {
215 | case "enqueue":
216 | return ((chunk: Uint8Array) => {
217 | target.enqueue(chunk);
218 | hook({
219 | value: chunk,
220 | done: false,
221 | });
222 | }).bind(target);
223 | default:
224 | return Reflect.get(target, prop, receiver) as unknown;
225 | }
226 | },
227 | });
228 | return newController;
229 | }
230 |
231 | /**
232 | * Register a hook and consume the result as an uint8 array readable stream
233 | * @param path
234 | * @param hookSystem
235 | * @returns an uint8 array readable stream
236 | */
237 | export function useUint8ArrayStreamHook(
238 | path: EncoderHookPath,
239 | hookSystem: EncoderHookSystem
240 | ): ReadableStream {
241 | const ref = {
242 | controller: null as ReadableStreamController | null,
243 | };
244 |
245 | const hook = ({ value, done }: IteratorResult) => {
246 | if (ref.controller === null) {
247 | return;
248 | }
249 | if (!done) {
250 | ref.controller.enqueue(value);
251 | } else {
252 | ref.controller.close();
253 | }
254 | };
255 |
256 | const readableStream = new ReadableStream({
257 | start(controller) {
258 | ref.controller = controller;
259 | },
260 | pull(controller) {
261 | if (!consumedHookSystems.get(hookSystem)) {
262 | // prevent endless awaiting when this readable stream is consumed as promise
263 | console.warn("You need to call encode() first and then consume hooks.");
264 | controller.close();
265 | ref.controller = null;
266 | }
267 | },
268 | });
269 |
270 | // register hook in hook system
271 | hookSystem.set(path, hook);
272 |
273 | return readableStream;
274 | }
275 |
276 | /**
277 | * Register a hook and consume the result as an array buffer promise
278 | * @param path
279 | * @param hookSystem
280 | * @returns an array buffer promise
281 | */
282 | export function useArrayBufferPromiseHook(
283 | path: EncoderHookPath,
284 | hookSystem: EncoderHookSystem
285 | ): Promise {
286 | const readableStream = useUint8ArrayStreamHook(path, hookSystem);
287 | const arrayBufferPromise = new Response(readableStream).arrayBuffer();
288 | return arrayBufferPromise;
289 | }
290 |
291 | /**
292 | * Register a hook and consume the result as a text promise
293 | * @param path
294 | * @param hookSystem
295 | * @returns a text promise
296 | */
297 | export function useTextPromiseHook(
298 | path: EncoderHookPath,
299 | hookSystem: EncoderHookSystem
300 | ): Promise {
301 | const readableStream = useUint8ArrayStreamHook(path, hookSystem);
302 | const textPromise = new Response(readableStream).text();
303 | return textPromise;
304 | }
305 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./encode.js";
2 | export * from "./decode.js";
3 | export * from "./create.js";
4 | export * from "./transformers/index.js";
5 | export * from "./utils/index.js";
6 |
--------------------------------------------------------------------------------
/src/transformers/blockHasher.ts:
--------------------------------------------------------------------------------
1 | import { nextPowerOfTwo } from "../utils/misc.js";
2 |
3 | /**
4 | * 32-byte-zeros
5 | */
6 | const ZEROS_32_BYTES = new Uint8Array(32);
7 |
8 | /**
9 | * Block hasher transformer class
10 | */
11 | class BlockHasherTransformer implements Transformer {
12 | blockCount = 0;
13 | merkleLeaves: Uint8Array[] = [];
14 | blocksPerPiece;
15 | constructor(blocksPerPiece: number) {
16 | this.blocksPerPiece = blocksPerPiece;
17 | }
18 | async transform(
19 | chunk: Uint8Array,
20 | controller: TransformStreamDefaultController
21 | ) {
22 | ++this.blockCount;
23 | let blockHash: Uint8Array;
24 | try {
25 | blockHash = new Uint8Array(await crypto.subtle.digest("SHA-256", chunk));
26 | } catch {
27 | const { default: jsSHA256 } = await import("jssha/sha256");
28 | const sha256Obj = new jsSHA256("SHA-256", "UINT8ARRAY");
29 | sha256Obj.update(chunk);
30 | blockHash = sha256Obj.getHash("UINT8ARRAY");
31 | }
32 | this.merkleLeaves.push(blockHash);
33 | if (this.merkleLeaves.length === this.blocksPerPiece) {
34 | controller.enqueue(this.merkleLeaves);
35 | this.merkleLeaves = [];
36 | }
37 | }
38 | flush(controller: TransformStreamDefaultController) {
39 | if (this.blockCount === 0) {
40 | return;
41 | }
42 | // http://bittorrent.org/beps/bep_0052.html#:~:text=The%20remaining%20leaf%20hashes%20beyond%20the%20end%20of%20the%20file%20required%20to%20construct%20upper%20layers%20of%20the%20merkle%20tree%20are%20set%20to%20zero
43 | let restBlockCount = 0;
44 | // If the file is smaller than one piece then the block hashes
45 | // should be padded to the next power of two instead of the next
46 | // piece boundary.
47 | if (this.blockCount < this.blocksPerPiece) {
48 | restBlockCount = nextPowerOfTwo(this.blockCount) - this.blockCount;
49 | } else {
50 | const residue = this.blockCount % this.blocksPerPiece;
51 | if (residue > 0) {
52 | restBlockCount = this.blocksPerPiece - residue;
53 | }
54 | }
55 | if (restBlockCount > 0) {
56 | for (let i = 0; i < restBlockCount; ++i) {
57 | this.merkleLeaves.push(ZEROS_32_BYTES);
58 | }
59 | }
60 | if (this.merkleLeaves.length > 0) {
61 | controller.enqueue(this.merkleLeaves);
62 | }
63 | }
64 | }
65 |
66 | export class BlockHasher extends TransformStream {
67 | constructor(blocksPerPiece: number) {
68 | const transformer = new BlockHasherTransformer(blocksPerPiece);
69 | super(transformer);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/transformers/chunkSplitter.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Chunk splitter transformer class
3 | */
4 | class ChunkSplitterTransformer implements Transformer {
5 | residuePointer = 0;
6 | chunkLength;
7 | residue;
8 | opts;
9 | constructor(chunkLength: number, opts = { padding: false }) {
10 | this.chunkLength = chunkLength;
11 | this.opts = opts;
12 | this.residue = new Uint8Array(this.chunkLength);
13 | }
14 | transform(
15 | chunk: Uint8Array,
16 | controller: TransformStreamDefaultController
17 | ) {
18 | while (this.residuePointer + chunk.byteLength >= this.chunkLength) {
19 | const chunkEnd = this.chunkLength - this.residuePointer;
20 | this.residue.set(chunk.subarray(0, chunkEnd), this.residuePointer);
21 | this.residuePointer = 0;
22 | controller.enqueue(new Uint8Array(this.residue));
23 | chunk = chunk.subarray(chunkEnd);
24 | }
25 | this.residue.set(chunk, this.residuePointer);
26 | this.residuePointer += chunk.byteLength;
27 | }
28 | flush(controller: TransformStreamDefaultController) {
29 | if (this.residuePointer <= 0) {
30 | return;
31 | }
32 | if (this.opts.padding) {
33 | this.residue.set(
34 | new Uint8Array(this.chunkLength - this.residuePointer),
35 | this.residuePointer
36 | );
37 | controller.enqueue(this.residue);
38 | } else {
39 | controller.enqueue(this.residue.subarray(0, this.residuePointer));
40 | }
41 | }
42 | }
43 |
44 | export class ChunkSplitter extends TransformStream {
45 | constructor(chunkLength: number, opts = { padding: false }) {
46 | const transformer = new ChunkSplitterTransformer(chunkLength, opts);
47 | super(transformer);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/transformers/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./blockHasher.js";
2 | export * from "./chunkSplitter.js";
3 | export * from "./merkleRootCalculator.js";
4 | export * from "./merkleTreeBalancer.js";
5 | export * from "./pieceHasher.js";
6 | export * from "./tokenizer.js";
7 |
--------------------------------------------------------------------------------
/src/transformers/merkleRootCalculator.ts:
--------------------------------------------------------------------------------
1 | import { UpdateProgress } from "../create.js";
2 | import { merkleRoot } from "../utils/misc.js";
3 | /**
4 | * Merkle root calculator transformer class
5 | */
6 | class MerkleRootCalculatorTransformer
7 | implements Transformer
8 | {
9 | updateProgress;
10 | constructor(updateProgress?: UpdateProgress) {
11 | this.updateProgress = updateProgress;
12 | }
13 | async transform(
14 | chunk: Uint8Array[],
15 | controller: TransformStreamDefaultController
16 | ) {
17 | controller.enqueue(await merkleRoot(chunk));
18 | if (this.updateProgress) {
19 | await this.updateProgress();
20 | }
21 | }
22 | }
23 |
24 | export class MerkleRootCalculator extends TransformStream<
25 | Uint8Array[],
26 | Uint8Array
27 | > {
28 | constructor(updateProgress?: UpdateProgress) {
29 | const transformer = new MerkleRootCalculatorTransformer(updateProgress);
30 | super(transformer);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/transformers/merkleTreeBalancer.ts:
--------------------------------------------------------------------------------
1 | import { nextPowerOfTwo, merkleRoot } from "../utils/misc.js";
2 |
3 | /**
4 | * Merkle tree balancer transformer class
5 | */
6 | class MerkleTreeBalancerTransformer
7 | implements Transformer
8 | {
9 | leafCount = 0;
10 | merkleLeaves: Uint8Array[] = [];
11 | blocksPerPiece;
12 | constructor(blocksPerPiece: number) {
13 | this.blocksPerPiece = blocksPerPiece;
14 | }
15 | transform(chunk: Uint8Array) {
16 | ++this.leafCount;
17 | this.merkleLeaves.push(chunk);
18 | }
19 | async flush(controller: TransformStreamDefaultController) {
20 | const restLeafCount = nextPowerOfTwo(this.leafCount) - this.leafCount;
21 | if (restLeafCount > 0) {
22 | const padLeaf = await this.padLeafPromise;
23 | for (let i = 0; i < restLeafCount; ++i) {
24 | this.merkleLeaves.push(padLeaf);
25 | }
26 | }
27 | controller.enqueue(this.merkleLeaves);
28 | }
29 | get padLeafPromise() {
30 | return merkleRoot(
31 | Array(this.blocksPerPiece).fill(new Uint8Array(32))
32 | );
33 | }
34 | }
35 |
36 | export class MerkleTreeBalancer extends TransformStream<
37 | Uint8Array,
38 | Uint8Array[]
39 | > {
40 | constructor(blocksPerPiece: number) {
41 | const transformer = new MerkleTreeBalancerTransformer(blocksPerPiece);
42 | super(transformer);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/transformers/pieceHasher.ts:
--------------------------------------------------------------------------------
1 | import { UpdateProgress } from "../create.js";
2 |
3 | /**
4 | * Piece hasher transformer class
5 | */
6 | class PieceHasherTransformer implements Transformer {
7 | updateProgress;
8 | constructor(updateProgress?: UpdateProgress) {
9 | this.updateProgress = updateProgress;
10 | }
11 | async transform(
12 | chunk: Uint8Array,
13 | controller: TransformStreamDefaultController
14 | ) {
15 | let pieceHash: Uint8Array;
16 | try {
17 | pieceHash = new Uint8Array(await crypto.subtle.digest("SHA-1", chunk));
18 | } catch {
19 | const { default: jsSHA1 } = await import("jssha/sha1");
20 | const sha1Obj = new jsSHA1("SHA-1", "UINT8ARRAY");
21 | sha1Obj.update(chunk);
22 | pieceHash = sha1Obj.getHash("UINT8ARRAY");
23 | }
24 | controller.enqueue(pieceHash);
25 | if (this.updateProgress) {
26 | await this.updateProgress();
27 | }
28 | }
29 | }
30 |
31 | export class PieceHasher extends TransformStream {
32 | constructor(updateProgress?: UpdateProgress) {
33 | const transformer = new PieceHasherTransformer(updateProgress);
34 | super(transformer);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/transformers/tokenizer.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BYTE_0,
3 | BYTE_COLON,
4 | BYTE_D,
5 | BYTE_E,
6 | BYTE_I,
7 | BYTE_L,
8 | isDigitByte,
9 | } from "../utils/codec.js";
10 | import { concatUint8Arrays } from "../utils/misc.js";
11 |
12 | export enum TokenType {
13 | Integer = "Integer",
14 | ByteString = "ByteString",
15 | ListStart = "ListStart",
16 | ListEnd = "ListEnd",
17 | DictionaryStart = "DictionaryStart",
18 | DictionaryEnd = "DictionaryEnd",
19 | }
20 |
21 | export interface IntegerToken {
22 | type: TokenType.Integer;
23 | value: Uint8Array;
24 | }
25 |
26 | export interface ByteStringToken {
27 | type: TokenType.ByteString;
28 | value: Uint8Array;
29 | }
30 |
31 | export interface ListStartToken {
32 | type: TokenType.ListStart;
33 | }
34 |
35 | export interface ListEndToken {
36 | type: TokenType.ListEnd;
37 | }
38 |
39 | export interface DictionaryStartToken {
40 | type: TokenType.DictionaryStart;
41 | }
42 |
43 | export interface DictionaryEndToken {
44 | type: TokenType.DictionaryEnd;
45 | }
46 |
47 | export type Token = T extends TokenType.Integer
48 | ? IntegerToken
49 | : T extends TokenType.ByteString
50 | ? ByteStringToken
51 | : T extends TokenType.ListStart
52 | ? ListStartToken
53 | : T extends TokenType.ListEnd
54 | ? ListEndToken
55 | : T extends TokenType.DictionaryStart
56 | ? DictionaryStartToken
57 | : T extends TokenType.DictionaryEnd
58 | ? DictionaryEndToken
59 | : never;
60 |
61 | class TokenizerTransformer implements Transformer {
62 | endStack: (TokenType.DictionaryEnd | TokenType.ListEnd)[] = [];
63 | token: Token | null = null;
64 | byteStringLength = -1;
65 | byteStringOffset = 0;
66 | transform(
67 | chunk: Uint8Array,
68 | controller: TransformStreamDefaultController
69 | ) {
70 | this.tokenize(chunk, controller);
71 | }
72 | flush() {
73 | if (
74 | this.endStack.length ||
75 | this.token ||
76 | this.byteStringLength !== -1 ||
77 | this.byteStringOffset
78 | ) {
79 | throw new SyntaxError("Unexpected end of torrent stream");
80 | }
81 | }
82 | tokenize(
83 | chunk: Uint8Array,
84 | controller: TransformStreamDefaultController
85 | ) {
86 | // empty chunk
87 | if (chunk.byteLength === 0) {
88 | return;
89 | }
90 | // check token type
91 | if (this.token === null && this.byteStringLength === -1) {
92 | const firstByte = chunk[0] as number;
93 | // integer
94 | if (firstByte === BYTE_I) {
95 | this.token = {
96 | type: TokenType.Integer,
97 | value: new Uint8Array(),
98 | };
99 | }
100 | // byte string length
101 | else if (isDigitByte(firstByte)) {
102 | // digit to number
103 | this.byteStringLength = firstByte - BYTE_0;
104 | }
105 | // list start
106 | else if (firstByte === BYTE_L) {
107 | // push list end byte to stack
108 | this.endStack.push(TokenType.ListEnd);
109 | // enqueue list start token
110 | controller.enqueue({
111 | type: TokenType.ListStart,
112 | });
113 | }
114 | // dictionary start
115 | else if (firstByte === BYTE_D) {
116 | // push dictionary end byte to stack
117 | this.endStack.push(TokenType.DictionaryEnd);
118 | // enqueue dictionary start token
119 | controller.enqueue({
120 | type: TokenType.DictionaryStart,
121 | });
122 | }
123 | // list or dictionary end
124 | else if (firstByte === BYTE_E) {
125 | // pop end byte from stack
126 | const tokenType = this.endStack.pop();
127 | // nothing is popped: unbalanced start and end byte
128 | if (!tokenType) {
129 | throw new SyntaxError("Unbalanced delimiter");
130 | }
131 | // enqueue list or dictionary end token
132 | controller.enqueue({
133 | type: tokenType,
134 | });
135 | }
136 | // unexpected first byte
137 | else {
138 | throw new SyntaxError(`Unexpected byte: ${firstByte}`);
139 | }
140 | // tokenize following bytes
141 | this.tokenize(chunk.subarray(1), controller);
142 | }
143 | // process token
144 | else if (this.token && this.byteStringLength === -1) {
145 | // integer
146 | if (this.token.type === TokenType.Integer) {
147 | const indexOfE = chunk.indexOf(BYTE_E);
148 | // integer end not found
149 | if (indexOfE === -1) {
150 | // gather all bytes into token value
151 | this.token.value = this.token.value
152 | ? concatUint8Arrays(this.token.value, chunk)
153 | : chunk;
154 | }
155 | // integer end found
156 | else {
157 | // gather bytes before end into token value
158 | this.token.value = this.token.value
159 | ? concatUint8Arrays(this.token.value, chunk.subarray(0, indexOfE))
160 | : chunk.subarray(0, indexOfE);
161 | // equeue integer token (token is copied)
162 | controller.enqueue(this.token);
163 | // reset token to null
164 | this.token = null;
165 | // tokenize following bytes
166 | this.tokenize(chunk.subarray(indexOfE + 1), controller);
167 | }
168 | }
169 | // byte string
170 | else if (this.token.type === TokenType.ByteString) {
171 | // total byte string length
172 | const byteStringLength = this.token.value.byteLength;
173 | // remaining byte string length
174 | const remainingByteStringLength =
175 | byteStringLength - this.byteStringOffset;
176 | // chunk length
177 | const chunkLength = chunk.byteLength;
178 | // chunk length smaller than remaining byte string length
179 | if (chunkLength < remainingByteStringLength) {
180 | // gather all bytes from the chunk
181 | this.token.value.set(chunk, this.byteStringOffset);
182 | // update offset
183 | this.byteStringOffset += chunkLength;
184 | }
185 | // chunk length equal to or greater than remaining byte string length
186 | else {
187 | // gather bytes before end into token value
188 | this.token.value.set(
189 | chunk.subarray(0, remainingByteStringLength),
190 | this.byteStringOffset
191 | );
192 | // reset byte string offset
193 | this.byteStringOffset = 0;
194 | // equeue byte string token (token is copied)
195 | controller.enqueue(this.token);
196 | // reset token to null
197 | this.token = null;
198 | // tokenize following bytes
199 | this.tokenize(chunk.subarray(remainingByteStringLength), controller);
200 | }
201 | }
202 | // program shouldn't reach here
203 | else {
204 | throw new Error("This is a bug");
205 | }
206 | }
207 | // process byte length
208 | else if (this.byteStringLength > -1 && this.token === null) {
209 | let indexOfColon = -1;
210 | for (const [index, byte] of chunk.entries()) {
211 | // byte string length digit
212 | if (isDigitByte(byte)) {
213 | // let's assume the byte string length is smaller than the max_safe_integer
214 | // or the torrent file would be huuuuuuuuuuuuuge!
215 | this.byteStringLength = 10 * this.byteStringLength - BYTE_0 + byte;
216 | }
217 | // byte string length end
218 | else if (byte === BYTE_COLON) {
219 | indexOfColon = index;
220 | break;
221 | }
222 | // unexpected byte
223 | else {
224 | throw new SyntaxError(`Unexpected byte: ${byte}`);
225 | }
226 | }
227 | // colon is found
228 | if (indexOfColon !== -1) {
229 | // initialize a byte string token with a fixed length uint8 array
230 | this.token = {
231 | type: TokenType.ByteString,
232 | value: new Uint8Array(this.byteStringLength),
233 | };
234 | // reset byte string length
235 | this.byteStringLength = -1;
236 | // tokenize following bytes
237 | this.tokenize(chunk.subarray(indexOfColon + 1), controller);
238 | }
239 | }
240 | // program shouldn't reach here
241 | else {
242 | throw new Error("This is a bug");
243 | }
244 | }
245 | }
246 |
247 | export class Tokenizer extends TransformStream {
248 | constructor() {
249 | const transformer = new TokenizerTransformer();
250 | super(transformer);
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/src/utils/codec.ts:
--------------------------------------------------------------------------------
1 | // bencode integer type
2 | export type BIntegerStrict = number | bigint;
3 | export type BIntegerLoose = BIntegerStrict | boolean;
4 | export type BInteger = Strict extends true
5 | ? BIntegerStrict
6 | : BIntegerLoose;
7 |
8 | // bencode byte string type
9 | export type BByteStringStrict = string;
10 | export type BByteStringLoose = BByteStringStrict | ArrayBuffer;
11 | export type BByteString = Strict extends true
12 | ? BByteStringStrict
13 | : BByteStringLoose;
14 |
15 | // bencode list type
16 | export type BListStrict = BData[];
17 | export type BListLoose = BData[];
18 | export type BList = Strict extends true
19 | ? BListStrict
20 | : BListLoose;
21 |
22 | // bencode dictionary type
23 | export interface BObjectStrict {
24 | [key: BByteString]: BData;
25 | }
26 | export interface BObjectLoose {
27 | [key: BByteString]: BData;
28 | }
29 | export type BObject = Strict extends true
30 | ? BObjectStrict
31 | : BObjectLoose;
32 | export type BMap = Map, BData>;
33 |
34 | export type BDictionaryStrict = BObject;
35 | export type BDictionaryLoose = BObject | BMap;
36 | export type BDictionary = Strict extends true
37 | ? BDictionaryStrict
38 | : BDictionaryLoose;
39 |
40 | // bencode data type
41 | export type BDataStrict =
42 | | BInteger
43 | | BByteString
44 | | BList
45 | | BDictionary;
46 | export type BDataLoose =
47 | | BInteger
48 | | BByteString
49 | | BList
50 | | BDictionary
51 | | undefined
52 | | null;
53 | export type BData = Strict extends true
54 | ? BDataStrict
55 | : BDataLoose;
56 |
57 | /**
58 | * bencode byte: l (stands for list start)
59 | */
60 | export const BYTE_L = 108;
61 |
62 | /**
63 | * bencode buff: l (stands for list start)
64 | */
65 | export const BUFF_L = new Uint8Array([BYTE_L]);
66 |
67 | /**
68 | * bencode byte: d (stands for dictionary start)
69 | */
70 | export const BYTE_D = 100;
71 |
72 | /**
73 | * bencode buff: d (stands for dictionary start)
74 | */
75 | export const BUFF_D = new Uint8Array([BYTE_D]);
76 |
77 | /**
78 | * bencode byte: e (stands for end)
79 | */
80 | export const BYTE_E = 101;
81 |
82 | /**
83 | * bencode buff: e (stands for end)
84 | */
85 | export const BUFF_E = new Uint8Array([BYTE_E]);
86 |
87 | /**
88 | * bencode byte: i (stands for integer start)
89 | */
90 | export const BYTE_I = 105;
91 |
92 | /**
93 | * bencode buff: i (stands for integer start)
94 | */
95 | export const BUFF_I = new Uint8Array([BYTE_I]);
96 |
97 | /**
98 | * bencode byte: : (stands for byte string length end)
99 | */
100 | export const BYTE_COLON = 58;
101 |
102 | /**
103 | * bencode buff: : (stands for byte string length end)
104 | */
105 | export const BUFF_COLON = new Uint8Array([BYTE_COLON]);
106 |
107 | /**
108 | * bencode byte: - (stands for -)
109 | */
110 | export const BYTE_MINUS = 45;
111 |
112 | /**
113 | * bencode buff: - (stands for -)
114 | */
115 | export const BUFF_MINUS = new Uint8Array([BYTE_MINUS]);
116 |
117 | /**
118 | * bencode byte: 0 (stands for 0)
119 | */
120 | export const BYTE_0 = 48;
121 |
122 | /**
123 | * bencode buff: 0 (stands for 0)
124 | */
125 | export const BUFF_0 = new Uint8Array([BYTE_0]);
126 |
127 | /**
128 | * is byte digit
129 | * @param byte
130 | * @returns
131 | */
132 | export function isDigitByte(byte: number) {
133 | if (byte >= BYTE_0 && byte <= BYTE_0 + 9) {
134 | return true;
135 | }
136 | return false;
137 | }
138 |
--------------------------------------------------------------------------------
/src/utils/fileDirLike.ts:
--------------------------------------------------------------------------------
1 | export type FileDirLike = FileSystemHandle | FileSystemEntry | File;
2 |
3 | export type FileDirLikes = Iterable | AsyncIterable;
4 |
5 | export function isFile(fileDirLike: FileDirLike): fileDirLike is File {
6 | return fileDirLike instanceof File;
7 | }
8 |
9 | export function isFileSystemDirectoryEntry(
10 | fileDirLike: FileDirLike
11 | ): fileDirLike is FileSystemDirectoryEntry {
12 | return (
13 | !isFileSystemDirectoryHandle(fileDirLike) &&
14 | !isFile(fileDirLike) &&
15 | fileDirLike.isDirectory
16 | );
17 | }
18 |
19 | export function isFileSystemFileEntry(
20 | fileDirLike: FileDirLike
21 | ): fileDirLike is FileSystemFileEntry {
22 | return (
23 | !isFileSystemDirectoryHandle(fileDirLike) &&
24 | !isFile(fileDirLike) &&
25 | fileDirLike.isFile
26 | );
27 | }
28 |
29 | export function isFileSystemDirectoryHandle(
30 | fileDirLike: FileDirLike
31 | ): fileDirLike is FileSystemDirectoryHandle {
32 | try {
33 | if (
34 | fileDirLike instanceof FileSystemHandle &&
35 | fileDirLike.kind === "directory"
36 | ) {
37 | return true;
38 | }
39 | } catch (e) {
40 | /* empty */
41 | }
42 | return false;
43 | }
44 |
45 | export function isFileSystemFileHandle(
46 | fileDirLike: FileDirLike
47 | ): fileDirLike is FileSystemFileHandle {
48 | try {
49 | if (
50 | fileDirLike instanceof FileSystemHandle &&
51 | fileDirLike.kind === "file"
52 | ) {
53 | return true;
54 | }
55 | } catch (e) {
56 | /* empty */
57 | }
58 | return false;
59 | }
60 |
61 | function readBatchOfEntries(
62 | dirReader: FileSystemDirectoryReader
63 | ): Promise {
64 | return new Promise((resolve, reject) => {
65 | dirReader.readEntries(resolve, reject);
66 | });
67 | }
68 |
69 | export async function* getEntriesOfDirEntry(
70 | dirEntry: FileSystemDirectoryEntry
71 | ) {
72 | const dirReader = dirEntry.createReader();
73 | let batchesOfEntries: FileSystemEntry[] = [];
74 | do {
75 | batchesOfEntries = await readBatchOfEntries(dirReader);
76 | for (const entry of batchesOfEntries) {
77 | yield entry;
78 | }
79 | } while (batchesOfEntries.length > 0);
80 | }
81 |
82 | export function getFileOfFileEntry(
83 | fileSystemFileEntry: FileSystemFileEntry
84 | ): Promise {
85 | return new Promise((resolve, reject) => {
86 | fileSystemFileEntry.file(resolve, reject);
87 | });
88 | }
89 |
--------------------------------------------------------------------------------
/src/utils/fileTree.ts:
--------------------------------------------------------------------------------
1 | import {
2 | FileDirLike,
3 | FileDirLikes,
4 | isFile,
5 | isFileSystemDirectoryEntry,
6 | isFileSystemDirectoryHandle,
7 | isFileSystemFileEntry,
8 | isFileSystemFileHandle,
9 | getFileOfFileEntry,
10 | getEntriesOfDirEntry,
11 | } from "./fileDirLike.js";
12 |
13 | import { BObject } from "./codec.js";
14 |
15 | import { getSortedIndex } from "./misc.js";
16 | import { v4 } from "uuid";
17 |
18 | /**
19 | * symlink file attribute
20 | */
21 | export type SymlinkAttr = "s";
22 |
23 | /**
24 | * executable file attribute
25 | */
26 | export type ExecutableAttr = "x";
27 |
28 | /**
29 | * hidden file attribute
30 | */
31 | export type HiddenAttr = "h";
32 |
33 | /**
34 | * padding file attribute
35 | */
36 | export type PaddingFileAttr = "p";
37 |
38 | /**
39 | * permutations template
40 | */
41 | export type Permutations<
42 | T extends string,
43 | U extends string = T
44 | > = T extends unknown ? T | `${T}${Permutations>}` : never;
45 |
46 | /**
47 | * file attributes
48 | */
49 | export type FileAttrs = Permutations<
50 | SymlinkAttr | ExecutableAttr | HiddenAttr | PaddingFileAttr
51 | >;
52 |
53 | /**
54 | * base file props
55 | */
56 | export interface FilePropsBase extends BObject {
57 | /**
58 | * Length of the file in bytes
59 | *
60 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=the%20following%20keys%3A-,length,-%2D%20The%20length%20of)
61 | * |
62 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#upgrade-path:~:text=pieces%20root32%3Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeeeeeee-,length,-Length%20of%20the)
63 | */
64 | length: number;
65 | /**
66 | * A variable-length string. When present,
67 | * the characters each represent a file attribute:
68 | * ```
69 | * l = symlink
70 | * x = executable
71 | * h = hidden
72 | * p = padding file
73 | * ```
74 | * [BEP 47](https://www.bittorrent.org/beps/bep_0047.html#:~:text=20%20bytes%3E%2C%0A%20%20%20%20...%0A%20%20%7D%2C%0A%20%20...%0A%7D-,attr,-A%20variable%2Dlength)
75 | */
76 | attr?: FileAttrs;
77 | }
78 |
79 | /**
80 | * v1 file props
81 | */
82 | export interface FilePropsV1 extends FilePropsBase {
83 | /**
84 | * A list of UTF-8 encoded strings
85 | * corresponding to **subdirectory** names
86 | *
87 | * [BEP 3](https://www.bittorrent.org/beps/bep_0003.html#:~:text=file%2C%20in%20bytes.-,path,-%2D%20A%20list%20of)
88 | */
89 | path: string[];
90 | }
91 |
92 | /**
93 | * v2 file props
94 | */
95 | export interface FilePropsV2 extends FilePropsBase {
96 | /**
97 | * For **non-empty** files this is the the root hash
98 | * of a merkle tree with a branching factor of 2,
99 | * constructed from 16KiB blocks of the file
100 | *
101 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=any%20sibling%20entries.-,pieces%20root,-For%20non%2Dempty)
102 | */
103 | ["pieces root"]?: ArrayBuffer;
104 | }
105 |
106 | /**
107 | * v1 file list
108 | */
109 | export type FileListV1 = FilePropsV1[];
110 |
111 | /**
112 | * v2 file tree file node
113 | */
114 | export interface FileTreeFileNode extends BObject {
115 | /**
116 | * Entries with zero-length keys describe the properties
117 | * of the composed path at that point
118 | *
119 | * [BEP 52](https://www.bittorrent.org/beps/bep_0052.html#:~:text=Entries%20with%20zero%2Dlength%20keys%20describe%20the%20properties%20of%20the%20composed%20path%20at%20that%20point)
120 | */
121 | "": FilePropsV2;
122 | }
123 |
124 | /**
125 | * v2 file entry
126 | */
127 | export type FileTreeFileEntry = [filename: string, value: FileTreeFileNode];
128 |
129 | /**
130 | * v2 file tree dir node
131 | */
132 | export type FileTreeDirNode = Map;
133 |
134 | /**
135 | * v2 dir entry
136 | */
137 | export type FileTreeDirEntry = [dirname: string, value: FileTreeEntries];
138 |
139 | /**
140 | * v2 packed dir entry
141 | */
142 | export type PackedFileTreeDirEntry = [dirname: string, value: FileTreeDirNode];
143 |
144 | /**
145 | * v2 file or dir entries
146 | */
147 | export type FileTreeEntries = (FileTreeFileEntry | FileTreeDirEntry)[];
148 |
149 | /**
150 | * v2 packed file or dir entries
151 | */
152 | export type PackedFileTreeEntries = (
153 | | FileTreeFileEntry
154 | | PackedFileTreeDirEntry
155 | )[];
156 |
157 | export function resolveCommonDirAndTorrentName(
158 | name: string | undefined,
159 | fileTree: FileTreeDirNode
160 | ): { name: string; commonDir: string | undefined } {
161 | // zero or multi file or folders
162 | if (fileTree.size > 1 || fileTree.size === 0) {
163 | return {
164 | // TODO: change UUID v4 to v5 ?
165 | name: name || v4(),
166 | commonDir: undefined,
167 | };
168 | }
169 | const [firstNodeName, firstNode] = fileTree.entries().next().value as
170 | | FileTreeFileEntry
171 | | PackedFileTreeDirEntry;
172 | // one file
173 | if (isFileTreeFileNode(firstNode)) {
174 | return {
175 | name: name || firstNodeName,
176 | commonDir: undefined,
177 | };
178 | }
179 | // one dir
180 | return {
181 | name: name || firstNodeName,
182 | commonDir: firstNodeName,
183 | };
184 | }
185 |
186 | export type PopulateOptions =
187 | | {
188 | sort?: false;
189 | compareFunction?: never;
190 | polyfillWebkitRelativePath?: boolean;
191 | }
192 | | {
193 | sort?: true;
194 | compareFunction?: (
195 | entry: FileTreeFileEntry | FileTreeDirEntry,
196 | name: string
197 | ) => number;
198 | polyfillWebkitRelativePath?: boolean;
199 | };
200 |
201 | export type TraverseTree = (
202 | node: FileTreeDirNode | FileTreeFileNode
203 | ) => Generator<[FileTreeFileNode, File], void, unknown>;
204 |
205 | /**
206 | * Parse file-dir-likes into a file tree
207 | * @param fileDirLikes
208 | * @param opts populate options
209 | * @returns file tree and a traverse function
210 | */
211 | export async function populateFileTree(
212 | fileDirLikes: FileDirLikes,
213 | {
214 | sort = true,
215 | compareFunction = compareEntryNames,
216 | polyfillWebkitRelativePath = true,
217 | }: PopulateOptions = {
218 | sort: true,
219 | compareFunction: compareEntryNames,
220 | polyfillWebkitRelativePath: true,
221 | }
222 | ): Promise<{
223 | fileTree: FileTreeDirNode;
224 | traverseTree: TraverseTree;
225 | totalFileSize: number;
226 | totalFileCount: number;
227 | }> {
228 | let totalFileSize = 0;
229 | let totalFileCount = 0;
230 | // weak map for internal use
231 | const fileEntryToHandleMap: WeakMap =
232 | new WeakMap();
233 | const dirEntryToHandleMap: WeakMap<
234 | FileTreeDirEntry,
235 | FileSystemDirectoryHandle
236 | > = new WeakMap();
237 | // weak map for traversal
238 | const fileNodeToFileMap: WeakMap = new WeakMap();
239 | // states
240 | const rootDirEntry: FileTreeDirEntry = ["file tree", []];
241 | const dirEntryStack: FileTreeDirEntry[] = [rootDirEntry];
242 | // flag: should deep pack
243 | let shouldDeepPack = false;
244 | // recursively populate
245 | for await (const fileDirLike of fileDirLikes) {
246 | await recursivelyPopulateFileTree(fileDirLike);
247 | }
248 | return {
249 | // NOTE: TYPE MUTATION!
250 | fileTree: packEntriesToDirNode(rootDirEntry[1], shouldDeepPack),
251 | traverseTree,
252 | totalFileSize,
253 | totalFileCount,
254 | };
255 | // recursive function
256 | async function recursivelyPopulateFileTree(fileDirLike: FileDirLike) {
257 | // get current entry
258 | const currentDirEntry = dirEntryStack.at(-1);
259 | if (!currentDirEntry) {
260 | throw new Error("This is a bug");
261 | }
262 | // flag: input is file
263 | const inputIsFile = isFile(fileDirLike);
264 | // flag: input is file system directory handle
265 | const inputIsFileSystemDirectoryHandle =
266 | isFileSystemDirectoryHandle(fileDirLike);
267 | // flag: input is file system directory entry
268 | const inputIsFileSystemDirectoryEntry =
269 | isFileSystemDirectoryEntry(fileDirLike);
270 | // flag: input is file system file handle
271 | const inputIsFileSystemFileHandle = isFileSystemFileHandle(fileDirLike);
272 | // flag: input is file system file entry
273 | const inputIsFileSystemFileEntry = isFileSystemFileEntry(fileDirLike);
274 | // directory handle or entry
275 | if (inputIsFileSystemDirectoryHandle || inputIsFileSystemDirectoryEntry) {
276 | const dirName = fileDirLike.name;
277 | const { index, result } = getSortedIndex(
278 | currentDirEntry[1],
279 | dirName,
280 | compareFunction
281 | );
282 | let subDirEntry = result;
283 | if (isFileTreeFileEntry(subDirEntry)) {
284 | throw new Error("A same name file already exists");
285 | }
286 | if (isFileTreeDirEntry(subDirEntry) && inputIsFileSystemDirectoryHandle) {
287 | const registeredHandle = dirEntryToHandleMap.get(subDirEntry);
288 | const isUnregistered =
289 | !registeredHandle ||
290 | !(await fileDirLike.isSameEntry(registeredHandle));
291 | if (isUnregistered) {
292 | throw new Error("File system handle not match");
293 | }
294 | }
295 | if (!subDirEntry) {
296 | subDirEntry = [dirName, []];
297 | sort
298 | ? currentDirEntry[1].splice(index, 0, subDirEntry)
299 | : currentDirEntry[1].push(subDirEntry);
300 | if (inputIsFileSystemDirectoryHandle) {
301 | dirEntryToHandleMap.set(subDirEntry, fileDirLike);
302 | }
303 | }
304 | dirEntryStack.push(subDirEntry);
305 | for await (const childFileSystemHandle of inputIsFileSystemDirectoryHandle
306 | ? fileDirLike.values()
307 | : getEntriesOfDirEntry(fileDirLike)) {
308 | await recursivelyPopulateFileTree(childFileSystemHandle);
309 | }
310 | // NOTE: TYPE MUTATION!
311 | (subDirEntry[1] as unknown as FileTreeDirNode) = new Map<
312 | string,
313 | FileTreeDirNode | FileTreeFileNode
314 | >(subDirEntry[1] as PackedFileTreeEntries);
315 | dirEntryStack.pop();
316 | }
317 | // file handle or entry
318 | else if (inputIsFileSystemFileHandle || inputIsFileSystemFileEntry) {
319 | const fileName = fileDirLike.name;
320 | const { index, result } = getSortedIndex(
321 | currentDirEntry[1],
322 | fileName,
323 | compareFunction
324 | );
325 | let fileEntry = result;
326 | if (isFileTreeFileEntry(fileEntry) && inputIsFileSystemFileHandle) {
327 | const registeredHandle = fileEntryToHandleMap.get(fileEntry);
328 | const isUnregistered =
329 | !registeredHandle ||
330 | !(await fileDirLike.isSameEntry(registeredHandle));
331 | if (isUnregistered) {
332 | throw new Error("A same name file already exists");
333 | }
334 | }
335 | if (isFileTreeDirEntry(fileEntry)) {
336 | throw new Error("A same name directory already exists");
337 | }
338 | if (fileEntry) {
339 | return;
340 | }
341 | const file = await (inputIsFileSystemFileHandle
342 | ? fileDirLike.getFile()
343 | : getFileOfFileEntry(fileDirLike));
344 | const fileSize = file.size;
345 | if (polyfillWebkitRelativePath) {
346 | const webkitRelativePath =
347 | dirEntryStack.reduce(
348 | (prevPath, dirEntry, index) =>
349 | index === 0 ? prevPath : prevPath + dirEntry[0] + "/",
350 | ""
351 | ) + file.name;
352 | Object.defineProperty(file, "webkitRelativePath", {
353 | configurable: true,
354 | enumerable: true,
355 | get: () => webkitRelativePath,
356 | });
357 | }
358 | const fileTreeFileNode: FileTreeFileNode = {
359 | "": {
360 | length: fileSize,
361 | },
362 | };
363 | fileEntry = [fileName, fileTreeFileNode];
364 | sort
365 | ? currentDirEntry[1].splice(index, 0, fileEntry)
366 | : currentDirEntry[1].push(fileEntry);
367 | if (inputIsFileSystemFileHandle) {
368 | fileEntryToHandleMap.set(fileEntry, fileDirLike);
369 | }
370 | fileNodeToFileMap.set(fileTreeFileNode, file);
371 | totalFileSize += fileSize;
372 | totalFileCount += 1;
373 | }
374 | // file
375 | else if (inputIsFile) {
376 | if (polyfillWebkitRelativePath && fileDirLike.webkitRelativePath === "") {
377 | Object.defineProperty(fileDirLike, "webkitRelativePath", {
378 | configurable: true,
379 | enumerable: true,
380 | get: () => fileDirLike.name,
381 | });
382 | }
383 | const pathSegments = (
384 | fileDirLike.webkitRelativePath || fileDirLike.name
385 | ).split("/");
386 | const localDirEntryStack = [rootDirEntry];
387 | for (const [segmentIndex, pathSegment] of pathSegments.entries()) {
388 | if (segmentIndex >= 1) {
389 | shouldDeepPack = true;
390 | }
391 | const localCurrentDirEntry = localDirEntryStack.at(-1);
392 | if (!localCurrentDirEntry) {
393 | throw new Error("This is a bug");
394 | }
395 | const { index, result } = getSortedIndex(
396 | localCurrentDirEntry[1],
397 | pathSegment,
398 | compareFunction
399 | );
400 | // dir
401 | if (segmentIndex < pathSegments.length - 1) {
402 | let subDirEntry = result;
403 | if (isFileTreeFileEntry(subDirEntry)) {
404 | throw new Error("A same name file already exists");
405 | }
406 | if (!subDirEntry) {
407 | subDirEntry = [pathSegment, []];
408 | sort
409 | ? localCurrentDirEntry[1].splice(index, 0, subDirEntry)
410 | : localCurrentDirEntry[1].push(subDirEntry);
411 | }
412 | localDirEntryStack.push(subDirEntry);
413 | }
414 | // file
415 | else {
416 | let fileEntry = result;
417 | if (isFileTreeDirEntry(fileEntry)) {
418 | throw new Error("A same name directory already exists");
419 | }
420 | if (fileEntry) {
421 | return;
422 | }
423 | const fileSize = fileDirLike.size;
424 | const fileTreeFileNode: FileTreeFileNode = {
425 | "": {
426 | length: fileSize,
427 | },
428 | };
429 | fileEntry = [pathSegment, fileTreeFileNode];
430 | sort
431 | ? localCurrentDirEntry[1].splice(index, 0, fileEntry)
432 | : localCurrentDirEntry[1].push(fileEntry);
433 | fileNodeToFileMap.set(fileTreeFileNode, fileDirLike);
434 | totalFileSize += fileSize;
435 | totalFileCount += 1;
436 | }
437 | }
438 | }
439 | // unrecognized type
440 | else {
441 | throw new Error("Unrecognized type of input");
442 | }
443 | }
444 | // traverse function
445 | function* traverseTree(
446 | node: FileTreeDirNode | FileTreeFileNode
447 | ): Generator<[FileTreeFileNode, File], void, unknown> {
448 | if (isFileTreeDirNode(node)) {
449 | for (const subNode of node.values()) {
450 | yield* traverseTree(subNode);
451 | }
452 | } else {
453 | yield [node, fileNodeToFileMap.get(node) as File];
454 | }
455 | }
456 | }
457 |
458 | export function packEntriesToDirNode(
459 | entries: PackedFileTreeEntries | FileTreeEntries,
460 | shouldDeepPack = false
461 | ): FileTreeDirNode {
462 | if (shouldDeepPack) {
463 | recursivelyPack(entries as FileTreeEntries);
464 | }
465 | return new Map(
466 | entries as PackedFileTreeEntries
467 | );
468 | }
469 |
470 | function recursivelyPack(fileTreeEntries: FileTreeEntries) {
471 | for (const fileTreeEntry of fileTreeEntries) {
472 | if (isFileTreeDirEntry(fileTreeEntry)) {
473 | const newEntries = fileTreeEntry[1];
474 | recursivelyPack(newEntries);
475 | (fileTreeEntry[1] as unknown as FileTreeDirNode) = new Map<
476 | string,
477 | FileTreeDirNode | FileTreeFileNode
478 | >(newEntries as PackedFileTreeEntries);
479 | }
480 | }
481 | }
482 |
483 | export function isFileTreeFileNode(
484 | node: FileTreeFileNode | FileTreeDirNode | undefined
485 | ): node is FileTreeFileNode {
486 | return node ? "" in node : false;
487 | }
488 |
489 | export function isFileTreeDirNode(
490 | node: FileTreeFileNode | FileTreeDirNode | undefined
491 | ): node is FileTreeDirNode {
492 | return node instanceof Map;
493 | }
494 |
495 | export function isFileTreeFileEntry(
496 | entry: FileTreeFileEntry | FileTreeDirEntry | undefined
497 | ): entry is FileTreeFileEntry {
498 | return entry ? "" in entry[1] : false;
499 | }
500 |
501 | export function isFileTreeDirEntry(
502 | entry: FileTreeFileEntry | FileTreeDirEntry | undefined
503 | ): entry is FileTreeDirEntry {
504 | return entry ? !("" in entry[1]) : false;
505 | }
506 |
507 | // compare function
508 | export function compareEntryNames(
509 | entry: FileTreeFileEntry | FileTreeDirEntry,
510 | name: string
511 | ) {
512 | return entry[0] < name ? -1 : entry[0] > name ? 1 : 0;
513 | }
514 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./codec.js";
2 | export * from "./fileDirLike.js";
3 | export * from "./fileTree.js";
4 | export * from "./misc.js";
5 |
--------------------------------------------------------------------------------
/src/utils/misc.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A helper function to determine the index
3 | * of a new value in an existed sorted array
4 | * @param array an existed sorted array
5 | * @param value the new value whose index is to be determined
6 | * @param compareFunction a compare function to determine the order, like the one in Array.prototype.sort()
7 | * @returns index of the value in the sorted array and indexed result if found
8 | */
9 | export function getSortedIndex(
10 | array: T[],
11 | value: V,
12 | compareFunction: (a: T, b: V) => number
13 | ) {
14 | let low = 0;
15 | let high = array.length;
16 | while (low < high) {
17 | const mid = (low + high) >>> 1;
18 | const result = array[mid] as T;
19 | const compareResult = compareFunction(result, value);
20 | if (compareResult === 0) {
21 | return {
22 | index: mid,
23 | result: array[mid],
24 | };
25 | }
26 | if (compareResult < 0) {
27 | low = mid + 1;
28 | } else {
29 | high = mid;
30 | }
31 | }
32 | return {
33 | index: low,
34 | result: undefined,
35 | };
36 | }
37 |
38 | /**
39 | * Sort iterable into a sorted array
40 | * @param iterable
41 | * @param compareFunction
42 | * @returns sorted array
43 | */
44 | export function iterableSort(
45 | iterable: IterableIterator,
46 | compareFunction: (a: T, b: T) => number
47 | ) {
48 | const sorted: T[] = [];
49 | for (const item of iterable) {
50 | const { index } = getSortedIndex(sorted, item, compareFunction);
51 | sorted.splice(index, 0, item);
52 | }
53 | return sorted;
54 | }
55 |
56 | /**
57 | * concat uint8 arrays
58 | * @param uint8Arrays
59 | * @returns
60 | */
61 | export function concatUint8Arrays(...uint8Arrays: Uint8Array[]) {
62 | const result = new Uint8Array(
63 | uint8Arrays.reduce(
64 | (length, uint8Array) => length + uint8Array.byteLength,
65 | 0
66 | )
67 | );
68 | uint8Arrays.reduce((offset, uint8Array) => {
69 | result.set(uint8Array, offset);
70 | return offset + uint8Array.byteLength;
71 | }, 0);
72 | return result;
73 | }
74 |
75 | /**
76 | * Calculate the next nearest power of 2
77 | * @param number number
78 | * @returns next nearest power of 2
79 | */
80 | export function nextPowerOfTwo(number: number) {
81 | return 1 << (32 - Math.clz32(number - 1));
82 | }
83 |
84 | /**
85 | * Get time stamp seconds now
86 | * @returns time stamp now in seconds
87 | */
88 | export function getTimeStampSecondsNow() {
89 | return Math.round(Date.now() / 1000);
90 | }
91 |
92 | /**
93 | * Calculate the root hash of the merkle tree
94 | * constructed by given leaves
95 | * @param leaves merkle leaves
96 | * @returns root hash
97 | */
98 | export async function merkleRoot(leaves: Uint8Array[]): Promise {
99 | if (leaves.length === 0) {
100 | throw new Error("Empty leaves");
101 | }
102 | const content = new Uint8Array(64);
103 | while (leaves.length > 1) {
104 | let hashFlag = false;
105 | const hashResult: Uint8Array[] = [];
106 | for (const leaf of leaves) {
107 | if (!hashFlag) {
108 | content.set(leaf);
109 | hashFlag = true;
110 | } else {
111 | content.set(leaf, 32);
112 | let hash: Uint8Array;
113 | try {
114 | hash = new Uint8Array(await crypto.subtle.digest("SHA-256", content));
115 | } catch {
116 | const { default: jsSHA256 } = await import("jssha/sha256");
117 | const sha256Obj = new jsSHA256("SHA-256", "UINT8ARRAY");
118 | sha256Obj.update(content);
119 | hash = sha256Obj.getHash("UINT8ARRAY");
120 | }
121 | hashResult.push(hash);
122 | hashFlag = false;
123 | }
124 | }
125 | leaves = hashResult;
126 | }
127 | return leaves[0] as Uint8Array;
128 | }
129 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "NodeNext",
6 | "types": ["wicg-file-system-access"],
7 | "allowJs": true,
8 | "outDir": "dist",
9 | "declaration": true,
10 | "esModuleInterop": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "strict": true,
13 | "noImplicitAny": true,
14 | "noUnusedLocals": true,
15 | "skipLibCheck": true,
16 | "noUncheckedIndexedAccess": true
17 | },
18 | "include": ["src/**/*"],
19 | "exclude": ["dist", "test"]
20 | }
21 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | import webpack from "webpack";
2 | import { fileURLToPath } from "node:url";
3 | import { resolve, dirname } from "node:path";
4 | import pkg from "./package.json" assert { type: "json" };
5 |
6 | const __filename = fileURLToPath(import.meta.url);
7 | const __dirname = dirname(__filename);
8 |
9 | /**
10 | *
11 | * @param {"esm"|"umd"} type
12 | * @returns { webpack.Configuration } config
13 | */
14 | function getConfig(type) {
15 | return {
16 | mode: "production",
17 | entry: {
18 | index: resolve(__dirname, "src/index.ts"),
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.ts$/,
24 | use: {
25 | loader: "ts-loader",
26 | ...(type === "umd" && {
27 | options: {
28 | compilerOptions: {
29 | declaration: false,
30 | },
31 | },
32 | }),
33 | },
34 | exclude: [resolve(__dirname, "node_modules")],
35 | },
36 | ],
37 | },
38 | resolve: {
39 | extensions: [".ts", ".js"],
40 | extensionAlias: {
41 | ".js": [".ts", ".js"],
42 | },
43 | },
44 | plugins: [
45 | new webpack.DefinePlugin({
46 | CREATED_BY: JSON.stringify(`${pkg.name} v${pkg.version}`),
47 | }),
48 | new webpack.optimize.LimitChunkCountPlugin({
49 | maxChunks: 1,
50 | }),
51 | ],
52 | output:
53 | type === "umd"
54 | ? {
55 | filename: "[name].umd.js",
56 | path: resolve(__dirname, "dist"),
57 | library: {
58 | name: pkg.name.replace(/-([a-z])/g, function (g) {
59 | return g[1].toUpperCase();
60 | }),
61 | type: "umd",
62 | },
63 | }
64 | : {
65 | filename: "[name].js",
66 | path: resolve(__dirname, "dist"),
67 | library: {
68 | type: "module",
69 | },
70 | chunkFormat: "module",
71 | },
72 | experiments: {
73 | outputModule: type === "umd" ? false : true,
74 | },
75 | target: "web",
76 | };
77 | }
78 |
79 | /**
80 | * Webpack Configuration
81 | * @type { webpack.Configuration }
82 | */
83 | export default [getConfig("esm"), getConfig("umd")];
84 |
--------------------------------------------------------------------------------