@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.
Files changed (2) hide show
  1. package/lib/validate.js +62 -40
  2. 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 (!configs.funding) {
209
- warnings.push("configurations.funding is not set; consider adding a sponsorship or funding URL to support contributors");
210
- } else if (!isValidUrl(configs.funding)) {
211
- warnings.push("configurations.funding has an invalid URL format");
212
- } else if (!skipUrlChecks) {
213
- const accessible = await isUrlAccessible(configs.funding);
214
- if (!accessible) {
215
- warnings.push("configurations.funding URL is not accessible");
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 validCspDirectives = ["connect-src", "script-src", "style-src", "img-src", "font-src", "frame-src"];
246
+ const cspExceptions = packageJson.cspExceptions;
237
247
 
238
- const hasAnyDirectives = Object.keys(packageJson.cspExceptions).length > 0;
239
- if (!hasAnyDirectives) {
240
- errors.push("cspExceptions cannot be empty. If CSP exceptions are not needed, remove the cspExceptions field");
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
- Object.keys(packageJson.cspExceptions).forEach((directive) => {
244
- if (!validCspDirectives.includes(directive)) {
245
- warnings.push(`Unknown CSP directive: ${directive}`);
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 VALID_FEATURE_KEYS = ["multiConnection", "minAPI"];
259
- const featureKeys = Object.keys(packageJson.features);
260
- const invalidKeys = featureKeys.filter((key) => !VALID_FEATURE_KEYS.includes(key));
261
-
262
- if (invalidKeys.length > 0) {
263
- errors.push(`features can only contain ${VALID_FEATURE_KEYS.map((k) => `'${k}'`).join(", ")} properties. Invalid properties: ${invalidKeys.join(", ")}`);
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
- if (packageJson.features.multiConnection === undefined) {
267
- errors.push("features.multiConnection is required when features object is provided");
268
- } else if (!VALID_MULTI_CONNECTION_VALUES.includes(packageJson.features.multiConnection)) {
269
- errors.push(`features.multiConnection must be one of: ${VALID_MULTI_CONNECTION_VALUES.join(", ")}`);
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
- if (packageJson.features.minAPI !== undefined) {
273
- if (typeof packageJson.features.minAPI !== "string" || !SEMVER_REGEX.test(packageJson.features.minAPI)) {
274
- errors.push("features.minAPI must be a valid semantic version string (e.g., '1.0.0')");
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pptb/types",
3
- "version": "1.1.3-beta.2",
3
+ "version": "1.2.0",
4
4
  "description": "Type definitions for Power Platform ToolBox APIs and validity checks for tool packages",
5
5
  "main": "index.d.ts",
6
6
  "types": "index.d.ts",