@sap-ux/odata-service-inquirer 2.1.3 → 2.2.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/dist/prompts/connectionValidator.d.ts +1 -2
- package/dist/prompts/connectionValidator.js +1 -1
- package/dist/prompts/datasources/sap-system/abap-on-btp/questions.d.ts +5 -2
- package/dist/prompts/datasources/sap-system/abap-on-btp/questions.js +35 -20
- package/dist/prompts/datasources/sap-system/cf-abap/questions.d.ts +13 -0
- package/dist/prompts/datasources/sap-system/cf-abap/questions.js +24 -0
- package/dist/prompts/datasources/sap-system/system-selection/prompt-helpers.d.ts +2 -1
- package/dist/prompts/datasources/sap-system/system-selection/questions.js +4 -0
- package/dist/prompts/types.d.ts +9 -0
- package/dist/prompts/types.js +6 -0
- package/dist/translations/odata-service-inquirer.i18n.json +2 -1
- package/package.json +12 -12
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { IValidationLink } from '@sap-devx/yeoman-ui-types';
|
|
2
1
|
import type { AxiosRequestConfig, CatalogService, ODataService, ServiceInfo, ServiceProvider } from '@sap-ux/axios-extension';
|
|
3
2
|
import { ODataVersion } from '@sap-ux/axios-extension';
|
|
4
3
|
import { type Destination } from '@sap-ux/btp-utils';
|
|
5
4
|
import { ERROR_TYPE } from '@sap-ux/inquirer-common';
|
|
5
|
+
import type { ValidationResult } from './types';
|
|
6
6
|
/**
|
|
7
7
|
* Structure to store validity information about url to be validated.
|
|
8
8
|
*/
|
|
@@ -13,7 +13,6 @@ interface Validity {
|
|
|
13
13
|
authenticated?: boolean;
|
|
14
14
|
canSkipCertError?: boolean;
|
|
15
15
|
}
|
|
16
|
-
export type ValidationResult = string | boolean | IValidationLink;
|
|
17
16
|
export type SystemAuthType = 'serviceKey' | 'reentranceTicket' | 'basic' | 'unknown';
|
|
18
17
|
/**
|
|
19
18
|
* Class that can be used to determine the connectivity using a service url, system url, or service info (UAA Key details) or reentrance ticket.
|
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ConnectionValidator = void 0;
|
|
7
7
|
const axios_extension_1 = require("@sap-ux/axios-extension");
|
|
8
8
|
const btp_utils_1 = require("@sap-ux/btp-utils");
|
|
9
|
-
const https_1 = __importDefault(require("https"));
|
|
10
9
|
const inquirer_common_1 = require("@sap-ux/inquirer-common");
|
|
10
|
+
const https_1 = __importDefault(require("https"));
|
|
11
11
|
const i18n_1 = require("../i18n");
|
|
12
12
|
const types_1 = require("../types");
|
|
13
13
|
const logger_helper_1 = __importDefault(require("./logger-helper"));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OdataVersion } from '@sap-ux/odata-service-writer';
|
|
1
2
|
import { type ServiceInstanceInfo } from '@sap/cf-tools';
|
|
2
3
|
import type { Question } from 'inquirer';
|
|
3
4
|
import { type OdataServiceAnswers, type OdataServicePromptOptions } from '../../../../types';
|
|
@@ -17,7 +18,7 @@ interface AbapOnBtpAnswers extends Partial<OdataServiceAnswers> {
|
|
|
17
18
|
[abapOnBtpPromptNames.cloudFoundryAbapSystem]?: ServiceInstanceInfo;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
|
-
* Get the questions for the ABAP on BTP system. The questions will prompt the user for the system type (Cloud Foundry, Service Key, Re-entrance Ticket).
|
|
21
|
+
* Get the questions for the ABAP on BTP system within the VSCode platform. The questions will prompt the user for the system type (Cloud Foundry, Service Key, Re-entrance Ticket).
|
|
21
22
|
*
|
|
22
23
|
* @param promptOptions The prompt options which control the service selection and system name]
|
|
23
24
|
* @returns The list of questions for the ABAP on BTP system
|
|
@@ -28,8 +29,10 @@ export declare function getAbapOnBTPSystemQuestions(promptOptions?: OdataService
|
|
|
28
29
|
* If the Cloud Foundry connection fails, a warning message will be displayed.
|
|
29
30
|
*
|
|
30
31
|
* @param connectionValidator The connection validator
|
|
32
|
+
* @param promptNamespace
|
|
33
|
+
* @param requiredOdataVersion
|
|
31
34
|
* @returns The Cloud Foundry ABAP system discovery prompt
|
|
32
35
|
*/
|
|
33
|
-
export declare function getCFDiscoverPrompts(connectionValidator: ConnectionValidator): Question[];
|
|
36
|
+
export declare function getCFDiscoverPrompts(connectionValidator: ConnectionValidator, promptNamespace?: string, requiredOdataVersion?: OdataVersion): Question[];
|
|
34
37
|
export {};
|
|
35
38
|
//# sourceMappingURL=questions.d.ts.map
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getAbapOnBTPSystemQuestions = getAbapOnBTPSystemQuestions;
|
|
7
7
|
exports.getCFDiscoverPrompts = getCFDiscoverPrompts;
|
|
8
|
+
const btp_utils_1 = require("@sap-ux/btp-utils");
|
|
8
9
|
const fiori_generator_shared_1 = require("@sap-ux/fiori-generator-shared");
|
|
9
10
|
const inquirer_common_1 = require("@sap-ux/inquirer-common");
|
|
10
11
|
const cf_tools_1 = require("@sap/cf-tools");
|
|
@@ -16,6 +17,7 @@ const prompt_helpers_1 = require("../../../prompt-helpers");
|
|
|
16
17
|
const questions_1 = require("../new-system/questions");
|
|
17
18
|
const types_1 = require("../new-system/types");
|
|
18
19
|
const service_selection_1 = require("../service-selection");
|
|
20
|
+
const prompt_helpers_2 = require("../system-selection/prompt-helpers");
|
|
19
21
|
const validators_1 = require("../validators");
|
|
20
22
|
const abapOnBtpPromptNamespace = 'abapOnBtp';
|
|
21
23
|
const systemUrlPromptName = `${abapOnBtpPromptNamespace}:${types_1.newSystemPromptNames.newSystemUrl}`;
|
|
@@ -26,7 +28,7 @@ const abapOnBtpPromptNames = {
|
|
|
26
28
|
'cloudFoundryAbapSystem': 'cloudFoundryAbapSystem'
|
|
27
29
|
};
|
|
28
30
|
/**
|
|
29
|
-
* Get the questions for the ABAP on BTP system. The questions will prompt the user for the system type (Cloud Foundry, Service Key, Re-entrance Ticket).
|
|
31
|
+
* Get the questions for the ABAP on BTP system within the VSCode platform. The questions will prompt the user for the system type (Cloud Foundry, Service Key, Re-entrance Ticket).
|
|
30
32
|
*
|
|
31
33
|
* @param promptOptions The prompt options which control the service selection and system name]
|
|
32
34
|
* @returns The list of questions for the ABAP on BTP system
|
|
@@ -75,17 +77,33 @@ function getAbapOnBTPSystemQuestions(promptOptions) {
|
|
|
75
77
|
return questions;
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
78
|
-
* Validate the service info for
|
|
79
|
-
* Updates the prompt state with the connected system.
|
|
80
|
+
* Validate the service info (returned from cf-tools APIs) for an ABAP on BTP system. This function will validate the service key file, or on BAS will create a new destination,
|
|
81
|
+
* and validate the connection to the ABAP system. Updates the prompt state with the connected system to be later used for catalog service selection.
|
|
80
82
|
*
|
|
81
|
-
* @param abapService the abap service as provided by CF tools
|
|
83
|
+
* @param abapService the abap service as provided by CF tools {@link ServiceInstanceInfo}
|
|
82
84
|
* @param connectionValidator connection validator instance
|
|
85
|
+
* @param requiredOdataVersion
|
|
83
86
|
* @param isCli validation on CLI ill throw rather than returning a validation message as users cannot change previous answers on CLI
|
|
84
87
|
* @returns true if the service info is valid, a validation message if the service info is invalid, or a validation link if the service info is not validated but some help is available
|
|
85
88
|
*/
|
|
86
|
-
async function
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
+
async function validateCFServiceInfo(abapService, connectionValidator, requiredOdataVersion, isCli = false) {
|
|
90
|
+
const cfAbapServiceName = abapService.label;
|
|
91
|
+
const uaaCreds = await (0, cf_tools_1.apiGetInstanceCredentials)(cfAbapServiceName); // should be abapService.serviceName in BAS?
|
|
92
|
+
if (!uaaCreds?.credentials?.uaa) {
|
|
93
|
+
return (0, i18n_1.t)('errors.cfInstanceCredentialsNotReturned', { serviceInstanceName: cfAbapServiceName });
|
|
94
|
+
}
|
|
95
|
+
let valResult = true;
|
|
96
|
+
let destination;
|
|
97
|
+
if ((0, utils_1.getPromptHostEnvironment)() === fiori_generator_shared_1.hostEnvironment.bas) {
|
|
98
|
+
destination = await (0, btp_utils_1.createOAuth2UserTokenExchangeDest)(cfAbapServiceName, {
|
|
99
|
+
uaaCredentials: uaaCreds.credentials.uaa,
|
|
100
|
+
hostUrl: uaaCreds.credentials.url
|
|
101
|
+
}, logger_helper_1.default.logger);
|
|
102
|
+
valResult = await (0, prompt_helpers_2.connectWithDestination)(destination, connectionValidator, requiredOdataVersion);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
valResult = await connectionValidator.validateServiceInfo(uaaCreds.credentials);
|
|
106
|
+
}
|
|
89
107
|
if (!isCli && valResult !== true) {
|
|
90
108
|
return valResult;
|
|
91
109
|
}
|
|
@@ -101,15 +119,9 @@ async function validateServiceInfo(abapService, connectionValidator, isCli = fal
|
|
|
101
119
|
throw new Error((0, i18n_1.t)('errors.abapServiceAuthenticationFailed'));
|
|
102
120
|
}
|
|
103
121
|
// CLI only ^^^
|
|
104
|
-
if (connectionValidator.serviceProvider) {
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
connectionValidator.connectedSystemName = `abap-cloud-${abapService.label}-${cfTarget.org}-${cfTarget.space}`
|
|
108
|
-
.replace(/[^\w]/gi, '-')
|
|
109
|
-
.toLowerCase();
|
|
110
|
-
utils_1.PromptState.odataService.connectedSystem = {
|
|
111
|
-
serviceProvider: connectionValidator.serviceProvider
|
|
112
|
-
};
|
|
122
|
+
if (connectionValidator.serviceProvider && (0, utils_1.getPromptHostEnvironment)() !== fiori_generator_shared_1.hostEnvironment.bas) {
|
|
123
|
+
// Connected system name is only used for VSCode as a default stored system name
|
|
124
|
+
connectionValidator.connectedSystemName = await (0, btp_utils_1.generateABAPCloudDestinationName)(cfAbapServiceName);
|
|
113
125
|
}
|
|
114
126
|
return true;
|
|
115
127
|
}
|
|
@@ -118,14 +130,17 @@ async function validateServiceInfo(abapService, connectionValidator, isCli = fal
|
|
|
118
130
|
* If the Cloud Foundry connection fails, a warning message will be displayed.
|
|
119
131
|
*
|
|
120
132
|
* @param connectionValidator The connection validator
|
|
133
|
+
* @param promptNamespace
|
|
134
|
+
* @param requiredOdataVersion
|
|
121
135
|
* @returns The Cloud Foundry ABAP system discovery prompt
|
|
122
136
|
*/
|
|
123
|
-
function getCFDiscoverPrompts(connectionValidator) {
|
|
137
|
+
function getCFDiscoverPrompts(connectionValidator, promptNamespace, requiredOdataVersion) {
|
|
124
138
|
let choices = [];
|
|
139
|
+
const promptName = `${promptNamespace ? promptNamespace + ':' : ''}${abapOnBtpPromptNames.cloudFoundryAbapSystem}`;
|
|
125
140
|
const questions = [
|
|
126
141
|
{
|
|
127
142
|
type: 'list',
|
|
128
|
-
name:
|
|
143
|
+
name: promptName,
|
|
129
144
|
guiOptions: {
|
|
130
145
|
breadcrumb: true,
|
|
131
146
|
applyDefaultWhenDirty: true
|
|
@@ -144,7 +159,7 @@ function getCFDiscoverPrompts(connectionValidator) {
|
|
|
144
159
|
message: (0, i18n_1.t)('prompts.cloudFoundryAbapSystem.message'),
|
|
145
160
|
validate: async (abapService) => {
|
|
146
161
|
if (abapService) {
|
|
147
|
-
return await
|
|
162
|
+
return await validateCFServiceInfo(abapService, connectionValidator, requiredOdataVersion, (0, utils_1.getPromptHostEnvironment)() === fiori_generator_shared_1.hostEnvironment.cli);
|
|
148
163
|
}
|
|
149
164
|
const errorType = prompt_helpers_1.errorHandler.getCurrentErrorType();
|
|
150
165
|
if (errorType === inquirer_common_1.ERROR_TYPE.NO_ABAP_ENVS) {
|
|
@@ -162,7 +177,7 @@ function getCFDiscoverPrompts(connectionValidator) {
|
|
|
162
177
|
when: async (answers) => {
|
|
163
178
|
const abapService = answers?.[abapOnBtpPromptNames.cloudFoundryAbapSystem];
|
|
164
179
|
if (abapService) {
|
|
165
|
-
await
|
|
180
|
+
await validateCFServiceInfo(abapService, connectionValidator, requiredOdataVersion, true);
|
|
166
181
|
}
|
|
167
182
|
return false;
|
|
168
183
|
},
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Question } from 'inquirer';
|
|
2
|
+
import type { ServiceSelectionPromptOptions } from '../../../../types';
|
|
3
|
+
import type { ServiceAnswer } from '../service-selection/types';
|
|
4
|
+
/**
|
|
5
|
+
* Cloud Foundry ABAP system prompts specifically for BAS environment since it requires additional destination configuration.
|
|
6
|
+
* These will call out to cf tools to discover the available abap systems and services, and create a new destination if necessary,
|
|
7
|
+
* to allow apps to access these cf hosted services.
|
|
8
|
+
*
|
|
9
|
+
* @param promptOptions prompt options to control some prompt behavior see {@link ServiceSelectionPromptOptions}
|
|
10
|
+
* @returns the prompt questions
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCfAbapBASQuestions(promptOptions?: ServiceSelectionPromptOptions): Question<ServiceAnswer>[];
|
|
13
|
+
//# sourceMappingURL=questions.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCfAbapBASQuestions = getCfAbapBASQuestions;
|
|
4
|
+
const connectionValidator_1 = require("../../../connectionValidator");
|
|
5
|
+
const questions_1 = require("../abap-on-btp/questions");
|
|
6
|
+
const questions_2 = require("../service-selection/questions");
|
|
7
|
+
/**
|
|
8
|
+
* Cloud Foundry ABAP system prompts specifically for BAS environment since it requires additional destination configuration.
|
|
9
|
+
* These will call out to cf tools to discover the available abap systems and services, and create a new destination if necessary,
|
|
10
|
+
* to allow apps to access these cf hosted services.
|
|
11
|
+
*
|
|
12
|
+
* @param promptOptions prompt options to control some prompt behavior see {@link ServiceSelectionPromptOptions}
|
|
13
|
+
* @returns the prompt questions
|
|
14
|
+
*/
|
|
15
|
+
function getCfAbapBASQuestions(promptOptions) {
|
|
16
|
+
// Using a prompt namespace allows re-use of system service (catalog based) selection prompt
|
|
17
|
+
const cfAbapBasPromptNamespace = 'cfAbapBas';
|
|
18
|
+
const connectionValidator = new connectionValidator_1.ConnectionValidator();
|
|
19
|
+
return [
|
|
20
|
+
...(0, questions_1.getCFDiscoverPrompts)(connectionValidator, cfAbapBasPromptNamespace, promptOptions?.requiredOdataVersion),
|
|
21
|
+
...(0, questions_2.getSystemServiceQuestion)(connectionValidator, cfAbapBasPromptNamespace, promptOptions)
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=questions.js.map
|
|
@@ -3,8 +3,9 @@ import type { OdataVersion } from '@sap-ux/odata-service-writer';
|
|
|
3
3
|
import type { BackendSystem } from '@sap-ux/store';
|
|
4
4
|
import type { ListChoiceOptions } from 'inquirer';
|
|
5
5
|
import { type DestinationFilters } from '../../../../types';
|
|
6
|
-
import type { ConnectionValidator
|
|
6
|
+
import type { ConnectionValidator } from '../../../connectionValidator';
|
|
7
7
|
import { type SystemSelectionAnswerType } from './questions';
|
|
8
|
+
import type { ValidationResult } from '../../../types';
|
|
8
9
|
export declare const NewSystemChoice = "!@\u00A3*&937newSystem*X~qy^";
|
|
9
10
|
export type NewSystemChoice = typeof NewSystemChoice;
|
|
10
11
|
export declare const CfAbapEnvServiceChoice = "cfAbapEnvService";
|
|
@@ -17,6 +17,7 @@ const logger_helper_1 = __importDefault(require("../../../logger-helper"));
|
|
|
17
17
|
const questions_1 = require("../credentials/questions");
|
|
18
18
|
const questions_2 = require("../new-system/questions");
|
|
19
19
|
const questions_3 = require("../service-selection/questions");
|
|
20
|
+
const questions_4 = require("../cf-abap/questions");
|
|
20
21
|
const validators_1 = require("../validators");
|
|
21
22
|
const prompt_helpers_1 = require("./prompt-helpers");
|
|
22
23
|
const systemSelectionPromptNamespace = 'systemSelection';
|
|
@@ -75,6 +76,9 @@ async function getSystemSelectionQuestions(promptOptions) {
|
|
|
75
76
|
if (!(0, btp_utils_1.isAppStudio)()) {
|
|
76
77
|
questions.push(...(0, inquirer_common_1.withCondition)((0, questions_2.getNewSystemQuestions)(promptOptions), (answers) => answers.systemSelection?.type === 'newSystemChoice'));
|
|
77
78
|
}
|
|
79
|
+
else {
|
|
80
|
+
questions.push(...(0, inquirer_common_1.withCondition)((0, questions_4.getCfAbapBASQuestions)(promptOptions?.serviceSelection), (answers) => answers.systemSelection?.type === 'cfAbapEnvService'));
|
|
81
|
+
}
|
|
78
82
|
return questions;
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal types for prompts
|
|
3
|
+
*/
|
|
4
|
+
import type { IValidationLink } from '@sap-devx/yeoman-ui-types';
|
|
5
|
+
/**
|
|
6
|
+
* Result of running a prompt `validate` function, including a string message, a boolean, or a validation link for Guided Answer help link support.
|
|
7
|
+
*/
|
|
8
|
+
export type ValidationResult = string | boolean | IValidationLink;
|
|
9
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -223,7 +223,8 @@
|
|
|
223
223
|
"unparseableMetadata": "Unable to parse entities from metadata document. {{-error}}",
|
|
224
224
|
"unparseableOdataVersion": "Unable to parse the odata version from the metadata.",
|
|
225
225
|
"unparseableXML": "Unparseable XML was specified: {{-error}}",
|
|
226
|
-
"noRelevantEntities": "The template and service selected have no relevant entities that you can use."
|
|
226
|
+
"noRelevantEntities": "The template and service selected have no relevant entities that you can use.",
|
|
227
|
+
"cfInstanceCredentialsNotReturned": "Could not retrieve credentials to access: {{serviceInstanceName}}"
|
|
227
228
|
},
|
|
228
229
|
"warnings": {
|
|
229
230
|
"largeMetadataDocument": "The metadata for this OData service is significantly large. It may take some time before this operation completes."
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap-ux/odata-service-inquirer",
|
|
3
3
|
"description": "Prompts module that can prompt users for inputs required for odata service writing",
|
|
4
|
-
"version": "2.1
|
|
4
|
+
"version": "2.2.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/SAP/open-ux-tools.git",
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
"i18next": "23.5.1",
|
|
29
29
|
"inquirer-autocomplete-prompt": "2.0.1",
|
|
30
30
|
"os-name": "4.0.1",
|
|
31
|
-
"@sap-ux/axios-extension": "1.18.
|
|
32
|
-
"@sap-ux/btp-utils": "1.0.
|
|
33
|
-
"@sap-ux/fiori-generator-shared": "0.7.
|
|
31
|
+
"@sap-ux/axios-extension": "1.18.5",
|
|
32
|
+
"@sap-ux/btp-utils": "1.0.1",
|
|
33
|
+
"@sap-ux/fiori-generator-shared": "0.7.27",
|
|
34
34
|
"@sap-ux/guided-answers-helper": "0.2.1",
|
|
35
|
-
"@sap-ux/telemetry": "0.5.
|
|
36
|
-
"@sap-ux/inquirer-common": "0.6.
|
|
35
|
+
"@sap-ux/telemetry": "0.5.59",
|
|
36
|
+
"@sap-ux/inquirer-common": "0.6.14",
|
|
37
37
|
"@sap-ux/logger": "0.6.0",
|
|
38
|
-
"@sap-ux/project-access": "1.29.
|
|
38
|
+
"@sap-ux/project-access": "1.29.5",
|
|
39
39
|
"@sap-ux/project-input-validator": "0.3.4",
|
|
40
40
|
"@sap-ux/store": "1.0.0"
|
|
41
41
|
},
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"@types/inquirer": "8.2.6",
|
|
47
47
|
"@types/lodash": "4.14.202",
|
|
48
48
|
"jest-extended": "3.2.4",
|
|
49
|
-
"@sap-ux/fiori-generator-shared": "0.7.
|
|
50
|
-
"@sap-ux/fiori-elements-writer": "2.1.
|
|
51
|
-
"@sap-ux/fiori-freestyle-writer": "2.0.
|
|
49
|
+
"@sap-ux/fiori-generator-shared": "0.7.27",
|
|
50
|
+
"@sap-ux/fiori-elements-writer": "2.1.4",
|
|
51
|
+
"@sap-ux/fiori-freestyle-writer": "2.0.12",
|
|
52
52
|
"@sap-ux/feature-toggle": "0.2.3",
|
|
53
|
-
"@sap-ux/odata-service-writer": "0.25.
|
|
54
|
-
"@sap-ux/cap-config-writer": "0.9.
|
|
53
|
+
"@sap-ux/odata-service-writer": "0.25.8",
|
|
54
|
+
"@sap-ux/cap-config-writer": "0.9.6"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=18.x"
|