@emasoft/svg-matrix 1.0.30 → 1.0.31

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 (45) hide show
  1. package/bin/svg-matrix.js +310 -61
  2. package/bin/svglinter.cjs +102 -3
  3. package/bin/svgm.js +236 -27
  4. package/package.json +1 -1
  5. package/src/animation-optimization.js +137 -17
  6. package/src/animation-references.js +123 -6
  7. package/src/arc-length.js +213 -4
  8. package/src/bezier-analysis.js +217 -21
  9. package/src/bezier-intersections.js +275 -12
  10. package/src/browser-verify.js +237 -4
  11. package/src/clip-path-resolver.js +168 -0
  12. package/src/convert-path-data.js +479 -28
  13. package/src/css-specificity.js +73 -10
  14. package/src/douglas-peucker.js +219 -2
  15. package/src/flatten-pipeline.js +284 -26
  16. package/src/geometry-to-path.js +250 -25
  17. package/src/gjk-collision.js +236 -33
  18. package/src/index.js +261 -3
  19. package/src/inkscape-support.js +86 -28
  20. package/src/logger.js +48 -3
  21. package/src/marker-resolver.js +278 -74
  22. package/src/mask-resolver.js +265 -66
  23. package/src/matrix.js +44 -5
  24. package/src/mesh-gradient.js +352 -102
  25. package/src/off-canvas-detection.js +382 -13
  26. package/src/path-analysis.js +192 -18
  27. package/src/path-data-plugins.js +309 -5
  28. package/src/path-optimization.js +129 -5
  29. package/src/path-simplification.js +188 -32
  30. package/src/pattern-resolver.js +454 -106
  31. package/src/polygon-clip.js +324 -1
  32. package/src/svg-boolean-ops.js +226 -9
  33. package/src/svg-collections.js +7 -5
  34. package/src/svg-flatten.js +386 -62
  35. package/src/svg-parser.js +179 -8
  36. package/src/svg-rendering-context.js +235 -6
  37. package/src/svg-toolbox.js +45 -8
  38. package/src/svg2-polyfills.js +40 -10
  39. package/src/transform-decomposition.js +258 -32
  40. package/src/transform-optimization.js +259 -13
  41. package/src/transforms2d.js +82 -9
  42. package/src/transforms3d.js +62 -10
  43. package/src/use-symbol-resolver.js +286 -42
  44. package/src/vector.js +64 -8
  45. package/src/verification.js +392 -1
package/bin/svgm.js CHANGED
@@ -121,6 +121,10 @@ let config = { ...DEFAULT_CONFIG };
121
121
  * @returns {void}
122
122
  */
123
123
  function log(msg) {
124
+ // Why: Validate parameter to prevent runtime errors with null/undefined
125
+ if (typeof msg !== "string") {
126
+ throw new TypeError(`log: expected string, got ${typeof msg}`);
127
+ }
124
128
  if (!config.quiet) console.log(msg);
125
129
  }
126
130
 
