@sakibcoolz/asset-import 0.0.0-stage → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 sakibcoolz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
- # Temporary Holding Version
1
+ # @sakibcoolz/asset-import
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Allow-list SVG sanitizer for reference underlays (no DOM): keeps shapes and paths, rejects scripts, external references and entities.
4
+
5
+ ```sh
6
+ npm install @sakibcoolz/asset-import
7
+ ```
8
+
9
+ ```ts
10
+ import { sanitizeSvg } from '@sakibcoolz/asset-import';
11
+ ```
12
+
13
+ Part of the [3D SCADA SDK](https://github.com/sakibcoolz/3d-scada-sdk). API reference: [docs/API.md](https://github.com/sakibcoolz/3d-scada-sdk/blob/develop/docs/API.md). ESM only, Node.js 20+.
14
+
15
+ The SDK visualises and simulates; it does not connect to or control PLCs or physical equipment.
16
+
17
+ MIT licence.
@@ -0,0 +1,3 @@
1
+ export { defaultSvgLimits, sanitizeSvg, toSvgMarkup } from './svg.js';
2
+ export type { SanitizedSvg, SanitizeResult, SvgError, SvgErrorCode, SvgLimits, SvgMatrix, SvgNote, SvgShape } from './svg.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // Public API of @sakibcoolz/asset-import (ADR-0012). See docs/API.md; `api.test.ts` fails when the runtime surface changes.
2
+ export { defaultSvgLimits, sanitizeSvg, toSvgMarkup } from './svg.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4HAA4H;AAC5H,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC","sourcesContent":["// Public API of @sakibcoolz/asset-import (ADR-0012). See docs/API.md; `api.test.ts` fails when the runtime surface changes.\nexport { defaultSvgLimits, sanitizeSvg, toSvgMarkup } from './svg.js';\nexport type { SanitizedSvg, SanitizeResult, SvgError, SvgErrorCode, SvgLimits, SvgMatrix, SvgNote, SvgShape } from './svg.js';\n"]}
package/dist/svg.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ export interface SvgLimits {
2
+ /** UTF-8 bytes. */
3
+ maxBytes: number;
4
+ maxElements: number;
5
+ maxDepth: number;
6
+ /** Path commands over the whole file, after shapes are turned into paths. */
7
+ maxPathCommands: number;
8
+ }
9
+ export declare const defaultSvgLimits: Readonly<SvgLimits>;
10
+ /** 2D affine transform [a b c d e f], as in SVG `matrix(a b c d e f)`. */
11
+ export type SvgMatrix = [number, number, number, number, number, number];
12
+ export interface SvgShape {
13
+ /** Absolute path data using only M, L, C, Q, A and Z. */
14
+ d: string;
15
+ transform: SvgMatrix;
16
+ /** `#rrggbb` or `none`. */
17
+ fill: string;
18
+ stroke: string;
19
+ strokeWidth: number;
20
+ opacity: number;
21
+ fillOpacity: number;
22
+ strokeOpacity: number;
23
+ fillRule: 'nonzero' | 'evenodd';
24
+ }
25
+ export interface SanitizedSvg {
26
+ viewBox: [number, number, number, number];
27
+ shapes: SvgShape[];
28
+ }
29
+ export type SvgErrorCode = 'TOO_LARGE' | 'MALFORMED_XML' | 'DOCTYPE' | 'NOT_SVG' | 'FORBIDDEN_ELEMENT' | 'FORBIDDEN_ATTRIBUTE' | 'FORBIDDEN_VALUE' | 'LIMIT_EXCEEDED' | 'MALFORMED_PATH' | 'MALFORMED_TRANSFORM' | 'INVALID_NUMBER' | 'MISSING_SIZE';
30
+ export interface SvgError {
31
+ code: SvgErrorCode;
32
+ /** Where in the document, e.g. `svg/g[0]/path[2]`. */
33
+ path: string;
34
+ message: string;
35
+ }
36
+ /** Things removed on purpose, so the user knows what will not appear. */
37
+ export interface SvgNote {
38
+ kind: 'dropped-element' | 'dropped-attribute';
39
+ name: string;
40
+ count: number;
41
+ }
42
+ export type SanitizeResult = {
43
+ ok: true;
44
+ svg: SanitizedSvg;
45
+ markup: string;
46
+ notes: SvgNote[];
47
+ } | {
48
+ ok: false;
49
+ errors: SvgError[];
50
+ };
51
+ /** Sanitizes SVG text into a flat shape model plus clean markup, or explains why it was refused. */
52
+ export declare function sanitizeSvg(text: string, limits?: Partial<SvgLimits>): SanitizeResult;
53
+ /** Clean markup written from the model only: one <path> per shape, defaults left out. */
54
+ export declare function toSvgMarkup(svg: SanitizedSvg): string;
55
+ //# sourceMappingURL=svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../src/svg.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,mBAAmB;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,SAAS,CAAqF,CAAC;AAEvI,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEzE,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,CAAC,EAAE,MAAM,CAAC;IACV,SAAS,EAAE,SAAS,CAAC;IACrB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,eAAe,GACf,SAAS,GACT,SAAS,GACT,mBAAmB,GACnB,qBAAqB,GACrB,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,GAChB,cAAc,CAAC;AAEnB,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yEAAyE;AACzE,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAsenI,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,cAAc,CASzF;AAID,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAcrD"}
package/dist/svg.js ADDED
@@ -0,0 +1,601 @@
1
+ // Allow-list SVG sanitizer (ADR-0012). No DOM: a small XML tokenizer, a security scan of the whole
2
+ // tree, then conversion of the few kept shapes into a flat model that is written back out cleanly.
3
+ export const defaultSvgLimits = { maxBytes: 512 * 1024, maxElements: 5000, maxDepth: 32, maxPathCommands: 20000 };
4
+ class SvgFailure extends Error {
5
+ code;
6
+ at;
7
+ constructor(code, at, message) {
8
+ super(message);
9
+ this.code = code;
10
+ this.at = at;
11
+ }
12
+ }
13
+ const fail = (code, at, message) => {
14
+ throw new SvgFailure(code, at, message);
15
+ };
16
+ const NAME = /[A-Za-z_][\w.:-]*/y;
17
+ const SPACE = /\s*/y;
18
+ function decodeEntities(text, at) {
19
+ return text.replace(/&([^;\s&]*);?/g, (match, body) => {
20
+ if (!match.endsWith(';'))
21
+ fail('MALFORMED_XML', at, 'A bare "&" is not allowed; write &amp;.');
22
+ const named = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'" };
23
+ if (Object.hasOwn(named, body))
24
+ return named[body];
25
+ const num = /^#(?:x([0-9a-fA-F]{1,6})|([0-9]{1,7}))$/.exec(body);
26
+ if (num) {
27
+ const code = num[1] ? parseInt(num[1], 16) : Number(num[2]);
28
+ if (code > 0 && code <= 0x10ffff)
29
+ return String.fromCodePoint(code);
30
+ }
31
+ return fail('MALFORMED_XML', at, `Unknown entity &${body}; (only the five XML entities and character references are allowed).`);
32
+ });
33
+ }
34
+ function parseXml(text, limits) {
35
+ let i = 0;
36
+ let count = 0;
37
+ const stack = [];
38
+ let root;
39
+ const at = () => stack[stack.length - 1]?.path ?? 'document';
40
+ const childIndex = new WeakMap();
41
+ if (text.charCodeAt(0) === 0xfeff)
42
+ i = 1;
43
+ const match = (re) => {
44
+ re.lastIndex = i;
45
+ const m = re.exec(text);
46
+ if (m)
47
+ i = re.lastIndex;
48
+ return m;
49
+ };
50
+ while (i < text.length) {
51
+ const lt = text.indexOf('<', i);
52
+ if (lt < 0) {
53
+ if (text.slice(i).trim())
54
+ fail('MALFORMED_XML', at(), 'Text outside the root element.');
55
+ break;
56
+ }
57
+ if (lt > i && !stack.length && text.slice(i, lt).trim())
58
+ fail('MALFORMED_XML', at(), 'Text outside the root element.');
59
+ if (lt > i && stack.length)
60
+ decodeEntities(text.slice(i, lt), at());
61
+ i = lt;
62
+ if (text.startsWith('<?xml', i) && (text[i + 5] === ' ' || text[i + 5] === '?') && !root && !stack.length) {
63
+ const end = text.indexOf('?>', i);
64
+ if (end < 0)
65
+ fail('MALFORMED_XML', at(), 'Unterminated XML declaration.');
66
+ i = end + 2;
67
+ continue;
68
+ }
69
+ if (text.startsWith('<?', i))
70
+ fail('MALFORMED_XML', at(), 'Processing instructions are not allowed.');
71
+ if (text.startsWith('<!--', i)) {
72
+ const end = text.indexOf('-->', i + 4);
73
+ if (end < 0)
74
+ fail('MALFORMED_XML', at(), 'Unterminated comment.');
75
+ i = end + 3;
76
+ continue;
77
+ }
78
+ if (/^<!doctype/i.test(text.slice(i, i + 9)) || text.startsWith('<!ENTITY', i))
79
+ fail('DOCTYPE', at(), 'DOCTYPE and entity declarations are not allowed.');
80
+ if (text.startsWith('<!', i))
81
+ fail('MALFORMED_XML', at(), 'CDATA sections and markup declarations are not allowed.');
82
+ if (text.startsWith('</', i)) {
83
+ i += 2;
84
+ const name = match(NAME)?.[0];
85
+ match(SPACE);
86
+ if (!name || text[i] !== '>')
87
+ fail('MALFORMED_XML', at(), 'Malformed end tag.');
88
+ i++;
89
+ const open = stack.pop();
90
+ if (!open || open.name !== name)
91
+ fail('MALFORMED_XML', open?.path ?? 'document', `End tag </${name}> does not match <${open?.name ?? '(none)'}>.`);
92
+ continue;
93
+ }
94
+ i++;
95
+ const name = match(NAME)?.[0];
96
+ if (!name)
97
+ fail('MALFORMED_XML', at(), 'Malformed start tag.');
98
+ if (root && !stack.length)
99
+ fail('MALFORMED_XML', at(), 'Only one root element is allowed.');
100
+ const parent = stack[stack.length - 1];
101
+ let path = name;
102
+ if (parent) {
103
+ const seen = childIndex.get(parent) ?? new Map();
104
+ childIndex.set(parent, seen);
105
+ const n = seen.get(name) ?? 0;
106
+ seen.set(name, n + 1);
107
+ path = `${parent.path}/${name}[${n}]`;
108
+ }
109
+ const el = { name: name, attrs: new Map(), children: [], path };
110
+ for (;;) {
111
+ const space = match(SPACE)[0];
112
+ if (text.startsWith('/>', i) || text[i] === '>')
113
+ break;
114
+ if (!space)
115
+ fail('MALFORMED_XML', path, 'Attributes must be separated by white space.');
116
+ const attr = match(NAME)?.[0];
117
+ if (!attr)
118
+ fail('MALFORMED_XML', path, 'Malformed attribute.');
119
+ match(SPACE);
120
+ if (text[i] !== '=')
121
+ fail('MALFORMED_XML', path, `Attribute "${attr}" has no value.`);
122
+ i++;
123
+ match(SPACE);
124
+ const quote = text[i];
125
+ if (quote !== '"' && quote !== "'")
126
+ fail('MALFORMED_XML', path, `Attribute "${attr}" must be quoted.`);
127
+ const end = text.indexOf(quote, i + 1);
128
+ if (end < 0)
129
+ fail('MALFORMED_XML', path, `Unterminated value of "${attr}".`);
130
+ const raw = text.slice(i + 1, end);
131
+ if (raw.includes('<'))
132
+ fail('MALFORMED_XML', path, `"<" in the value of "${attr}".`);
133
+ if (el.attrs.has(attr))
134
+ fail('MALFORMED_XML', path, `Duplicate attribute "${attr}".`);
135
+ el.attrs.set(attr, decodeEntities(raw, path));
136
+ i = end + 1;
137
+ }
138
+ if (++count > limits.maxElements)
139
+ fail('LIMIT_EXCEEDED', path, `More than ${limits.maxElements} elements.`);
140
+ if (stack.length + 1 > limits.maxDepth)
141
+ fail('LIMIT_EXCEEDED', path, `Nested deeper than ${limits.maxDepth} levels.`);
142
+ if (parent)
143
+ parent.children.push(el);
144
+ else
145
+ root = el;
146
+ if (text.startsWith('/>', i))
147
+ i += 2;
148
+ else {
149
+ i++;
150
+ stack.push(el);
151
+ }
152
+ }
153
+ if (stack.length)
154
+ fail('MALFORMED_XML', stack[stack.length - 1].path, `<${stack[stack.length - 1].name}> is never closed.`);
155
+ if (!root)
156
+ fail('NOT_SVG', 'document', 'No root element.');
157
+ return root;
158
+ }
159
+ // ---------------------------------------------------------------------------------------------
160
+ // Security scan and allow-lists
161
+ const local = (name) => name.slice(name.indexOf(':') + 1).toLowerCase();
162
+ const FORBIDDEN_ELEMENTS = new Set(['script', 'foreignobject', 'image', 'use', 'iframe', 'object', 'embed', 'animate', 'animatemotion', 'animatetransform', 'animatecolor', 'set', 'audio', 'video', 'canvas', 'link', 'meta', 'a', 'handler', 'listener']);
163
+ const KEPT = new Set(['svg', 'g', 'path', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon']);
164
+ const DANGEROUS_VALUE = /url\s*\(|javascript\s*:|vbscript\s*:|expression\s*\(|data\s*:/i;
165
+ const STYLE_PROPS = new Set(['fill', 'stroke', 'stroke-width', 'opacity', 'fill-opacity', 'stroke-opacity', 'fill-rule']);
166
+ const GEOMETRY = {
167
+ svg: ['viewBox', 'width', 'height'],
168
+ g: [],
169
+ path: ['d'],
170
+ rect: ['x', 'y', 'width', 'height', 'rx', 'ry'],
171
+ circle: ['cx', 'cy', 'r'],
172
+ ellipse: ['cx', 'cy', 'rx', 'ry'],
173
+ line: ['x1', 'y1', 'x2', 'y2'],
174
+ polyline: ['points'],
175
+ polygon: ['points'],
176
+ };
177
+ /** Harmless bookkeeping attributes that are removed without a note. */
178
+ const SILENT = (name) => name === 'xmlns' || name.startsWith('xmlns:') || name === 'version' || name === 'id' || name === 'xml:space' || name === 'class' || name === 'baseProfile';
179
+ function scan(el) {
180
+ if (FORBIDDEN_ELEMENTS.has(local(el.name)))
181
+ fail('FORBIDDEN_ELEMENT', el.path, `<${el.name}> is not allowed.`);
182
+ for (const [name, value] of el.attrs) {
183
+ const n = local(name);
184
+ if (n.startsWith('on'))
185
+ fail('FORBIDDEN_ATTRIBUTE', el.path, `Event handler "${name}" is not allowed.`);
186
+ if (n === 'href' || n === 'src')
187
+ fail('FORBIDDEN_ATTRIBUTE', el.path, `"${name}" (a reference to other content) is not allowed.`);
188
+ if (DANGEROUS_VALUE.test(value))
189
+ fail('FORBIDDEN_VALUE', el.path, `The value of "${name}" refers to other content (url(), data: or a script).`);
190
+ }
191
+ el.children.forEach(scan);
192
+ }
193
+ // ---------------------------------------------------------------------------------------------
194
+ // Numbers, colours, transforms
195
+ const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?(?:px|mm|cm|in|pt|pc)?$/;
196
+ function num(value, at, what, fallback) {
197
+ if (value === undefined) {
198
+ if (fallback !== undefined)
199
+ return fallback;
200
+ return fail('INVALID_NUMBER', at, `Missing ${what}.`);
201
+ }
202
+ const v = value.trim();
203
+ if (!NUMBER.test(v))
204
+ return fail('INVALID_NUMBER', at, `${what} "${value}" is not a number.`);
205
+ const n = parseFloat(v);
206
+ if (!Number.isFinite(n))
207
+ return fail('INVALID_NUMBER', at, `${what} "${value}" is not a finite number.`);
208
+ return n;
209
+ }
210
+ const round = (n) => {
211
+ const r = Math.round(n * 1e4) / 1e4;
212
+ return Object.is(r, -0) ? 0 : r;
213
+ };
214
+ const fmt = (n) => String(round(n));
215
+ const NAMED = {
216
+ black: '#000000', white: '#ffffff', red: '#ff0000', green: '#008000', blue: '#0000ff', yellow: '#ffff00', orange: '#ffa500', purple: '#800080',
217
+ gray: '#808080', grey: '#808080', silver: '#c0c0c0', maroon: '#800000', navy: '#000080', teal: '#008080', olive: '#808000', lime: '#00ff00',
218
+ aqua: '#00ffff', cyan: '#00ffff', fuchsia: '#ff00ff', magenta: '#ff00ff', brown: '#a52a2a', pink: '#ffc0cb', darkgray: '#a9a9a9', lightgray: '#d3d3d3',
219
+ };
220
+ function colour(value) {
221
+ const v = value.trim().toLowerCase();
222
+ if (v === 'none' || v === 'transparent')
223
+ return 'none';
224
+ if (v === 'currentcolor')
225
+ return '#000000';
226
+ if (Object.hasOwn(NAMED, v))
227
+ return NAMED[v];
228
+ const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(v);
229
+ if (hex)
230
+ return hex[1].length === 3 ? `#${[...hex[1]].map((c) => c + c).join('')}` : `#${hex[1]}`;
231
+ const rgb = /^rgb\(\s*([\d.]+%?)\s*,\s*([\d.]+%?)\s*,\s*([\d.]+%?)\s*\)$/.exec(v);
232
+ if (rgb) {
233
+ const channel = (c) => Math.round(Math.min(255, Math.max(0, c.endsWith('%') ? (parseFloat(c) * 255) / 100 : parseFloat(c))));
234
+ const parts = rgb.slice(1).map(channel);
235
+ if (parts.every(Number.isFinite))
236
+ return `#${parts.map((p) => p.toString(16).padStart(2, '0')).join('')}`;
237
+ }
238
+ return undefined;
239
+ }
240
+ const multiply = (p, c) => [
241
+ p[0] * c[0] + p[2] * c[1],
242
+ p[1] * c[0] + p[3] * c[1],
243
+ p[0] * c[2] + p[2] * c[3],
244
+ p[1] * c[2] + p[3] * c[3],
245
+ p[0] * c[4] + p[2] * c[5] + p[4],
246
+ p[1] * c[4] + p[3] * c[5] + p[5],
247
+ ];
248
+ const IDENTITY = [1, 0, 0, 1, 0, 0];
249
+ function parseTransform(value, at) {
250
+ let m = IDENTITY;
251
+ const re = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(([^)]*)\)\s*,?/gy;
252
+ let i = 0;
253
+ const v = value.trim();
254
+ while (i < v.length) {
255
+ re.lastIndex = i;
256
+ const part = re.exec(v);
257
+ if (!part)
258
+ return fail('MALFORMED_TRANSFORM', at, `Cannot read transform "${value}".`);
259
+ i = re.lastIndex;
260
+ const args = part[2].trim() ? part[2].trim().split(/[\s,]+/).map((a) => num(a, at, 'transform value')) : [];
261
+ const need = { matrix: [6], translate: [1, 2], scale: [1, 2], rotate: [1, 3], skewX: [1], skewY: [1] };
262
+ if (!need[part[1]].includes(args.length))
263
+ return fail('MALFORMED_TRANSFORM', at, `${part[1]}() takes ${need[part[1]].join(' or ')} values.`);
264
+ const rad = (deg) => (deg * Math.PI) / 180;
265
+ let t;
266
+ switch (part[1]) {
267
+ case 'matrix':
268
+ t = args;
269
+ break;
270
+ case 'translate':
271
+ t = [1, 0, 0, 1, args[0], args[1] ?? 0];
272
+ break;
273
+ case 'scale':
274
+ t = [args[0], 0, 0, args[1] ?? args[0], 0, 0];
275
+ break;
276
+ case 'rotate': {
277
+ const [c, s] = [Math.cos(rad(args[0])), Math.sin(rad(args[0]))];
278
+ const [cx, cy] = [args[1] ?? 0, args[2] ?? 0];
279
+ t = multiply(multiply([1, 0, 0, 1, cx, cy], [c, s, -s, c, 0, 0]), [1, 0, 0, 1, -cx, -cy]);
280
+ break;
281
+ }
282
+ case 'skewX':
283
+ t = [1, 0, Math.tan(rad(args[0])), 1, 0, 0];
284
+ break;
285
+ default: t = [1, Math.tan(rad(args[0])), 0, 1, 0, 0];
286
+ }
287
+ m = multiply(m, t);
288
+ }
289
+ return m;
290
+ }
291
+ // ---------------------------------------------------------------------------------------------
292
+ // Path data
293
+ const PARAMS = { m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7, z: 0 };
294
+ /** Parses path data into absolute M/L/C/Q/A/Z with numbers rounded to 1e-4; counts commands. */
295
+ function normalisePath(d, at, budget) {
296
+ const out = [];
297
+ let i = 0;
298
+ let cmd = '';
299
+ let [cx, cy, sx, sy] = [0, 0, 0, 0];
300
+ let lastC, lastQ;
301
+ const skip = () => { while (i < d.length && /[\s,]/.test(d[i]))
302
+ i++; };
303
+ const NUM = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/y;
304
+ const readNumber = () => {
305
+ skip();
306
+ NUM.lastIndex = i;
307
+ const m = NUM.exec(d);
308
+ if (!m)
309
+ return fail('MALFORMED_PATH', at, `Expected a number at position ${i} of the path data.`);
310
+ i = NUM.lastIndex;
311
+ const n = parseFloat(m[0]);
312
+ if (!Number.isFinite(n))
313
+ return fail('INVALID_NUMBER', at, 'Path data contains a non-finite number.');
314
+ return n;
315
+ };
316
+ const readFlag = () => {
317
+ skip();
318
+ if (d[i] === '0' || d[i] === '1')
319
+ return d[i++] === '1' ? 1 : 0;
320
+ return fail('MALFORMED_PATH', at, `Expected an arc flag (0 or 1) at position ${i}.`);
321
+ };
322
+ const emit = (s) => {
323
+ if (--budget.left < 0)
324
+ fail('LIMIT_EXCEEDED', at, 'Too many path commands.');
325
+ out.push(s);
326
+ };
327
+ skip();
328
+ if (i < d.length && !/[Mm]/.test(d[i]))
329
+ fail('MALFORMED_PATH', at, 'Path data must start with M.');
330
+ while (skip(), i < d.length) {
331
+ if (/[A-Za-z]/.test(d[i])) {
332
+ if (!Object.hasOwn(PARAMS, d[i].toLowerCase()))
333
+ fail('MALFORMED_PATH', at, `Unknown path command "${d[i]}".`);
334
+ cmd = d[i++];
335
+ }
336
+ else if (!cmd || cmd === 'z' || cmd === 'Z')
337
+ fail('MALFORMED_PATH', at, `Unexpected "${d[i]}" in path data.`);
338
+ const rel = cmd === cmd.toLowerCase();
339
+ const k = cmd.toLowerCase();
340
+ const ox = rel ? cx : 0, oy = rel ? cy : 0;
341
+ if (k === 'z') {
342
+ emit('Z');
343
+ [cx, cy] = [sx, sy];
344
+ lastC = lastQ = undefined;
345
+ continue;
346
+ }
347
+ if (k === 'a') {
348
+ const [rx, ry, rot] = [readNumber(), readNumber(), readNumber()];
349
+ const [large, sweep] = [readFlag(), readFlag()];
350
+ const [x, y] = [readNumber() + ox, readNumber() + oy];
351
+ emit(`A${fmt(Math.abs(rx))} ${fmt(Math.abs(ry))} ${fmt(rot)} ${large} ${sweep} ${fmt(x)} ${fmt(y)}`);
352
+ [cx, cy] = [x, y];
353
+ lastC = lastQ = undefined;
354
+ }
355
+ else if (k === 'h' || k === 'v') {
356
+ const n = readNumber();
357
+ if (k === 'h')
358
+ cx = n + ox;
359
+ else
360
+ cy = n + oy;
361
+ emit(`L${fmt(cx)} ${fmt(cy)}`);
362
+ lastC = lastQ = undefined;
363
+ }
364
+ else {
365
+ const values = Array.from({ length: PARAMS[k] }, (_, j) => readNumber() + (j % 2 === 0 ? ox : oy));
366
+ if (k === 'm') {
367
+ [cx, cy] = values;
368
+ [sx, sy] = [cx, cy];
369
+ emit(`M${fmt(cx)} ${fmt(cy)}`);
370
+ cmd = rel ? 'l' : 'L'; // further pairs are line-tos
371
+ lastC = lastQ = undefined;
372
+ }
373
+ else if (k === 'l' || k === 't') {
374
+ const [x, y] = values;
375
+ if (k === 't') {
376
+ const q = lastQ ? [2 * cx - lastQ[0], 2 * cy - lastQ[1]] : [cx, cy];
377
+ emit(`Q${fmt(q[0])} ${fmt(q[1])} ${fmt(x)} ${fmt(y)}`);
378
+ lastQ = q;
379
+ lastC = undefined;
380
+ }
381
+ else {
382
+ emit(`L${fmt(x)} ${fmt(y)}`);
383
+ lastC = lastQ = undefined;
384
+ }
385
+ [cx, cy] = [x, y];
386
+ }
387
+ else if (k === 'c' || k === 's') {
388
+ const [x1, y1, x2, y2, x, y] = k === 'c' ? values : [...(lastC ? [2 * cx - lastC[0], 2 * cy - lastC[1]] : [cx, cy]), ...values];
389
+ emit(`C${[x1, y1, x2, y2, x, y].map(fmt).join(' ')}`);
390
+ lastC = [x2, y2];
391
+ lastQ = undefined;
392
+ [cx, cy] = [x, y];
393
+ }
394
+ else {
395
+ const [x1, y1, x, y] = values;
396
+ emit(`Q${[x1, y1, x, y].map(fmt).join(' ')}`);
397
+ lastQ = [x1, y1];
398
+ lastC = undefined;
399
+ [cx, cy] = [x, y];
400
+ }
401
+ }
402
+ }
403
+ return out.join('');
404
+ }
405
+ function points(value, at) {
406
+ const parts = value.trim() ? value.trim().split(/[\s,]+/) : [];
407
+ if (parts.length % 2)
408
+ fail('MALFORMED_PATH', at, 'Points must come in x,y pairs.');
409
+ return parts.map((p) => num(p, at, 'point'));
410
+ }
411
+ /** The element's geometry as path data in its own coordinates. */
412
+ function shapePath(el, kind, at, budget) {
413
+ const a = (name, fallback) => num(el.attrs.get(name), at, name, fallback);
414
+ switch (kind) {
415
+ case 'path':
416
+ return normalisePath(el.attrs.get('d') ?? '', at, budget);
417
+ case 'rect': {
418
+ const [x, y, w, h] = [a('x', 0), a('y', 0), a('width'), a('height')];
419
+ let rx = Math.min(Math.abs(a('rx', el.attrs.has('ry') ? a('ry') : 0)), w / 2);
420
+ let ry = Math.min(Math.abs(a('ry', el.attrs.has('rx') ? a('rx') : 0)), h / 2);
421
+ if (!(rx > 0 && ry > 0))
422
+ rx = ry = 0;
423
+ const d = rx
424
+ ? `M${x + rx} ${y}L${x + w - rx} ${y}A${rx} ${ry} 0 0 1 ${x + w} ${y + ry}L${x + w} ${y + h - ry}A${rx} ${ry} 0 0 1 ${x + w - rx} ${y + h}L${x + rx} ${y + h}A${rx} ${ry} 0 0 1 ${x} ${y + h - ry}L${x} ${y + ry}A${rx} ${ry} 0 0 1 ${x + rx} ${y}Z`
425
+ : `M${x} ${y}L${x + w} ${y}L${x + w} ${y + h}L${x} ${y + h}Z`;
426
+ return normalisePath(d, at, budget);
427
+ }
428
+ case 'circle':
429
+ case 'ellipse': {
430
+ const [cx, cy] = [a('cx', 0), a('cy', 0)];
431
+ const [rx, ry] = kind === 'circle' ? [a('r'), a('r')] : [a('rx'), a('ry')];
432
+ return normalisePath(`M${cx - rx} ${cy}A${rx} ${ry} 0 1 0 ${cx + rx} ${cy}A${rx} ${ry} 0 1 0 ${cx - rx} ${cy}Z`, at, budget);
433
+ }
434
+ case 'line':
435
+ return normalisePath(`M${a('x1', 0)} ${a('y1', 0)}L${a('x2', 0)} ${a('y2', 0)}`, at, budget);
436
+ default: {
437
+ const p = points(el.attrs.get('points') ?? '', at);
438
+ if (p.length < 4)
439
+ fail('MALFORMED_PATH', at, 'A polyline or polygon needs at least two points.');
440
+ const d = p.reduce((s, n, j) => s + (j % 2 === 0 ? `${j === 0 ? 'M' : 'L'}${n}` : ` ${n}`), '') + (kind === 'polygon' ? 'Z' : '');
441
+ return normalisePath(d, at, budget);
442
+ }
443
+ }
444
+ }
445
+ const DEFAULT_STYLE = { fill: '#000000', stroke: 'none', strokeWidth: 1, opacity: 1, fillOpacity: 1, strokeOpacity: 1, fillRule: 'nonzero' };
446
+ function sanitize(text, limits) {
447
+ if (new TextEncoder().encode(text).length > limits.maxBytes)
448
+ fail('TOO_LARGE', 'document', `Larger than ${limits.maxBytes} bytes.`);
449
+ const root = parseXml(text, limits);
450
+ if (local(root.name) !== 'svg')
451
+ fail('NOT_SVG', root.path, `The root element is <${root.name}>, not <svg>.`);
452
+ scan(root);
453
+ const counts = new Map();
454
+ const note = (kind, name) => counts.set(`${kind}\u0000${name}`, (counts.get(`${kind}\u0000${name}`) ?? 0) + 1);
455
+ const budget = { left: limits.maxPathCommands };
456
+ const shapes = [];
457
+ const styleOf = (el, parent) => {
458
+ const props = new Map();
459
+ for (const [name, value] of el.attrs)
460
+ if (STYLE_PROPS.has(name))
461
+ props.set(name, value);
462
+ const inline = el.attrs.get('style');
463
+ if (inline !== undefined) {
464
+ for (const decl of inline.split(';')) {
465
+ if (!decl.trim())
466
+ continue;
467
+ const colon = decl.indexOf(':');
468
+ const prop = (colon < 0 ? decl : decl.slice(0, colon)).trim().toLowerCase();
469
+ if (colon < 0 || !STYLE_PROPS.has(prop)) {
470
+ note('dropped-attribute', `style:${prop}`);
471
+ continue;
472
+ }
473
+ props.set(prop, decl.slice(colon + 1).trim());
474
+ }
475
+ }
476
+ const s = { ...parent, opacity: parent.opacity };
477
+ for (const [prop, value] of props) {
478
+ if (prop === 'fill' || prop === 'stroke') {
479
+ const c = colour(value);
480
+ if (c)
481
+ s[prop] = c;
482
+ else
483
+ note('dropped-attribute', prop);
484
+ }
485
+ else if (prop === 'fill-rule') {
486
+ if (value === 'evenodd' || value === 'nonzero')
487
+ s.fillRule = value;
488
+ else
489
+ note('dropped-attribute', prop);
490
+ }
491
+ else {
492
+ const v = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?(?:px)?$/.test(value.trim()) ? parseFloat(value) : NaN;
493
+ if (!Number.isFinite(v) || v < 0) {
494
+ note('dropped-attribute', prop);
495
+ continue;
496
+ }
497
+ if (prop === 'stroke-width')
498
+ s.strokeWidth = v;
499
+ else if (prop === 'opacity')
500
+ s.opacity = parent.opacity * Math.min(1, v);
501
+ else if (prop === 'fill-opacity')
502
+ s.fillOpacity = Math.min(1, v);
503
+ else
504
+ s.strokeOpacity = Math.min(1, v);
505
+ }
506
+ }
507
+ return s;
508
+ };
509
+ const visit = (el, parentMatrix, parentStyle, isRoot) => {
510
+ const kind = local(el.name);
511
+ if (!KEPT.has(kind)) {
512
+ note('dropped-element', kind);
513
+ return;
514
+ }
515
+ for (const name of el.attrs.keys()) {
516
+ if (GEOMETRY[kind].includes(name) || STYLE_PROPS.has(name) || name === 'style' || name === 'transform' || SILENT(name))
517
+ continue;
518
+ note('dropped-attribute', name);
519
+ }
520
+ const matrix = el.attrs.has('transform') && !isRoot ? multiply(parentMatrix, parseTransform(el.attrs.get('transform'), el.path)) : parentMatrix;
521
+ const style = styleOf(el, parentStyle);
522
+ if (kind === 'svg' || kind === 'g') {
523
+ el.children.forEach((c) => visit(c, matrix, style, false));
524
+ return;
525
+ }
526
+ shapes.push({
527
+ d: shapePath(el, kind, el.path, budget),
528
+ transform: matrix.map(round),
529
+ fill: style.fill,
530
+ stroke: style.stroke,
531
+ strokeWidth: round(style.strokeWidth),
532
+ opacity: round(style.opacity),
533
+ fillOpacity: round(style.fillOpacity),
534
+ strokeOpacity: round(style.strokeOpacity),
535
+ fillRule: style.fillRule,
536
+ });
537
+ };
538
+ let viewBox;
539
+ const vb = root.attrs.get('viewBox');
540
+ if (vb !== undefined) {
541
+ const parts = vb.trim().split(/[\s,]+/).map((p) => num(p, root.path, 'viewBox value'));
542
+ if (parts.length !== 4 || !(parts[2] > 0 && parts[3] > 0))
543
+ fail('INVALID_NUMBER', root.path, 'viewBox must be four numbers with a positive width and height.');
544
+ viewBox = parts.map(round);
545
+ }
546
+ else if (root.attrs.has('width') && root.attrs.has('height')) {
547
+ const [w, h] = [num(root.attrs.get('width'), root.path, 'width'), num(root.attrs.get('height'), root.path, 'height')];
548
+ if (!(w > 0 && h > 0))
549
+ fail('INVALID_NUMBER', root.path, 'width and height must be positive.');
550
+ viewBox = [0, 0, round(w), round(h)];
551
+ }
552
+ else {
553
+ viewBox = fail('MISSING_SIZE', root.path, 'The drawing has neither a viewBox nor a width and height.');
554
+ }
555
+ visit(root, IDENTITY, DEFAULT_STYLE, true);
556
+ const notes = [...counts].map(([k, count]) => {
557
+ const [kind, name] = k.split('\u0000');
558
+ return { kind, name, count };
559
+ });
560
+ return { svg: { viewBox, shapes }, notes };
561
+ }
562
+ /** Sanitizes SVG text into a flat shape model plus clean markup, or explains why it was refused. */
563
+ export function sanitizeSvg(text, limits = {}) {
564
+ try {
565
+ if (typeof text !== 'string')
566
+ return { ok: false, errors: [{ code: 'NOT_SVG', path: 'document', message: 'Expected SVG text.' }] };
567
+ const { svg, notes } = sanitize(text, { ...defaultSvgLimits, ...limits });
568
+ return { ok: true, svg, markup: toSvgMarkup(svg), notes };
569
+ }
570
+ catch (e) {
571
+ if (e instanceof SvgFailure)
572
+ return { ok: false, errors: [{ code: e.code, path: e.at, message: e.message }] };
573
+ return { ok: false, errors: [{ code: 'MALFORMED_XML', path: 'document', message: 'The file could not be read as SVG.' }] };
574
+ }
575
+ }
576
+ const escape = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
577
+ /** Clean markup written from the model only: one <path> per shape, defaults left out. */
578
+ export function toSvgMarkup(svg) {
579
+ const body = svg.shapes.map((s) => {
580
+ const attrs = [`d="${escape(s.d)}"`];
581
+ if (s.transform.some((v, i) => v !== IDENTITY[i]))
582
+ attrs.push(`transform="matrix(${s.transform.map(fmt).join(' ')})"`);
583
+ if (s.fill !== DEFAULT_STYLE.fill)
584
+ attrs.push(`fill="${s.fill}"`);
585
+ if (s.stroke !== DEFAULT_STYLE.stroke)
586
+ attrs.push(`stroke="${s.stroke}"`);
587
+ if (s.strokeWidth !== DEFAULT_STYLE.strokeWidth)
588
+ attrs.push(`stroke-width="${fmt(s.strokeWidth)}"`);
589
+ if (s.opacity !== 1)
590
+ attrs.push(`opacity="${fmt(s.opacity)}"`);
591
+ if (s.fillOpacity !== 1)
592
+ attrs.push(`fill-opacity="${fmt(s.fillOpacity)}"`);
593
+ if (s.strokeOpacity !== 1)
594
+ attrs.push(`stroke-opacity="${fmt(s.strokeOpacity)}"`);
595
+ if (s.fillRule !== 'nonzero')
596
+ attrs.push(`fill-rule="${s.fillRule}"`);
597
+ return `<path ${attrs.join(' ')}/>`;
598
+ });
599
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${svg.viewBox.map(fmt).join(' ')}">${body.join('')}</svg>`;
600
+ }
601
+ //# sourceMappingURL=svg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg.js","sourceRoot":"","sources":["../src/svg.ts"],"names":[],"mappings":"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","sourcesContent":["// Allow-list SVG sanitizer (ADR-0012). No DOM: a small XML tokenizer, a security scan of the whole\n// tree, then conversion of the few kept shapes into a flat model that is written back out cleanly.\n\nexport interface SvgLimits {\n /** UTF-8 bytes. */\n maxBytes: number;\n maxElements: number;\n maxDepth: number;\n /** Path commands over the whole file, after shapes are turned into paths. */\n maxPathCommands: number;\n}\n\nexport const defaultSvgLimits: Readonly<SvgLimits> = { maxBytes: 512 * 1024, maxElements: 5000, maxDepth: 32, maxPathCommands: 20000 };\n\n/** 2D affine transform [a b c d e f], as in SVG `matrix(a b c d e f)`. */\nexport type SvgMatrix = [number, number, number, number, number, number];\n\nexport interface SvgShape {\n /** Absolute path data using only M, L, C, Q, A and Z. */\n d: string;\n transform: SvgMatrix;\n /** `#rrggbb` or `none`. */\n fill: string;\n stroke: string;\n strokeWidth: number;\n opacity: number;\n fillOpacity: number;\n strokeOpacity: number;\n fillRule: 'nonzero' | 'evenodd';\n}\n\nexport interface SanitizedSvg {\n viewBox: [number, number, number, number];\n shapes: SvgShape[];\n}\n\nexport type SvgErrorCode =\n | 'TOO_LARGE'\n | 'MALFORMED_XML'\n | 'DOCTYPE'\n | 'NOT_SVG'\n | 'FORBIDDEN_ELEMENT'\n | 'FORBIDDEN_ATTRIBUTE'\n | 'FORBIDDEN_VALUE'\n | 'LIMIT_EXCEEDED'\n | 'MALFORMED_PATH'\n | 'MALFORMED_TRANSFORM'\n | 'INVALID_NUMBER'\n | 'MISSING_SIZE';\n\nexport interface SvgError {\n code: SvgErrorCode;\n /** Where in the document, e.g. `svg/g[0]/path[2]`. */\n path: string;\n message: string;\n}\n\n/** Things removed on purpose, so the user knows what will not appear. */\nexport interface SvgNote {\n kind: 'dropped-element' | 'dropped-attribute';\n name: string;\n count: number;\n}\n\nexport type SanitizeResult = { ok: true; svg: SanitizedSvg; markup: string; notes: SvgNote[] } | { ok: false; errors: SvgError[] };\n\nclass SvgFailure extends Error {\n constructor(readonly code: SvgErrorCode, readonly at: string, message: string) {\n super(message);\n }\n}\nconst fail = (code: SvgErrorCode, at: string, message: string): never => {\n throw new SvgFailure(code, at, message);\n};\n\n// ---------------------------------------------------------------------------------------------\n// XML tokenizer\n\ninterface Element {\n name: string;\n attrs: Map<string, string>;\n children: Element[];\n path: string;\n}\n\nconst NAME = /[A-Za-z_][\\w.:-]*/y;\nconst SPACE = /\\s*/y;\n\nfunction decodeEntities(text: string, at: string): string {\n return text.replace(/&([^;\\s&]*);?/g, (match, body: string) => {\n if (!match.endsWith(';')) fail('MALFORMED_XML', at, 'A bare \"&\" is not allowed; write &amp;.');\n const named: Record<string, string> = { amp: '&', lt: '<', gt: '>', quot: '\"', apos: \"'\" };\n if (Object.hasOwn(named, body)) return named[body];\n const num = /^#(?:x([0-9a-fA-F]{1,6})|([0-9]{1,7}))$/.exec(body);\n if (num) {\n const code = num[1] ? parseInt(num[1], 16) : Number(num[2]);\n if (code > 0 && code <= 0x10ffff) return String.fromCodePoint(code);\n }\n return fail('MALFORMED_XML', at, `Unknown entity &${body}; (only the five XML entities and character references are allowed).`);\n });\n}\n\nfunction parseXml(text: string, limits: SvgLimits): Element {\n let i = 0;\n let count = 0;\n const stack: Element[] = [];\n let root: Element | undefined;\n const at = () => stack[stack.length - 1]?.path ?? 'document';\n const childIndex = new WeakMap<Element, Map<string, number>>();\n if (text.charCodeAt(0) === 0xfeff) i = 1;\n const match = (re: RegExp) => {\n re.lastIndex = i;\n const m = re.exec(text);\n if (m) i = re.lastIndex;\n return m;\n };\n while (i < text.length) {\n const lt = text.indexOf('<', i);\n if (lt < 0) {\n if (text.slice(i).trim()) fail('MALFORMED_XML', at(), 'Text outside the root element.');\n break;\n }\n if (lt > i && !stack.length && text.slice(i, lt).trim()) fail('MALFORMED_XML', at(), 'Text outside the root element.');\n if (lt > i && stack.length) decodeEntities(text.slice(i, lt), at());\n i = lt;\n if (text.startsWith('<?xml', i) && (text[i + 5] === ' ' || text[i + 5] === '?') && !root && !stack.length) {\n const end = text.indexOf('?>', i);\n if (end < 0) fail('MALFORMED_XML', at(), 'Unterminated XML declaration.');\n i = end + 2;\n continue;\n }\n if (text.startsWith('<?', i)) fail('MALFORMED_XML', at(), 'Processing instructions are not allowed.');\n if (text.startsWith('<!--', i)) {\n const end = text.indexOf('-->', i + 4);\n if (end < 0) fail('MALFORMED_XML', at(), 'Unterminated comment.');\n i = end + 3;\n continue;\n }\n if (/^<!doctype/i.test(text.slice(i, i + 9)) || text.startsWith('<!ENTITY', i)) fail('DOCTYPE', at(), 'DOCTYPE and entity declarations are not allowed.');\n if (text.startsWith('<!', i)) fail('MALFORMED_XML', at(), 'CDATA sections and markup declarations are not allowed.');\n if (text.startsWith('</', i)) {\n i += 2;\n const name = match(NAME)?.[0];\n match(SPACE);\n if (!name || text[i] !== '>') fail('MALFORMED_XML', at(), 'Malformed end tag.');\n i++;\n const open = stack.pop();\n if (!open || open.name !== name) fail('MALFORMED_XML', open?.path ?? 'document', `End tag </${name}> does not match <${open?.name ?? '(none)'}>.`);\n continue;\n }\n i++;\n const name = match(NAME)?.[0];\n if (!name) fail('MALFORMED_XML', at(), 'Malformed start tag.');\n if (root && !stack.length) fail('MALFORMED_XML', at(), 'Only one root element is allowed.');\n const parent = stack[stack.length - 1];\n let path = name!;\n if (parent) {\n const seen = childIndex.get(parent) ?? new Map<string, number>();\n childIndex.set(parent, seen);\n const n = seen.get(name!) ?? 0;\n seen.set(name!, n + 1);\n path = `${parent.path}/${name}[${n}]`;\n }\n const el: Element = { name: name!, attrs: new Map(), children: [], path };\n for (;;) {\n const space = match(SPACE)![0];\n if (text.startsWith('/>', i) || text[i] === '>') break;\n if (!space) fail('MALFORMED_XML', path, 'Attributes must be separated by white space.');\n const attr = match(NAME)?.[0];\n if (!attr) fail('MALFORMED_XML', path, 'Malformed attribute.');\n match(SPACE);\n if (text[i] !== '=') fail('MALFORMED_XML', path, `Attribute \"${attr}\" has no value.`);\n i++;\n match(SPACE);\n const quote = text[i];\n if (quote !== '\"' && quote !== \"'\") fail('MALFORMED_XML', path, `Attribute \"${attr}\" must be quoted.`);\n const end = text.indexOf(quote, i + 1);\n if (end < 0) fail('MALFORMED_XML', path, `Unterminated value of \"${attr}\".`);\n const raw = text.slice(i + 1, end);\n if (raw.includes('<')) fail('MALFORMED_XML', path, `\"<\" in the value of \"${attr}\".`);\n if (el.attrs.has(attr!)) fail('MALFORMED_XML', path, `Duplicate attribute \"${attr}\".`);\n el.attrs.set(attr!, decodeEntities(raw, path));\n i = end + 1;\n }\n if (++count > limits.maxElements) fail('LIMIT_EXCEEDED', path, `More than ${limits.maxElements} elements.`);\n if (stack.length + 1 > limits.maxDepth) fail('LIMIT_EXCEEDED', path, `Nested deeper than ${limits.maxDepth} levels.`);\n if (parent) parent.children.push(el);\n else root = el;\n if (text.startsWith('/>', i)) i += 2;\n else {\n i++;\n stack.push(el);\n }\n }\n if (stack.length) fail('MALFORMED_XML', stack[stack.length - 1].path, `<${stack[stack.length - 1].name}> is never closed.`);\n if (!root) fail('NOT_SVG', 'document', 'No root element.');\n return root!;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Security scan and allow-lists\n\nconst local = (name: string) => name.slice(name.indexOf(':') + 1).toLowerCase();\nconst FORBIDDEN_ELEMENTS = new Set(['script', 'foreignobject', 'image', 'use', 'iframe', 'object', 'embed', 'animate', 'animatemotion', 'animatetransform', 'animatecolor', 'set', 'audio', 'video', 'canvas', 'link', 'meta', 'a', 'handler', 'listener']);\nconst KEPT = new Set(['svg', 'g', 'path', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon']);\nconst DANGEROUS_VALUE = /url\\s*\\(|javascript\\s*:|vbscript\\s*:|expression\\s*\\(|data\\s*:/i;\nconst STYLE_PROPS = new Set(['fill', 'stroke', 'stroke-width', 'opacity', 'fill-opacity', 'stroke-opacity', 'fill-rule']);\nconst GEOMETRY: Record<string, string[]> = {\n svg: ['viewBox', 'width', 'height'],\n g: [],\n path: ['d'],\n rect: ['x', 'y', 'width', 'height', 'rx', 'ry'],\n circle: ['cx', 'cy', 'r'],\n ellipse: ['cx', 'cy', 'rx', 'ry'],\n line: ['x1', 'y1', 'x2', 'y2'],\n polyline: ['points'],\n polygon: ['points'],\n};\n/** Harmless bookkeeping attributes that are removed without a note. */\nconst SILENT = (name: string) => name === 'xmlns' || name.startsWith('xmlns:') || name === 'version' || name === 'id' || name === 'xml:space' || name === 'class' || name === 'baseProfile';\n\nfunction scan(el: Element) {\n if (FORBIDDEN_ELEMENTS.has(local(el.name))) fail('FORBIDDEN_ELEMENT', el.path, `<${el.name}> is not allowed.`);\n for (const [name, value] of el.attrs) {\n const n = local(name);\n if (n.startsWith('on')) fail('FORBIDDEN_ATTRIBUTE', el.path, `Event handler \"${name}\" is not allowed.`);\n if (n === 'href' || n === 'src') fail('FORBIDDEN_ATTRIBUTE', el.path, `\"${name}\" (a reference to other content) is not allowed.`);\n if (DANGEROUS_VALUE.test(value)) fail('FORBIDDEN_VALUE', el.path, `The value of \"${name}\" refers to other content (url(), data: or a script).`);\n }\n el.children.forEach(scan);\n}\n\n// ---------------------------------------------------------------------------------------------\n// Numbers, colours, transforms\n\nconst NUMBER = /^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?(?:px|mm|cm|in|pt|pc)?$/;\nfunction num(value: string | undefined, at: string, what: string, fallback?: number): number {\n if (value === undefined) {\n if (fallback !== undefined) return fallback;\n return fail('INVALID_NUMBER', at, `Missing ${what}.`);\n }\n const v = value.trim();\n if (!NUMBER.test(v)) return fail('INVALID_NUMBER', at, `${what} \"${value}\" is not a number.`);\n const n = parseFloat(v);\n if (!Number.isFinite(n)) return fail('INVALID_NUMBER', at, `${what} \"${value}\" is not a finite number.`);\n return n;\n}\nconst round = (n: number) => {\n const r = Math.round(n * 1e4) / 1e4;\n return Object.is(r, -0) ? 0 : r;\n};\nconst fmt = (n: number) => String(round(n));\n\nconst NAMED: Record<string, string> = {\n black: '#000000', white: '#ffffff', red: '#ff0000', green: '#008000', blue: '#0000ff', yellow: '#ffff00', orange: '#ffa500', purple: '#800080',\n gray: '#808080', grey: '#808080', silver: '#c0c0c0', maroon: '#800000', navy: '#000080', teal: '#008080', olive: '#808000', lime: '#00ff00',\n aqua: '#00ffff', cyan: '#00ffff', fuchsia: '#ff00ff', magenta: '#ff00ff', brown: '#a52a2a', pink: '#ffc0cb', darkgray: '#a9a9a9', lightgray: '#d3d3d3',\n};\nfunction colour(value: string): string | undefined {\n const v = value.trim().toLowerCase();\n if (v === 'none' || v === 'transparent') return 'none';\n if (v === 'currentcolor') return '#000000';\n if (Object.hasOwn(NAMED, v)) return NAMED[v];\n const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(v);\n if (hex) return hex[1].length === 3 ? `#${[...hex[1]].map((c) => c + c).join('')}` : `#${hex[1]}`;\n const rgb = /^rgb\\(\\s*([\\d.]+%?)\\s*,\\s*([\\d.]+%?)\\s*,\\s*([\\d.]+%?)\\s*\\)$/.exec(v);\n if (rgb) {\n const channel = (c: string) => Math.round(Math.min(255, Math.max(0, c.endsWith('%') ? (parseFloat(c) * 255) / 100 : parseFloat(c))));\n const parts = rgb.slice(1).map(channel);\n if (parts.every(Number.isFinite)) return `#${parts.map((p) => p.toString(16).padStart(2, '0')).join('')}`;\n }\n return undefined;\n}\n\nconst multiply = (p: SvgMatrix, c: SvgMatrix): SvgMatrix => [\n p[0] * c[0] + p[2] * c[1],\n p[1] * c[0] + p[3] * c[1],\n p[0] * c[2] + p[2] * c[3],\n p[1] * c[2] + p[3] * c[3],\n p[0] * c[4] + p[2] * c[5] + p[4],\n p[1] * c[4] + p[3] * c[5] + p[5],\n];\nconst IDENTITY: SvgMatrix = [1, 0, 0, 1, 0, 0];\n\nfunction parseTransform(value: string, at: string): SvgMatrix {\n let m: SvgMatrix = IDENTITY;\n const re = /\\s*(matrix|translate|scale|rotate|skewX|skewY)\\s*\\(([^)]*)\\)\\s*,?/gy;\n let i = 0;\n const v = value.trim();\n while (i < v.length) {\n re.lastIndex = i;\n const part = re.exec(v);\n if (!part) return fail('MALFORMED_TRANSFORM', at, `Cannot read transform \"${value}\".`);\n i = re.lastIndex;\n const args = part[2].trim() ? part[2].trim().split(/[\\s,]+/).map((a) => num(a, at, 'transform value')) : [];\n const need: Record<string, number[]> = { matrix: [6], translate: [1, 2], scale: [1, 2], rotate: [1, 3], skewX: [1], skewY: [1] };\n if (!need[part[1]].includes(args.length)) return fail('MALFORMED_TRANSFORM', at, `${part[1]}() takes ${need[part[1]].join(' or ')} values.`);\n const rad = (deg: number) => (deg * Math.PI) / 180;\n let t: SvgMatrix;\n switch (part[1]) {\n case 'matrix': t = args as SvgMatrix; break;\n case 'translate': t = [1, 0, 0, 1, args[0], args[1] ?? 0]; break;\n case 'scale': t = [args[0], 0, 0, args[1] ?? args[0], 0, 0]; break;\n case 'rotate': {\n const [c, s] = [Math.cos(rad(args[0])), Math.sin(rad(args[0]))];\n const [cx, cy] = [args[1] ?? 0, args[2] ?? 0];\n t = multiply(multiply([1, 0, 0, 1, cx, cy], [c, s, -s, c, 0, 0]), [1, 0, 0, 1, -cx, -cy]);\n break;\n }\n case 'skewX': t = [1, 0, Math.tan(rad(args[0])), 1, 0, 0]; break;\n default: t = [1, Math.tan(rad(args[0])), 0, 1, 0, 0];\n }\n m = multiply(m, t);\n }\n return m;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Path data\n\nconst PARAMS: Record<string, number> = { m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7, z: 0 };\n\n/** Parses path data into absolute M/L/C/Q/A/Z with numbers rounded to 1e-4; counts commands. */\nfunction normalisePath(d: string, at: string, budget: { left: number }): string {\n const out: string[] = [];\n let i = 0;\n let cmd = '';\n let [cx, cy, sx, sy] = [0, 0, 0, 0];\n let lastC: [number, number] | undefined, lastQ: [number, number] | undefined;\n const skip = () => { while (i < d.length && /[\\s,]/.test(d[i])) i++; };\n const NUM = /[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?/y;\n const readNumber = (): number => {\n skip();\n NUM.lastIndex = i;\n const m = NUM.exec(d);\n if (!m) return fail('MALFORMED_PATH', at, `Expected a number at position ${i} of the path data.`);\n i = NUM.lastIndex;\n const n = parseFloat(m[0]);\n if (!Number.isFinite(n)) return fail('INVALID_NUMBER', at, 'Path data contains a non-finite number.');\n return n;\n };\n const readFlag = (): number => {\n skip();\n if (d[i] === '0' || d[i] === '1') return d[i++] === '1' ? 1 : 0;\n return fail('MALFORMED_PATH', at, `Expected an arc flag (0 or 1) at position ${i}.`);\n };\n const emit = (s: string) => {\n if (--budget.left < 0) fail('LIMIT_EXCEEDED', at, 'Too many path commands.');\n out.push(s);\n };\n skip();\n if (i < d.length && !/[Mm]/.test(d[i])) fail('MALFORMED_PATH', at, 'Path data must start with M.');\n while (skip(), i < d.length) {\n if (/[A-Za-z]/.test(d[i])) {\n if (!Object.hasOwn(PARAMS, d[i].toLowerCase())) fail('MALFORMED_PATH', at, `Unknown path command \"${d[i]}\".`);\n cmd = d[i++];\n } else if (!cmd || cmd === 'z' || cmd === 'Z') fail('MALFORMED_PATH', at, `Unexpected \"${d[i]}\" in path data.`);\n const rel = cmd === cmd.toLowerCase();\n const k = cmd.toLowerCase();\n const ox = rel ? cx : 0, oy = rel ? cy : 0;\n if (k === 'z') {\n emit('Z');\n [cx, cy] = [sx, sy];\n lastC = lastQ = undefined;\n continue;\n }\n if (k === 'a') {\n const [rx, ry, rot] = [readNumber(), readNumber(), readNumber()];\n const [large, sweep] = [readFlag(), readFlag()];\n const [x, y] = [readNumber() + ox, readNumber() + oy];\n emit(`A${fmt(Math.abs(rx))} ${fmt(Math.abs(ry))} ${fmt(rot)} ${large} ${sweep} ${fmt(x)} ${fmt(y)}`);\n [cx, cy] = [x, y];\n lastC = lastQ = undefined;\n } else if (k === 'h' || k === 'v') {\n const n = readNumber();\n if (k === 'h') cx = n + ox; else cy = n + oy;\n emit(`L${fmt(cx)} ${fmt(cy)}`);\n lastC = lastQ = undefined;\n } else {\n const values = Array.from({ length: PARAMS[k] }, (_, j) => readNumber() + (j % 2 === 0 ? ox : oy));\n if (k === 'm') {\n [cx, cy] = values as [number, number];\n [sx, sy] = [cx, cy];\n emit(`M${fmt(cx)} ${fmt(cy)}`);\n cmd = rel ? 'l' : 'L'; // further pairs are line-tos\n lastC = lastQ = undefined;\n } else if (k === 'l' || k === 't') {\n const [x, y] = values;\n if (k === 't') {\n const q: [number, number] = lastQ ? [2 * cx - lastQ[0], 2 * cy - lastQ[1]] : [cx, cy];\n emit(`Q${fmt(q[0])} ${fmt(q[1])} ${fmt(x)} ${fmt(y)}`);\n lastQ = q;\n lastC = undefined;\n } else {\n emit(`L${fmt(x)} ${fmt(y)}`);\n lastC = lastQ = undefined;\n }\n [cx, cy] = [x, y];\n } else if (k === 'c' || k === 's') {\n const [x1, y1, x2, y2, x, y] = k === 'c' ? values : [...(lastC ? [2 * cx - lastC[0], 2 * cy - lastC[1]] : [cx, cy]), ...values];\n emit(`C${[x1, y1, x2, y2, x, y].map(fmt).join(' ')}`);\n lastC = [x2, y2];\n lastQ = undefined;\n [cx, cy] = [x, y];\n } else {\n const [x1, y1, x, y] = values;\n emit(`Q${[x1, y1, x, y].map(fmt).join(' ')}`);\n lastQ = [x1, y1];\n lastC = undefined;\n [cx, cy] = [x, y];\n }\n }\n }\n return out.join('');\n}\n\nfunction points(value: string, at: string): number[] {\n const parts = value.trim() ? value.trim().split(/[\\s,]+/) : [];\n if (parts.length % 2) fail('MALFORMED_PATH', at, 'Points must come in x,y pairs.');\n return parts.map((p) => num(p, at, 'point'));\n}\n\n/** The element's geometry as path data in its own coordinates. */\nfunction shapePath(el: Element, kind: string, at: string, budget: { left: number }): string {\n const a = (name: string, fallback?: number) => num(el.attrs.get(name), at, name, fallback);\n switch (kind) {\n case 'path':\n return normalisePath(el.attrs.get('d') ?? '', at, budget);\n case 'rect': {\n const [x, y, w, h] = [a('x', 0), a('y', 0), a('width'), a('height')];\n let rx = Math.min(Math.abs(a('rx', el.attrs.has('ry') ? a('ry') : 0)), w / 2);\n let ry = Math.min(Math.abs(a('ry', el.attrs.has('rx') ? a('rx') : 0)), h / 2);\n if (!(rx > 0 && ry > 0)) rx = ry = 0;\n const d = rx\n ? `M${x + rx} ${y}L${x + w - rx} ${y}A${rx} ${ry} 0 0 1 ${x + w} ${y + ry}L${x + w} ${y + h - ry}A${rx} ${ry} 0 0 1 ${x + w - rx} ${y + h}L${x + rx} ${y + h}A${rx} ${ry} 0 0 1 ${x} ${y + h - ry}L${x} ${y + ry}A${rx} ${ry} 0 0 1 ${x + rx} ${y}Z`\n : `M${x} ${y}L${x + w} ${y}L${x + w} ${y + h}L${x} ${y + h}Z`;\n return normalisePath(d, at, budget);\n }\n case 'circle':\n case 'ellipse': {\n const [cx, cy] = [a('cx', 0), a('cy', 0)];\n const [rx, ry] = kind === 'circle' ? [a('r'), a('r')] : [a('rx'), a('ry')];\n return normalisePath(`M${cx - rx} ${cy}A${rx} ${ry} 0 1 0 ${cx + rx} ${cy}A${rx} ${ry} 0 1 0 ${cx - rx} ${cy}Z`, at, budget);\n }\n case 'line':\n return normalisePath(`M${a('x1', 0)} ${a('y1', 0)}L${a('x2', 0)} ${a('y2', 0)}`, at, budget);\n default: {\n const p = points(el.attrs.get('points') ?? '', at);\n if (p.length < 4) fail('MALFORMED_PATH', at, 'A polyline or polygon needs at least two points.');\n const d = p.reduce((s, n, j) => s + (j % 2 === 0 ? `${j === 0 ? 'M' : 'L'}${n}` : ` ${n}`), '') + (kind === 'polygon' ? 'Z' : '');\n return normalisePath(d, at, budget);\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------\n// Conversion\n\ninterface Style { fill: string; stroke: string; strokeWidth: number; opacity: number; fillOpacity: number; strokeOpacity: number; fillRule: 'nonzero' | 'evenodd' }\nconst DEFAULT_STYLE: Style = { fill: '#000000', stroke: 'none', strokeWidth: 1, opacity: 1, fillOpacity: 1, strokeOpacity: 1, fillRule: 'nonzero' };\n\nfunction sanitize(text: string, limits: SvgLimits): { svg: SanitizedSvg; notes: SvgNote[] } {\n if (new TextEncoder().encode(text).length > limits.maxBytes) fail('TOO_LARGE', 'document', `Larger than ${limits.maxBytes} bytes.`);\n const root = parseXml(text, limits);\n if (local(root.name) !== 'svg') fail('NOT_SVG', root.path, `The root element is <${root.name}>, not <svg>.`);\n scan(root);\n const counts = new Map<string, number>();\n const note = (kind: SvgNote['kind'], name: string) => counts.set(`${kind}\\u0000${name}`, (counts.get(`${kind}\\u0000${name}`) ?? 0) + 1);\n const budget = { left: limits.maxPathCommands };\n const shapes: SvgShape[] = [];\n\n const styleOf = (el: Element, parent: Style): Style => {\n const props = new Map<string, string>();\n for (const [name, value] of el.attrs) if (STYLE_PROPS.has(name)) props.set(name, value);\n const inline = el.attrs.get('style');\n if (inline !== undefined) {\n for (const decl of inline.split(';')) {\n if (!decl.trim()) continue;\n const colon = decl.indexOf(':');\n const prop = (colon < 0 ? decl : decl.slice(0, colon)).trim().toLowerCase();\n if (colon < 0 || !STYLE_PROPS.has(prop)) { note('dropped-attribute', `style:${prop}`); continue; }\n props.set(prop, decl.slice(colon + 1).trim());\n }\n }\n const s: Style = { ...parent, opacity: parent.opacity };\n for (const [prop, value] of props) {\n if (prop === 'fill' || prop === 'stroke') {\n const c = colour(value);\n if (c) s[prop] = c; else note('dropped-attribute', prop);\n } else if (prop === 'fill-rule') {\n if (value === 'evenodd' || value === 'nonzero') s.fillRule = value; else note('dropped-attribute', prop);\n } else {\n const v = /^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?(?:px)?$/.test(value.trim()) ? parseFloat(value) : NaN;\n if (!Number.isFinite(v) || v < 0) { note('dropped-attribute', prop); continue; }\n if (prop === 'stroke-width') s.strokeWidth = v;\n else if (prop === 'opacity') s.opacity = parent.opacity * Math.min(1, v);\n else if (prop === 'fill-opacity') s.fillOpacity = Math.min(1, v);\n else s.strokeOpacity = Math.min(1, v);\n }\n }\n return s;\n };\n\n const visit = (el: Element, parentMatrix: SvgMatrix, parentStyle: Style, isRoot: boolean) => {\n const kind = local(el.name);\n if (!KEPT.has(kind)) { note('dropped-element', kind); return; }\n for (const name of el.attrs.keys()) {\n if (GEOMETRY[kind].includes(name) || STYLE_PROPS.has(name) || name === 'style' || name === 'transform' || SILENT(name)) continue;\n note('dropped-attribute', name);\n }\n const matrix = el.attrs.has('transform') && !isRoot ? multiply(parentMatrix, parseTransform(el.attrs.get('transform')!, el.path)) : parentMatrix;\n const style = styleOf(el, parentStyle);\n if (kind === 'svg' || kind === 'g') {\n el.children.forEach((c) => visit(c, matrix, style, false));\n return;\n }\n shapes.push({\n d: shapePath(el, kind, el.path, budget),\n transform: matrix.map(round) as SvgMatrix,\n fill: style.fill,\n stroke: style.stroke,\n strokeWidth: round(style.strokeWidth),\n opacity: round(style.opacity),\n fillOpacity: round(style.fillOpacity),\n strokeOpacity: round(style.strokeOpacity),\n fillRule: style.fillRule,\n });\n };\n\n let viewBox: [number, number, number, number];\n const vb = root.attrs.get('viewBox');\n if (vb !== undefined) {\n const parts = vb.trim().split(/[\\s,]+/).map((p) => num(p, root.path, 'viewBox value'));\n if (parts.length !== 4 || !(parts[2] > 0 && parts[3] > 0)) fail('INVALID_NUMBER', root.path, 'viewBox must be four numbers with a positive width and height.');\n viewBox = parts.map(round) as [number, number, number, number];\n } else if (root.attrs.has('width') && root.attrs.has('height')) {\n const [w, h] = [num(root.attrs.get('width'), root.path, 'width'), num(root.attrs.get('height'), root.path, 'height')];\n if (!(w > 0 && h > 0)) fail('INVALID_NUMBER', root.path, 'width and height must be positive.');\n viewBox = [0, 0, round(w), round(h)];\n } else {\n viewBox = fail('MISSING_SIZE', root.path, 'The drawing has neither a viewBox nor a width and height.');\n }\n visit(root, IDENTITY, DEFAULT_STYLE, true);\n const notes = [...counts].map(([k, count]) => {\n const [kind, name] = k.split('\\u0000') as [SvgNote['kind'], string];\n return { kind, name, count };\n });\n return { svg: { viewBox, shapes }, notes };\n}\n\n/** Sanitizes SVG text into a flat shape model plus clean markup, or explains why it was refused. */\nexport function sanitizeSvg(text: string, limits: Partial<SvgLimits> = {}): SanitizeResult {\n try {\n if (typeof text !== 'string') return { ok: false, errors: [{ code: 'NOT_SVG', path: 'document', message: 'Expected SVG text.' }] };\n const { svg, notes } = sanitize(text, { ...defaultSvgLimits, ...limits });\n return { ok: true, svg, markup: toSvgMarkup(svg), notes };\n } catch (e) {\n if (e instanceof SvgFailure) return { ok: false, errors: [{ code: e.code, path: e.at, message: e.message }] };\n return { ok: false, errors: [{ code: 'MALFORMED_XML', path: 'document', message: 'The file could not be read as SVG.' }] };\n }\n}\n\nconst escape = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\"/g, '&quot;');\n\n/** Clean markup written from the model only: one <path> per shape, defaults left out. */\nexport function toSvgMarkup(svg: SanitizedSvg): string {\n const body = svg.shapes.map((s) => {\n const attrs: string[] = [`d=\"${escape(s.d)}\"`];\n if (s.transform.some((v, i) => v !== IDENTITY[i])) attrs.push(`transform=\"matrix(${s.transform.map(fmt).join(' ')})\"`);\n if (s.fill !== DEFAULT_STYLE.fill) attrs.push(`fill=\"${s.fill}\"`);\n if (s.stroke !== DEFAULT_STYLE.stroke) attrs.push(`stroke=\"${s.stroke}\"`);\n if (s.strokeWidth !== DEFAULT_STYLE.strokeWidth) attrs.push(`stroke-width=\"${fmt(s.strokeWidth)}\"`);\n if (s.opacity !== 1) attrs.push(`opacity=\"${fmt(s.opacity)}\"`);\n if (s.fillOpacity !== 1) attrs.push(`fill-opacity=\"${fmt(s.fillOpacity)}\"`);\n if (s.strokeOpacity !== 1) attrs.push(`stroke-opacity=\"${fmt(s.strokeOpacity)}\"`);\n if (s.fillRule !== 'nonzero') attrs.push(`fill-rule=\"${s.fillRule}\"`);\n return `<path ${attrs.join(' ')}/>`;\n });\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${svg.viewBox.map(fmt).join(' ')}\">${body.join('')}</svg>`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,49 @@
1
1
  {
2
2
  "name": "@sakibcoolz/asset-import",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Optional allow-list SVG sanitizer and shape model for the 3D SCADA SDK (no DOM dependency).",
5
+ "license": "MIT",
6
+ "author": "sakibcoolz",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/sakibcoolz/3d-scada-sdk.git",
10
+ "directory": "packages/asset-import"
11
+ },
12
+ "homepage": "https://github.com/sakibcoolz/3d-scada-sdk#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/sakibcoolz/3d-scada-sdk/issues"
15
+ },
16
+ "keywords": [
17
+ "scada",
18
+ "3d",
19
+ "threejs",
20
+ "industrial",
21
+ "digital-twin",
22
+ "svg",
23
+ "sanitizer"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "files": [
31
+ "dist",
32
+ "LICENSE"
33
+ ],
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/index.js"
38
+ }
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc -p tsconfig.build.json",
45
+ "typecheck": "tsc --noEmit -p tsconfig.json"
46
+ },
47
+ "types": "./dist/index.d.ts",
48
+ "main": "./dist/index.js"
6
49
  }