@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/svglinter.cjs CHANGED
@@ -75,6 +75,10 @@ function getTomlParser() {
75
75
  * @returns {Object} Parsed object with sections as nested objects
76
76
  */
77
77
  function parseIni(content) {
78
+ // Why: Parameter validation prevents runtime errors from invalid input
79
+ if (typeof content !== "string") {
80
+ throw new TypeError("parseIni expects a string argument");
81
+ }
78
82
  const result = {};
79
83
  let currentSection = result;
80
84
 
@@ -97,7 +101,8 @@ function parseIni(content) {
97
101
 
98
102
  // Key=value pair
99
103
  const eqIndex = trimmed.indexOf("=");
100
- if (eqIndex > 0) {
104
+ // Why: Use >= 0 to handle edge case where "=" is first character (eqIndex === 0)
105
+ if (eqIndex >= 0) {
101
106
  const key = trimmed.slice(0, eqIndex).trim();
102
107
  let value = trimmed.slice(eqIndex + 1).trim();
103
108
 
@@ -134,6 +139,10 @@ function parseIni(content) {
134
139
  * @returns {string} JSON content with comments removed
135
140
  */
136
141
  function stripJsonComments(str) {
142
+ // Why: Parameter validation prevents runtime errors from invalid input
143
+ if (typeof str !== "string") {
144
+ throw new TypeError("stripJsonComments expects a string argument");
145
+ }
137
146
  let result = "";
138
147
  let i = 0;
139
148
  const len = str.length;
@@ -1061,6 +1070,15 @@ function getNestedValue(obj, keyPath) {
1061
1070
  * @throws {Error} If parsing fails or format is unsupported
1062
1071
  */
1063
1072
  function parseConfigContent(content, filepath) {
1073
+ // Why: Parameter validation prevents runtime errors from invalid input
1074
+ if (typeof content !== "string") {
1075
+ throw new TypeError("parseConfigContent expects content to be a string");
1076
+ }
1077
+ if (typeof filepath !== "string" || filepath.trim() === "") {
1078
+ throw new TypeError(
1079
+ "parseConfigContent expects filepath to be a non-empty string",
1080
+ );
1081
+ }
1064
1082
  const ext = path.extname(filepath).toLowerCase();
1065
1083
  const basename = path.basename(filepath).toLowerCase();
1066
1084
 
@@ -1277,6 +1295,10 @@ function loadConfig(configPath) {
1277
1295
  * @returns {Object} Parsed arguments object
1278
1296
  */
1279
1297
  function parseArgs(argv) {
1298
+ // Why: Parameter validation prevents runtime errors from invalid input
1299
+ if (!Array.isArray(argv) || argv.length < 2) {
1300
+ throw new TypeError("parseArgs expects argv to be an array with at least 2 elements");
1301
+ }
1280
1302
  const args = {
1281
1303
  files: [],
1282
1304
  fix: false,
@@ -1954,6 +1976,12 @@ ${c("bold", "Usage:")}
1954
1976
  * @returns {void}
1955
1977
  */
1956
1978
  function explainRule(code) {
1979
+ // Why: Parameter validation prevents runtime errors from invalid input
1980
+ if (typeof code !== "string" || code.trim() === "") {
1981
+ console.error(c("red", "Error: Rule code must be a non-empty string"));
1982
+ console.error(`Run 'svglinter --list-rules' to see all available rules.`);
1983
+ process.exit(2);
1984
+ }
1957
1985
  const rule = RULES[code];
1958
1986
 
1959
1987
  if (!rule) {
@@ -2667,6 +2695,10 @@ ${c("dim", "Run svglinter --help for more information.")}
2667
2695
  * @returns {void}
2668
2696
  */
2669
2697
  function showStats(results) {
2698
+ // Why: Parameter validation prevents runtime errors from invalid input
2699
+ if (!Array.isArray(results)) {
2700
+ throw new TypeError("showStats expects results to be an array");
2701
+ }
2670
2702
  const stats = {};
2671
2703
  const totalFiles = results.length;
2672
2704
  let filesWithIssues = 0;
@@ -2748,6 +2780,10 @@ ${c("bold", "Summary:")}
2748
2780
  * @returns {Promise<string[]>} Array of resolved SVG file paths
2749
2781
  */
2750
2782
  async function expandFiles(patterns) {
2783
+ // Why: Parameter validation prevents runtime errors from invalid input
2784
+ if (!Array.isArray(patterns)) {
2785
+ throw new TypeError("expandFiles expects patterns to be an array");
2786
+ }
2751
2787
  const files = new Set();
2752
2788
 
2753
2789
  for (const pattern of patterns) {
@@ -3009,7 +3045,13 @@ async function findSvgFilesWithExt(dir, ext, visited = new Set()) {
3009
3045
  * @returns {boolean} True if rule should be ignored
3010
3046
  */
3011
3047
  function shouldIgnoreRule(code, ignoreList) {
3012
- if (!ignoreList || ignoreList.length === 0) return false;
3048
+ // Why: Parameter validation prevents runtime errors from invalid input
3049
+ if (typeof code !== "string") {
3050
+ return false; // Graceful degradation for invalid code
3051
+ }
3052
+ if (!Array.isArray(ignoreList) || ignoreList.length === 0) {
3053
+ return false;
3054
+ }
3013
3055
 
3014
3056
  for (const pattern of ignoreList) {
3015
3057
  // Exact match
@@ -3032,7 +3074,13 @@ function shouldIgnoreRule(code, ignoreList) {
3032
3074
  * @returns {boolean} True if rule is selected
3033
3075
  */
3034
3076
  function isRuleSelected(code, selectList) {
3035
- if (!selectList || selectList.length === 0) return true;
3077
+ // Why: Parameter validation prevents runtime errors from invalid input
3078
+ if (typeof code !== "string") {
3079
+ return false; // Graceful degradation for invalid code
3080
+ }
3081
+ if (!Array.isArray(selectList) || selectList.length === 0) {
3082
+ return true;
3083
+ }
3036
3084
 
3037
3085
  for (const pattern of selectList) {
3038
3086
  if (code === pattern) return true;
@@ -3119,6 +3167,17 @@ function parseInlineDisables(content) {
3119
3167
  * @returns {boolean} True if issue is disabled
3120
3168
  */
3121
3169
  function isDisabledByInline(issue, disabledRanges) {
3170
+ // Why: Parameter validation prevents runtime errors from invalid input
3171
+ if (
3172
+ typeof issue !== "object" ||
3173
+ issue === null ||
3174
+ typeof issue.type !== "string"
3175
+ ) {
3176
+ return false; // Graceful degradation for invalid issue
3177
+ }
3178
+ if (!Array.isArray(disabledRanges)) {
3179
+ return false; // Graceful degradation for invalid ranges
3180
+ }
3122
3181
  const code = TYPE_TO_CODE[issue.type] || "";
3123
3182
  const line = issue.line || 0;
3124
3183
 
@@ -3941,6 +4000,15 @@ function formatResults(
3941
4000
  showIgnored,
3942
4001
  maxLineWidth = 80,
3943
4002
  ) {
4003
+ // Why: Parameter validation prevents runtime errors from invalid input
4004
+ if (!Array.isArray(results)) {
4005
+ throw new TypeError("formatResults expects results to be an array");
4006
+ }
4007
+ if (typeof outputFormat !== "string" || outputFormat.trim() === "") {
4008
+ throw new TypeError(
4009
+ "formatResults expects outputFormat to be a non-empty string",
4010
+ );
4011
+ }
3944
4012
  switch (outputFormat) {
3945
4013
  case "json":
3946
4014
  return formatJson(results);
@@ -4696,6 +4764,13 @@ if (require.main === module) {
4696
4764
  * @returns {Promise<Array>} Array of results
4697
4765
  */
4698
4766
  async function lint(patterns, options = {}) {
4767
+ // Why: Parameter validation prevents runtime errors from invalid input
4768
+ if (!Array.isArray(patterns)) {
4769
+ throw new TypeError("lint expects patterns to be an array");
4770
+ }
4771
+ if (typeof options !== "object" || options === null) {
4772
+ throw new TypeError("lint expects options to be an object");
4773
+ }
4699
4774
  // validateSVGAsync is dynamically imported per-file via lintFile
4700
4775
  const files = await expandFiles(patterns);
4701
4776
  const results = [];
@@ -4715,6 +4790,13 @@ async function lint(patterns, options = {}) {
4715
4790
  * @returns {Promise<Object>} Lint result
4716
4791
  */
4717
4792
  async function lintFile(filePath, options = {}) {
4793
+ // Why: Parameter validation prevents runtime errors from invalid input
4794
+ if (typeof filePath !== "string" || filePath.trim() === "") {
4795
+ throw new TypeError("lintFile expects filePath to be a non-empty string");
4796
+ }
4797
+ if (typeof options !== "object" || options === null) {
4798
+ throw new TypeError("lintFile expects options to be an object");
4799
+ }
4718
4800
  const { validateSVGAsync } = await import("../src/svg-toolbox.js");
4719
4801
 
4720
4802
  const content = await readFileWithTimeout(filePath);
@@ -4783,6 +4865,13 @@ async function lintFile(filePath, options = {}) {
4783
4865
  * @returns {Promise<Object>} Lint result
4784
4866
  */
4785
4867
  async function lintString(svgContent, options = {}) {
4868
+ // Why: Parameter validation prevents runtime errors from invalid input
4869
+ if (typeof svgContent !== "string") {
4870
+ throw new TypeError("lintString expects svgContent to be a string");
4871
+ }
4872
+ if (typeof options !== "object" || options === null) {
4873
+ throw new TypeError("lintString expects options to be an object");
4874
+ }
4786
4875
  const { validateSVGAsync } = await import("../src/svg-toolbox.js");
4787
4876
 
4788
4877
  const processedContent = normalizeLineEndings(stripBOM(svgContent));
@@ -4852,6 +4941,16 @@ async function lintString(svgContent, options = {}) {
4852
4941
  * @returns {string} Formatted output
4853
4942
  */
4854
4943
  function format(results, formatType = "stylish", options = {}) {
4944
+ // Why: Parameter validation prevents runtime errors from invalid input
4945
+ if (!Array.isArray(results)) {
4946
+ throw new TypeError("format expects results to be an array");
4947
+ }
4948
+ if (typeof formatType !== "string" || formatType.trim() === "") {
4949
+ throw new TypeError("format expects formatType to be a non-empty string");
4950
+ }
4951
+ if (typeof options !== "object" || options === null) {
4952
+ throw new TypeError("format expects options to be an object");
4953
+ }
4855
4954
  // BUG FIX: Validate maxLineWidth bounds for library users (CLI validates in main())
4856
4955
  let maxLineWidth = options.maxLineWidth || DEFAULT_MAX_LINE_WIDTH;
4857
4956
  if (maxLineWidth < MIN_MAX_LINE_WIDTH) {