@anmiles/downloader 5.0.2 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nycrc.json +27 -0
- package/CHANGELOG.md +12 -0
- package/cspell.json +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/downloader.d.ts +5 -13
- package/dist/lib/downloader.d.ts.map +1 -1
- package/dist/lib/downloader.js +43 -10
- package/dist/lib/downloader.js.map +1 -1
- package/eslint.config.mts +15 -0
- package/jest.config.js +10 -7
- package/package.json +34 -26
- package/src/index.ts +1 -1
- package/src/lib/__tests__/downloader.test.ts +103 -64
- package/src/lib/downloader.ts +18 -18
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +0 -5
- package/tsconfig.test.json +7 -0
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -9
- package/.vscode/settings.json +0 -7
- package/coverage.config.js +0 -8
package/.nycrc.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"all": true,
|
|
3
|
+
"check-coverage" : true,
|
|
4
|
+
|
|
5
|
+
"statements" : 100,
|
|
6
|
+
"branches" : 100,
|
|
7
|
+
"lines" : 100,
|
|
8
|
+
"functions" : 100,
|
|
9
|
+
|
|
10
|
+
"report-dir" : "./coverage",
|
|
11
|
+
"temp-dir" : "./coverage",
|
|
12
|
+
|
|
13
|
+
"reporter" : [
|
|
14
|
+
"text",
|
|
15
|
+
"html"
|
|
16
|
+
],
|
|
17
|
+
"extension": [
|
|
18
|
+
".js",
|
|
19
|
+
".mjs",
|
|
20
|
+
".cjs",
|
|
21
|
+
".jsx",
|
|
22
|
+
".ts",
|
|
23
|
+
".cts",
|
|
24
|
+
".mts",
|
|
25
|
+
".tsx"
|
|
26
|
+
]
|
|
27
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [6.0.1](../../tags/v6.0.1) - 2025-07-24
|
|
9
|
+
### Changed
|
|
10
|
+
- Fix vulnerable dependencies
|
|
11
|
+
|
|
12
|
+
## [6.0.0](../../tags/v6.0.0) - 2025-05-18
|
|
13
|
+
__(BREAKING) Dropped support for NodeJS 18 (EOL). Minimum required version is now NodeJS 20.__
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Migrated to NodeJS 20.19
|
|
17
|
+
- Migrated to ESLint V9 flat configs
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
|
|
8
20
|
## [5.0.2](../../tags/v5.0.2) - 2024-03-20
|
|
9
21
|
### Changed
|
|
10
22
|
- Update dependencies
|
package/cspell.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2",
|
|
3
|
+
"enabled": true,
|
|
4
|
+
"language": "en",
|
|
5
|
+
"minWordLength": 4,
|
|
6
|
+
"enableGlobDot": true,
|
|
7
|
+
"useGitignore": true,
|
|
8
|
+
"ignorePaths": [
|
|
9
|
+
".git",
|
|
10
|
+
"package-lock.json"
|
|
11
|
+
],
|
|
12
|
+
"words": [
|
|
13
|
+
"anmiles",
|
|
14
|
+
"anatoliy",
|
|
15
|
+
"khtml",
|
|
16
|
+
"oblaukhov"
|
|
17
|
+
],
|
|
18
|
+
"flagWords": []
|
|
19
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './lib/downloader';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* istanbul ignore file */
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
15
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
16
|
+
};
|
|
3
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
|
|
5
|
-
var downloader_1 = require("./lib/downloader");
|
|
6
|
-
Object.defineProperty(exports, "download", { enumerable: true, get: function () { return downloader_1.download; } });
|
|
7
|
-
Object.defineProperty(exports, "downloadString", { enumerable: true, get: function () { return downloader_1.downloadString; } });
|
|
8
|
-
Object.defineProperty(exports, "downloadJSON", { enumerable: true, get: function () { return downloader_1.downloadJSON; } });
|
|
18
|
+
__exportStar(require("./lib/downloader"), exports);
|
|
9
19
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,0BAA0B
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,0BAA0B;;;;;;;;;;;;;;;;AAE1B,mDAAiC"}
|
package/dist/lib/downloader.d.ts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
declare function download(url: string
|
|
4
|
-
declare function download(url: string, file: string, options?: {
|
|
1
|
+
export type BufferEncoding = Parameters<Buffer['toString']>[0];
|
|
2
|
+
export declare function download(url: string): Promise<Buffer>;
|
|
3
|
+
export declare function download(url: string, file: string, options?: {
|
|
5
4
|
append?: boolean;
|
|
6
5
|
}): Promise<undefined>;
|
|
7
|
-
declare function downloadString(url: string, encoding?: BufferEncoding): Promise<string>;
|
|
8
|
-
declare function downloadJSON(url: string, encoding?: BufferEncoding): Promise<unknown>;
|
|
9
|
-
export { download, downloadString, downloadJSON };
|
|
10
|
-
declare const _default: {
|
|
11
|
-
download: typeof download;
|
|
12
|
-
downloadString: typeof downloadString;
|
|
13
|
-
downloadJSON: typeof downloadJSON;
|
|
14
|
-
};
|
|
15
|
-
export default _default;
|
|
6
|
+
export declare function downloadString(url: string, encoding?: BufferEncoding): Promise<string>;
|
|
7
|
+
export declare function downloadJSON(url: string, encoding?: BufferEncoding): Promise<unknown>;
|
|
16
8
|
//# sourceMappingURL=downloader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../../src/lib/downloader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../../src/lib/downloader.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AACvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAgDxG,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQpG;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAInG"}
|
package/dist/lib/downloader.js
CHANGED
|
@@ -1,14 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
39
|
+
exports.download = download;
|
|
40
|
+
exports.downloadString = downloadString;
|
|
41
|
+
exports.downloadJSON = downloadJSON;
|
|
7
42
|
const fs_1 = __importDefault(require("fs"));
|
|
8
43
|
const http_1 = __importDefault(require("http"));
|
|
9
44
|
const https_1 = __importDefault(require("https"));
|
|
10
45
|
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
|
11
|
-
const
|
|
46
|
+
const downloader = __importStar(require("./downloader"));
|
|
12
47
|
async function download(url, file, options) {
|
|
13
48
|
return new Promise((resolve, reject) => {
|
|
14
49
|
let protocol;
|
|
@@ -51,19 +86,17 @@ async function download(url, file, options) {
|
|
|
51
86
|
});
|
|
52
87
|
});
|
|
53
88
|
}
|
|
54
|
-
exports.download = download;
|
|
55
89
|
async function downloadString(url, encoding = 'utf8') {
|
|
56
90
|
if (!Buffer.isEncoding(encoding)) {
|
|
57
91
|
throw new Error(`Unknown encoding ${String(encoding)}`);
|
|
58
92
|
}
|
|
59
|
-
const
|
|
60
|
-
|
|
93
|
+
const bufferPromise = downloader.download(url);
|
|
94
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
95
|
+
return bufferPromise.then((buffer) => iconv_lite_1.default.decode(buffer, encoding));
|
|
61
96
|
}
|
|
62
|
-
exports.downloadString = downloadString;
|
|
63
97
|
async function downloadJSON(url, encoding = 'utf8') {
|
|
64
|
-
const
|
|
65
|
-
|
|
98
|
+
const jsonPromise = downloader.downloadString(url, encoding);
|
|
99
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
100
|
+
return jsonPromise.then((json) => JSON.parse(json));
|
|
66
101
|
}
|
|
67
|
-
exports.downloadJSON = downloadJSON;
|
|
68
|
-
exports.default = { download, downloadString, downloadJSON };
|
|
69
102
|
//# sourceMappingURL=downloader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloader.js","sourceRoot":"","sources":["../../src/lib/downloader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"downloader.js","sourceRoot":"","sources":["../../src/lib/downloader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { configs } from '@anmiles/eslint-config';
|
|
2
|
+
import type { Linter } from 'eslint';
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
...configs.base,
|
|
6
|
+
...configs.ts,
|
|
7
|
+
...configs.jest,
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
ignores: [
|
|
11
|
+
'coverage/*',
|
|
12
|
+
'dist/*',
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
] as Linter.Config[];
|
package/jest.config.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
preset
|
|
3
|
-
transform
|
|
4
|
-
'^.+\\.tsx?$'
|
|
2
|
+
preset : 'ts-jest',
|
|
3
|
+
transform: {
|
|
4
|
+
'^.+\\.tsx?$': [ 'ts-jest', {
|
|
5
|
+
tsconfig: './tsconfig.test.json',
|
|
6
|
+
} ],
|
|
5
7
|
},
|
|
6
8
|
|
|
7
|
-
clearMocks
|
|
9
|
+
clearMocks: true,
|
|
8
10
|
|
|
9
|
-
roots
|
|
10
|
-
testMatch
|
|
11
|
+
roots : [ '<rootDir>/src' ],
|
|
12
|
+
testMatch: [ '<rootDir>/src/**/__tests__/*.test.{ts,tsx}' ],
|
|
11
13
|
|
|
12
|
-
collectCoverageFrom
|
|
14
|
+
collectCoverageFrom: [
|
|
13
15
|
'<rootDir>/src/**/*.{ts,tsx}',
|
|
14
16
|
'!<rootDir>/src/**/__tests__/**',
|
|
17
|
+
'!<rootDir>/src/**/__mocks__/**',
|
|
15
18
|
],
|
|
16
19
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anmiles/downloader",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Wrapper for downloading data as string, buffer or complex types",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"download",
|
|
@@ -12,44 +12,52 @@
|
|
|
12
12
|
"repository": "github:anmiles/downloader",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
15
|
+
"node": ">=20.19.0"
|
|
16
16
|
},
|
|
17
17
|
"main": "dist/index.js",
|
|
18
18
|
"scripts": {
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"spellcheck": "cspell .",
|
|
20
|
+
"prebuild": "rimraf dist",
|
|
21
|
+
"build": "tsc -p ./tsconfig.build.json",
|
|
22
|
+
"lint": "eslint",
|
|
23
|
+
"lint:fix": "eslint --fix",
|
|
22
24
|
"test": "jest --verbose",
|
|
23
25
|
"test:coverage": "npm test -- --coverage",
|
|
24
26
|
"test:ci": "npm test -- --ci --coverage",
|
|
25
27
|
"test:watch": "npm test -- --watch",
|
|
26
28
|
"test:watch:coverage": "npm test -- --watch --coverage",
|
|
27
|
-
"test:report:coverage": "nyc report
|
|
29
|
+
"test:report:coverage": "nyc report"
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"iconv-lite": "^0.6.3"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
|
-
"@anmiles/eslint-config": "^
|
|
34
|
-
"@anmiles/tsconfig": "^
|
|
35
|
-
"@
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
38
|
-
"@
|
|
39
|
-
"@
|
|
40
|
-
"eslint": "^
|
|
41
|
-
"
|
|
35
|
+
"@anmiles/eslint-config": "^9.0.3",
|
|
36
|
+
"@anmiles/tsconfig": "^4.0.0",
|
|
37
|
+
"@eslint/compat": "^1.3.1",
|
|
38
|
+
"@eslint/css": "^0.10.0",
|
|
39
|
+
"@eslint/js": "^9.31.0",
|
|
40
|
+
"@eslint/json": "^0.13.1",
|
|
41
|
+
"@eslint/markdown": "^7.1.0",
|
|
42
|
+
"@stylistic/eslint-plugin": "^5.2.2",
|
|
43
|
+
"@types/jest": "^30.0.0",
|
|
44
|
+
"@types/node": "^24.1.0",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
46
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
47
|
+
"cspell": "^9.2.0",
|
|
48
|
+
"eslint": "^9.31.0",
|
|
49
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
42
50
|
"eslint-plugin-align-assignments": "^1.1.2",
|
|
43
|
-
"eslint-plugin-
|
|
44
|
-
"eslint-plugin-
|
|
45
|
-
"eslint-plugin-
|
|
46
|
-
"eslint-plugin-n": "^
|
|
47
|
-
"eslint-plugin-promise": "^
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"nyc": "^
|
|
51
|
-
"rimraf": "^
|
|
52
|
-
"ts-jest": "^29.
|
|
53
|
-
"typescript": "^5.
|
|
51
|
+
"eslint-plugin-i18next": "^6.1.3",
|
|
52
|
+
"eslint-plugin-import": "^2.32.0",
|
|
53
|
+
"eslint-plugin-jest": "^29.0.1",
|
|
54
|
+
"eslint-plugin-n": "^17.21.0",
|
|
55
|
+
"eslint-plugin-promise": "^7.2.1",
|
|
56
|
+
"jest": "^30.0.5",
|
|
57
|
+
"jiti": "^2.5.0",
|
|
58
|
+
"nyc": "^17.1.0",
|
|
59
|
+
"rimraf": "^6.0.1",
|
|
60
|
+
"ts-jest": "^29.4.0",
|
|
61
|
+
"typescript": "^5.8.3"
|
|
54
62
|
}
|
|
55
63
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
1
2
|
import fs from 'fs';
|
|
2
3
|
import http from 'http';
|
|
3
4
|
import https from 'https';
|
|
4
|
-
import emitter from 'event-emitter';
|
|
5
|
-
import iconv from 'iconv-lite';
|
|
6
|
-
|
|
7
|
-
import downloader from '../downloader';
|
|
8
5
|
|
|
9
|
-
|
|
6
|
+
import iconv from 'iconv-lite';
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
downloadString : jest.fn().mockImplementation(async (...args: Parameters<typeof original.downloadString>) => original.downloadString(...args)),
|
|
14
|
-
}));
|
|
8
|
+
import { download, downloadJSON, downloadString } from '../downloader';
|
|
9
|
+
import type { BufferEncoding } from '../downloader';
|
|
15
10
|
|
|
16
11
|
let request: http.ClientRequest;
|
|
17
12
|
let response: http.IncomingMessage;
|
|
18
13
|
|
|
14
|
+
let emitFakeResponse: ()=> void = () => {};
|
|
15
|
+
|
|
19
16
|
/* eslint-disable promise/prefer-await-to-callbacks -- similar signature to original http.get */
|
|
20
|
-
function get(_url: URL | string, _options: https.RequestOptions, callback?: ((res: http.IncomingMessage)
|
|
17
|
+
function get(_url: URL | string, _options: https.RequestOptions, callback?: ((res: http.IncomingMessage)=> void) | undefined): http.ClientRequest {
|
|
21
18
|
if (callback) {
|
|
22
19
|
callback(response);
|
|
23
20
|
}
|
|
@@ -27,8 +24,15 @@ function get(_url: URL | string, _options: https.RequestOptions, callback?: ((re
|
|
|
27
24
|
/* eslint-enable promise/prefer-await-to-callbacks */
|
|
28
25
|
|
|
29
26
|
beforeEach(() => {
|
|
30
|
-
request =
|
|
31
|
-
response =
|
|
27
|
+
request = new EventEmitter() as typeof request; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
|
28
|
+
response = new EventEmitter() as typeof response; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
|
29
|
+
|
|
30
|
+
emitFakeResponse = () => {
|
|
31
|
+
response.emit('data', new Uint8Array([ 10, 11, 12 ]));
|
|
32
|
+
response.emit('data', new Uint8Array([ 20, 21, 22 ]));
|
|
33
|
+
response.emit('data', new Uint8Array([ 30, 31, 32 ]));
|
|
34
|
+
response.emit('end');
|
|
35
|
+
};
|
|
32
36
|
|
|
33
37
|
response.pipe = jest.fn();
|
|
34
38
|
response.resume = jest.fn();
|
|
@@ -38,128 +42,163 @@ beforeEach(() => {
|
|
|
38
42
|
|
|
39
43
|
const httpGetSpy = jest.spyOn(http, 'get').mockImplementation(get);
|
|
40
44
|
const httpsGetSpy = jest.spyOn(https, 'get').mockImplementation(get);
|
|
41
|
-
let downloaded: Buffer;
|
|
42
45
|
|
|
43
46
|
describe('src/lib/downloader', () => {
|
|
44
47
|
describe('download', () => {
|
|
45
48
|
it('should throw if url protocol is not supported', async () => {
|
|
46
|
-
const promise
|
|
49
|
+
const promise = download('ftp://url');
|
|
50
|
+
emitFakeResponse();
|
|
51
|
+
|
|
47
52
|
await expect(promise).rejects.toEqual(new Error('Unknown protocol in url ftp://url, expected one of "http" or "https"'));
|
|
48
53
|
});
|
|
49
54
|
|
|
50
55
|
it('should call http.get if url protocol is http', async () => {
|
|
51
|
-
const promise =
|
|
52
|
-
|
|
56
|
+
const promise = download('http://url');
|
|
57
|
+
emitFakeResponse();
|
|
53
58
|
await promise;
|
|
59
|
+
|
|
54
60
|
expect(httpGetSpy.mock.calls[0]?.[0]).toEqual('http://url');
|
|
55
61
|
});
|
|
56
62
|
|
|
57
63
|
it('should call https.get if url protocol is https', async () => {
|
|
58
|
-
const promise =
|
|
59
|
-
|
|
64
|
+
const promise = download('https://url');
|
|
65
|
+
emitFakeResponse();
|
|
60
66
|
await promise;
|
|
67
|
+
|
|
61
68
|
expect(httpsGetSpy.mock.calls[0]?.[0]).toEqual('https://url');
|
|
62
69
|
});
|
|
63
70
|
|
|
64
71
|
it('should pass user-agent in options', async () => {
|
|
65
|
-
const promise =
|
|
66
|
-
|
|
72
|
+
const promise = download('http://url');
|
|
73
|
+
emitFakeResponse();
|
|
67
74
|
await promise;
|
|
75
|
+
|
|
68
76
|
expect(httpGetSpy.mock.calls[0]?.[1]).toEqual(expect.objectContaining({
|
|
69
|
-
headers
|
|
70
|
-
'User-Agent'
|
|
77
|
+
headers: {
|
|
78
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
|
|
71
79
|
},
|
|
72
80
|
}));
|
|
73
81
|
});
|
|
74
82
|
|
|
75
83
|
it('should reject and resume response if status code is not 200', async () => {
|
|
76
|
-
response.statusCode
|
|
77
|
-
|
|
84
|
+
response.statusCode = 404;
|
|
85
|
+
|
|
86
|
+
const promise = download('http://url');
|
|
87
|
+
|
|
78
88
|
await expect(promise).rejects.toEqual(new Error('Request to http://url returned with status code: 404'));
|
|
79
|
-
expect(response.resume).toHaveBeenCalled();
|
|
89
|
+
expect(response.resume).toHaveBeenCalled(); // eslint-disable-line @typescript-eslint/unbound-method
|
|
80
90
|
});
|
|
81
91
|
|
|
82
92
|
it('should reject if response errored', async () => {
|
|
83
|
-
const promise =
|
|
93
|
+
const promise = download('http://url');
|
|
84
94
|
request.emit('error', new Error('request error'));
|
|
95
|
+
|
|
85
96
|
await expect(promise).rejects.toEqual(new Error('Request to http://url failed with error: request error'));
|
|
86
97
|
});
|
|
87
98
|
|
|
88
99
|
it('should concat and resolve received data if no file specified', async () => {
|
|
89
|
-
const promise =
|
|
90
|
-
|
|
91
|
-
response.emit('data', new Uint8Array([ 20, 21, 22 ]));
|
|
92
|
-
response.emit('data', new Uint8Array([ 30, 31, 32 ]));
|
|
93
|
-
response.emit('end');
|
|
100
|
+
const promise = download('http://url');
|
|
101
|
+
emitFakeResponse();
|
|
94
102
|
const result = await promise;
|
|
103
|
+
|
|
95
104
|
expect(result).toEqual(Buffer.from([ 10, 11, 12, 20, 21, 22, 30, 31, 32 ]));
|
|
96
105
|
});
|
|
97
106
|
|
|
98
107
|
it('should pipe response stream to file if specified with write mode', async () => {
|
|
99
|
-
const stream = {} as fs.WriteStream;
|
|
108
|
+
const stream = {} as fs.WriteStream; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
|
100
109
|
const createWriteStreamSpy = jest.spyOn(fs, 'createWriteStream').mockReturnValue(stream);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
response.emit('data', new Uint8Array([ 30, 31, 32 ]));
|
|
105
|
-
response.emit('end');
|
|
110
|
+
|
|
111
|
+
const promise = download('http://url', 'file');
|
|
112
|
+
emitFakeResponse();
|
|
106
113
|
await promise;
|
|
107
|
-
|
|
108
|
-
expect(
|
|
114
|
+
|
|
115
|
+
expect(createWriteStreamSpy).toHaveBeenCalledWith('file', { flags: 'w' });
|
|
116
|
+
expect(response.pipe).toHaveBeenCalledWith(stream); // eslint-disable-line @typescript-eslint/unbound-method
|
|
109
117
|
createWriteStreamSpy.mockRestore();
|
|
110
118
|
});
|
|
111
119
|
|
|
112
120
|
it('should pipe response stream to file if specified with append mode', async () => {
|
|
113
|
-
const stream = {} as fs.WriteStream;
|
|
121
|
+
const stream = {} as fs.WriteStream; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
|
114
122
|
const createWriteStreamSpy = jest.spyOn(fs, 'createWriteStream').mockReturnValue(stream);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
response.emit('data', new Uint8Array([ 30, 31, 32 ]));
|
|
119
|
-
response.emit('end');
|
|
123
|
+
|
|
124
|
+
const promise = download('http://url', 'file', { append: true });
|
|
125
|
+
emitFakeResponse();
|
|
120
126
|
await promise;
|
|
121
|
-
|
|
122
|
-
expect(
|
|
127
|
+
|
|
128
|
+
expect(createWriteStreamSpy).toHaveBeenCalledWith('file', { flags: 'a' });
|
|
129
|
+
expect(response.pipe).toHaveBeenCalledWith(stream); // eslint-disable-line @typescript-eslint/unbound-method
|
|
123
130
|
createWriteStreamSpy.mockRestore();
|
|
124
131
|
});
|
|
125
132
|
});
|
|
126
133
|
|
|
127
134
|
describe('downloadString', () => {
|
|
128
135
|
it('should throw if unknown encoding specified', async () => {
|
|
129
|
-
const promise
|
|
136
|
+
const promise = downloadString('http://url', 'wrong_encoding' as BufferEncoding); // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
|
137
|
+
emitFakeResponse();
|
|
138
|
+
|
|
130
139
|
await expect(promise).rejects.toEqual(new Error('Unknown encoding wrong_encoding'));
|
|
131
140
|
});
|
|
132
141
|
|
|
133
142
|
it('should return string decoded with utf8', async () => {
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
143
|
+
const text = iconv.encode('test', 'utf8');
|
|
144
|
+
const bytes = Buffer.from(text);
|
|
145
|
+
|
|
146
|
+
const promise = downloadString('http://url');
|
|
147
|
+
response.emit('data', bytes);
|
|
148
|
+
response.emit('end');
|
|
149
|
+
const result = await promise;
|
|
150
|
+
|
|
137
151
|
expect(result).toEqual('test');
|
|
138
152
|
});
|
|
139
153
|
|
|
140
154
|
it('should return string decoded with specified encoding', async () => {
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
155
|
+
const text = iconv.encode('test', 'base64');
|
|
156
|
+
const bytes = Buffer.from(text);
|
|
157
|
+
|
|
158
|
+
const promise = downloadString('http://url', 'base64');
|
|
159
|
+
response.emit('data', bytes);
|
|
160
|
+
response.emit('end');
|
|
161
|
+
const result = await promise;
|
|
162
|
+
|
|
144
163
|
expect(result).toEqual('test');
|
|
145
164
|
});
|
|
165
|
+
|
|
166
|
+
it('should return string decoded with different encoding', async () => {
|
|
167
|
+
const text = iconv.encode('test', 'utf8');
|
|
168
|
+
const bytes = Buffer.from(text);
|
|
169
|
+
|
|
170
|
+
const promise = downloadString('http://url', 'base64');
|
|
171
|
+
response.emit('data', bytes);
|
|
172
|
+
response.emit('end');
|
|
173
|
+
const result = await promise;
|
|
174
|
+
|
|
175
|
+
expect(result).toEqual('dGVzdA==');
|
|
176
|
+
});
|
|
146
177
|
});
|
|
147
178
|
|
|
148
179
|
describe('downloadJSON', () => {
|
|
149
180
|
it('should JSON.parse downloaded string decoded with utf8', async () => {
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
const text = iconv.encode('{"key1": "value", "key2": 5}', 'utf8');
|
|
182
|
+
const bytes = Buffer.from(text);
|
|
183
|
+
|
|
184
|
+
const promise = downloadJSON('http://url');
|
|
185
|
+
response.emit('data', bytes);
|
|
186
|
+
response.emit('end');
|
|
187
|
+
const result = await promise;
|
|
188
|
+
|
|
189
|
+
expect(result).toEqual({ key1: 'value', key2: 5 });
|
|
155
190
|
});
|
|
156
191
|
|
|
157
192
|
it('should JSON.parse downloaded string decoded with specified encoding', async () => {
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
193
|
+
const text = iconv.encode('{"key1": "value", "key2": 5}', 'ucs2');
|
|
194
|
+
const bytes = Buffer.from(text);
|
|
195
|
+
|
|
196
|
+
const promise = downloadJSON('http://url', 'ucs2');
|
|
197
|
+
response.emit('data', bytes);
|
|
198
|
+
response.emit('end');
|
|
199
|
+
const result = await promise;
|
|
200
|
+
|
|
201
|
+
expect(result).toEqual({ key1: 'value', key2: 5 });
|
|
163
202
|
});
|
|
164
203
|
});
|
|
165
204
|
});
|
package/src/lib/downloader.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import http from 'http';
|
|
3
3
|
import https from 'https';
|
|
4
|
+
|
|
4
5
|
import iconv from 'iconv-lite';
|
|
5
6
|
|
|
6
|
-
import downloader from './downloader';
|
|
7
|
+
import * as downloader from './downloader';
|
|
7
8
|
|
|
8
|
-
type BufferEncoding = Parameters<Buffer['toString']>[0];
|
|
9
|
+
export type BufferEncoding = Parameters<Buffer['toString']>[0];
|
|
9
10
|
|
|
10
|
-
function download(url: string): Promise<Buffer>;
|
|
11
|
-
function download(url: string, file: string, options?: { append
|
|
12
|
-
async function download(url: string, file?: string, options?: { append
|
|
11
|
+
export function download(url: string): Promise<Buffer>;
|
|
12
|
+
export function download(url: string, file: string, options?: { append?: boolean }): Promise<undefined>;
|
|
13
|
+
export async function download(url: string, file?: string, options?: { append?: boolean }): Promise<Buffer | undefined> {
|
|
13
14
|
return new Promise<Buffer | undefined>((resolve, reject) => {
|
|
14
|
-
let protocol
|
|
15
|
+
let protocol: typeof http | typeof https;
|
|
15
16
|
|
|
16
17
|
if (url.startsWith('https://')) {
|
|
17
18
|
protocol = https;
|
|
@@ -22,8 +23,8 @@ async function download(url: string, file?: string, options?: { append? : boolea
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const reqOptions = {
|
|
25
|
-
headers
|
|
26
|
-
'User-Agent'
|
|
26
|
+
headers: {
|
|
27
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
|
|
27
28
|
},
|
|
28
29
|
};
|
|
29
30
|
|
|
@@ -44,7 +45,7 @@ async function download(url: string, file?: string, options?: { append? : boolea
|
|
|
44
45
|
resolve(Buffer.concat(chunks));
|
|
45
46
|
});
|
|
46
47
|
} else {
|
|
47
|
-
res.pipe(fs.createWriteStream(file, { flags
|
|
48
|
+
res.pipe(fs.createWriteStream(file, { flags: options?.append ? 'a' : 'w' }));
|
|
48
49
|
|
|
49
50
|
res.on('end', function() {
|
|
50
51
|
resolve(undefined);
|
|
@@ -56,19 +57,18 @@ async function download(url: string, file?: string, options?: { append? : boolea
|
|
|
56
57
|
});
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
async function downloadString(url: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
|
|
60
|
+
export async function downloadString(url: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
|
|
60
61
|
if (!Buffer.isEncoding(encoding)) {
|
|
61
62
|
throw new Error(`Unknown encoding ${String(encoding)}`);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
const
|
|
65
|
-
|
|
65
|
+
const bufferPromise = downloader.download(url);
|
|
66
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
67
|
+
return bufferPromise.then((buffer) => iconv.decode(buffer, encoding));
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
async function downloadJSON(url: string, encoding: BufferEncoding = 'utf8'): Promise<unknown> {
|
|
69
|
-
const
|
|
70
|
-
|
|
70
|
+
export async function downloadJSON(url: string, encoding: BufferEncoding = 'utf8'): Promise<unknown> {
|
|
71
|
+
const jsonPromise = downloader.downloadString(url, encoding);
|
|
72
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
73
|
+
return jsonPromise.then((json) => JSON.parse(json) as unknown);
|
|
71
74
|
}
|
|
72
|
-
|
|
73
|
-
export { download, downloadString, downloadJSON };
|
|
74
|
-
export default { download, downloadString, downloadJSON };
|
package/tsconfig.build.json
CHANGED
package/tsconfig.json
CHANGED
package/.eslintignore
DELETED
package/.eslintrc.js
DELETED
package/.vscode/settings.json
DELETED