@@ -130,6 +134,11 @@ function log(msg) {
130
134
  * @returns {void}
131
135
  */
132
136
  function logError(msg) {
137
+ // Why: Validate parameter to prevent runtime errors with null/undefined
138
+ if (typeof msg !== "string") {
139
+ console.error(`${colors.red}error:${colors.reset} Invalid error message type: ${typeof msg}`);
140
+ return;
141
+ }
133
142
  console.error(`${colors.red}error:${colors.reset} ${msg}`);
134
143
  }
135
144
 
@@ -288,6 +297,10 @@ const DEFAULT_PIPELINE = [
288
297
  * @returns {string} Normalized path
289
298
  */
290
299
  function normalizePath(p) {
300
+ // Why: Validate parameter to prevent runtime errors with null/undefined
301
+ if (typeof p !== "string") {
302
+ throw new TypeError(`normalizePath: expected string, got ${typeof p}`);
303
+ }
291
304
  return p.replace(/\\/g, "/");
292
305
  }
293
306
 
@@ -297,6 +310,10 @@ function normalizePath(p) {
297
310
  * @returns {string} Absolute normalized path
298
311
  */
299
312
  function resolvePath(p) {
313
+ // Why: Validate parameter to prevent runtime errors with null/undefined
314
+ if (typeof p !== "string") {
315
+ throw new TypeError(`resolvePath: expected string, got ${typeof p}`);
316
+ }
300
317
  return isAbsolute(p)
301
318
  ? normalizePath(p)
302
319
  : normalizePath(resolve(process.cwd(), p));
@@ -308,6 +325,8 @@ function resolvePath(p) {
308
325
  * @returns {boolean} True if directory exists
309
326
  */
310
327
  function isDir(p) {
328
+ // Why: Validate parameter to prevent runtime errors with null/undefined
329
+ if (typeof p !== "string") return false;
311
330
  try {
312
331
  return statSync(p).isDirectory();
313
332
  } catch {
@@ -321,6 +340,8 @@ function isDir(p) {
321
340
  * @returns {boolean} True if file exists
322
341
  */
323
342
  function isFile(p) {
343
+ // Why: Validate parameter to prevent runtime errors with null/undefined
344
+ if (typeof p !== "string") return false;
324
345
  try {
325
346
  return statSync(p).isFile();
326
347
  } catch {
@@ -334,6 +355,10 @@ function isFile(p) {
334
355
  * @returns {void}
335
356
  */
336
357
  function ensureDir(dir) {
358
+ // Why: Validate parameter to prevent runtime errors with null/undefined
359
+ if (typeof dir !== "string") {
360
+ throw new TypeError(`ensureDir: expected string, got ${typeof dir}`);
361
+ }
337
362
  if (!existsSync(dir)) {
338
363
  mkdirSync(dir, { recursive: true });
339
364
  }
@@ -347,14 +372,32 @@ function ensureDir(dir) {
347
372
  * @returns {string[]} Array of SVG file paths
348
373
  */
349
374
  function getSvgFiles(dir, recursive = false, exclude = []) {
375
+ // Why: Validate parameters to prevent runtime errors
376
+ if (typeof dir !== "string") {
377
+ throw new TypeError(`getSvgFiles: expected string dir, got ${typeof dir}`);
378
+ }
379
+ if (!existsSync(dir)) {
380
+ throw new Error(`getSvgFiles: directory does not exist: ${dir}`);
381
+ }
382
+ if (!Array.isArray(exclude)) {
383
+ throw new TypeError(`getSvgFiles: expected array exclude, got ${typeof exclude}`);
384
+ }
385
+
350
386
  const files = [];
351
387
  function scan(d) {
352
388
  for (const entry of readdirSync(d, { withFileTypes: true })) {
353
389
  const fullPath = join(d, entry.name);
354
390
  // Check exclusion patterns
355
391
  const shouldExclude = exclude.some((pattern) => {
356
- const regex = new RegExp(pattern);
357
- return regex.test(fullPath) || regex.test(entry.name);
392
+ try {
393
+ // Why: Wrap RegExp constructor in try-catch to handle invalid patterns
394
+ const regex = new RegExp(pattern);
395
+ return regex.test(fullPath) || regex.test(entry.name);
396
+ } catch (_e) {
397
+ // Why: Catch invalid regex patterns (unused error marked with underscore per ESLint)
398
+ logError(`Invalid exclusion pattern: ${pattern}`);
399
+ return false;
400
+ }
358
401
  });
359
402
  if (shouldExclude) continue;
360
403
 
@@ -381,6 +424,16 @@ function getSvgFiles(dir, recursive = false, exclude = []) {
381
424
  * @returns {Promise<string>} Optimized SVG content
382
425
  */
383
426
  async function optimizeSvg(content, options = {}) {
427
+ // Why: Validate parameters to prevent runtime errors
428
+ if (typeof content !== "string") {
429
+ throw new TypeError(`optimizeSvg: expected string content, got ${typeof content}`);
430
+ }
431
+ if (content.length === 0) {
432
+ throw new Error("optimizeSvg: content is empty");
433
+ }
434
+ if (options !== null && typeof options !== "object") {
435
+ throw new TypeError(`optimizeSvg: expected object options, got ${typeof options}`);
436
+ }
384
437
  const doc = parseSVG(content);
385
438
  const pipeline = DEFAULT_PIPELINE;
386
439
 
@@ -464,6 +517,10 @@ async function optimizeSvg(content, options = {}) {
464
517
  * @returns {string} Minified XML
465
518
  */
466
519
  function minifyXml(xml) {
520
+ // Why: Validate parameter to prevent runtime errors
521
+ if (typeof xml !== "string") {
522
+ throw new TypeError(`minifyXml: expected string, got ${typeof xml}`);
523
+ }
467
524
  return (
468
525
  xml
469
526
  // Remove newlines and collapse whitespace between tags
@@ -480,6 +537,13 @@ function minifyXml(xml) {
480
537
  * @returns {string} Prettified XML
481
538
  */
482
539
  function prettifyXml(xml, indent = 2) {
540
+ // Why: Validate parameters to prevent runtime errors
541
+ if (typeof xml !== "string") {
542
+ throw new TypeError(`prettifyXml: expected string xml, got ${typeof xml}`);
543
+ }
544
+ if (typeof indent !== "number" || indent < 0 || indent > 16) {
545
+ throw new RangeError(`prettifyXml: indent must be number 0-16, got ${indent}`);
546
+ }
483
547
  // Simple XML prettifier
484
548
  const indentStr = " ".repeat(indent);
485
549
  let formatted = "";
@@ -523,6 +587,14 @@ function prettifyXml(xml, indent = 2) {
523
587
  * @returns {string} Data URI string
524
588
  */
525
589
  function toDataUri(content, format) {
590
+ // Why: Validate parameters to prevent runtime errors
591
+ if (typeof content !== "string") {
592
+ throw new TypeError(`toDataUri: expected string content, got ${typeof content}`);
593
+ }
594
+ const validFormats = ["base64", "enc", "unenc"];
595
+ if (!validFormats.includes(format)) {
596
+ throw new Error(`toDataUri: format must be one of ${validFormats.join(", ")}, got ${format}`);
597
+ }
526
598
  if (format === "base64") {
527
599
  return (
528
600
  "data:image/svg+xml;base64," + Buffer.from(content).toString("base64")
@@ -545,10 +617,30 @@ function toDataUri(content, format) {
545
617
  * @returns {Promise<Object>} Processing result with success status and metrics
546
618
  */
547
619
  async function processFile(inputPath, outputPath, options) {
620
+ // Why: Validate parameters to prevent runtime errors
621
+ if (typeof inputPath !== "string") {
622
+ return { success: false, error: `Invalid input path: ${typeof inputPath}`, inputPath };
623
+ }
624
+ if (typeof outputPath !== "string") {
625
+ return { success: false, error: `Invalid output path: ${typeof outputPath}`, inputPath };
626
+ }
627
+ if (options !== null && typeof options !== "object") {
628
+ return { success: false, error: `Invalid options: ${typeof options}`, inputPath };
629
+ }
630
+
548
631
  try {
549
632
  let content = readFileSync(inputPath, "utf8");
550
633
  const originalSize = Buffer.byteLength(content);
551
634
 
635
+ // Why: Validate file size to prevent processing extremely large files
636
+ if (originalSize > CONSTANTS.MAX_FILE_SIZE_BYTES) {
637
+ return {
638
+ success: false,
639
+ error: `File too large: ${originalSize} bytes (max ${CONSTANTS.MAX_FILE_SIZE_BYTES} bytes)`,
640
+ inputPath
641
+ };
642
+ }
643
+
552
644
  // Apply embedding if enabled
553
645
  if (options.embed) {
554
646
  const doc = parseSVG(content);
@@ -594,7 +686,10 @@ async function processFile(inputPath, outputPath, options) {
594
686
  }
595
687
 
596
688
  const savings = originalSize - optimizedSize;
597
- const percent = ((savings / originalSize) * 100).toFixed(1);
689
+ // Why: Handle case where optimizedSize > originalSize (negative savings)
690
+ const percent = originalSize > 0
691
+ ? ((savings / originalSize) * 100).toFixed(1)
692
+ : "0.0";
598
693
 
599
694
  return {
600
695
  success: true,
@@ -716,6 +811,11 @@ function showPlugins() {
716
811
  * @returns {Object} Parsed configuration
717
812
  */
718
813
  function loadConfigFile(configPath) {
814
+ // Why: Validate parameter to prevent runtime errors
815
+ if (typeof configPath !== "string") {
816
+ logError(`Invalid config path: expected string, got ${typeof configPath}`);
817
+ process.exit(CONSTANTS.EXIT_ERROR);
818
+ }
719
819
  try {
720
820
  const absolutePath = resolvePath(configPath);
721
821
  if (!existsSync(absolutePath)) {
@@ -723,7 +823,14 @@ function loadConfigFile(configPath) {
723
823
  process.exit(CONSTANTS.EXIT_ERROR);
724
824
  }
725
825
  const content = readFileSync(absolutePath, "utf8");
726
- const loadedConfig = yaml.load(content);
826
+ // Why: Use safeLoad to prevent arbitrary code execution via YAML (security fix)
827
+ const loadedConfig = yaml.load(content, { schema: yaml.FAILSAFE_SCHEMA });
828
+
829
+ // Why: Validate loaded config is an object to prevent runtime errors
830
+ if (loadedConfig === null || typeof loadedConfig !== "object") {
831
+ logError(`Invalid config file: expected object, got ${typeof loadedConfig}`);
832
+ process.exit(CONSTANTS.EXIT_ERROR);
833
+ }
727
834
 
728
835
  // Convert YAML config structure to CLI config structure
729
836
  const result = {};
@@ -780,12 +887,42 @@ function loadConfigFile(configPath) {
780
887
  // ============================================================================
781
888
  // ARGUMENT PARSING
782
889
  // ============================================================================
890
+ /**
891
+ * Helper to safely get next argument with bounds checking.
892
+ * @param {string[]} args - Arguments array
893
+ * @param {number} i - Current index
894
+ * @param {string} flag - Flag name for error message
895
+ * @returns {string} Next argument value
896
+ */
897
+ function getNextArg(args, i, flag) {
898
+ // Why: Validate parameters to prevent runtime errors
899
+ if (!Array.isArray(args)) {
900
+ logError(`getNextArg: expected array args, got ${typeof args}`);
901
+ process.exit(CONSTANTS.EXIT_ERROR);
902
+ }
903
+ if (typeof i !== "number" || i < 0) {
904
+ logError(`getNextArg: expected non-negative number i, got ${typeof i}`);
905
+ process.exit(CONSTANTS.EXIT_ERROR);
906
+ }
907
+ // Why: Validate bounds to prevent accessing undefined arguments
908
+ if (i + 1 >= args.length) {
909
+ logError(`${flag} requires a value`);
910
+ process.exit(CONSTANTS.EXIT_ERROR);
911
+ }
912
+ return args[i + 1];
913
+ }
914
+
783
915
  /**
784
916
  * Parse command-line arguments.
785
917
  * @param {string[]} args - Command-line arguments
786
918
  * @returns {Object} Parsed configuration object
787
919
  */
788
920
  function parseArgs(args) {
921
+ // Why: Validate parameter to prevent runtime errors
922
+ if (!Array.isArray(args)) {
923
+ logError(`parseArgs: expected array, got ${typeof args}`);
924
+ process.exit(CONSTANTS.EXIT_ERROR);
925
+ }
789
926
  let cfg = { ...DEFAULT_CONFIG };
790
927
  const inputs = [];
791
928
  let i = 0;
@@ -826,12 +963,16 @@ function parseArgs(args) {
826
963
 
827
964
  case "-s":
828
965
  case "--string":
829
- cfg.string = args[++i];
966
+ // Why: Use helper to safely get next argument with bounds checking
967
+ cfg.string = getNextArg(args, i, arg);
968
+ i++;
830
969
  break;
831
970
 
832
971
  case "-f":
833
972
  case "--folder":
834
- cfg.folder = args[++i];
973
+ // Why: Use helper to safely get next argument with bounds checking
974
+ cfg.folder = getNextArg(args, i, arg);
975
+ i++;
835
976
  break;
836
977
 
837
978
  case "-o":
@@ -850,11 +991,24 @@ function parseArgs(args) {
850
991
 
851
992
  case "-p":
852
993
  case "--precision":
853
- cfg.precision = parseInt(args[++i], 10);
994
+ // Why: Use helper to safely get next argument with bounds checking
995
+ {
996
+ const precisionArg = getNextArg(args, i, arg);
997
+ const parsed = parseInt(precisionArg, 10);
998
+ // Why: Validate parseInt result to prevent NaN values
999
+ if (isNaN(parsed)) {
1000
+ logError(`--precision requires a valid number, got: ${precisionArg}`);
1001
+ process.exit(CONSTANTS.EXIT_ERROR);
1002
+ }
1003
+ cfg.precision = parsed;
1004
+ i++;
1005
+ }
854
1006
  break;
855
1007
 
856
1008
  case "--datauri":
857
- cfg.datauri = args[++i];
1009
+ // Why: Use helper to safely get next argument with bounds checking
1010
+ cfg.datauri = getNextArg(args, i, arg);
1011
+ i++;
858
1012
  break;
859
1013
 
860
1014
  case "--multipass":
@@ -866,11 +1020,24 @@ function parseArgs(args) {
866
1020
  break;
867
1021
 
868
1022
  case "--indent":
869
- cfg.indent = parseInt(args[++i], 10);
1023
+ // Why: Use helper to safely get next argument with bounds checking
1024
+ {
1025
+ const indentArg = getNextArg(args, i, arg);
1026
+ const parsed = parseInt(indentArg, 10);
1027
+ // Why: Validate parseInt result to prevent NaN values
1028
+ if (isNaN(parsed)) {
1029
+ logError(`--indent requires a valid number, got: ${indentArg}`);
1030
+ process.exit(CONSTANTS.EXIT_ERROR);
1031
+ }
1032
+ cfg.indent = parsed;
1033
+ i++;
1034
+ }
870
1035
  break;
871
1036
 
872
1037
  case "--eol":
873
- cfg.eol = args[++i];
1038
+ // Why: Use helper to safely get next argument with bounds checking
1039
+ cfg.eol = getNextArg(args, i, arg);
1040
+ i++;
874
1041
  break;
875
1042
 
876
1043
  case "--final-newline":
@@ -902,12 +1069,14 @@ function parseArgs(args) {
902
1069
 
903
1070
  case "--preserve-ns":
904
1071
  {
905
- const val = argValue || args[++i];
1072
+ // Why: Use helper to safely get next argument with bounds checking
1073
+ const val = argValue || getNextArg(args, i, arg);
1074
+ if (!argValue) i++; // Only increment if we used getNextArg
906
1075
  if (!val) {
907
1076
  logError(
908
1077
  "--preserve-ns requires a comma-separated list of namespaces",
909
1078
  );
910
- process.exit(1);
1079
+ process.exit(CONSTANTS.EXIT_ERROR);
911
1080
  }
912
1081
  cfg.preserveNamespaces = val
913
1082
  .split(",")
@@ -934,7 +1103,9 @@ function parseArgs(args) {
934
1103
  break;
935
1104
 
936
1105
  case "--config":
937
- cfg.configFile = args[++i];
1106
+ // Why: Use helper to safely get next argument with bounds checking
1107
+ cfg.configFile = getNextArg(args, i, arg);
1108
+ i++;
938
1109
  break;
939
1110
 
940
1111
  case "--embed":
@@ -961,7 +1132,9 @@ function parseArgs(args) {
961
1132
  break;
962
1133
 
963
1134
  case "--embed-svg-mode":
964
- cfg.embedExternalSVGMode = args[++i];
1135
+ // Why: Use helper to safely get next argument with bounds checking
1136
+ cfg.embedExternalSVGMode = getNextArg(args, i, arg);
1137
+ i++;
965
1138
  break;
966
1139
 
967
1140
  case "--embed-css":
@@ -995,15 +1168,39 @@ function parseArgs(args) {
995
1168
  break;
996
1169
 
997
1170
  case "--embed-max-depth":
998
- cfg.embedMaxRecursionDepth = parseInt(args[++i], 10);
1171
+ // Why: Use helper to safely get next argument with bounds checking
1172
+ {
1173
+ const depthArg = getNextArg(args, i, arg);
1174
+ const parsed = parseInt(depthArg, 10);
1175
+ // Why: Validate parseInt result to prevent NaN values
1176
+ if (isNaN(parsed)) {
1177
+ logError(`--embed-max-depth requires a valid number, got: ${depthArg}`);
1178
+ process.exit(CONSTANTS.EXIT_ERROR);
1179
+ }
1180
+ cfg.embedMaxRecursionDepth = parsed;
1181
+ i++;
1182
+ }
999
1183
  break;
1000
1184
 
1001
1185
  case "--embed-timeout":
1002
- cfg.embedTimeout = parseInt(args[++i], 10);
1186
+ // Why: Use helper to safely get next argument with bounds checking
1187
+ {
1188
+ const timeoutArg = getNextArg(args, i, arg);
1189
+ const parsed = parseInt(timeoutArg, 10);
1190
+ // Why: Validate parseInt result to prevent NaN values
1191
+ if (isNaN(parsed)) {
1192
+ logError(`--embed-timeout requires a valid number, got: ${timeoutArg}`);
1193
+ process.exit(CONSTANTS.EXIT_ERROR);
1194
+ }
1195
+ cfg.embedTimeout = parsed;
1196
+ i++;
1197
+ }
1003
1198
  break;
1004
1199
 
1005
1200
  case "--embed-on-missing":
1006
- cfg.embedOnMissingResource = args[++i];
1201
+ // Why: Use helper to safely get next argument with bounds checking
1202
+ cfg.embedOnMissingResource = getNextArg(args, i, arg);
1203
+ i++;
1007
1204
  break;
1008
1205
 
1009
1206
  default:
@@ -1019,23 +1216,25 @@ function parseArgs(args) {
1019
1216
  cfg.inputs = inputs;
1020
1217
 
1021
1218
  // Validate numeric arguments
1219
+ // Why: Check type first before using isNaN to prevent incorrect validation
1022
1220
  if (
1023
- cfg.precision !== undefined &&
1024
- (isNaN(cfg.precision) || cfg.precision < 0 || cfg.precision > 20)
1221
+ cfg.precision !== undefined && cfg.precision !== null &&
1222
+ (typeof cfg.precision !== "number" || isNaN(cfg.precision) ||
1223
+ cfg.precision < CONSTANTS.MIN_PRECISION || cfg.precision > CONSTANTS.MAX_PRECISION)
1025
1224
  ) {
1026
- logError("--precision must be a number between 0 and 20");
1225
+ logError(`--precision must be a number between ${CONSTANTS.MIN_PRECISION} and ${CONSTANTS.MAX_PRECISION}`);
1027
1226
  process.exit(CONSTANTS.EXIT_ERROR);
1028
1227
  }
1029
1228
  if (
1030
- cfg.indent !== undefined &&
1031
- (isNaN(cfg.indent) || cfg.indent < 0 || cfg.indent > 16)
1229
+ cfg.indent !== undefined && cfg.indent !== null &&
1230
+ (typeof cfg.indent !== "number" || isNaN(cfg.indent) || cfg.indent < 0 || cfg.indent > 16)
1032
1231
  ) {
1033
1232
  logError("--indent must be a number between 0 and 16");
1034
1233
  process.exit(CONSTANTS.EXIT_ERROR);
1035
1234
  }
1036
1235
  if (
1037
- cfg.embedMaxRecursionDepth !== undefined &&
1038
- (isNaN(cfg.embedMaxRecursionDepth) ||
1236
+ cfg.embedMaxRecursionDepth !== undefined && cfg.embedMaxRecursionDepth !== null &&
1237
+ (typeof cfg.embedMaxRecursionDepth !== "number" || isNaN(cfg.embedMaxRecursionDepth) ||
1039
1238
  cfg.embedMaxRecursionDepth < 1 ||
1040
1239
  cfg.embedMaxRecursionDepth > 100)
1041
1240
  ) {
@@ -1043,8 +1242,8 @@ function parseArgs(args) {
1043
1242
  process.exit(CONSTANTS.EXIT_ERROR);
1044
1243
  }
1045
1244
  if (
1046
- cfg.embedTimeout !== undefined &&
1047
- (isNaN(cfg.embedTimeout) ||
1245
+ cfg.embedTimeout !== undefined && cfg.embedTimeout !== null &&
1246
+ (typeof cfg.embedTimeout !== "number" || isNaN(cfg.embedTimeout) ||
1048
1247
  cfg.embedTimeout < 1000 ||
1049
1248
  cfg.embedTimeout > 300000)
1050
1249
  ) {
@@ -1099,6 +1298,11 @@ function parseArgs(args) {
1099
1298
  * @returns {Promise<void>}
1100
1299
  */
1101
1300
  async function main() {
1301
+ // Why: Validate process.argv exists and is an array to prevent runtime errors
1302
+ if (!Array.isArray(process.argv)) {
1303
+ logError("process.argv is not an array");
1304
+ process.exit(CONSTANTS.EXIT_ERROR);
1305
+ }
1102
1306
  const args = process.argv.slice(2);
1103
1307
 
1104
1308
  if (args.length === 0) {
@@ -1209,7 +1413,12 @@ async function main() {
1209
1413
  if (config.output === "-") {
1210
1414
  outputPath = "-";
1211
1415
  } else if (Array.isArray(config.output)) {
1212
- outputPath = config.output[i] || config.output[0];
1416
+ // Why: Bounds check when accessing array to prevent undefined values
1417
+ if (config.output.length === 0) {
1418
+ logError("Output array is empty");
1419
+ process.exit(CONSTANTS.EXIT_ERROR);
1420
+ }
1421
+ outputPath = config.output[i] || config.output[config.output.length - 1];
1213
1422
  } else if (files.length > 1 || isDir(resolvePath(config.output))) {
1214
1423
  // Multiple files or output is a directory
1215
1424
  outputPath = join(resolvePath(config.output), basename(inputPath));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emasoft/svg-matrix",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "description": "Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js",
5
5
  "type": "module",
6
6
  "main": "src/index.js",