esa-cli 0.0.1-beta.4 → 0.0.1-beta.6
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/dist/commands/commit/index.js +8 -3
- package/dist/commands/deploy/helper.js +10 -5
- package/dist/commands/init/index.js +58 -39
- package/dist/i18n/locales.json +16 -0
- package/dist/libs/apiService.js +3 -3
- package/dist/package.json +6 -1
- package/dist/utils/checkIsRoutineCreated.js +8 -3
- package/dist/utils/fileUtils/index.js +6 -1
- package/package.json +6 -1
|
@@ -40,7 +40,7 @@ const commit = {
|
|
|
40
40
|
export default commit;
|
|
41
41
|
export function handleCommit(argv) {
|
|
42
42
|
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
var _a, _b;
|
|
43
|
+
var _a, _b, _c;
|
|
44
44
|
if (!checkDirectory())
|
|
45
45
|
return;
|
|
46
46
|
const projectConfig = getProjectConfig();
|
|
@@ -59,8 +59,13 @@ export function handleCommit(argv) {
|
|
|
59
59
|
if (!response) {
|
|
60
60
|
logger.log(`🙅 ${t('commit_er_not_exist').d('No routine found, creating a new one')}`);
|
|
61
61
|
description = yield descriptionInput(`🖊️ ${t('commit_er_description').d('Enter a description for the routine')}:`, false);
|
|
62
|
-
const specList = yield server.
|
|
63
|
-
|
|
62
|
+
const specList = ((_c = (_b = (yield server.ListRoutineOptionalSpecs())) === null || _b === void 0 ? void 0 : _b.data.Specs) !== null && _c !== void 0 ? _c : []).reduce((acc, item) => {
|
|
63
|
+
if (item.IsAvailable) {
|
|
64
|
+
acc.push(item.SpecName);
|
|
65
|
+
}
|
|
66
|
+
return acc;
|
|
67
|
+
}, []);
|
|
68
|
+
specName = yield displaySelectSpec(specList);
|
|
64
69
|
}
|
|
65
70
|
else {
|
|
66
71
|
logger.log(`🔄 ${t('commit_er_exist').d('Routine exists, updating the code')}`);
|
|
@@ -64,7 +64,7 @@ export function displaySelectDeployType() {
|
|
|
64
64
|
}
|
|
65
65
|
export function createAndDeployVersion(projectConfig_1) {
|
|
66
66
|
return __awaiter(this, arguments, void 0, function* (projectConfig, createUnstable = false, customEntry) {
|
|
67
|
-
var _a, _b, _c;
|
|
67
|
+
var _a, _b, _c, _d;
|
|
68
68
|
try {
|
|
69
69
|
const server = yield ApiService.getInstance();
|
|
70
70
|
const description = yield descriptionInput(createUnstable
|
|
@@ -72,15 +72,20 @@ export function createAndDeployVersion(projectConfig_1) {
|
|
|
72
72
|
: `🖊️ ${t('deploy_description_version').d('Enter the description of the code version')}:`, false);
|
|
73
73
|
yield prodBuild(false, customEntry);
|
|
74
74
|
const code = readEdgeRoutineFile();
|
|
75
|
-
const specList = yield server.
|
|
75
|
+
const specList = ((_b = (_a = (yield server.ListRoutineOptionalSpecs())) === null || _a === void 0 ? void 0 : _a.data.Specs) !== null && _b !== void 0 ? _b : []).reduce((acc, item) => {
|
|
76
|
+
if (item.IsAvailable) {
|
|
77
|
+
acc.push(item.SpecName);
|
|
78
|
+
}
|
|
79
|
+
return acc;
|
|
80
|
+
}, []);
|
|
76
81
|
let specName;
|
|
77
82
|
if (createUnstable) {
|
|
78
|
-
specName = yield displaySelectSpec(
|
|
83
|
+
specName = yield displaySelectSpec(specList);
|
|
79
84
|
}
|
|
80
85
|
else {
|
|
81
|
-
const req = { Name: (
|
|
86
|
+
const req = { Name: (_c = projectConfig.name) !== null && _c !== void 0 ? _c : '' };
|
|
82
87
|
const response = yield server.getRoutine(req);
|
|
83
|
-
specName = (
|
|
88
|
+
specName = (_d = response === null || response === void 0 ? void 0 : response.data.Envs[0].SpecName) !== null && _d !== void 0 ? _d : '50ms';
|
|
84
89
|
}
|
|
85
90
|
const edgeRoutine = {
|
|
86
91
|
name: projectConfig.name,
|
|
@@ -8,12 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import SelectItems from '../../components/selectInput.js';
|
|
11
|
-
import
|
|
12
|
-
import fs from 'fs';
|
|
11
|
+
import fs from 'fs-extra';
|
|
13
12
|
import Template from '../../libs/templates/index.js';
|
|
14
|
-
import {
|
|
13
|
+
import { installGit } from '../../libs/git/index.js';
|
|
15
14
|
import { descriptionInput } from '../../components/descriptionInput.js';
|
|
16
|
-
import { generateConfigFile, getProjectConfig, updateProjectConfigFile } from '../../utils/fileUtils/index.js';
|
|
15
|
+
import { generateConfigFile, getDirName, getProjectConfig, getTemplatesConfig, templateHubPath, updateProjectConfigFile } from '../../utils/fileUtils/index.js';
|
|
17
16
|
import t from '../../i18n/index.js';
|
|
18
17
|
import logger from '../../libs/logger.js';
|
|
19
18
|
import { quickDeploy } from '../deploy/index.js';
|
|
@@ -22,6 +21,7 @@ import { ApiService } from '../../libs/apiService.js';
|
|
|
22
21
|
import { exit } from 'process';
|
|
23
22
|
import { checkRoutineExist } from '../../utils/checkIsRoutineCreated.js';
|
|
24
23
|
import path from 'path';
|
|
24
|
+
import { execSync } from 'child_process';
|
|
25
25
|
const secondSetOfItems = [
|
|
26
26
|
{ label: 'Yes', value: 'yesInstall' },
|
|
27
27
|
{ label: 'No', value: 'noInstall' }
|
|
@@ -41,12 +41,13 @@ const init = {
|
|
|
41
41
|
})
|
|
42
42
|
};
|
|
43
43
|
export default init;
|
|
44
|
-
const downloadEntireTemplates = (entry) => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
-
yield cloneRepository('https://github.com/aliyun/alibabacloud-esa-er-templates.git', entry);
|
|
46
|
-
});
|
|
47
44
|
export function handleInit(argv) {
|
|
48
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
49
46
|
const { config } = argv;
|
|
47
|
+
// 更新npm包
|
|
48
|
+
const __dirname = getDirName(import.meta.url);
|
|
49
|
+
const projectPath = path.join(__dirname, '../../..');
|
|
50
|
+
execSync('npm install', { stdio: 'ignore', cwd: projectPath });
|
|
50
51
|
if (config !== undefined) {
|
|
51
52
|
yield generateConfigFile(String(config));
|
|
52
53
|
return;
|
|
@@ -57,45 +58,50 @@ export function handleInit(argv) {
|
|
|
57
58
|
logger.error(t('init_name_error').d('Error: The project name must be at least 2 characters long and can only contain lowercase letters, numbers, and hyphens.'));
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (fs.existsSync(hiddenFolder)) {
|
|
63
|
-
fs.rmdirSync(hiddenFolder, { recursive: true });
|
|
64
|
-
}
|
|
65
|
-
// 下载所有模板到隐藏文件夹
|
|
66
|
-
yield downloadEntireTemplates(hiddenFolder);
|
|
67
|
-
//获取隐藏文件夹所有子目录
|
|
68
|
-
const templatePaths = fs.readdirSync(hiddenFolder).filter((item) => {
|
|
69
|
-
const itemPath = path.join(hiddenFolder, item);
|
|
61
|
+
const templatePaths = fs.readdirSync(templateHubPath).filter((item) => {
|
|
62
|
+
const itemPath = path.join(templateHubPath, item);
|
|
70
63
|
const stats = fs.statSync(itemPath);
|
|
71
|
-
return stats.isDirectory() &&
|
|
64
|
+
return (stats.isDirectory() &&
|
|
65
|
+
item !== '.git' &&
|
|
66
|
+
item !== 'node_modules' &&
|
|
67
|
+
item !== 'lib');
|
|
72
68
|
});
|
|
73
69
|
const templateList = templatePaths.map((item) => {
|
|
74
70
|
var _a;
|
|
75
|
-
const projectPath =
|
|
71
|
+
const projectPath = templateHubPath + '/' + item;
|
|
76
72
|
const projectConfig = getProjectConfig(projectPath);
|
|
77
73
|
const templateName = (_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.name) !== null && _a !== void 0 ? _a : '';
|
|
78
74
|
return new Template(projectPath, templateName);
|
|
79
75
|
});
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
const templateConfig = getTemplatesConfig();
|
|
77
|
+
const firstSetOfItems = templateConfig
|
|
78
|
+
.map((template) => {
|
|
79
|
+
const name = template.Title_EN;
|
|
80
|
+
const templatePath = templateList.find((item) => {
|
|
81
|
+
return name === item.title;
|
|
82
|
+
});
|
|
83
|
+
return templatePath
|
|
84
|
+
? {
|
|
85
|
+
label: name,
|
|
86
|
+
value: templatePath.path
|
|
87
|
+
}
|
|
88
|
+
: null;
|
|
89
|
+
})
|
|
90
|
+
.filter((item) => item !== null);
|
|
83
91
|
let selectTemplate;
|
|
84
92
|
let targetPath;
|
|
85
93
|
let projectConfig;
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
const preInstallDependencies = () => __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
const packageJsonPath = path.join(targetPath, 'package.json');
|
|
96
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
97
|
+
logger.info(t('init_install_dependence').d('⌛️ Installing dependencies...'));
|
|
98
|
+
execSync('npm install', { stdio: 'inherit', cwd: targetPath });
|
|
99
|
+
logger.success(t('init_install_dependencies_success').d('Dependencies installed successfully.'));
|
|
100
|
+
logger.log(t('init_build_project').d('⌛️ Building project...'));
|
|
101
|
+
execSync('npm run build', { stdio: 'inherit', cwd: targetPath });
|
|
102
|
+
logger.success(t('init_build_project_success').d('Project built successfully.'));
|
|
89
103
|
}
|
|
90
|
-
|
|
91
|
-
logger.info(t('init_skip_git').d('Git installation was skipped.'));
|
|
92
|
-
}
|
|
93
|
-
logger.info(t('auto_deploy').d('Do you want to deploy your project?'));
|
|
94
|
-
SelectItems({
|
|
95
|
-
items: secondSetOfItems,
|
|
96
|
-
handleSelect: handleThirdSelection
|
|
97
|
-
});
|
|
98
|
-
};
|
|
104
|
+
});
|
|
99
105
|
const handleFirstSelection = (item) => __awaiter(this, void 0, void 0, function* () {
|
|
100
106
|
const configPath = item.value;
|
|
101
107
|
selectTemplate = new Template(configPath, name);
|
|
@@ -103,21 +109,35 @@ export function handleInit(argv) {
|
|
|
103
109
|
if (!projectConfig)
|
|
104
110
|
return logger.notInProject();
|
|
105
111
|
const newPath = process.cwd() + '/' + name;
|
|
112
|
+
console.log(newPath);
|
|
106
113
|
targetPath = newPath;
|
|
107
114
|
if (fs.existsSync(newPath)) {
|
|
108
|
-
fs.rmdirSync(hiddenFolder, { recursive: true });
|
|
109
115
|
logger.error(t('already_exist_file_error').d('Error: The project already exists. It looks like a folder named "<project-name>" is already present in the current directory. Please try the following options: 1. Choose a different project name. 2. Delete the existing folder if it\'s not needed: `rm -rf <project-name>` (use with caution!). 3. Move to a different directory before running the init command.'));
|
|
110
|
-
|
|
116
|
+
exit(0);
|
|
111
117
|
}
|
|
112
|
-
yield
|
|
118
|
+
yield fs.copy(configPath, newPath);
|
|
113
119
|
projectConfig.name = name;
|
|
114
120
|
updateProjectConfigFile(projectConfig, newPath);
|
|
121
|
+
preInstallDependencies();
|
|
115
122
|
logger.info(t('init_git').d('Do you want to init git in your project?'));
|
|
116
123
|
SelectItems({
|
|
117
124
|
items: secondSetOfItems,
|
|
118
125
|
handleSelect: handleSecondSelection
|
|
119
126
|
});
|
|
120
127
|
});
|
|
128
|
+
const handleSecondSelection = (item) => {
|
|
129
|
+
if (item.value === 'yesInstall') {
|
|
130
|
+
installGit(targetPath);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
logger.info(t('init_skip_git').d('Git installation was skipped.'));
|
|
134
|
+
}
|
|
135
|
+
logger.info(t('auto_deploy').d('Do you want to deploy your project?'));
|
|
136
|
+
SelectItems({
|
|
137
|
+
items: secondSetOfItems,
|
|
138
|
+
handleSelect: handleThirdSelection
|
|
139
|
+
});
|
|
140
|
+
};
|
|
121
141
|
const handleThirdSelection = (item) => __awaiter(this, void 0, void 0, function* () {
|
|
122
142
|
var _a, _b, _c;
|
|
123
143
|
// 选择自动生成版本并发布
|
|
@@ -132,7 +152,7 @@ export function handleInit(argv) {
|
|
|
132
152
|
logger.warn(t('deploy_url_warn').d('The domain may take some time to take effect, please try again later.'));
|
|
133
153
|
}
|
|
134
154
|
selectTemplate.printSummary();
|
|
135
|
-
exit();
|
|
155
|
+
exit(0);
|
|
136
156
|
});
|
|
137
157
|
try {
|
|
138
158
|
SelectItems({
|
|
@@ -143,7 +163,6 @@ export function handleInit(argv) {
|
|
|
143
163
|
catch (error) {
|
|
144
164
|
logger.error(t('init_error').d('An error occurred while initializing.'));
|
|
145
165
|
console.log(error);
|
|
146
|
-
fs.rmdirSync(hiddenFolder, { recursive: true });
|
|
147
166
|
}
|
|
148
167
|
});
|
|
149
168
|
}
|
package/dist/i18n/locales.json
CHANGED
|
@@ -762,5 +762,21 @@
|
|
|
762
762
|
"deploy_url_warn": {
|
|
763
763
|
"en": "The domain may take some time to take effect, please try again later.",
|
|
764
764
|
"zh_CN": "域名生效可能需要一段时间,请稍后再试。"
|
|
765
|
+
},
|
|
766
|
+
"init_install_dependence": {
|
|
767
|
+
"en": "⌛️ Installing dependencies...",
|
|
768
|
+
"zh_CN": "⌛️ 正在安装依赖..."
|
|
769
|
+
},
|
|
770
|
+
"init_install_dependencies_success": {
|
|
771
|
+
"en": "Dependencies installed successfully.",
|
|
772
|
+
"zh_CN": "依赖安装成功。"
|
|
773
|
+
},
|
|
774
|
+
"init_build_project": {
|
|
775
|
+
"en": "⌛️ Building project...",
|
|
776
|
+
"zh_CN": "⌛️ 正在构建项目..."
|
|
777
|
+
},
|
|
778
|
+
"init_build_project_success": {
|
|
779
|
+
"en": "Project built successfully.",
|
|
780
|
+
"zh_CN": "项目构建成功。"
|
|
765
781
|
}
|
|
766
782
|
}
|
package/dist/libs/apiService.js
CHANGED
|
@@ -572,7 +572,7 @@ export class ApiService {
|
|
|
572
572
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
573
573
|
try {
|
|
574
574
|
let params = {
|
|
575
|
-
action: '
|
|
575
|
+
action: 'GetRoutine',
|
|
576
576
|
version: '2024-09-10',
|
|
577
577
|
protocol: 'https',
|
|
578
578
|
method: 'GET',
|
|
@@ -772,11 +772,11 @@ export class ApiService {
|
|
|
772
772
|
return null;
|
|
773
773
|
});
|
|
774
774
|
}
|
|
775
|
-
|
|
775
|
+
ListRoutineOptionalSpecs() {
|
|
776
776
|
return __awaiter(this, void 0, void 0, function* () {
|
|
777
777
|
try {
|
|
778
778
|
let params = {
|
|
779
|
-
action: '
|
|
779
|
+
action: 'ListRoutineOptionalSpecs',
|
|
780
780
|
version: '2024-09-10',
|
|
781
781
|
protocol: 'https',
|
|
782
782
|
method: 'GET',
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esa-cli",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.6",
|
|
4
4
|
"description": "A CLI for operating Alibaba Cloud ESA EdgeRoutine (Edge Functions).",
|
|
5
5
|
"main": "bin/enter.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@babel/plugin-syntax-import-attributes": "^7.24.7",
|
|
34
|
+
"@testing-library/jest-dom": "^6.5.0",
|
|
35
|
+
"@testing-library/react": "^16.0.1",
|
|
34
36
|
"@types/cli-table": "^0.3.4",
|
|
35
37
|
"@types/cross-spawn": "^6.0.6",
|
|
36
38
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -47,6 +49,8 @@
|
|
|
47
49
|
"eslint-plugin-react": "^7.33.2",
|
|
48
50
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
49
51
|
"husky": "^8.0.3",
|
|
52
|
+
"jsdom": "^25.0.1",
|
|
53
|
+
"@testing-library/dom": "^10.4.0",
|
|
50
54
|
"lint-staged": "^15.0.2",
|
|
51
55
|
"prettier": "^3.0.3",
|
|
52
56
|
"react": "^18.2.0",
|
|
@@ -68,6 +72,7 @@
|
|
|
68
72
|
"chokidar": "^3.5.3",
|
|
69
73
|
"cli-table3": "^0.6.5",
|
|
70
74
|
"cross-spawn": "^7.0.3",
|
|
75
|
+
"esa-template": "^0.0.1-beta.2",
|
|
71
76
|
"esbuild": "^0.21.1",
|
|
72
77
|
"esbuild-plugin-less": "^1.3.8",
|
|
73
78
|
"form-data": "^4.0.0",
|
|
@@ -36,7 +36,7 @@ export function validRoutine(name) {
|
|
|
36
36
|
}
|
|
37
37
|
export function checkRoutineExist(name, entry) {
|
|
38
38
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
-
var _a;
|
|
39
|
+
var _a, _b;
|
|
40
40
|
const isCreatedRoutine = yield isRoutineExist(name);
|
|
41
41
|
if (!isCreatedRoutine) {
|
|
42
42
|
logger.log(t('first_deploy').d('This is the first time to deploy, we will create a new routine for you.'));
|
|
@@ -44,8 +44,13 @@ export function checkRoutineExist(name, entry) {
|
|
|
44
44
|
yield prodBuild(false, entryFile, entry);
|
|
45
45
|
const code = readEdgeRoutineFile(entry) || '';
|
|
46
46
|
const server = yield ApiService.getInstance();
|
|
47
|
-
const specList = yield server.
|
|
48
|
-
|
|
47
|
+
const specList = ((_b = (_a = (yield server.ListRoutineOptionalSpecs())) === null || _a === void 0 ? void 0 : _a.data.Specs) !== null && _b !== void 0 ? _b : []).reduce((acc, item) => {
|
|
48
|
+
if (item.IsAvailable) {
|
|
49
|
+
acc.push(item.SpecName);
|
|
50
|
+
}
|
|
51
|
+
return acc;
|
|
52
|
+
}, []);
|
|
53
|
+
const spec = yield displaySelectSpec(specList);
|
|
49
54
|
yield createEdgeRoutine({
|
|
50
55
|
name: name,
|
|
51
56
|
specName: spec,
|
|
@@ -47,7 +47,6 @@ export const getRoot = (root) => {
|
|
|
47
47
|
};
|
|
48
48
|
const root = getRoot();
|
|
49
49
|
export const projectConfigPath = path.join(root, projectConfigFile);
|
|
50
|
-
// export const cliConfigPath = path.join(__dirname, '..', '..', cliConfigFile);
|
|
51
50
|
export const cliConfigPath = path.join(os.homedir(), '.esa/config/default.toml');
|
|
52
51
|
export const hiddenConfigDir = path.join(os.homedir(), '.esa/config');
|
|
53
52
|
export const generateHiddenConfigDir = () => {
|
|
@@ -217,3 +216,9 @@ export const getApiConfig = () => {
|
|
|
217
216
|
};
|
|
218
217
|
return config;
|
|
219
218
|
};
|
|
219
|
+
export const templateHubPath = path.join(getDirName(import.meta.url), '../../../node_modules/esa-template/src');
|
|
220
|
+
export const getTemplatesConfig = () => {
|
|
221
|
+
const manifestPath = path.join(templateHubPath, 'manifest.json');
|
|
222
|
+
const config = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
223
|
+
return config;
|
|
224
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esa-cli",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.6",
|
|
4
4
|
"description": "A CLI for operating Alibaba Cloud ESA EdgeRoutine (Edge Functions).",
|
|
5
5
|
"main": "bin/enter.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@babel/plugin-syntax-import-attributes": "^7.24.7",
|
|
34
|
+
"@testing-library/jest-dom": "^6.5.0",
|
|
35
|
+
"@testing-library/react": "^16.0.1",
|
|
34
36
|
"@types/cli-table": "^0.3.4",
|
|
35
37
|
"@types/cross-spawn": "^6.0.6",
|
|
36
38
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -47,6 +49,8 @@
|
|
|
47
49
|
"eslint-plugin-react": "^7.33.2",
|
|
48
50
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
49
51
|
"husky": "^8.0.3",
|
|
52
|
+
"jsdom": "^25.0.1",
|
|
53
|
+
"@testing-library/dom": "^10.4.0",
|
|
50
54
|
"lint-staged": "^15.0.2",
|
|
51
55
|
"prettier": "^3.0.3",
|
|
52
56
|
"react": "^18.2.0",
|
|
@@ -68,6 +72,7 @@
|
|
|
68
72
|
"chokidar": "^3.5.3",
|
|
69
73
|
"cli-table3": "^0.6.5",
|
|
70
74
|
"cross-spawn": "^7.0.3",
|
|
75
|
+
"esa-template": "^0.0.1-beta.2",
|
|
71
76
|
"esbuild": "^0.21.1",
|
|
72
77
|
"esbuild-plugin-less": "^1.3.8",
|
|
73
78
|
"form-data": "^4.0.0",
|