@emasoft/svg-matrix 1.0.6 → 1.0.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emasoft/svg-matrix",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
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",
@@ -9,7 +9,8 @@
9
9
  "test:browser": "node test/browser-verify.mjs",
10
10
  "test:playwright": "node test/playwright-diagnose.js",
11
11
  "ci-test": "npm ci && npm test",
12
- "prepublishOnly": "npm test"
12
+ "prepublishOnly": "npm test",
13
+ "postinstall": "node scripts/postinstall.js || true"
13
14
  },
14
15
  "repository": {
15
16
  "type": "git",
@@ -42,11 +43,20 @@
42
43
  },
43
44
  "author": "Emasoft",
44
45
  "license": "MIT",
46
+ "homepage": "https://github.com/Emasoft/SVG-MATRIX#readme",
47
+ "bugs": {
48
+ "url": "https://github.com/Emasoft/SVG-MATRIX/issues"
49
+ },
50
+ "bin": {
51
+ "svg-matrix": "./bin/svg-matrix.js"
52
+ },
45
53
  "engines": {
46
54
  "node": ">=24.0.0"
47
55
  },
48
56
  "files": [
49
57
  "src/",
58
+ "bin/",
59
+ "scripts/",
50
60
  "LICENSE",
51
61
  "README.md"
52
62
  ],
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # bootstrap_repo.sh
5
+ # Creates a new repository (owner/repo), populates it with SVG-MATRIX files,
6
+ # commits and pushes initial branch, and optionally sets OIDC secrets.
7
+ #
8
+ # Requirements:
9
+ # - gh CLI authenticated (gh auth status)
10
+ # - git installed
11
+ # - node/npm not required to run script (only to run tests later)
12
+ #
13
+ # Usage:
14
+ # OWNER=Emasoft REPO=SVG-MATRIX ./bootstrap_repo.sh
15
+ # Optional env vars:
16
+ # VISIBILITY (public|private) default: public
17
+ # BRANCH default: main
18
+ # NPM_OIDC_TOKEN_URL and NPM_OIDC_AUDIENCE - optional secrets to set in the repo (if provided they'll be added)
19
+
20
+ OWNER="${OWNER:-Emasoft}"
21
+ REPO="${REPO:-SVG-MATRIX}"
22
+ VISIBILITY="${VISIBILITY:-public}"
23
+ BRANCH="${BRANCH:-main}"
24
+ DIR="${REPO}"
25
+
26
+ echo "Bootstrap repo: ${OWNER}/${REPO} (visibility=${VISIBILITY}, branch=${BRANCH})"
27
+
28
+ # check prerequisites
29
+ if ! command -v gh >/dev/null 2>&1; then
30
+ echo "gh CLI not found. Install and authenticate (gh auth login) and try again."
31
+ exit 1
32
+ fi
33
+ if ! command -v git >/dev/null 2>&1; then
34
+ echo "git not found. Please install git and try again."
35
+ exit 1
36
+ fi
37
+
38
+ # check gh auth
39
+ if ! gh auth status >/dev/null 2>&1; then
40
+ echo "gh CLI not authenticated. Run: gh auth login"
41
+ gh auth status || true
42
+ exit 1
43
+ fi
44
+
45
+ # create local dir (safe)
46
+ if [ -d "${DIR}" ]; then
47
+ echo "Directory ${DIR} already exists. Please remove or move it and re-run, or run in a different location."
48
+ exit 1
49
+ fi
50
+
51
+ mkdir "${DIR}"
52
+ cd "${DIR}"
53
+
54
+ # create files (each file is written with a here-doc)
55
+ mkdir -p src test .github/workflows scripts
56
+
57
+ cat > .gitignore <<'EOF'
58
+ node_modules
59
+ dist
60
+ .DS_Store
61
+ .env
62
+ EOF
63
+
64
+ cat > package.json <<'EOF'
65
+ {
66
+ "name": "svg-matrix",
67
+ "version": "1.0.0",
68
+ "description": "Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js",
69
+ "type": "module",
70
+ "main": "src/index.js",
71
+ "scripts": {
72
+ "test": "node test/examples.js",
73
+ "ci-test": "npm ci && npm test",
74
+ "prepublishOnly": "npm test"
75
+ },
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "https://github.com/Emasoft/SVG-MATRIX.git"
79
+ },
80
+ "keywords": [
81
+ "matrix",
82
+ "vector",
83
+ "arbitrary-precision",
84
+ "decimal",
85
+ "linear-algebra",
86
+ "affine",
87
+ "transform",
88
+ "svg",
89
+ "geometry"
90
+ ],
91
+ "author": "Emasoft",
92
+ "license": "MIT",
93
+ "dependencies": {
94
+ "decimal.js": "^11.4.3"
95
+ },
96
+ "devDependencies": {}
97
+ }
98
+ EOF
99
+
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Post-install welcome message for @emasoft/svg-matrix
4
+ * Displays a compact summary of available features after npm install.
5
+ *
6
+ * Design principles:
7
+ * - Fail silently: npm install must never fail because of postinstall.
8
+ * Any error is caught and swallowed with exit(0).
9
+ * - Be respectful of terminal real estate: Keep the message compact.
10
+ * Users want to continue working, not read documentation.
11
+ * - Cross-platform: Handle Windows legacy terminals that don't support ANSI.
12
+ * - Standards-compliant: Respect NO_COLOR environment variable.
13
+ *
14
+ * @module scripts/postinstall
15
+ * @license MIT
16
+ */
17
+
18
+ import { readFileSync } from 'fs';
19
+ import { fileURLToPath } from 'url';
20
+ import { dirname, join } from 'path';
21
+
22
+ // ============================================================================
23
+ // VERSION DETECTION
24
+ // ============================================================================
25
+ /**
26
+ * Read version from package.json dynamically.
27
+ *
28
+ * Why dynamic reading instead of hardcoding:
29
+ * - Avoids version mismatch bugs where postinstall shows wrong version.
30
+ * - Single source of truth in package.json.
31
+ *
32
+ * Why wrapped in try-catch:
33
+ * - postinstall scripts must NEVER fail - this would break npm install.
34
+ * - Return 'unknown' gracefully if file read fails for any reason.
35
+ *
36
+ * @returns {string} Version string or 'unknown' on error
37
+ */
38
+ function getVersion() {
39
+ try {
40
+ // Why: ESM doesn't have __dirname, must derive from import.meta.url
41
+ const __dirname = dirname(fileURLToPath(import.meta.url));
42
+ // Why: Go up one level because this script is in scripts/ subfolder
43
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
44
+ return pkg.version || 'unknown';
45
+ } catch {
46
+ // Why: Return safe default - never throw from postinstall
47
+ return 'unknown';
48
+ }
49
+ }
50
+
51
+ // ============================================================================
52
+ // COLOR SUPPORT DETECTION
53
+ // ============================================================================
54
+ /**
55
+ * Check if colors should be disabled.
56
+ *
57
+ * Why this function:
58
+ * - Not all terminals support ANSI escape codes.
59
+ * - Showing raw escape codes is worse than no colors.
60
+ * - NO_COLOR is an emerging standard: https://no-color.org
61
+ *
62
+ * @returns {boolean} True if colors should be disabled
63
+ */
64
+ function shouldDisableColors() {
65
+ // Why: NO_COLOR standard - presence (any value) means disable colors
66
+ // Check for presence, not truthiness, per spec
67
+ if (process.env.NO_COLOR !== undefined) {
68
+ return true;
69
+ }
70
+
71
+ // Why: Windows cmd.exe (not PowerShell/Terminal) doesn't support ANSI by default
72
+ // Check for known ANSI-capable Windows terminals
73
+ if (process.platform === 'win32') {
74
+ const supportsAnsi =
75
+ process.env.WT_SESSION || // Windows Terminal sets this
76
+ process.env.ConEmuANSI === 'ON' || // ConEmu explicitly signals support
77
+ process.env.TERM_PROGRAM || // VS Code, Hyper, etc. set this
78
+ process.env.ANSICON; // ANSICON utility for legacy cmd
79
+ // Why: Disable colors unless we detect ANSI support
80
+ return !supportsAnsi;
81
+ }
82
+
83
+ // Why: Unix terminals generally support ANSI, so enable by default
84
+ return false;
85
+ }
86
+
87
+ /**
88
+ * ANSI color codes for terminal output.
89
+ *
90
+ * Why function instead of constant:
91
+ * - Need to check color support at runtime, not module load time.
92
+ * - Environment variables may change between import and execution.
93
+ *
94
+ * @param {boolean} disabled - Whether colors are disabled
95
+ * @returns {Object} Color code object
96
+ */
97
+ function getColors(disabled) {
98
+ // Why: Return empty strings instead of undefined to avoid string concat issues
99
+ if (disabled) {
100
+ return {
101
+ reset: '', bright: '', dim: '',
102
+ cyan: '', green: '', yellow: '',
103
+ magenta: '', blue: '', white: '',
104
+ };
105
+ }
106
+ // Why: Use standard ANSI SGR (Select Graphic Rendition) codes
107
+ // Format: ESC[<code>m where ESC is \x1b
108
+ return {
109
+ reset: '\x1b[0m', // Reset all attributes
110
+ bright: '\x1b[1m', // Bold/bright
111
+ dim: '\x1b[2m', // Dim/faint
112
+ cyan: '\x1b[36m', // Cyan foreground
113
+ green: '\x1b[32m', // Green foreground
114
+ yellow: '\x1b[33m', // Yellow foreground
115
+ magenta: '\x1b[35m', // Magenta foreground
116
+ blue: '\x1b[34m', // Blue foreground
117
+ white: '\x1b[37m', // White foreground
118
+ };
119
+ }
120
+
121
+ // ============================================================================
122
+ // UNICODE SUPPORT DETECTION
123
+ // ============================================================================
124
+ /**
125
+ * Check if terminal likely supports Unicode box drawing characters.
126
+ *
127
+ * Why this check:
128
+ * - Some older terminals or SSH sessions may not render Unicode correctly.
129
+ * - Windows cmd.exe before Windows 10 has limited Unicode support.
130
+ * - Better to show ASCII fallback than broken box characters.
131
+ *
132
+ * @returns {boolean} True if Unicode is likely supported
133
+ */
134
+ function supportsUnicode() {
135
+ // Why: Check common environment indicators for Unicode support
136
+
137
+ // Windows legacy cmd.exe typically has codepage issues
138
+ if (process.platform === 'win32') {
139
+ // Modern Windows Terminal supports Unicode
140
+ if (process.env.WT_SESSION) return true;
141
+ // Check for UTF-8 codepage
142
+ if (process.env.CHCP === '65001') return true;
143
+ // ConEmu and other modern terminals
144
+ if (process.env.ConEmuANSI === 'ON') return true;
145
+ if (process.env.TERM_PROGRAM) return true;
146
+ // Default to ASCII on Windows for safety
147
+ return false;
148
+ }
149
+
150
+ // Why: Unix terminals generally support Unicode if LANG/LC_CTYPE mentions UTF-8
151
+ const lang = process.env.LANG || process.env.LC_CTYPE || '';
152
+ if (lang.toLowerCase().includes('utf')) return true;
153
+
154
+ // Why: Modern terminal emulators typically support Unicode
155
+ if (process.env.TERM_PROGRAM) return true;
156
+
157
+ // Default to Unicode on Unix (most modern systems support it)
158
+ return true;
159
+ }
160
+
161
+ // ============================================================================
162
+ // CI DETECTION
163
+ // ============================================================================
164
+ /**
165
+ * Check if running in CI environment.
166
+ *
167
+ * Why skip in CI:
168
+ * - CI logs should be clean and focused on build/test output.
169
+ * - Welcome messages just add noise to CI logs.
170
+ * - CI systems can set CI=true to suppress this and similar messages.
171
+ *
172
+ * @returns {boolean} True if in CI
173
+ */
174
+ function isCI() {
175
+ // Why: Check multiple CI indicators since there's no universal standard
176
+ // Each CI system sets different environment variables
177
+ return !!(
178
+ process.env.CI || // GitHub Actions, GitLab CI, CircleCI
179
+ process.env.CONTINUOUS_INTEGRATION || // Travis CI
180
+ process.env.GITHUB_ACTIONS || // GitHub Actions (redundant with CI but explicit)
181
+ process.env.GITLAB_CI || // GitLab CI
182
+ process.env.CIRCLECI || // CircleCI
183
+ process.env.TRAVIS || // Travis CI
184
+ process.env.JENKINS_URL || // Jenkins
185
+ process.env.BUILD_ID // Various CI systems
186
+ );
187
+ }
188
+
189
+ /**
190
+ * Display the welcome message.
191
+ * Main entry point for the postinstall script.
192
+ *
193
+ * Why this design:
194
+ * - Skip silently in CI: CI environments don't need welcome messages, and
195
+ * they clutter build logs. Most CI systems set CI=true or similar.
196
+ * - Skip in non-TTY: If stdout isn't a terminal (e.g., piped to file),
197
+ * ANSI codes would be visible as garbage characters.
198
+ * - Compact message: Users just installed the package and want to continue
199
+ * their work. A brief message with the essentials respects their time.
200
+ * Full documentation belongs in README, not terminal output.
201
+ */
202
+ function showWelcome() {
203
+ // Why: CI builds don't need welcome messages, they just add noise to logs
204
+ if (isCI()) {
205
+ process.exit(0);
206
+ }
207
+
208
+ // Why: Non-TTY means output is being piped/redirected - ANSI codes would be garbage
209
+ if (!process.stdout.isTTY) {
210
+ process.exit(0);
211
+ }
212
+
213
+ const version = getVersion();
214
+ const colorsDisabled = shouldDisableColors();
215
+ const c = getColors(colorsDisabled);
216
+ const unicode = supportsUnicode();
217
+
218
+ // Why: Use ASCII box characters as fallback for terminals without Unicode
219
+ const box = unicode ? {
220
+ tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│'
221
+ } : {
222
+ tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|'
223
+ };
224
+
225
+ // Why: Keep message compact - users want to continue their work, not read a manual.
226
+ // Link to docs for those who want more. Use box drawing for visual structure.
227
+ const message = `
228
+ ${c.cyan}${box.tl}${box.h.repeat(45)}${box.tr}${c.reset}
229
+ ${c.cyan}${box.v}${c.reset} ${c.bright}@emasoft/svg-matrix${c.reset} v${version} ${c.cyan}${box.v}${c.reset}
230
+ ${c.cyan}${box.v}${c.reset} High-precision SVG matrix transforms ${c.cyan}${box.v}${c.reset}
231
+ ${c.cyan}${box.bl}${box.h.repeat(45)}${box.br}${c.reset}
232
+
233
+ ${c.green}CLI:${c.reset} npx svg-matrix flatten input.svg -o out.svg
234
+ ${c.green}API:${c.reset} import { Matrix, Transforms2D } from '@emasoft/svg-matrix'
235
+ ${c.green}Docs:${c.reset} ${c.blue}https://github.com/Emasoft/SVG-MATRIX#readme${c.reset}
236
+ `;
237
+
238
+ console.log(message);
239
+ }
240
+
241
+ // ============================================================================
242
+ // MAIN ENTRY
243
+ // ============================================================================
244
+ // Why try-catch: postinstall scripts must NEVER fail or throw.
245
+ // A failed postinstall would break `npm install` for users, which is
246
+ // absolutely unacceptable. Catch everything and exit cleanly.
247
+ try {
248
+ showWelcome();
249
+ } catch {
250
+ // Why: Silent exit - don't break npm install for a welcome message
251
+ process.exit(0);
252
+ }
@@ -41,6 +41,7 @@ import {
41
41
  parseTransformAttribute,
42
42
  transformPathData
43
43
  } from './svg-flatten.js';
