@chargebee/chargebee-apps-shared 0.0.2
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 +24 -0
- package/README.md +113 -0
- package/SECURITY.md +8 -0
- package/dist/config/environment.d.ts +78 -0
- package/dist/config/environment.js +150 -0
- package/dist/config/path-resolver.d.ts +117 -0
- package/dist/config/path-resolver.js +247 -0
- package/dist/dependency/dependency-manager.d.ts +66 -0
- package/dist/dependency/dependency-manager.js +257 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +17 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +36 -0
- package/dist/libs/archive-service.d.ts +21 -0
- package/dist/libs/archive-service.js +145 -0
- package/dist/libs/file-management-service.d.ts +59 -0
- package/dist/libs/file-management-service.js +189 -0
- package/dist/libs/iparams-service.d.ts +69 -0
- package/dist/libs/iparams-service.js +136 -0
- package/dist/libs/template-service.d.ts +57 -0
- package/dist/libs/template-service.js +152 -0
- package/dist/logger/cb-logger.d.ts +56 -0
- package/dist/logger/cb-logger.js +13 -0
- package/dist/logger/internal-logger.d.ts +81 -0
- package/dist/logger/internal-logger.js +146 -0
- package/dist/node/file-system.d.ts +66 -0
- package/dist/node/file-system.js +76 -0
- package/dist/node/process.d.ts +30 -0
- package/dist/node/process.js +25 -0
- package/dist/sandbox/sandbox-context.d.ts +37 -0
- package/dist/sandbox/sandbox-context.js +48 -0
- package/dist/sandbox/sandbox-wrapper.d.ts +75 -0
- package/dist/sandbox/sandbox-wrapper.js +2 -0
- package/dist/types/common.d.ts +135 -0
- package/dist/types/common.js +20 -0
- package/dist/validator/iparam-constants.d.ts +49 -0
- package/dist/validator/iparam-constants.js +52 -0
- package/dist/validator/iparam-defns-validator.d.ts +46 -0
- package/dist/validator/iparam-defns-validator.js +291 -0
- package/dist/validator/iparams-inputs-validator.d.ts +62 -0
- package/dist/validator/iparams-inputs-validator.js +289 -0
- package/dist/validator/manifest-validator.d.ts +48 -0
- package/dist/validator/manifest-validator.js +138 -0
- package/dist/validator/package-validator.d.ts +27 -0
- package/dist/validator/package-validator.js +43 -0
- package/dist/validator/validator-utils.d.ts +13 -0
- package/dist/validator/validator-utils.js +26 -0
- package/package.json +45 -0
|
@@ -0,0 +1,291 @@
|
|
|
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
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.IparamDefnsValidator = void 0;
|
|
37
|
+
const common_1 = require("../types/common");
|
|
38
|
+
const iparam_constants_1 = require("./iparam-constants");
|
|
39
|
+
const iparams_inputs_validator_1 = require("./iparams-inputs-validator");
|
|
40
|
+
const validatorUtils = __importStar(require("./validator-utils"));
|
|
41
|
+
/**
|
|
42
|
+
* Validator for iparamDefns
|
|
43
|
+
* Validates the sectioned iparams.json schema
|
|
44
|
+
*/
|
|
45
|
+
class IparamDefnsValidator {
|
|
46
|
+
constructor(fileSystem) {
|
|
47
|
+
this.fileSystem = fileSystem;
|
|
48
|
+
this.inputsValidator = new iparams_inputs_validator_1.IparamInputsValidator(fileSystem);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validates and loads the iparamDefns from a file
|
|
52
|
+
* @param {string} path - Path to the iparams.json file
|
|
53
|
+
* @returns {IparamDefns} The validated iparamDefns
|
|
54
|
+
* @throws {Error} If the iparamDefns is invalid or cannot be read
|
|
55
|
+
*/
|
|
56
|
+
validateAndGetIparamDefnsFile(path) {
|
|
57
|
+
validatorUtils.ensureFileExists(this.fileSystem, path);
|
|
58
|
+
try {
|
|
59
|
+
const iparamDefns = JSON.parse(this.fileSystem.readFileSync(path, 'utf8'));
|
|
60
|
+
try {
|
|
61
|
+
this.validate(iparamDefns);
|
|
62
|
+
return iparamDefns;
|
|
63
|
+
}
|
|
64
|
+
catch (validationError) {
|
|
65
|
+
const err = new Error(validationError.message);
|
|
66
|
+
err.rawContent = iparamDefns;
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error.rawContent !== undefined) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
if (error instanceof SyntaxError) {
|
|
75
|
+
throw new Error(`Failed to parse ${iparam_constants_1.IPARAMS_JSON_FILENAME}: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`Failed to read ${iparam_constants_1.IPARAMS_JSON_FILENAME}: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Validates an iparamDefns object
|
|
82
|
+
* @param {IparamDefns} iparamDefns - The iparamDefns object to validate
|
|
83
|
+
* @throws {Error} If the iparamDefns is invalid
|
|
84
|
+
*/
|
|
85
|
+
validate(iparamDefns) {
|
|
86
|
+
if (!iparamDefns || typeof iparamDefns !== 'object' || Array.isArray(iparamDefns)) {
|
|
87
|
+
throw new Error(`${iparam_constants_1.IPARAMS_JSON_FILENAME} must be an object`);
|
|
88
|
+
}
|
|
89
|
+
this.rejectUnknownKeys(iparamDefns, iparam_constants_1.IPARAMS_ROOT_ALLOWED_KEYS, iparam_constants_1.IPARAMS_JSON_FILENAME);
|
|
90
|
+
if (!Object.prototype.hasOwnProperty.call(iparamDefns, iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_LABEL)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const installationParameters = iparamDefns.installation_parameters;
|
|
94
|
+
if (!installationParameters || typeof installationParameters !== 'object' || Array.isArray(installationParameters)) {
|
|
95
|
+
throw new Error(`${iparam_constants_1.IPARAMS_JSON_FILENAME} must include ${iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_LABEL} as an object containing a ${iparam_constants_1.IPARAMS_SECTIONS_LABEL} array`);
|
|
96
|
+
}
|
|
97
|
+
this.rejectUnknownKeys(installationParameters, iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_ALLOWED_KEYS, iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_LABEL);
|
|
98
|
+
if (!Object.prototype.hasOwnProperty.call(installationParameters, iparam_constants_1.IPARAMS_SECTIONS_LABEL)) {
|
|
99
|
+
throw new Error(`${iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_LABEL} must include ${iparam_constants_1.IPARAMS_SECTIONS_LABEL} in ${iparam_constants_1.IPARAMS_JSON_FILENAME}`);
|
|
100
|
+
}
|
|
101
|
+
const sections = installationParameters.sections;
|
|
102
|
+
if (!Array.isArray(sections)) {
|
|
103
|
+
throw new Error(`${iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_LABEL}.${iparam_constants_1.IPARAMS_SECTIONS_LABEL} must be an array of section objects`);
|
|
104
|
+
}
|
|
105
|
+
if (sections.length === 0) {
|
|
106
|
+
throw new Error(`${iparam_constants_1.IPARAMS_INSTALLATION_PARAMETERS_LABEL}.${iparam_constants_1.IPARAMS_SECTIONS_LABEL} must contain at least one section`);
|
|
107
|
+
}
|
|
108
|
+
for (let i = 0; i < sections.length; i++) {
|
|
109
|
+
this.validateSection(sections[i], i);
|
|
110
|
+
}
|
|
111
|
+
const totalParamCount = sections.reduce((count, section) => count + section.parameters.length, 0);
|
|
112
|
+
if (totalParamCount > iparam_constants_1.IPARAMS_MAX_COUNT) {
|
|
113
|
+
throw new Error(`${iparam_constants_1.IPARAMS_JSON_FILENAME} cannot define more than ${iparam_constants_1.IPARAMS_MAX_COUNT} parameters (found ${totalParamCount})`);
|
|
114
|
+
}
|
|
115
|
+
const sectionNames = sections.map((section) => section.name.trim());
|
|
116
|
+
const seenSectionNames = new Set();
|
|
117
|
+
const duplicateSections = [];
|
|
118
|
+
for (const name of sectionNames) {
|
|
119
|
+
if (seenSectionNames.has(name))
|
|
120
|
+
duplicateSections.push(name);
|
|
121
|
+
else
|
|
122
|
+
seenSectionNames.add(name);
|
|
123
|
+
}
|
|
124
|
+
if (duplicateSections.length > 0) {
|
|
125
|
+
throw new Error(`Duplicate section names found: ${duplicateSections.join(', ')}`);
|
|
126
|
+
}
|
|
127
|
+
for (const section of sections) {
|
|
128
|
+
const paramNames = section.parameters.map((param) => param.name.trim());
|
|
129
|
+
const seenParamNames = new Set();
|
|
130
|
+
const duplicateParams = [];
|
|
131
|
+
for (const name of paramNames) {
|
|
132
|
+
if (seenParamNames.has(name))
|
|
133
|
+
duplicateParams.push(name);
|
|
134
|
+
else
|
|
135
|
+
seenParamNames.add(name);
|
|
136
|
+
}
|
|
137
|
+
if (duplicateParams.length > 0) {
|
|
138
|
+
throw new Error(`Duplicate parameter names found in section "${section.name}": ${duplicateParams.join(', ')}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Validates a single section definition
|
|
144
|
+
* @param {any} section - The section definition to validate
|
|
145
|
+
* @param {number} index - The index of the section in the array (for error messages)
|
|
146
|
+
* @throws {Error} If the section is invalid
|
|
147
|
+
*/
|
|
148
|
+
validateSection(section, index) {
|
|
149
|
+
if (!section || typeof section !== 'object') {
|
|
150
|
+
throw new Error(`Section at index ${index} must be an object`);
|
|
151
|
+
}
|
|
152
|
+
const sectionName = this.validateSectionName(section, index);
|
|
153
|
+
this.requireNonEmptyStringField(section, sectionName, 'display_name', iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.DISPLAY_NAME, 'Section');
|
|
154
|
+
this.requireNonEmptyStringField(section, sectionName, 'description', iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.DESCRIPTION, 'Section');
|
|
155
|
+
const parameters = section['parameters'];
|
|
156
|
+
if (!Array.isArray(parameters)) {
|
|
157
|
+
throw new Error(`Section "${sectionName}": "parameters" must be an array`);
|
|
158
|
+
}
|
|
159
|
+
if (parameters.length === 0) {
|
|
160
|
+
throw new Error(`Section "${sectionName}": "parameters" must contain at least one parameter`);
|
|
161
|
+
}
|
|
162
|
+
for (let i = 0; i < parameters.length; i++) {
|
|
163
|
+
this.validateParameter(parameters[i], sectionName, i);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Validates a single parameter definition
|
|
168
|
+
* @param {any} param - The parameter definition to validate
|
|
169
|
+
* @param {string} sectionName - Parent section name for error messages
|
|
170
|
+
* @param {number} index - The index of the parameter in the section (for error messages)
|
|
171
|
+
* @throws {Error} If the parameter is invalid
|
|
172
|
+
*/
|
|
173
|
+
validateParameter(param, sectionName, index) {
|
|
174
|
+
if (!param || typeof param !== 'object') {
|
|
175
|
+
throw new Error(`Section "${sectionName}": parameter at index ${index} must be an object`);
|
|
176
|
+
}
|
|
177
|
+
const paramName = this.validateParamName(param, sectionName, index);
|
|
178
|
+
const sectionDotParam = `${sectionName}.${paramName}`;
|
|
179
|
+
this.requireNonEmptyStringField(param, sectionDotParam, 'display_name', iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.DISPLAY_NAME);
|
|
180
|
+
this.requireNonEmptyStringField(param, sectionDotParam, 'description', iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.DESCRIPTION);
|
|
181
|
+
this.validateType(param, sectionDotParam);
|
|
182
|
+
if (param.required !== undefined && typeof param.required !== 'boolean') {
|
|
183
|
+
throw new Error(`Parameter "${sectionDotParam}": "required" must be a boolean`);
|
|
184
|
+
}
|
|
185
|
+
this.validateOptions(param, sectionDotParam);
|
|
186
|
+
if (param.type === 'SECRET' && param.default !== undefined && param.default !== null) {
|
|
187
|
+
throw new Error(`Parameter "${sectionDotParam}": SECRET type cannot have a default value`);
|
|
188
|
+
}
|
|
189
|
+
this.validateDefaultValue(param, sectionDotParam);
|
|
190
|
+
}
|
|
191
|
+
requireNonEmptyStringField(entity, entityName, fieldName, maxLength, entityLabel = 'Parameter') {
|
|
192
|
+
const value = entity[fieldName];
|
|
193
|
+
if (!validatorUtils.isNonEmptyString(value)) {
|
|
194
|
+
throw new Error(`${entityLabel} "${entityName}": "${fieldName}" must be a non-empty string`);
|
|
195
|
+
}
|
|
196
|
+
const trimmed = value.trim();
|
|
197
|
+
if (maxLength !== undefined && trimmed.length > maxLength) {
|
|
198
|
+
throw new Error(`${entityLabel} "${entityName}": "${fieldName}" cannot exceed ${maxLength} characters`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
rejectUnknownKeys(entity, allowedKeys, entityLabel) {
|
|
202
|
+
for (const key of Object.keys(entity)) {
|
|
203
|
+
if (!allowedKeys.includes(key)) {
|
|
204
|
+
throw new Error(`${entityLabel} must only contain ${allowedKeys.map((k) => `"${k}"`).join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
validateSectionName(section, index) {
|
|
209
|
+
const name = section['name'];
|
|
210
|
+
if (!validatorUtils.isNonEmptyString(name)) {
|
|
211
|
+
throw new Error(`Section at index ${index}: "name" must be a non-empty string`);
|
|
212
|
+
}
|
|
213
|
+
const sectionName = name.trim();
|
|
214
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(sectionName)) {
|
|
215
|
+
throw new Error(`Section "${sectionName}": "name" can only contain letters, numbers, underscores, and hyphens, and must start with a letter or underscore`);
|
|
216
|
+
}
|
|
217
|
+
if (sectionName.length > iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.NAME) {
|
|
218
|
+
throw new Error(`Section "${sectionName}": "name" must be between 1 and ${iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.NAME} characters`);
|
|
219
|
+
}
|
|
220
|
+
return sectionName;
|
|
221
|
+
}
|
|
222
|
+
validateParamName(param, sectionName, index) {
|
|
223
|
+
const name = param['name'];
|
|
224
|
+
if (!validatorUtils.isNonEmptyString(name)) {
|
|
225
|
+
throw new Error(`Section "${sectionName}": parameter at index ${index}: "name" must be a non-empty string`);
|
|
226
|
+
}
|
|
227
|
+
const paramName = name.trim();
|
|
228
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(paramName)) {
|
|
229
|
+
throw new Error(`Parameter "${sectionName}.${paramName}": "name" can only contain letters, numbers, underscores, and hyphens, and must start with a letter or underscore`);
|
|
230
|
+
}
|
|
231
|
+
if (paramName.length > iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.NAME) {
|
|
232
|
+
throw new Error(`Parameter "${sectionName}.${paramName}": "name" must be between 1 and ${iparam_constants_1.IPARAM_DEFN_MAX_LENGTHS.NAME} characters`);
|
|
233
|
+
}
|
|
234
|
+
return paramName;
|
|
235
|
+
}
|
|
236
|
+
validateType(param, sectionDotParam) {
|
|
237
|
+
const type = param['type'];
|
|
238
|
+
if (!type || !common_1.VALID_IPARAM_TYPES.includes(type)) {
|
|
239
|
+
throw new Error(`Parameter "${sectionDotParam}": "type" must be one of: ${common_1.VALID_IPARAM_TYPES.join(', ')}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
validateOptions(param, sectionDotParam) {
|
|
243
|
+
const paramType = param['type'];
|
|
244
|
+
if (paramType !== 'DROPDOWN' && paramType !== 'MULTISELECT_DROPDOWN') {
|
|
245
|
+
if (Object.prototype.hasOwnProperty.call(param, 'options')) {
|
|
246
|
+
throw new Error(`Parameter "${sectionDotParam}": "options" is only allowed for DROPDOWN and MULTISELECT_DROPDOWN types`);
|
|
247
|
+
}
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const options = param['options'];
|
|
251
|
+
if (!options || !Array.isArray(options) || options.length === 0) {
|
|
252
|
+
throw new Error(`Parameter "${sectionDotParam}": "options" is required and must be a non-empty array of strings for ${paramType} type`);
|
|
253
|
+
}
|
|
254
|
+
if (options.length > iparam_constants_1.OPTIONS_LIMITS.MAX_COUNT) {
|
|
255
|
+
throw new Error(`Parameter "${sectionDotParam}": "options" cannot exceed ${iparam_constants_1.OPTIONS_LIMITS.MAX_COUNT} items for ${paramType} type`);
|
|
256
|
+
}
|
|
257
|
+
const seenTrimmedOptions = new Set();
|
|
258
|
+
for (let i = 0; i < options.length; i++) {
|
|
259
|
+
if (typeof options[i] !== 'string') {
|
|
260
|
+
throw new Error(`Parameter "${sectionDotParam}": option at index ${i} must be a string`);
|
|
261
|
+
}
|
|
262
|
+
const optionStr = options[i].trim();
|
|
263
|
+
if (optionStr === '') {
|
|
264
|
+
throw new Error(`Parameter "${sectionDotParam}": option at index ${i} cannot be an empty string`);
|
|
265
|
+
}
|
|
266
|
+
if (seenTrimmedOptions.has(optionStr)) {
|
|
267
|
+
throw new Error(`Parameter "${sectionDotParam}": "options" must not contain duplicate entries`);
|
|
268
|
+
}
|
|
269
|
+
seenTrimmedOptions.add(optionStr);
|
|
270
|
+
if (optionStr.length > iparam_constants_1.OPTIONS_LIMITS.MAX_OPTION_LENGTH) {
|
|
271
|
+
throw new Error(`Parameter "${sectionDotParam}": option at index ${i} cannot exceed ${iparam_constants_1.OPTIONS_LIMITS.MAX_OPTION_LENGTH} characters`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
validateDefaultValue(param, sectionDotParam) {
|
|
276
|
+
const defaultValue = param['default'];
|
|
277
|
+
if (defaultValue === undefined || defaultValue === null)
|
|
278
|
+
return;
|
|
279
|
+
if (typeof defaultValue === 'string' && defaultValue.trim() === '') {
|
|
280
|
+
throw new Error(`Parameter "${sectionDotParam}": default value cannot be an empty string. Either remove the "default" key, set it to null, or provide a non-empty value.`);
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
this.inputsValidator.validateParamValue(param, defaultValue, sectionDotParam);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
const msg = error.message.replace(/\bvalue\b/g, 'default value');
|
|
287
|
+
throw new Error(msg);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
exports.IparamDefnsValidator = IparamDefnsValidator;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CBFileSystem } from '../node/file-system';
|
|
2
|
+
import { IparamDefn, IparamInputs, IparamDefns, IparamValue } from '../types/common';
|
|
3
|
+
/**
|
|
4
|
+
* Validator for parameter inputs
|
|
5
|
+
* Validates input values against the sectioned iparamDefns
|
|
6
|
+
*/
|
|
7
|
+
export declare class IparamInputsValidator {
|
|
8
|
+
private fileSystem;
|
|
9
|
+
constructor(fileSystem: CBFileSystem);
|
|
10
|
+
/**
|
|
11
|
+
* Validates whether the value counts as a provided input.
|
|
12
|
+
* Not whether it is allowed by iparam definitions—that is handled by {@link IparamInputsValidator.validateInputs}.
|
|
13
|
+
*/
|
|
14
|
+
static isProvidedIparamInputValue(value: IparamValue | undefined | null): value is IparamValue;
|
|
15
|
+
/**
|
|
16
|
+
* Validates and loads the iparam inputs from a file (no validation against iparamDefns).
|
|
17
|
+
* Only ensures the file is valid JSON and the root value is an object.
|
|
18
|
+
* @param {string} path - Path to the iparams.local.json file
|
|
19
|
+
* @returns {IparamInputs} The loaded inputs
|
|
20
|
+
* @throws {Error} If the file does not exist, cannot be read or parsed, or root is not an object
|
|
21
|
+
*/
|
|
22
|
+
validateAndGetIparamInputsFile(path: string): IparamInputs;
|
|
23
|
+
/**
|
|
24
|
+
* Ensures the inputs value is a plain object
|
|
25
|
+
* @param {any} inputs - The value to check
|
|
26
|
+
* @throws {Error} If inputs is not a plain object
|
|
27
|
+
*/
|
|
28
|
+
ensureInputsIsObject(inputs: any): void;
|
|
29
|
+
/**
|
|
30
|
+
* Validates inputs against the iparamDefns (without applying defaults)
|
|
31
|
+
* Only validates provided values, does not include defaults in the result
|
|
32
|
+
* @param {IparamInputs} inputs - The input values to validate
|
|
33
|
+
* @param {IparamDefns} iparamDefns - The iparamDefns
|
|
34
|
+
* @returns {IparamInputs} The validated inputs (only provided values, no defaults)
|
|
35
|
+
* @throws {Error} If validation fails
|
|
36
|
+
* @throws {MissingRequiredParamsError} If required parameters are missing
|
|
37
|
+
*/
|
|
38
|
+
validateInputs(inputs: IparamInputs, iparamDefns: IparamDefns): IparamInputs;
|
|
39
|
+
/**
|
|
40
|
+
* Validates a single parameter value against its parameter definition.
|
|
41
|
+
* @param {IparamDefn} param - The parameter definition
|
|
42
|
+
* @param {any} value - The value to validate
|
|
43
|
+
* @param {string} [sectionDotParam] - Optional section.param identifier for error messages (e.g. processing_fee_configuration.fee_percentage)
|
|
44
|
+
* @throws {Error} If the value is invalid
|
|
45
|
+
*/
|
|
46
|
+
validateParamValue(param: IparamDefn, value: unknown, sectionDotParam?: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Requires value to be a non-empty string, optionally with max length.
|
|
49
|
+
* @throws {Error} If not a string, empty after trim, or exceeds maxLength
|
|
50
|
+
*/
|
|
51
|
+
private requireNonEmptyString;
|
|
52
|
+
/**
|
|
53
|
+
* Requires value to be one of the allowed options.
|
|
54
|
+
*/
|
|
55
|
+
private requireInOptions;
|
|
56
|
+
private validateNumber;
|
|
57
|
+
private validateBoolean;
|
|
58
|
+
private validateUrl;
|
|
59
|
+
private validateDate;
|
|
60
|
+
private validateDropdown;
|
|
61
|
+
private validateMultiselectDropdown;
|
|
62
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
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
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.IparamInputsValidator = void 0;
|
|
37
|
+
const errors_1 = require("../errors");
|
|
38
|
+
const internal_logger_1 = require("../logger/internal-logger");
|
|
39
|
+
const iparam_constants_1 = require("./iparam-constants");
|
|
40
|
+
const validatorUtils = __importStar(require("./validator-utils"));
|
|
41
|
+
/**
|
|
42
|
+
* Validator for parameter inputs
|
|
43
|
+
* Validates input values against the sectioned iparamDefns
|
|
44
|
+
*/
|
|
45
|
+
class IparamInputsValidator {
|
|
46
|
+
constructor(fileSystem) {
|
|
47
|
+
this.fileSystem = fileSystem;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validates whether the value counts as a provided input.
|
|
51
|
+
* Not whether it is allowed by iparam definitions—that is handled by {@link IparamInputsValidator.validateInputs}.
|
|
52
|
+
*/
|
|
53
|
+
static isProvidedIparamInputValue(value) {
|
|
54
|
+
if (value === undefined || value === null)
|
|
55
|
+
return false;
|
|
56
|
+
if (typeof value === 'string')
|
|
57
|
+
return value.trim() !== '';
|
|
58
|
+
if (Array.isArray(value))
|
|
59
|
+
return value.length > 0;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validates and loads the iparam inputs from a file (no validation against iparamDefns).
|
|
64
|
+
* Only ensures the file is valid JSON and the root value is an object.
|
|
65
|
+
* @param {string} path - Path to the iparams.local.json file
|
|
66
|
+
* @returns {IparamInputs} The loaded inputs
|
|
67
|
+
* @throws {Error} If the file does not exist, cannot be read or parsed, or root is not an object
|
|
68
|
+
*/
|
|
69
|
+
validateAndGetIparamInputsFile(path) {
|
|
70
|
+
validatorUtils.ensureFileExists(this.fileSystem, path);
|
|
71
|
+
try {
|
|
72
|
+
const inputs = JSON.parse(this.fileSystem.readFileSync(path, 'utf8'));
|
|
73
|
+
if (typeof inputs !== 'object' || Array.isArray(inputs)) {
|
|
74
|
+
throw new Error('iparams.local.json must be an object');
|
|
75
|
+
}
|
|
76
|
+
return inputs;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error instanceof SyntaxError) {
|
|
80
|
+
throw new Error(`Failed to parse iparams.local.json: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Failed to read iparams.local.json: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ensures the inputs value is a plain object
|
|
87
|
+
* @param {any} inputs - The value to check
|
|
88
|
+
* @throws {Error} If inputs is not a plain object
|
|
89
|
+
*/
|
|
90
|
+
ensureInputsIsObject(inputs) {
|
|
91
|
+
if (inputs === null || typeof inputs !== 'object' || Array.isArray(inputs)) {
|
|
92
|
+
throw new Error('Inputs must be an object');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Validates inputs against the iparamDefns (without applying defaults)
|
|
97
|
+
* Only validates provided values, does not include defaults in the result
|
|
98
|
+
* @param {IparamInputs} inputs - The input values to validate
|
|
99
|
+
* @param {IparamDefns} iparamDefns - The iparamDefns
|
|
100
|
+
* @returns {IparamInputs} The validated inputs (only provided values, no defaults)
|
|
101
|
+
* @throws {Error} If validation fails
|
|
102
|
+
* @throws {MissingRequiredParamsError} If required parameters are missing
|
|
103
|
+
*/
|
|
104
|
+
validateInputs(inputs, iparamDefns) {
|
|
105
|
+
this.ensureInputsIsObject(inputs);
|
|
106
|
+
const validatedInputs = {};
|
|
107
|
+
const missingRequired = [];
|
|
108
|
+
const sections = iparamDefns.installation_parameters?.sections ?? [];
|
|
109
|
+
const sectionByName = new Map(sections.map((section) => [section.name, section]));
|
|
110
|
+
for (const section of sections) {
|
|
111
|
+
const sectionInputs = inputs[section.name];
|
|
112
|
+
if (sectionInputs !== undefined && sectionInputs !== null) {
|
|
113
|
+
if (typeof sectionInputs !== 'object' || Array.isArray(sectionInputs)) {
|
|
114
|
+
throw new Error(`Section "${section.name}": inputs must be an object`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const sectionObject = {};
|
|
118
|
+
for (const param of section.parameters) {
|
|
119
|
+
const sectionDotParam = `${section.name}.${param.name}`;
|
|
120
|
+
const value = sectionInputs?.[param.name];
|
|
121
|
+
if (IparamInputsValidator.isProvidedIparamInputValue(value)) {
|
|
122
|
+
this.validateParamValue(param, value, sectionDotParam);
|
|
123
|
+
sectionObject[param.name] = value;
|
|
124
|
+
}
|
|
125
|
+
else if (param.required && (param.default === undefined || param.default === null)) {
|
|
126
|
+
missingRequired.push(sectionDotParam);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (Object.keys(sectionObject).length > 0) {
|
|
130
|
+
validatedInputs[section.name] = sectionObject;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (missingRequired.length > 0) {
|
|
134
|
+
throw new errors_1.MissingRequiredParamsError(missingRequired);
|
|
135
|
+
}
|
|
136
|
+
for (const [sectionName, sectionInputs] of Object.entries(inputs)) {
|
|
137
|
+
const section = sectionByName.get(sectionName);
|
|
138
|
+
if (!section) {
|
|
139
|
+
internal_logger_1.__logger.warn(`Unknown section "${sectionName}" will be ignored`);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (sectionInputs === undefined || sectionInputs === null)
|
|
143
|
+
continue;
|
|
144
|
+
if (typeof sectionInputs !== 'object' || Array.isArray(sectionInputs))
|
|
145
|
+
continue;
|
|
146
|
+
const knownParamNames = new Set(section.parameters.map((param) => param.name));
|
|
147
|
+
for (const key of Object.keys(sectionInputs)) {
|
|
148
|
+
if (!knownParamNames.has(key)) {
|
|
149
|
+
internal_logger_1.__logger.warn(`Unknown parameter "${sectionName}.${key}" will be ignored`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return validatedInputs;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Validates a single parameter value against its parameter definition.
|
|
157
|
+
* @param {IparamDefn} param - The parameter definition
|
|
158
|
+
* @param {any} value - The value to validate
|
|
159
|
+
* @param {string} [sectionDotParam] - Optional section.param identifier for error messages (e.g. processing_fee_configuration.fee_percentage)
|
|
160
|
+
* @throws {Error} If the value is invalid
|
|
161
|
+
*/
|
|
162
|
+
validateParamValue(param, value, sectionDotParam = param.name) {
|
|
163
|
+
switch (param.type) {
|
|
164
|
+
case 'NUMBER':
|
|
165
|
+
this.validateNumber(sectionDotParam, value);
|
|
166
|
+
break;
|
|
167
|
+
case 'TEXT':
|
|
168
|
+
this.requireNonEmptyString(sectionDotParam, value, iparam_constants_1.IPARAM_VALUE_MAX_LENGTHS.TEXT);
|
|
169
|
+
break;
|
|
170
|
+
case 'SECRET':
|
|
171
|
+
this.requireNonEmptyString(sectionDotParam, value, iparam_constants_1.IPARAM_VALUE_MAX_LENGTHS.SECRET);
|
|
172
|
+
break;
|
|
173
|
+
case 'BOOLEAN':
|
|
174
|
+
this.validateBoolean(sectionDotParam, value);
|
|
175
|
+
break;
|
|
176
|
+
case 'URL':
|
|
177
|
+
this.validateUrl(sectionDotParam, value);
|
|
178
|
+
break;
|
|
179
|
+
case 'DATE':
|
|
180
|
+
this.validateDate(sectionDotParam, value);
|
|
181
|
+
break;
|
|
182
|
+
case 'DROPDOWN':
|
|
183
|
+
this.validateDropdown(param, value, sectionDotParam);
|
|
184
|
+
break;
|
|
185
|
+
case 'MULTISELECT_DROPDOWN':
|
|
186
|
+
this.validateMultiselectDropdown(param, value, sectionDotParam);
|
|
187
|
+
break;
|
|
188
|
+
default: {
|
|
189
|
+
const exhaustive = param.type;
|
|
190
|
+
throw new Error(`Parameter "${sectionDotParam}": unknown type "${exhaustive}"`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Requires value to be a non-empty string, optionally with max length.
|
|
196
|
+
* @throws {Error} If not a string, empty after trim, or exceeds maxLength
|
|
197
|
+
*/
|
|
198
|
+
requireNonEmptyString(paramName, value, maxLength) {
|
|
199
|
+
if (!validatorUtils.isNonEmptyString(value)) {
|
|
200
|
+
throw new Error(`Parameter "${paramName}": value must be a non-empty string`);
|
|
201
|
+
}
|
|
202
|
+
if (maxLength !== undefined && value.length > maxLength) {
|
|
203
|
+
throw new Error(`Parameter "${paramName}": value cannot exceed ${maxLength} characters`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Requires value to be one of the allowed options.
|
|
208
|
+
*/
|
|
209
|
+
requireInOptions(paramName, value, options) {
|
|
210
|
+
if (options.length === 0 || options.includes(value))
|
|
211
|
+
return;
|
|
212
|
+
throw new Error(`Parameter "${paramName}": value must be one of: ${options.join(', ')}`);
|
|
213
|
+
}
|
|
214
|
+
validateNumber(paramName, value) {
|
|
215
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
216
|
+
throw new Error(`Parameter "${paramName}": value must be a finite number`);
|
|
217
|
+
}
|
|
218
|
+
if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
|
|
219
|
+
throw new Error(`Parameter "${paramName}": value must be within the range of ${Number.MIN_SAFE_INTEGER} to ${Number.MAX_SAFE_INTEGER}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
validateBoolean(paramName, value) {
|
|
223
|
+
if (typeof value !== 'boolean') {
|
|
224
|
+
throw new Error(`Parameter "${paramName}": value must be a boolean`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
validateUrl(paramName, value) {
|
|
228
|
+
this.requireNonEmptyString(paramName, value, iparam_constants_1.IPARAM_VALUE_MAX_LENGTHS.URL);
|
|
229
|
+
let parsed;
|
|
230
|
+
try {
|
|
231
|
+
parsed = new URL(value);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
throw new Error(`Parameter "${paramName}": value must be a valid URL`);
|
|
235
|
+
}
|
|
236
|
+
if (!iparam_constants_1.IPARAM_URL_ALLOWED_PROTOCOLS.some((p) => p === parsed.protocol)) {
|
|
237
|
+
throw new Error(`Parameter "${paramName}": value must be a valid URL with protocol ${iparam_constants_1.IPARAM_URL_ALLOWED_PROTOCOLS.join(' or ')}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
validateDate(paramName, value) {
|
|
241
|
+
this.requireNonEmptyString(paramName, value, iparam_constants_1.IPARAM_VALUE_MAX_LENGTHS.DATE);
|
|
242
|
+
const str = value;
|
|
243
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(str)) {
|
|
244
|
+
throw new Error(`Parameter "${paramName}": value must be a valid date in YYYY-MM-DD format (e.g., 2025-12-31)`);
|
|
245
|
+
}
|
|
246
|
+
const [y, m, d] = str.split('-').map(Number);
|
|
247
|
+
const dateObj = new Date(Date.UTC(y, m - 1, d));
|
|
248
|
+
if (isNaN(dateObj.getTime()) ||
|
|
249
|
+
dateObj.getUTCFullYear() !== y ||
|
|
250
|
+
dateObj.getUTCMonth() !== m - 1 ||
|
|
251
|
+
dateObj.getUTCDate() !== d) {
|
|
252
|
+
throw new Error(`Parameter "${paramName}": value must be a valid date in YYYY-MM-DD format (e.g., 2025-12-31)`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
validateDropdown(param, value, sectionDotParam) {
|
|
256
|
+
if (typeof value !== 'string') {
|
|
257
|
+
throw new Error(`Parameter "${sectionDotParam}": value must be a string for DROPDOWN type`);
|
|
258
|
+
}
|
|
259
|
+
this.requireNonEmptyString(sectionDotParam, value);
|
|
260
|
+
if (param.options)
|
|
261
|
+
this.requireInOptions(sectionDotParam, value, param.options);
|
|
262
|
+
}
|
|
263
|
+
validateMultiselectDropdown(param, value, sectionDotParam) {
|
|
264
|
+
if (!Array.isArray(value)) {
|
|
265
|
+
throw new Error(`Parameter "${sectionDotParam}": value must be an array of strings for MULTISELECT_DROPDOWN type`);
|
|
266
|
+
}
|
|
267
|
+
if (value.length === 0) {
|
|
268
|
+
throw new Error(`Parameter "${sectionDotParam}": value must be a non-empty array of strings for MULTISELECT_DROPDOWN type`);
|
|
269
|
+
}
|
|
270
|
+
const seenTrimmed = new Set();
|
|
271
|
+
for (let i = 0; i < value.length; i++) {
|
|
272
|
+
try {
|
|
273
|
+
this.requireNonEmptyString(sectionDotParam, value[i]);
|
|
274
|
+
if (param.options)
|
|
275
|
+
this.requireInOptions(sectionDotParam, value[i], param.options);
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
const msg = error.message.replace(/\bvalue\b/g, `value array item at index ${i}`);
|
|
279
|
+
throw new Error(msg);
|
|
280
|
+
}
|
|
281
|
+
const trimmed = value[i].trim();
|
|
282
|
+
if (seenTrimmed.has(trimmed)) {
|
|
283
|
+
throw new Error(`Parameter "${sectionDotParam}": multiselect value must not contain duplicate entries`);
|
|
284
|
+
}
|
|
285
|
+
seenTrimmed.add(trimmed);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
exports.IparamInputsValidator = IparamInputsValidator;
|