@testingbot/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/dist/auth.d.ts +16 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +47 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +329 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +20 -0
- package/dist/models/credentials.d.ts +9 -0
- package/dist/models/credentials.d.ts.map +1 -0
- package/dist/models/credentials.js +20 -0
- package/dist/models/espresso_options.d.ts +116 -0
- package/dist/models/espresso_options.d.ts.map +1 -0
- package/dist/models/espresso_options.js +194 -0
- package/dist/models/maestro_options.d.ts +101 -0
- package/dist/models/maestro_options.d.ts.map +1 -0
- package/dist/models/maestro_options.js +176 -0
- package/dist/models/testingbot_error.d.ts +3 -0
- package/dist/models/testingbot_error.d.ts.map +1 -0
- package/dist/models/testingbot_error.js +5 -0
- package/dist/models/xcuitest_options.d.ts +88 -0
- package/dist/models/xcuitest_options.d.ts.map +1 -0
- package/dist/models/xcuitest_options.js +146 -0
- package/dist/providers/espresso.d.ts +67 -0
- package/dist/providers/espresso.d.ts.map +1 -0
- package/dist/providers/espresso.js +527 -0
- package/dist/providers/login.d.ts +18 -0
- package/dist/providers/login.d.ts.map +1 -0
- package/dist/providers/login.js +284 -0
- package/dist/providers/maestro.d.ts +92 -0
- package/dist/providers/maestro.d.ts.map +1 -0
- package/dist/providers/maestro.js +1010 -0
- package/dist/providers/xcuitest.d.ts +67 -0
- package/dist/providers/xcuitest.d.ts.map +1 -0
- package/dist/providers/xcuitest.js +529 -0
- package/dist/upload.d.ts +21 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +94 -0
- package/dist/utils/file-type-detector.d.ts +15 -0
- package/dist/utils/file-type-detector.d.ts.map +1 -0
- package/dist/utils/file-type-detector.js +38 -0
- package/dist/utils/platform.d.ts +26 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +58 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +48 -0
- package/package.json +78 -0
package/dist/upload.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
10
|
+
const testingbot_error_1 = __importDefault(require("./models/testingbot_error"));
|
|
11
|
+
const utils_1 = __importDefault(require("./utils"));
|
|
12
|
+
class Upload {
|
|
13
|
+
lastProgressPercent = 0;
|
|
14
|
+
async upload(options) {
|
|
15
|
+
const { filePath, url, credentials, contentType, showProgress = false, } = options;
|
|
16
|
+
await this.validateFile(filePath);
|
|
17
|
+
const fileName = node_path_1.default.basename(filePath);
|
|
18
|
+
const fileStats = await node_fs_1.default.promises.stat(filePath);
|
|
19
|
+
const fileStream = node_fs_1.default.createReadStream(filePath);
|
|
20
|
+
const formData = new form_data_1.default();
|
|
21
|
+
formData.append('file', fileStream);
|
|
22
|
+
try {
|
|
23
|
+
const response = await axios_1.default.post(url, formData, {
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': contentType,
|
|
26
|
+
'Content-Disposition': `attachment; filename=${fileName}`,
|
|
27
|
+
'User-Agent': utils_1.default.getUserAgent(),
|
|
28
|
+
},
|
|
29
|
+
auth: {
|
|
30
|
+
username: credentials.userName,
|
|
31
|
+
password: credentials.accessKey,
|
|
32
|
+
},
|
|
33
|
+
maxContentLength: Infinity,
|
|
34
|
+
maxBodyLength: Infinity,
|
|
35
|
+
onUploadProgress: showProgress
|
|
36
|
+
? (progressEvent) => {
|
|
37
|
+
this.handleProgress(progressEvent, fileStats.size, fileName);
|
|
38
|
+
}
|
|
39
|
+
: undefined,
|
|
40
|
+
});
|
|
41
|
+
const result = response.data;
|
|
42
|
+
if (result.id) {
|
|
43
|
+
if (showProgress) {
|
|
44
|
+
this.clearProgressLine();
|
|
45
|
+
}
|
|
46
|
+
return { id: result.id };
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
throw new testingbot_error_1.default(`Upload failed: ${result.error || 'Unknown error'}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof testingbot_error_1.default) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
57
|
+
const message = error.response?.data?.error || error.message;
|
|
58
|
+
throw new testingbot_error_1.default(`Upload failed: ${message}`);
|
|
59
|
+
}
|
|
60
|
+
throw new testingbot_error_1.default(`Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async validateFile(filePath) {
|
|
64
|
+
try {
|
|
65
|
+
await node_fs_1.default.promises.access(filePath, node_fs_1.default.constants.R_OK);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new testingbot_error_1.default(`File not found or not readable: ${filePath}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
handleProgress(progressEvent, totalSize, fileName) {
|
|
72
|
+
const loaded = progressEvent.loaded;
|
|
73
|
+
const total = progressEvent.total || totalSize;
|
|
74
|
+
const percent = Math.round((loaded / total) * 100);
|
|
75
|
+
if (percent !== this.lastProgressPercent) {
|
|
76
|
+
this.lastProgressPercent = percent;
|
|
77
|
+
this.displayProgress(fileName, percent, loaded, total);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
displayProgress(fileName, percent, loaded, total) {
|
|
81
|
+
const barWidth = 30;
|
|
82
|
+
const filledWidth = Math.round((percent / 100) * barWidth);
|
|
83
|
+
const emptyWidth = barWidth - filledWidth;
|
|
84
|
+
const bar = 'â–ˆ'.repeat(filledWidth) + 'â–‘'.repeat(emptyWidth);
|
|
85
|
+
const loadedMB = (loaded / (1024 * 1024)).toFixed(2);
|
|
86
|
+
const totalMB = (total / (1024 * 1024)).toFixed(2);
|
|
87
|
+
process.stdout.write(`\r ${fileName}: [${bar}] ${percent}% (${loadedMB}/${totalMB} MB)`);
|
|
88
|
+
}
|
|
89
|
+
clearProgressLine() {
|
|
90
|
+
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
91
|
+
this.lastProgressPercent = 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.default = Upload;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FileTypeResult {
|
|
2
|
+
ext: string;
|
|
3
|
+
mime: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Detect file type from file content using magic bytes.
|
|
7
|
+
* Returns undefined if the file type cannot be determined.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectFileType(filePath: string): Promise<FileTypeResult | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* Detect platform (Android or iOS) from app file.
|
|
12
|
+
* Uses magic bytes for content detection, with extension fallback for zip-based formats.
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectPlatformFromFile(filePath: string): Promise<'Android' | 'iOS' | undefined>;
|
|
15
|
+
//# sourceMappingURL=file-type-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-type-detector.d.ts","sourceRoot":"","sources":["../../src/utils/file-type-detector.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CASrC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC,CAmBxC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectFileType = detectFileType;
|
|
4
|
+
exports.detectPlatformFromFile = detectPlatformFromFile;
|
|
5
|
+
/**
|
|
6
|
+
* Detect file type from file content using magic bytes.
|
|
7
|
+
* Returns undefined if the file type cannot be determined.
|
|
8
|
+
*/
|
|
9
|
+
async function detectFileType(filePath) {
|
|
10
|
+
try {
|
|
11
|
+
// Dynamic import for ESM-only file-type package
|
|
12
|
+
const { fileTypeFromFile } = await import('file-type');
|
|
13
|
+
const result = await fileTypeFromFile(filePath);
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Detect platform (Android or iOS) from app file.
|
|
22
|
+
* Uses magic bytes for content detection, with extension fallback for zip-based formats.
|
|
23
|
+
*/
|
|
24
|
+
async function detectPlatformFromFile(filePath) {
|
|
25
|
+
const fileType = await detectFileType(filePath);
|
|
26
|
+
if (fileType) {
|
|
27
|
+
// APK files are detected as 'application/zip' with ext 'apk'
|
|
28
|
+
// or as 'application/vnd.android.package-archive'
|
|
29
|
+
if (fileType.ext === 'apk' ||
|
|
30
|
+
fileType.mime === 'application/vnd.android.package-archive') {
|
|
31
|
+
return 'Android';
|
|
32
|
+
}
|
|
33
|
+
if (fileType.ext === 'zip' || fileType.mime === 'application/zip') {
|
|
34
|
+
return 'iOS';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform utilities for terminal operations and signal handling
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Clear the current line in the terminal.
|
|
6
|
+
* Uses ANSI escape codes on Unix/macOS, and space overwrite on Windows.
|
|
7
|
+
*/
|
|
8
|
+
export declare function clearLine(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Setup signal handlers for graceful shutdown.
|
|
11
|
+
* SIGINT (Ctrl+C) works on all platforms.
|
|
12
|
+
* SIGTERM only works on Unix/macOS.
|
|
13
|
+
*/
|
|
14
|
+
export declare function setupSignalHandlers(handler: () => void): void;
|
|
15
|
+
/**
|
|
16
|
+
* Remove signal handlers.
|
|
17
|
+
*/
|
|
18
|
+
export declare function removeSignalHandlers(handler: () => void): void;
|
|
19
|
+
declare const _default: {
|
|
20
|
+
isWindows: boolean;
|
|
21
|
+
clearLine: typeof clearLine;
|
|
22
|
+
setupSignalHandlers: typeof setupSignalHandlers;
|
|
23
|
+
removeSignalHandlers: typeof removeSignalHandlers;
|
|
24
|
+
};
|
|
25
|
+
export default _default;
|
|
26
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAehC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAO7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAM9D;;;;;;;AAED,wBAKE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform utilities for terminal operations and signal handling
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.clearLine = clearLine;
|
|
7
|
+
exports.setupSignalHandlers = setupSignalHandlers;
|
|
8
|
+
exports.removeSignalHandlers = removeSignalHandlers;
|
|
9
|
+
const isWindows = process.platform === 'win32';
|
|
10
|
+
/**
|
|
11
|
+
* Clear the current line in the terminal.
|
|
12
|
+
* Uses ANSI escape codes on Unix/macOS, and space overwrite on Windows.
|
|
13
|
+
*/
|
|
14
|
+
function clearLine() {
|
|
15
|
+
if (isWindows) {
|
|
16
|
+
// Windows fallback: overwrite with spaces and return to start
|
|
17
|
+
// Use readline if available for better Windows support
|
|
18
|
+
if (process.stdout.clearLine && process.stdout.cursorTo) {
|
|
19
|
+
process.stdout.clearLine(0);
|
|
20
|
+
process.stdout.cursorTo(0);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
// Fallback: write spaces to clear typical line width
|
|
24
|
+
process.stdout.write('\r' + ' '.repeat(120) + '\r');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Unix/macOS: ANSI escape sequence
|
|
29
|
+
process.stdout.write('\r\x1b[K');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Setup signal handlers for graceful shutdown.
|
|
34
|
+
* SIGINT (Ctrl+C) works on all platforms.
|
|
35
|
+
* SIGTERM only works on Unix/macOS.
|
|
36
|
+
*/
|
|
37
|
+
function setupSignalHandlers(handler) {
|
|
38
|
+
process.on('SIGINT', handler);
|
|
39
|
+
// SIGTERM is not supported on Windows
|
|
40
|
+
if (!isWindows) {
|
|
41
|
+
process.on('SIGTERM', handler);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Remove signal handlers.
|
|
46
|
+
*/
|
|
47
|
+
function removeSignalHandlers(handler) {
|
|
48
|
+
process.removeListener('SIGINT', handler);
|
|
49
|
+
if (!isWindows) {
|
|
50
|
+
process.removeListener('SIGTERM', handler);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.default = {
|
|
54
|
+
isWindows,
|
|
55
|
+
clearLine,
|
|
56
|
+
setupSignalHandlers,
|
|
57
|
+
removeSignalHandlers,
|
|
58
|
+
};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
getUserAgent(): string;
|
|
3
|
+
getCurrentVersion(): string;
|
|
4
|
+
/**
|
|
5
|
+
* Compare two semver version strings
|
|
6
|
+
* Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
7
|
+
*/
|
|
8
|
+
compareVersions(v1: string, v2: string): number;
|
|
9
|
+
/**
|
|
10
|
+
* Check if a newer version is available and display update notice
|
|
11
|
+
*/
|
|
12
|
+
checkForUpdate(latestVersion: string | undefined): void;
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
15
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";oBAOkB,MAAM;yBAID,MAAM;IAI3B;;;OAGG;wBACiB,MAAM,MAAM,MAAM,GAAG,MAAM;IAa/C;;OAEG;kCAC2B,MAAM,GAAG,SAAS,GAAG,IAAI;;AA7BzD,wBAiDE"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const package_json_1 = __importDefault(require("../package.json"));
|
|
7
|
+
const logger_1 = __importDefault(require("./logger"));
|
|
8
|
+
const colors_1 = __importDefault(require("colors"));
|
|
9
|
+
let versionCheckDisplayed = false;
|
|
10
|
+
exports.default = {
|
|
11
|
+
getUserAgent() {
|
|
12
|
+
return `TestingBot-CTL-${package_json_1.default.version}`;
|
|
13
|
+
},
|
|
14
|
+
getCurrentVersion() {
|
|
15
|
+
return package_json_1.default.version;
|
|
16
|
+
},
|
|
17
|
+
/**
|
|
18
|
+
* Compare two semver version strings
|
|
19
|
+
* Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
20
|
+
*/
|
|
21
|
+
compareVersions(v1, v2) {
|
|
22
|
+
const parts1 = v1.split('.').map(Number);
|
|
23
|
+
const parts2 = v2.split('.').map(Number);
|
|
24
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
25
|
+
const p1 = parts1[i] || 0;
|
|
26
|
+
const p2 = parts2[i] || 0;
|
|
27
|
+
if (p1 < p2)
|
|
28
|
+
return -1;
|
|
29
|
+
if (p1 > p2)
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Check if a newer version is available and display update notice
|
|
36
|
+
*/
|
|
37
|
+
checkForUpdate(latestVersion) {
|
|
38
|
+
if (!latestVersion || versionCheckDisplayed) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const currentVersion = this.getCurrentVersion();
|
|
42
|
+
if (this.compareVersions(currentVersion, latestVersion) < 0) {
|
|
43
|
+
versionCheckDisplayed = true;
|
|
44
|
+
logger_1.default.warn(colors_1.default.yellow(`\n📦 A new version of testingbotctl is available: ${colors_1.default.green(latestVersion)} (current: ${currentVersion})`));
|
|
45
|
+
logger_1.default.warn(colors_1.default.yellow(` Run ${colors_1.default.cyan('npm update -g testingbotctl')} to update.\n`));
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@testingbot/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to run Espresso, XCUITest, and Maestro tests on TestingBot's cloud infrastructure",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"testingbot": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "prettier --check '**/*.{js,ts}' && eslint src/",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"clean": "rm -rf dist",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"format": "prettier --write '**/*.{js,ts}'",
|
|
22
|
+
"test": "jest"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"testingbot",
|
|
26
|
+
"mobile-testing",
|
|
27
|
+
"espresso",
|
|
28
|
+
"xcuitest",
|
|
29
|
+
"maestro",
|
|
30
|
+
"android",
|
|
31
|
+
"ios",
|
|
32
|
+
"test-automation",
|
|
33
|
+
"cloud-testing",
|
|
34
|
+
"cli",
|
|
35
|
+
"real-devices",
|
|
36
|
+
"emulators",
|
|
37
|
+
"simulators"
|
|
38
|
+
],
|
|
39
|
+
"author": "TestingBot",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/testingbot/testingbotctl.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://testingbot.com",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/testingbot/testingbotctl/issues"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"archiver": "^7.0.1",
|
|
51
|
+
"axios": "^1.13.2",
|
|
52
|
+
"colors": "^1.4.0",
|
|
53
|
+
"commander": "^14.0.2",
|
|
54
|
+
"file-type": "^21.1.1",
|
|
55
|
+
"form-data": "^4.0.5",
|
|
56
|
+
"glob": "^13.0.0",
|
|
57
|
+
"js-yaml": "^4.1.1",
|
|
58
|
+
"socket.io-client": "^4.8.1",
|
|
59
|
+
"tracer": "^1.3.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@eslint/js": "^9.39.1",
|
|
63
|
+
"@tsconfig/node20": "^20.1.8",
|
|
64
|
+
"@types/archiver": "^7.0.0",
|
|
65
|
+
"@types/jest": "^29.5.14",
|
|
66
|
+
"@types/js-yaml": "^4.0.9",
|
|
67
|
+
"@types/node": "^20.19.0",
|
|
68
|
+
"babel-jest": "^29.7.0",
|
|
69
|
+
"eslint": "^9.39.1",
|
|
70
|
+
"eslint-config-prettier": "^10.1.8",
|
|
71
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
72
|
+
"jest": "^29.7.0",
|
|
73
|
+
"prettier": "^3.7.4",
|
|
74
|
+
"ts-jest": "^29.4.6",
|
|
75
|
+
"typescript": "^5.9.3",
|
|
76
|
+
"typescript-eslint": "^8.48.1"
|
|
77
|
+
}
|
|
78
|
+
}
|