@pptb/types 1.1.3-beta.2 → 1.2.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/lib/validate.js +62 -40
- package/package.json +1 -1
package/lib/validate.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/** @typedef {{ name: string; url?: string }} Contributor */
|
|
10
|
-
/** @typedef {{ "connect-src"?: string[]; "script-src"?: string[]; "style-src"?: string[]; "img-src"?: string[]; "font-src"?: string[]; "frame-src"?: string[] }} CspExceptions */
|
|
10
|
+
/** @typedef {{ "connect-src"?: string[]; "script-src"?: string[]; "style-src"?: string[]; "img-src"?: string[]; "font-src"?: string[]; "frame-src"?: string[]; "media-src"?: string[] }} CspExceptions */
|
|
11
11
|
/** @typedef {{ repository?: string; website?: string; funding?: string; readmeUrl?: string }} Configurations */
|
|
12
12
|
/** @typedef {{ multiConnection?: "required" | "optional" | "none"; minAPI?: string }} Features */
|
|
13
13
|
/**
|
|
@@ -85,6 +85,16 @@ function validateIconPath(fieldName, iconPath, errors) {
|
|
|
85
85
|
errors.push(`${fieldName} must be a relative path (e.g., 'icon.svg' or 'icons/icon.svg')`);
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
|
+
// Reject Windows absolute paths like "C:\..." or "C:/"
|
|
89
|
+
if (/^[a-zA-Z]:/.test(iconPath)) {
|
|
90
|
+
errors.push(`${fieldName} must be a relative path (e.g., 'icon.svg' or 'icons/icon.svg')`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Reject backslashes — icon paths must use forward slashes
|
|
94
|
+
if (iconPath.includes("\\")) {
|
|
95
|
+
errors.push(`${fieldName} must use forward slashes, not backslashes (e.g., 'icons/icon.svg')`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
88
98
|
if (iconPath.includes("..")) {
|
|
89
99
|
errors.push(`${fieldName} cannot contain '..' (path traversal not allowed)`);
|
|
90
100
|
return;
|
|
@@ -205,14 +215,14 @@ async function validatePackageJson(packageJson, options = {}) {
|
|
|
205
215
|
}
|
|
206
216
|
|
|
207
217
|
// Funding validation (optional but recommended)
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
if (configs.funding) {
|
|
219
|
+
if (!isValidUrl(configs.funding)) {
|
|
220
|
+
warnings.push("configurations.funding has an invalid URL format");
|
|
221
|
+
} else if (!skipUrlChecks) {
|
|
222
|
+
const accessible = await isUrlAccessible(configs.funding);
|
|
223
|
+
if (!accessible) {
|
|
224
|
+
warnings.push("configurations.funding URL is not accessible");
|
|
225
|
+
}
|
|
216
226
|
}
|
|
217
227
|
}
|
|
218
228
|
|
|
@@ -233,45 +243,57 @@ async function validatePackageJson(packageJson, options = {}) {
|
|
|
233
243
|
|
|
234
244
|
// CSP Exceptions validation (optional, but validated if present)
|
|
235
245
|
if (packageJson.cspExceptions) {
|
|
236
|
-
const
|
|
246
|
+
const cspExceptions = packageJson.cspExceptions;
|
|
237
247
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
248
|
+
if (typeof cspExceptions !== "object" || cspExceptions === null || Array.isArray(cspExceptions)) {
|
|
249
|
+
errors.push("cspExceptions must be an object mapping CSP directives to arrays of strings");
|
|
250
|
+
} else {
|
|
251
|
+
const validCspDirectives = ["connect-src", "script-src", "style-src", "img-src", "font-src", "frame-src", "media-src"];
|
|
242
252
|
|
|
243
|
-
|
|
244
|
-
if (!
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
const values = packageJson.cspExceptions?.[/** @type {keyof CspExceptions} */ (directive)];
|
|
248
|
-
if (values && !Array.isArray(values)) {
|
|
249
|
-
errors.push(`CSP directive "${directive}" must be an array of strings`);
|
|
250
|
-
} else if (values && values.length === 0) {
|
|
251
|
-
errors.push(`CSP directive "${directive}" cannot be an empty array`);
|
|
253
|
+
const hasAnyDirectives = Object.keys(cspExceptions).length > 0;
|
|
254
|
+
if (!hasAnyDirectives) {
|
|
255
|
+
errors.push("cspExceptions cannot be empty. If CSP exceptions are not needed, remove the cspExceptions field");
|
|
252
256
|
}
|
|
253
|
-
|
|
257
|
+
|
|
258
|
+
Object.keys(cspExceptions).forEach((directive) => {
|
|
259
|
+
if (!validCspDirectives.includes(directive)) {
|
|
260
|
+
warnings.push(`Unknown CSP directive: ${directive}`);
|
|
261
|
+
}
|
|
262
|
+
const values = cspExceptions[/** @type {keyof CspExceptions} */ (directive)];
|
|
263
|
+
if (values && !Array.isArray(values)) {
|
|
264
|
+
errors.push(`CSP directive "${directive}" must be an array of strings`);
|
|
265
|
+
} else if (values && values.length === 0) {
|
|
266
|
+
errors.push(`CSP directive "${directive}" cannot be an empty array`);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
254
270
|
}
|
|
255
271
|
|
|
256
272
|
// Features validation (optional, but validated if present)
|
|
257
|
-
if (packageJson.features) {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
if (packageJson.features !== undefined) {
|
|
274
|
+
const features = packageJson.features;
|
|
275
|
+
|
|
276
|
+
if (features === null || typeof features !== "object" || Array.isArray(features)) {
|
|
277
|
+
errors.push("features must be a non-array object with optional 'multiConnection' and 'minAPI' properties");
|
|
278
|
+
} else {
|
|
279
|
+
const VALID_FEATURE_KEYS = ["multiConnection", "minAPI"];
|
|
280
|
+
const featureKeys = Object.keys(features);
|
|
281
|
+
const invalidKeys = featureKeys.filter((key) => !VALID_FEATURE_KEYS.includes(key));
|
|
282
|
+
|
|
283
|
+
if (invalidKeys.length > 0) {
|
|
284
|
+
errors.push(`features can only contain ${VALID_FEATURE_KEYS.map((k) => `'${k}'`).join(", ")} properties. Invalid properties: ${invalidKeys.join(", ")}`);
|
|
285
|
+
}
|
|
265
286
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
287
|
+
if (features.multiConnection === undefined) {
|
|
288
|
+
errors.push("features.multiConnection is required when features object is provided");
|
|
289
|
+
} else if (!VALID_MULTI_CONNECTION_VALUES.includes(features.multiConnection)) {
|
|
290
|
+
errors.push(`features.multiConnection must be one of: ${VALID_MULTI_CONNECTION_VALUES.join(", ")}`);
|
|
291
|
+
}
|
|
271
292
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
293
|
+
if (features.minAPI !== undefined) {
|
|
294
|
+
if (typeof features.minAPI !== "string" || !SEMVER_REGEX.test(features.minAPI)) {
|
|
295
|
+
errors.push("features.minAPI must be a valid semantic version string (e.g., '1.0.0')");
|
|
296
|
+
}
|
|
275
297
|
}
|
|
276
298
|
}
|
|
277
299
|
}
|