44
+ import { Logger } from './logger.js';
44
45
 
45
46
  // Alias for cleaner code
46
47
  const parseTransform = parseTransformAttribute;
@@ -732,7 +733,7 @@ export function polygonToPathData(polygon, precision = 6) {
732
733
  export function resolveNestedClipPath(clipPathDef, defsMap, targetElement, ctm = null, visited = new Set(), options = {}) {
733
734
  const clipId = clipPathDef.id;
734
735
  if (clipId && visited.has(clipId)) {
735
- console.warn(`Circular clipPath reference detected: ${clipId}`);
736
+ Logger.warn(`Circular clipPath reference detected: ${clipId}`);
736
737
  return [];
737
738
  }
738
739
  if (clipId) visited.add(clipId);
package/src/index.js CHANGED
@@ -43,12 +43,13 @@ import * as UseSymbolResolver from './use-symbol-resolver.js';
43
43
  import * as MarkerResolver from './marker-resolver.js';
44
44
  import * as MeshGradient from './mesh-gradient.js';
45
45
  import * as TextToPath from './text-to-path.js';
46
+ import { Logger, LogLevel, setLogLevel, getLogLevel as getLoggerLevel, enableFileLogging, disableFileLogging } from './logger.js';
46
47
 
47
48
  /**
48
49
  * Library version
49
50
  * @constant {string}
50
51
  */
51
- export const VERSION = '1.0.5';
52
+ export const VERSION = '1.0.8';
52
53
 
53
54
  /**
54
55
  * Default precision for path output (decimal places)
@@ -83,6 +84,12 @@ export { ClipPathResolver, MaskResolver, PatternResolver };
83
84
  export { UseSymbolResolver, MarkerResolver };
84
85
  export { MeshGradient, TextToPath };
85
86
 
87
+ // ============================================================================
88
+ // LOGGING: Configurable logging control
89
+ // ============================================================================
90
+
91
+ export { Logger, LogLevel, setLogLevel, enableFileLogging, disableFileLogging };
92
+
86
93
  // ============================================================================
87
94
  // CONVENIENCE FUNCTIONS: Quick access to common operations
88
95
  // ============================================================================
@@ -409,6 +416,13 @@ export default {
409
416
  MeshGradient,
410
417
  TextToPath,
411
418
 
419
+ // Logging
420
+ Logger,
421
+ LogLevel,
422
+ setLogLevel,
423
+ enableFileLogging,
424
+ disableFileLogging,
425
+
412
426
  // Convenience functions
413
427
  translate2D,
414
428
  rotate2D,