@tradik/xslt-processor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3302 @@
1
+ var XsltProcessorLib = (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.js
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ VERSION: () => VERSION,
24
+ XPathContext: () => XPathContext,
25
+ XPathEvaluator: () => XPathEvaluator,
26
+ XPathResultType: () => XPathResultType,
27
+ XSLTProcessor: () => XSLTProcessor,
28
+ XsltContext: () => XsltContext,
29
+ XsltEngine: () => XsltEngine,
30
+ default: () => XSLTProcessor_default,
31
+ evaluateXPath: () => evaluate,
32
+ installGlobal: () => installGlobal,
33
+ isBrowser: () => isBrowser,
34
+ isNativeXSLTSupported: () => isNativeXSLTSupported,
35
+ isNode: () => isNode,
36
+ parseXPath: () => parse,
37
+ selectFirstXPath: () => selectFirst,
38
+ selectXPath: () => select
39
+ });
40
+
41
+ // src/xpath/tokenizer.js
42
+ var TokenType = {
43
+ // Literals
44
+ NUMBER: "NUMBER",
45
+ LITERAL: "LITERAL",
46
+ // Operators
47
+ SLASH: "SLASH",
48
+ DOUBLE_SLASH: "DOUBLE_SLASH",
49
+ PIPE: "PIPE",
50
+ PLUS: "PLUS",
51
+ MINUS: "MINUS",
52
+ STAR: "STAR",
53
+ DIV: "DIV",
54
+ MOD: "MOD",
55
+ EQUALS: "EQUALS",
56
+ NOT_EQUALS: "NOT_EQUALS",
57
+ LT: "LT",
58
+ LTE: "LTE",
59
+ GT: "GT",
60
+ GTE: "GTE",
61
+ AND: "AND",
62
+ OR: "OR",
63
+ // Brackets
64
+ LPAREN: "LPAREN",
65
+ RPAREN: "RPAREN",
66
+ LBRACKET: "LBRACKET",
67
+ RBRACKET: "RBRACKET",
68
+ // Axes
69
+ AXIS: "AXIS",
70
+ AT: "AT",
71
+ DOT: "DOT",
72
+ DOUBLE_DOT: "DOUBLE_DOT",
73
+ DOUBLE_COLON: "DOUBLE_COLON",
74
+ // Names
75
+ NAME: "NAME",
76
+ NCNAME: "NCNAME",
77
+ PREFIX: "PREFIX",
78
+ FUNCTION: "FUNCTION",
79
+ NODE_TYPE: "NODE_TYPE",
80
+ // Other
81
+ COMMA: "COMMA",
82
+ DOLLAR: "DOLLAR",
83
+ COLON: "COLON",
84
+ EOF: "EOF"
85
+ };
86
+ var AXIS_NAMES = /* @__PURE__ */ new Set([
87
+ "ancestor",
88
+ "ancestor-or-self",
89
+ "attribute",
90
+ "child",
91
+ "descendant",
92
+ "descendant-or-self",
93
+ "following",
94
+ "following-sibling",
95
+ "namespace",
96
+ "parent",
97
+ "preceding",
98
+ "preceding-sibling",
99
+ "self"
100
+ ]);
101
+ var NODE_TYPES = /* @__PURE__ */ new Set([
102
+ "comment",
103
+ "text",
104
+ "processing-instruction",
105
+ "node"
106
+ ]);
107
+ var OPERATORS = /* @__PURE__ */ new Set(["and", "or", "mod", "div"]);
108
+ var Token = class {
109
+ constructor(type, value, position) {
110
+ this.type = type;
111
+ this.value = value;
112
+ this.position = position;
113
+ }
114
+ toString() {
115
+ return `Token(${this.type}, ${JSON.stringify(this.value)}, pos=${this.position})`;
116
+ }
117
+ };
118
+ var OPERATOR_CONTEXT_BLOCKERS = /* @__PURE__ */ new Set([
119
+ TokenType.AT,
120
+ TokenType.DOUBLE_COLON,
121
+ TokenType.LPAREN,
122
+ TokenType.LBRACKET,
123
+ TokenType.COMMA,
124
+ // Operators - after an operator, next name token should be a name, not an operator
125
+ TokenType.SLASH,
126
+ TokenType.DOUBLE_SLASH,
127
+ TokenType.PIPE,
128
+ TokenType.PLUS,
129
+ TokenType.MINUS,
130
+ TokenType.STAR,
131
+ TokenType.DIV,
132
+ TokenType.MOD,
133
+ TokenType.EQUALS,
134
+ TokenType.NOT_EQUALS,
135
+ TokenType.LT,
136
+ TokenType.LTE,
137
+ TokenType.GT,
138
+ TokenType.GTE,
139
+ TokenType.AND,
140
+ TokenType.OR
141
+ ]);
142
+ var XPathTokenizer = class {
143
+ constructor(expression) {
144
+ this.expression = expression;
145
+ this.position = 0;
146
+ this.tokens = [];
147
+ }
148
+ /**
149
+ * Get the last token that was added (if any)
150
+ */
151
+ getLastToken() {
152
+ return this.tokens.length > 0 ? this.tokens[this.tokens.length - 1] : null;
153
+ }
154
+ /**
155
+ * Check if the current context allows operator names (div, mod, and, or)
156
+ * Per XPath 1.0 spec disambiguation rules.
157
+ */
158
+ isOperatorContext() {
159
+ const lastToken = this.getLastToken();
160
+ if (!lastToken) return false;
161
+ if (OPERATOR_CONTEXT_BLOCKERS.has(lastToken.type)) return false;
162
+ return true;
163
+ }
164
+ tokenize() {
165
+ this.tokens = [];
166
+ this.position = 0;
167
+ while (this.position < this.expression.length) {
168
+ this.skipWhitespace();
169
+ if (this.position >= this.expression.length) break;
170
+ const token = this.nextToken();
171
+ if (token) {
172
+ this.tokens.push(token);
173
+ }
174
+ }
175
+ this.tokens.push(new Token(TokenType.EOF, null, this.position));
176
+ return this.tokens;
177
+ }
178
+ skipWhitespace() {
179
+ while (this.position < this.expression.length && /\s/.test(this.expression[this.position])) {
180
+ this.position++;
181
+ }
182
+ }
183
+ peek(offset = 0) {
184
+ return this.expression[this.position + offset];
185
+ }
186
+ consume() {
187
+ return this.expression[this.position++];
188
+ }
189
+ nextToken() {
190
+ const startPos = this.position;
191
+ const char = this.peek();
192
+ if (char === '"' || char === "'") {
193
+ return this.readStringLiteral();
194
+ }
195
+ if (/[0-9]/.test(char) || char === "." && /[0-9]/.test(this.peek(1))) {
196
+ return this.readNumber();
197
+ }
198
+ if (char === "/" && this.peek(1) === "/") {
199
+ this.position += 2;
200
+ return new Token(TokenType.DOUBLE_SLASH, "//", startPos);
201
+ }
202
+ if (char === "." && this.peek(1) === ".") {
203
+ this.position += 2;
204
+ return new Token(TokenType.DOUBLE_DOT, "..", startPos);
205
+ }
206
+ if (char === ":" && this.peek(1) === ":") {
207
+ this.position += 2;
208
+ return new Token(TokenType.DOUBLE_COLON, "::", startPos);
209
+ }
210
+ if (char === "!" && this.peek(1) === "=") {
211
+ this.position += 2;
212
+ return new Token(TokenType.NOT_EQUALS, "!=", startPos);
213
+ }
214
+ if (char === "<" && this.peek(1) === "=") {
215
+ this.position += 2;
216
+ return new Token(TokenType.LTE, "<=", startPos);
217
+ }
218
+ if (char === ">" && this.peek(1) === "=") {
219
+ this.position += 2;
220
+ return new Token(TokenType.GTE, ">=", startPos);
221
+ }
222
+ const singleCharTokens = {
223
+ "/": TokenType.SLASH,
224
+ "|": TokenType.PIPE,
225
+ "+": TokenType.PLUS,
226
+ "-": TokenType.MINUS,
227
+ "*": TokenType.STAR,
228
+ "=": TokenType.EQUALS,
229
+ "<": TokenType.LT,
230
+ ">": TokenType.GT,
231
+ "(": TokenType.LPAREN,
232
+ ")": TokenType.RPAREN,
233
+ "[": TokenType.LBRACKET,
234
+ "]": TokenType.RBRACKET,
235
+ "@": TokenType.AT,
236
+ ".": TokenType.DOT,
237
+ ",": TokenType.COMMA,
238
+ $: TokenType.DOLLAR,
239
+ ":": TokenType.COLON
240
+ };
241
+ if (singleCharTokens[char]) {
242
+ this.position++;
243
+ return new Token(singleCharTokens[char], char, startPos);
244
+ }
245
+ if (this.isNameStartChar(char)) {
246
+ return this.readName();
247
+ }
248
+ throw new Error(
249
+ `Unexpected character '${char}' at position ${this.position} in expression: ${this.expression}`
250
+ );
251
+ }
252
+ readStringLiteral() {
253
+ const startPos = this.position;
254
+ const quote = this.consume();
255
+ let value = "";
256
+ while (this.position < this.expression.length && this.peek() !== quote) {
257
+ value += this.consume();
258
+ }
259
+ if (this.position >= this.expression.length) {
260
+ throw new Error(`Unterminated string literal at position ${startPos}`);
261
+ }
262
+ this.consume();
263
+ return new Token(TokenType.LITERAL, value, startPos);
264
+ }
265
+ readNumber() {
266
+ const startPos = this.position;
267
+ let value = "";
268
+ while (this.position < this.expression.length && /[0-9]/.test(this.peek())) {
269
+ value += this.consume();
270
+ }
271
+ if (this.peek() === "." && /[0-9]/.test(this.peek(1))) {
272
+ value += this.consume();
273
+ while (this.position < this.expression.length && /[0-9]/.test(this.peek())) {
274
+ value += this.consume();
275
+ }
276
+ }
277
+ return new Token(TokenType.NUMBER, parseFloat(value), startPos);
278
+ }
279
+ readName() {
280
+ const startPos = this.position;
281
+ let value = "";
282
+ while (this.position < this.expression.length && this.isNameChar(this.peek())) {
283
+ value += this.consume();
284
+ }
285
+ this.skipWhitespace();
286
+ if (AXIS_NAMES.has(value) && this.peek() === ":" && this.peek(1) === ":") {
287
+ return new Token(TokenType.AXIS, value, startPos);
288
+ }
289
+ const savedPos = this.position;
290
+ this.skipWhitespace();
291
+ if (this.peek() === "(") {
292
+ if (NODE_TYPES.has(value)) {
293
+ return new Token(TokenType.NODE_TYPE, value, startPos);
294
+ }
295
+ return new Token(TokenType.FUNCTION, value, startPos);
296
+ }
297
+ this.position = savedPos;
298
+ if (OPERATORS.has(value) && this.isOperatorContext()) {
299
+ const opTokens = {
300
+ and: TokenType.AND,
301
+ or: TokenType.OR,
302
+ mod: TokenType.MOD,
303
+ div: TokenType.DIV
304
+ };
305
+ return new Token(opTokens[value], value, startPos);
306
+ }
307
+ return new Token(TokenType.NAME, value, startPos);
308
+ }
309
+ isNameStartChar(char) {
310
+ if (!char) return false;
311
+ const code = char.charCodeAt(0);
312
+ return char === "_" || code >= 65 && code <= 90 || // A-Z
313
+ code >= 97 && code <= 122 || // a-z
314
+ code >= 192;
315
+ }
316
+ isNameChar(char) {
317
+ if (!char) return false;
318
+ const code = char.charCodeAt(0);
319
+ return this.isNameStartChar(char) || char === "-" || char === "." || code >= 48 && code <= 57;
320
+ }
321
+ };
322
+ function tokenize(expression) {
323
+ const tokenizer = new XPathTokenizer(expression);
324
+ return tokenizer.tokenize();
325
+ }
326
+
327
+ // src/xpath/parser.js
328
+ var NodeType = {
329
+ // Expression types
330
+ OR_EXPR: "OrExpr",
331
+ AND_EXPR: "AndExpr",
332
+ EQUALITY_EXPR: "EqualityExpr",
333
+ RELATIONAL_EXPR: "RelationalExpr",
334
+ ADDITIVE_EXPR: "AdditiveExpr",
335
+ MULTIPLICATIVE_EXPR: "MultiplicativeExpr",
336
+ UNARY_EXPR: "UnaryExpr",
337
+ UNION_EXPR: "UnionExpr",
338
+ // Path expressions
339
+ PATH_EXPR: "PathExpr",
340
+ LOCATION_PATH: "LocationPath",
341
+ STEP: "Step",
342
+ PREDICATE: "Predicate",
343
+ // Primaries
344
+ VARIABLE_REF: "VariableRef",
345
+ LITERAL: "Literal",
346
+ NUMBER: "Number",
347
+ FUNCTION_CALL: "FunctionCall",
348
+ // Node tests
349
+ NAME_TEST: "NameTest",
350
+ NODE_TYPE_TEST: "NodeTypeTest",
351
+ PI_TEST: "ProcessingInstructionTest"
352
+ };
353
+ var ASTNode = class {
354
+ constructor(type, props = {}) {
355
+ this.type = type;
356
+ Object.assign(this, props);
357
+ }
358
+ };
359
+ var XPathParser = class {
360
+ constructor(tokens) {
361
+ this.tokens = tokens;
362
+ this.position = 0;
363
+ }
364
+ parse() {
365
+ const expr = this.parseExpr();
366
+ if (!this.isAtEnd()) {
367
+ throw new Error(
368
+ `Unexpected token ${this.peek().type} at position ${this.peek().position}`
369
+ );
370
+ }
371
+ return expr;
372
+ }
373
+ // Expr ::= OrExpr
374
+ parseExpr() {
375
+ return this.parseOrExpr();
376
+ }
377
+ // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
378
+ parseOrExpr() {
379
+ let left = this.parseAndExpr();
380
+ while (this.match(TokenType.OR)) {
381
+ const right = this.parseAndExpr();
382
+ left = new ASTNode(NodeType.OR_EXPR, { left, right });
383
+ }
384
+ return left;
385
+ }
386
+ // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
387
+ parseAndExpr() {
388
+ let left = this.parseEqualityExpr();
389
+ while (this.match(TokenType.AND)) {
390
+ const right = this.parseEqualityExpr();
391
+ left = new ASTNode(NodeType.AND_EXPR, { left, right });
392
+ }
393
+ return left;
394
+ }
395
+ // EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr | EqualityExpr '!=' RelationalExpr
396
+ parseEqualityExpr() {
397
+ let left = this.parseRelationalExpr();
398
+ while (true) {
399
+ if (this.match(TokenType.EQUALS)) {
400
+ const right = this.parseRelationalExpr();
401
+ left = new ASTNode(NodeType.EQUALITY_EXPR, {
402
+ operator: "=",
403
+ left,
404
+ right
405
+ });
406
+ } else if (this.match(TokenType.NOT_EQUALS)) {
407
+ const right = this.parseRelationalExpr();
408
+ left = new ASTNode(NodeType.EQUALITY_EXPR, {
409
+ operator: "!=",
410
+ left,
411
+ right
412
+ });
413
+ } else {
414
+ break;
415
+ }
416
+ }
417
+ return left;
418
+ }
419
+ // RelationalExpr ::= AdditiveExpr | RelationalExpr '<' AdditiveExpr | ...
420
+ parseRelationalExpr() {
421
+ let left = this.parseAdditiveExpr();
422
+ while (true) {
423
+ if (this.match(TokenType.LT)) {
424
+ const right = this.parseAdditiveExpr();
425
+ left = new ASTNode(NodeType.RELATIONAL_EXPR, {
426
+ operator: "<",
427
+ left,
428
+ right
429
+ });
430
+ } else if (this.match(TokenType.LTE)) {
431
+ const right = this.parseAdditiveExpr();
432
+ left = new ASTNode(NodeType.RELATIONAL_EXPR, {
433
+ operator: "<=",
434
+ left,
435
+ right
436
+ });
437
+ } else if (this.match(TokenType.GT)) {
438
+ const right = this.parseAdditiveExpr();
439
+ left = new ASTNode(NodeType.RELATIONAL_EXPR, {
440
+ operator: ">",
441
+ left,
442
+ right
443
+ });
444
+ } else if (this.match(TokenType.GTE)) {
445
+ const right = this.parseAdditiveExpr();
446
+ left = new ASTNode(NodeType.RELATIONAL_EXPR, {
447
+ operator: ">=",
448
+ left,
449
+ right
450
+ });
451
+ } else {
452
+ break;
453
+ }
454
+ }
455
+ return left;
456
+ }
457
+ // AdditiveExpr ::= MultiplicativeExpr | AdditiveExpr '+' MultiplicativeExpr | AdditiveExpr '-' MultiplicativeExpr
458
+ parseAdditiveExpr() {
459
+ let left = this.parseMultiplicativeExpr();
460
+ while (true) {
461
+ if (this.match(TokenType.PLUS)) {
462
+ const right = this.parseMultiplicativeExpr();
463
+ left = new ASTNode(NodeType.ADDITIVE_EXPR, {
464
+ operator: "+",
465
+ left,
466
+ right
467
+ });
468
+ } else if (this.match(TokenType.MINUS)) {
469
+ const right = this.parseMultiplicativeExpr();
470
+ left = new ASTNode(NodeType.ADDITIVE_EXPR, {
471
+ operator: "-",
472
+ left,
473
+ right
474
+ });
475
+ } else {
476
+ break;
477
+ }
478
+ }
479
+ return left;
480
+ }
481
+ // MultiplicativeExpr ::= UnaryExpr | MultiplicativeExpr '*' UnaryExpr | ...
482
+ parseMultiplicativeExpr() {
483
+ let left = this.parseUnaryExpr();
484
+ while (true) {
485
+ if (this.match(TokenType.STAR)) {
486
+ const right = this.parseUnaryExpr();
487
+ left = new ASTNode(NodeType.MULTIPLICATIVE_EXPR, {
488
+ operator: "*",
489
+ left,
490
+ right
491
+ });
492
+ } else if (this.match(TokenType.DIV)) {
493
+ const right = this.parseUnaryExpr();
494
+ left = new ASTNode(NodeType.MULTIPLICATIVE_EXPR, {
495
+ operator: "div",
496
+ left,
497
+ right
498
+ });
499
+ } else if (this.match(TokenType.MOD)) {
500
+ const right = this.parseUnaryExpr();
501
+ left = new ASTNode(NodeType.MULTIPLICATIVE_EXPR, {
502
+ operator: "mod",
503
+ left,
504
+ right
505
+ });
506
+ } else {
507
+ break;
508
+ }
509
+ }
510
+ return left;
511
+ }
512
+ // UnaryExpr ::= UnionExpr | '-' UnaryExpr
513
+ parseUnaryExpr() {
514
+ if (this.match(TokenType.MINUS)) {
515
+ const operand = this.parseUnaryExpr();
516
+ return new ASTNode(NodeType.UNARY_EXPR, { operator: "-", operand });
517
+ }
518
+ return this.parseUnionExpr();
519
+ }
520
+ // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
521
+ parseUnionExpr() {
522
+ let left = this.parsePathExpr();
523
+ while (this.match(TokenType.PIPE)) {
524
+ const right = this.parsePathExpr();
525
+ left = new ASTNode(NodeType.UNION_EXPR, { left, right });
526
+ }
527
+ return left;
528
+ }
529
+ // PathExpr ::= LocationPath | FilterExpr | FilterExpr '/' RelativeLocationPath | FilterExpr '//' RelativeLocationPath
530
+ parsePathExpr() {
531
+ if (this.check(TokenType.SLASH) || this.check(TokenType.DOUBLE_SLASH)) {
532
+ return this.parseLocationPath();
533
+ }
534
+ if (this.isStepStart()) {
535
+ return this.parseLocationPath();
536
+ }
537
+ const filter = this.parseFilterExpr();
538
+ if (this.check(TokenType.SLASH) || this.check(TokenType.DOUBLE_SLASH)) {
539
+ const steps = [];
540
+ while (this.check(TokenType.SLASH) || this.check(TokenType.DOUBLE_SLASH)) {
541
+ const isDescendant = this.match(TokenType.DOUBLE_SLASH);
542
+ if (!isDescendant) this.advance();
543
+ if (isDescendant) {
544
+ steps.push(
545
+ new ASTNode(NodeType.STEP, {
546
+ axis: "descendant-or-self",
547
+ nodeTest: new ASTNode(NodeType.NODE_TYPE_TEST, {
548
+ nodeType: "node"
549
+ }),
550
+ predicates: []
551
+ })
552
+ );
553
+ }
554
+ steps.push(this.parseStep());
555
+ }
556
+ return new ASTNode(NodeType.PATH_EXPR, { filter, steps });
557
+ }
558
+ return filter;
559
+ }
560
+ // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
561
+ parseLocationPath() {
562
+ let absolute = false;
563
+ const steps = [];
564
+ if (this.match(TokenType.DOUBLE_SLASH)) {
565
+ absolute = true;
566
+ steps.push(
567
+ new ASTNode(NodeType.STEP, {
568
+ axis: "descendant-or-self",
569
+ nodeTest: new ASTNode(NodeType.NODE_TYPE_TEST, { nodeType: "node" }),
570
+ predicates: []
571
+ })
572
+ );
573
+ if (!this.isAtEnd() && !this.check(TokenType.EOF)) {
574
+ steps.push(this.parseStep());
575
+ }
576
+ } else if (this.match(TokenType.SLASH)) {
577
+ absolute = true;
578
+ if (this.isStepStart()) {
579
+ steps.push(this.parseStep());
580
+ }
581
+ } else {
582
+ steps.push(this.parseStep());
583
+ }
584
+ while (this.check(TokenType.SLASH) || this.check(TokenType.DOUBLE_SLASH)) {
585
+ const isDescendant = this.match(TokenType.DOUBLE_SLASH);
586
+ if (!isDescendant) this.advance();
587
+ if (isDescendant) {
588
+ steps.push(
589
+ new ASTNode(NodeType.STEP, {
590
+ axis: "descendant-or-self",
591
+ nodeTest: new ASTNode(NodeType.NODE_TYPE_TEST, {
592
+ nodeType: "node"
593
+ }),
594
+ predicates: []
595
+ })
596
+ );
597
+ }
598
+ steps.push(this.parseStep());
599
+ }
600
+ return new ASTNode(NodeType.LOCATION_PATH, { absolute, steps });
601
+ }
602
+ // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
603
+ parseStep() {
604
+ if (this.match(TokenType.DOT)) {
605
+ return new ASTNode(NodeType.STEP, {
606
+ axis: "self",
607
+ nodeTest: new ASTNode(NodeType.NODE_TYPE_TEST, { nodeType: "node" }),
608
+ predicates: []
609
+ });
610
+ }
611
+ if (this.match(TokenType.DOUBLE_DOT)) {
612
+ return new ASTNode(NodeType.STEP, {
613
+ axis: "parent",
614
+ nodeTest: new ASTNode(NodeType.NODE_TYPE_TEST, { nodeType: "node" }),
615
+ predicates: []
616
+ });
617
+ }
618
+ let axis = "child";
619
+ if (this.match(TokenType.AT)) {
620
+ axis = "attribute";
621
+ } else if (this.check(TokenType.AXIS)) {
622
+ axis = this.advance().value;
623
+ this.expect(TokenType.DOUBLE_COLON);
624
+ }
625
+ const nodeTest = this.parseNodeTest();
626
+ const predicates = [];
627
+ while (this.check(TokenType.LBRACKET)) {
628
+ predicates.push(this.parsePredicate());
629
+ }
630
+ return new ASTNode(NodeType.STEP, { axis, nodeTest, predicates });
631
+ }
632
+ // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'
633
+ parseNodeTest() {
634
+ if (this.check(TokenType.NODE_TYPE)) {
635
+ const nodeType = this.advance().value;
636
+ this.expect(TokenType.LPAREN);
637
+ if (nodeType === "processing-instruction" && this.check(TokenType.LITERAL)) {
638
+ const name = this.advance().value;
639
+ this.expect(TokenType.RPAREN);
640
+ return new ASTNode(NodeType.PI_TEST, { name });
641
+ }
642
+ this.expect(TokenType.RPAREN);
643
+ return new ASTNode(NodeType.NODE_TYPE_TEST, { nodeType });
644
+ }
645
+ return this.parseNameTest();
646
+ }
647
+ // NameTest ::= '*' | NCName ':' '*' | QName
648
+ parseNameTest() {
649
+ if (this.match(TokenType.STAR)) {
650
+ return new ASTNode(NodeType.NAME_TEST, { name: "*", prefix: null });
651
+ }
652
+ if (!this.check(TokenType.NAME)) {
653
+ throw new Error(`Expected name at position ${this.peek().position}`);
654
+ }
655
+ const name = this.advance().value;
656
+ if (this.match(TokenType.COLON)) {
657
+ if (this.match(TokenType.STAR)) {
658
+ return new ASTNode(NodeType.NAME_TEST, { name: "*", prefix: name });
659
+ }
660
+ if (this.check(TokenType.NAME)) {
661
+ const localName = this.advance().value;
662
+ return new ASTNode(NodeType.NAME_TEST, {
663
+ name: localName,
664
+ prefix: name
665
+ });
666
+ }
667
+ throw new Error(
668
+ `Expected name or * after : at position ${this.peek().position}`
669
+ );
670
+ }
671
+ return new ASTNode(NodeType.NAME_TEST, { name, prefix: null });
672
+ }
673
+ // Predicate ::= '[' Expr ']'
674
+ parsePredicate() {
675
+ this.expect(TokenType.LBRACKET);
676
+ const expr = this.parseExpr();
677
+ this.expect(TokenType.RBRACKET);
678
+ return new ASTNode(NodeType.PREDICATE, { expr });
679
+ }
680
+ // FilterExpr ::= PrimaryExpr | FilterExpr Predicate
681
+ parseFilterExpr() {
682
+ let primary = this.parsePrimaryExpr();
683
+ while (this.check(TokenType.LBRACKET)) {
684
+ const predicate = this.parsePredicate();
685
+ primary = new ASTNode(NodeType.PATH_EXPR, {
686
+ filter: primary,
687
+ predicates: [predicate]
688
+ });
689
+ }
690
+ return primary;
691
+ }
692
+ // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
693
+ parsePrimaryExpr() {
694
+ if (this.match(TokenType.DOLLAR)) {
695
+ if (!this.check(TokenType.NAME)) {
696
+ throw new Error(
697
+ `Expected variable name at position ${this.peek().position}`
698
+ );
699
+ }
700
+ const name = this.advance().value;
701
+ let prefix = null;
702
+ if (this.match(TokenType.COLON)) {
703
+ prefix = name;
704
+ if (!this.check(TokenType.NAME)) {
705
+ throw new Error(
706
+ `Expected local name at position ${this.peek().position}`
707
+ );
708
+ }
709
+ const localName = this.advance().value;
710
+ return new ASTNode(NodeType.VARIABLE_REF, { name: localName, prefix });
711
+ }
712
+ return new ASTNode(NodeType.VARIABLE_REF, { name, prefix });
713
+ }
714
+ if (this.match(TokenType.LPAREN)) {
715
+ const expr = this.parseExpr();
716
+ this.expect(TokenType.RPAREN);
717
+ return expr;
718
+ }
719
+ if (this.check(TokenType.LITERAL)) {
720
+ return new ASTNode(NodeType.LITERAL, { value: this.advance().value });
721
+ }
722
+ if (this.check(TokenType.NUMBER)) {
723
+ return new ASTNode(NodeType.NUMBER, { value: this.advance().value });
724
+ }
725
+ if (this.check(TokenType.FUNCTION)) {
726
+ return this.parseFunctionCall();
727
+ }
728
+ throw new Error(
729
+ `Unexpected token ${this.peek().type} at position ${this.peek().position}`
730
+ );
731
+ }
732
+ // FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')'
733
+ // Note: Prefixed function calls (prefix:fn()) are not supported because
734
+ // the tokenizer identifies functions by NAME followed by '(' - prefixed
735
+ // names like 'fn:name()' are tokenized as NAME:NAME() which is parsed
736
+ // as a location path, not a function call.
737
+ parseFunctionCall() {
738
+ const name = this.advance().value;
739
+ return this.parseFunctionCallArgs(name, null);
740
+ }
741
+ parseFunctionCallArgs(name, prefix) {
742
+ this.expect(TokenType.LPAREN);
743
+ const args = [];
744
+ if (!this.check(TokenType.RPAREN)) {
745
+ args.push(this.parseExpr());
746
+ while (this.match(TokenType.COMMA)) {
747
+ args.push(this.parseExpr());
748
+ }
749
+ }
750
+ this.expect(TokenType.RPAREN);
751
+ return new ASTNode(NodeType.FUNCTION_CALL, { name, prefix, args });
752
+ }
753
+ // Helper methods
754
+ isStepStart() {
755
+ const type = this.peek().type;
756
+ return type === TokenType.NAME || type === TokenType.STAR || type === TokenType.AT || type === TokenType.DOT || type === TokenType.DOUBLE_DOT || type === TokenType.AXIS || type === TokenType.NODE_TYPE;
757
+ }
758
+ peek() {
759
+ return this.tokens[this.position];
760
+ }
761
+ advance() {
762
+ if (!this.isAtEnd()) {
763
+ return this.tokens[this.position++];
764
+ }
765
+ return this.tokens[this.position];
766
+ }
767
+ check(type) {
768
+ if (this.isAtEnd()) return false;
769
+ return this.peek().type === type;
770
+ }
771
+ match(type) {
772
+ if (this.check(type)) {
773
+ this.advance();
774
+ return true;
775
+ }
776
+ return false;
777
+ }
778
+ expect(type) {
779
+ if (!this.check(type)) {
780
+ throw new Error(
781
+ `Expected ${type} but got ${this.peek().type} at position ${this.peek().position}`
782
+ );
783
+ }
784
+ return this.advance();
785
+ }
786
+ isAtEnd() {
787
+ return this.peek().type === TokenType.EOF;
788
+ }
789
+ };
790
+ function parse(expression) {
791
+ const tokens = tokenize(expression);
792
+ const parser = new XPathParser(tokens);
793
+ return parser.parse();
794
+ }
795
+
796
+ // src/xpath/evaluator.js
797
+ var XPathResultType = {
798
+ ANY_TYPE: 0,
799
+ NUMBER_TYPE: 1,
800
+ STRING_TYPE: 2,
801
+ BOOLEAN_TYPE: 3,
802
+ UNORDERED_NODE_ITERATOR_TYPE: 4,
803
+ ORDERED_NODE_ITERATOR_TYPE: 5,
804
+ UNORDERED_NODE_SNAPSHOT_TYPE: 6,
805
+ ORDERED_NODE_SNAPSHOT_TYPE: 7,
806
+ ANY_UNORDERED_NODE_TYPE: 8,
807
+ FIRST_ORDERED_NODE_TYPE: 9
808
+ };
809
+ var XPathLimits = {
810
+ MAX_RECURSION_DEPTH: 100,
811
+ MAX_RESULT_SIZE: 1e4,
812
+ MAX_STRING_LENGTH: 1e6
813
+ };
814
+ var FORBIDDEN_VARIABLE_NAMES = Object.freeze([
815
+ "__proto__",
816
+ "constructor",
817
+ "prototype",
818
+ "__defineGetter__",
819
+ "__defineSetter__",
820
+ "__lookupGetter__",
821
+ "__lookupSetter__"
822
+ ]);
823
+ var XPathContext = class _XPathContext {
824
+ constructor(node, position = 1, size = 1, variables = {}, namespaces = {}) {
825
+ this.node = node;
826
+ this.position = position;
827
+ this.size = size;
828
+ this.variables = variables;
829
+ this.namespaces = namespaces;
830
+ }
831
+ clone(overrides = {}) {
832
+ return new _XPathContext(
833
+ overrides.node ?? this.node,
834
+ overrides.position ?? this.position,
835
+ overrides.size ?? this.size,
836
+ overrides.variables ?? this.variables,
837
+ overrides.namespaces ?? this.namespaces
838
+ );
839
+ }
840
+ };
841
+ var XPathEvaluator = class {
842
+ constructor(options = {}) {
843
+ this.functions = this.initCoreFunctions();
844
+ this.maxRecursionDepth = options.maxRecursionDepth ?? XPathLimits.MAX_RECURSION_DEPTH;
845
+ this.maxResultSize = options.maxResultSize ?? XPathLimits.MAX_RESULT_SIZE;
846
+ this.maxStringLength = options.maxStringLength ?? XPathLimits.MAX_STRING_LENGTH;
847
+ this.recursionDepth = 0;
848
+ }
849
+ /**
850
+ * Evaluate XPath expression against a context
851
+ * @throws {Error} If recursion depth exceeds limit
852
+ * @throws {Error} If AST is invalid
853
+ */
854
+ evaluate(ast, context) {
855
+ if (!ast || typeof ast !== "object") {
856
+ throw new Error("Invalid AST: expected object");
857
+ }
858
+ if (!ast.type) {
859
+ throw new Error("Invalid AST: missing type property");
860
+ }
861
+ this.recursionDepth++;
862
+ if (this.recursionDepth > this.maxRecursionDepth) {
863
+ this.recursionDepth = 0;
864
+ throw new Error(
865
+ `Maximum recursion depth exceeded (${this.maxRecursionDepth})`
866
+ );
867
+ }
868
+ try {
869
+ return this.evaluateInternal(ast, context);
870
+ } finally {
871
+ this.recursionDepth--;
872
+ }
873
+ }
874
+ /**
875
+ * Internal evaluation (after validation)
876
+ */
877
+ evaluateInternal(ast, context) {
878
+ switch (ast.type) {
879
+ case NodeType.OR_EXPR:
880
+ return this.evalOrExpr(ast, context);
881
+ case NodeType.AND_EXPR:
882
+ return this.evalAndExpr(ast, context);
883
+ case NodeType.EQUALITY_EXPR:
884
+ return this.evalEqualityExpr(ast, context);
885
+ case NodeType.RELATIONAL_EXPR:
886
+ return this.evalRelationalExpr(ast, context);
887
+ case NodeType.ADDITIVE_EXPR:
888
+ return this.evalAdditiveExpr(ast, context);
889
+ case NodeType.MULTIPLICATIVE_EXPR:
890
+ return this.evalMultiplicativeExpr(ast, context);
891
+ case NodeType.UNARY_EXPR:
892
+ return this.evalUnaryExpr(ast, context);
893
+ case NodeType.UNION_EXPR:
894
+ return this.evalUnionExpr(ast, context);
895
+ case NodeType.PATH_EXPR:
896
+ return this.evalPathExpr(ast, context);
897
+ case NodeType.LOCATION_PATH:
898
+ return this.evalLocationPath(ast, context);
899
+ case NodeType.VARIABLE_REF:
900
+ return this.evalVariableRef(ast, context);
901
+ case NodeType.LITERAL:
902
+ return this.validateString(ast.value);
903
+ case NodeType.NUMBER:
904
+ return ast.value;
905
+ case NodeType.FUNCTION_CALL:
906
+ return this.evalFunctionCall(ast, context);
907
+ default:
908
+ throw new Error(`Unknown AST node type: ${String(ast.type)}`);
909
+ }
910
+ }
911
+ /**
912
+ * Validate and limit string length
913
+ */
914
+ validateString(str) {
915
+ if (typeof str === "string" && str.length > this.maxStringLength) {
916
+ throw new Error(
917
+ `String exceeds maximum length (${this.maxStringLength})`
918
+ );
919
+ }
920
+ return str;
921
+ }
922
+ /**
923
+ * Validate result size
924
+ */
925
+ validateResultSize(nodes) {
926
+ if (Array.isArray(nodes) && nodes.length > this.maxResultSize) {
927
+ throw new Error(
928
+ `Result set exceeds maximum size (${this.maxResultSize})`
929
+ );
930
+ }
931
+ return nodes;
932
+ }
933
+ evalOrExpr(ast, context) {
934
+ return this.toBoolean(this.evaluate(ast.left, context)) || this.toBoolean(this.evaluate(ast.right, context));
935
+ }
936
+ evalAndExpr(ast, context) {
937
+ return this.toBoolean(this.evaluate(ast.left, context)) && this.toBoolean(this.evaluate(ast.right, context));
938
+ }
939
+ evalEqualityExpr(ast, context) {
940
+ const left = this.evaluate(ast.left, context);
941
+ const right = this.evaluate(ast.right, context);
942
+ const isEqual = this.compareValues(left, right, "=");
943
+ return ast.operator === "=" ? isEqual : !isEqual;
944
+ }
945
+ evalRelationalExpr(ast, context) {
946
+ const left = this.evaluate(ast.left, context);
947
+ const right = this.evaluate(ast.right, context);
948
+ return this.compareValues(left, right, ast.operator);
949
+ }
950
+ evalAdditiveExpr(ast, context) {
951
+ const left = this.toNumber(this.evaluate(ast.left, context));
952
+ const right = this.toNumber(this.evaluate(ast.right, context));
953
+ return ast.operator === "+" ? left + right : left - right;
954
+ }
955
+ evalMultiplicativeExpr(ast, context) {
956
+ const left = this.toNumber(this.evaluate(ast.left, context));
957
+ const right = this.toNumber(this.evaluate(ast.right, context));
958
+ switch (ast.operator) {
959
+ case "*":
960
+ return left * right;
961
+ case "div":
962
+ return left / right;
963
+ case "mod":
964
+ return left % right;
965
+ default:
966
+ throw new Error(`Unknown multiplicative operator: ${ast.operator}`);
967
+ }
968
+ }
969
+ evalUnaryExpr(ast, context) {
970
+ const value = this.toNumber(this.evaluate(ast.operand, context));
971
+ return -value;
972
+ }
973
+ evalUnionExpr(ast, context) {
974
+ const left = this.evaluate(ast.left, context);
975
+ const right = this.evaluate(ast.right, context);
976
+ const leftNodes = Array.isArray(left) ? left : [left];
977
+ const rightNodes = Array.isArray(right) ? right : [right];
978
+ const seen = /* @__PURE__ */ new Set();
979
+ const result = [];
980
+ for (const node of [...leftNodes, ...rightNodes]) {
981
+ if (!seen.has(node)) {
982
+ seen.add(node);
983
+ result.push(node);
984
+ }
985
+ }
986
+ this.validateResultSize(result);
987
+ return this.sortByDocumentOrder(result);
988
+ }
989
+ evalPathExpr(ast, context) {
990
+ if (ast.filter) {
991
+ let result = this.evaluate(ast.filter, context);
992
+ if (ast.predicates) {
993
+ for (const pred of ast.predicates) {
994
+ result = this.filterByPredicate(result, pred, context);
995
+ }
996
+ }
997
+ if (ast.steps) {
998
+ for (const step of ast.steps) {
999
+ result = this.evalStepOnNodes(step, result, context);
1000
+ }
1001
+ }
1002
+ return result;
1003
+ }
1004
+ return [];
1005
+ }
1006
+ evalLocationPath(ast, context) {
1007
+ let nodes;
1008
+ if (ast.absolute) {
1009
+ const doc = context.node.ownerDocument || context.node;
1010
+ nodes = [doc];
1011
+ } else {
1012
+ nodes = [context.node];
1013
+ }
1014
+ for (const step of ast.steps) {
1015
+ nodes = this.evalStepOnNodes(step, nodes, context);
1016
+ this.validateResultSize(nodes);
1017
+ }
1018
+ return nodes;
1019
+ }
1020
+ evalStepOnNodes(step, nodes, context) {
1021
+ const allNodes = Array.isArray(nodes) ? nodes : [nodes];
1022
+ let result = [];
1023
+ for (const node of allNodes) {
1024
+ const stepNodes = this.evalStep(step, context.clone({ node }));
1025
+ result = result.concat(stepNodes);
1026
+ if (result.length > this.maxResultSize * 2) {
1027
+ this.validateResultSize(result);
1028
+ }
1029
+ }
1030
+ const uniqueResult = [...new Set(result)];
1031
+ this.validateResultSize(uniqueResult);
1032
+ return this.sortByDocumentOrder(uniqueResult);
1033
+ }
1034
+ evalStep(step, context) {
1035
+ let nodes = this.getAxisNodes(step.axis, context.node);
1036
+ nodes = nodes.filter((n) => this.matchNodeTest(step.nodeTest, n, context));
1037
+ for (const predicate of step.predicates) {
1038
+ nodes = this.filterByPredicate(nodes, predicate, context);
1039
+ }
1040
+ return nodes;
1041
+ }
1042
+ getAxisNodes(axis, node) {
1043
+ switch (axis) {
1044
+ case "child":
1045
+ return Array.from(node.childNodes || []);
1046
+ case "parent":
1047
+ return node.parentNode ? [node.parentNode] : [];
1048
+ case "self":
1049
+ return [node];
1050
+ case "descendant":
1051
+ return this.getDescendants(node, false);
1052
+ case "descendant-or-self":
1053
+ return this.getDescendants(node, true);
1054
+ case "ancestor":
1055
+ return this.getAncestors(node, false);
1056
+ case "ancestor-or-self":
1057
+ return this.getAncestors(node, true);
1058
+ case "following-sibling":
1059
+ return this.getFollowingSiblings(node);
1060
+ case "preceding-sibling":
1061
+ return this.getPrecedingSiblings(node);
1062
+ case "following":
1063
+ return this.getFollowing(node);
1064
+ case "preceding":
1065
+ return this.getPreceding(node);
1066
+ case "attribute":
1067
+ if (node.attributes) {
1068
+ return Array.from(node.attributes);
1069
+ }
1070
+ return [];
1071
+ case "namespace":
1072
+ return [];
1073
+ default:
1074
+ throw new Error(`Unknown axis: ${axis}`);
1075
+ }
1076
+ }
1077
+ getDescendants(node, includeSelf) {
1078
+ const result = includeSelf ? [node] : [];
1079
+ const stack = Array.from(node.childNodes || []).reverse();
1080
+ while (stack.length > 0) {
1081
+ const current = stack.pop();
1082
+ result.push(current);
1083
+ if (current.childNodes) {
1084
+ for (let i = current.childNodes.length - 1; i >= 0; i--) {
1085
+ stack.push(current.childNodes[i]);
1086
+ }
1087
+ }
1088
+ }
1089
+ return result;
1090
+ }
1091
+ getAncestors(node, includeSelf) {
1092
+ const result = includeSelf ? [node] : [];
1093
+ let current = node.parentNode;
1094
+ while (current) {
1095
+ result.push(current);
1096
+ current = current.parentNode;
1097
+ }
1098
+ return result;
1099
+ }
1100
+ getFollowingSiblings(node) {
1101
+ const result = [];
1102
+ if (!node) return result;
1103
+ let current = node.nextSibling;
1104
+ while (current) {
1105
+ result.push(current);
1106
+ current = current.nextSibling;
1107
+ }
1108
+ return result;
1109
+ }
1110
+ getPrecedingSiblings(node) {
1111
+ const result = [];
1112
+ if (!node) return result;
1113
+ let current = node.previousSibling;
1114
+ while (current) {
1115
+ result.push(current);
1116
+ current = current.previousSibling;
1117
+ }
1118
+ return result.reverse();
1119
+ }
1120
+ getFollowing(node) {
1121
+ const result = [];
1122
+ let current = node;
1123
+ while (current) {
1124
+ if (current.nextSibling) {
1125
+ current = current.nextSibling;
1126
+ result.push(current);
1127
+ result.push(...this.getDescendants(current, false));
1128
+ } else {
1129
+ current = current.parentNode;
1130
+ }
1131
+ }
1132
+ return result;
1133
+ }
1134
+ getPreceding(node) {
1135
+ const result = [];
1136
+ let current = node;
1137
+ while (current) {
1138
+ if (current.previousSibling) {
1139
+ current = current.previousSibling;
1140
+ const descendants = this.getDescendants(current, false);
1141
+ result.unshift(...descendants.reverse());
1142
+ result.unshift(current);
1143
+ } else {
1144
+ current = current.parentNode;
1145
+ if (current && current.nodeType !== 9) {
1146
+ }
1147
+ }
1148
+ }
1149
+ return result;
1150
+ }
1151
+ matchNodeTest(nodeTest, node, context) {
1152
+ switch (nodeTest.type) {
1153
+ case NodeType.NAME_TEST:
1154
+ return this.matchNameTest(nodeTest, node, context);
1155
+ case NodeType.NODE_TYPE_TEST:
1156
+ return this.matchNodeTypeTest(nodeTest.nodeType, node);
1157
+ case NodeType.PI_TEST:
1158
+ return node.nodeType === 7 && node.nodeName === nodeTest.name;
1159
+ default:
1160
+ return false;
1161
+ }
1162
+ }
1163
+ matchNameTest(nodeTest, node, context) {
1164
+ if (node.nodeType !== 1 && node.nodeType !== 2) {
1165
+ return false;
1166
+ }
1167
+ const name = nodeTest.name;
1168
+ const prefix = nodeTest.prefix;
1169
+ if (name === "*" && !prefix) {
1170
+ return true;
1171
+ }
1172
+ const nodeName = node.localName || node.nodeName;
1173
+ const nodeNs = node.namespaceURI || null;
1174
+ if (name === "*" && prefix) {
1175
+ const ns2 = context.namespaces[prefix];
1176
+ return nodeNs === ns2;
1177
+ }
1178
+ if (!prefix) {
1179
+ const doc = node.ownerDocument;
1180
+ if (doc && doc.contentType === "text/html" && node.nodeType === 1) {
1181
+ return nodeName.toLowerCase() === name.toLowerCase();
1182
+ }
1183
+ return nodeName === name;
1184
+ }
1185
+ const ns = context.namespaces[prefix];
1186
+ return nodeName === name && nodeNs === ns;
1187
+ }
1188
+ matchNodeTypeTest(nodeType, node) {
1189
+ switch (nodeType) {
1190
+ case "node":
1191
+ return true;
1192
+ case "text":
1193
+ return node.nodeType === 3;
1194
+ case "comment":
1195
+ return node.nodeType === 8;
1196
+ case "processing-instruction":
1197
+ return node.nodeType === 7;
1198
+ default:
1199
+ return false;
1200
+ }
1201
+ }
1202
+ filterByPredicate(nodes, predicate, context) {
1203
+ const result = [];
1204
+ const nodeArray = Array.isArray(nodes) ? nodes : [nodes];
1205
+ const size = nodeArray.length;
1206
+ for (let i = 0; i < nodeArray.length; i++) {
1207
+ const node = nodeArray[i];
1208
+ const predicateContext = context.clone({
1209
+ node,
1210
+ position: i + 1,
1211
+ size
1212
+ });
1213
+ const value = this.evaluate(predicate.expr, predicateContext);
1214
+ if (typeof value === "number") {
1215
+ if (value === i + 1) {
1216
+ result.push(node);
1217
+ }
1218
+ } else if (this.toBoolean(value)) {
1219
+ result.push(node);
1220
+ }
1221
+ }
1222
+ return result;
1223
+ }
1224
+ evalVariableRef(ast, context) {
1225
+ const name = ast.prefix ? `${ast.prefix}:${ast.name}` : ast.name;
1226
+ if (FORBIDDEN_VARIABLE_NAMES.includes(name) || FORBIDDEN_VARIABLE_NAMES.includes(ast.name)) {
1227
+ throw new Error(`Forbidden variable name: $${name}`);
1228
+ }
1229
+ if (!Object.prototype.hasOwnProperty.call(context.variables, name)) {
1230
+ throw new Error(`Undefined variable: $${name}`);
1231
+ }
1232
+ return context.variables[name];
1233
+ }
1234
+ evalFunctionCall(ast, context) {
1235
+ const name = ast.prefix ? `${ast.prefix}:${ast.name}` : ast.name;
1236
+ const fn = this.functions[name];
1237
+ if (!fn) {
1238
+ throw new Error(`Unknown function: ${name}`);
1239
+ }
1240
+ return fn.call(this, ast.args, context);
1241
+ }
1242
+ // Type conversion functions
1243
+ toBoolean(value) {
1244
+ if (typeof value === "boolean") return value;
1245
+ if (typeof value === "number") return value !== 0 && !isNaN(value);
1246
+ if (typeof value === "string") return value.length > 0;
1247
+ if (Array.isArray(value)) return value.length > 0;
1248
+ if (value && value.nodeType) return true;
1249
+ return Boolean(value);
1250
+ }
1251
+ toNumber(value) {
1252
+ if (typeof value === "number") return value;
1253
+ if (typeof value === "boolean") return value ? 1 : 0;
1254
+ if (typeof value === "string") {
1255
+ const trimmed = value.trim();
1256
+ if (trimmed === "") return NaN;
1257
+ const num = Number(trimmed);
1258
+ return num;
1259
+ }
1260
+ if (Array.isArray(value)) {
1261
+ return this.toNumber(this.toString(value));
1262
+ }
1263
+ if (value && value.nodeType) {
1264
+ return this.toNumber(this.getStringValue(value));
1265
+ }
1266
+ return NaN;
1267
+ }
1268
+ toString(value) {
1269
+ if (typeof value === "string") return value;
1270
+ if (typeof value === "number") {
1271
+ if (isNaN(value)) return "NaN";
1272
+ if (value === Infinity) return "Infinity";
1273
+ if (value === -Infinity) return "-Infinity";
1274
+ if (value === 0) return "0";
1275
+ return String(value);
1276
+ }
1277
+ if (typeof value === "boolean") return value ? "true" : "false";
1278
+ if (Array.isArray(value)) {
1279
+ if (value.length === 0) return "";
1280
+ return this.getStringValue(value[0]);
1281
+ }
1282
+ if (value && value.nodeType) {
1283
+ return this.getStringValue(value);
1284
+ }
1285
+ return String(value);
1286
+ }
1287
+ getStringValue(node) {
1288
+ if (!node) return "";
1289
+ switch (node.nodeType) {
1290
+ case 1:
1291
+ // Element
1292
+ case 9:
1293
+ // Document
1294
+ case 11: {
1295
+ let text = "";
1296
+ const walker = (n) => {
1297
+ if (n.nodeType === 3) {
1298
+ text += n.nodeValue || "";
1299
+ } else if (n.childNodes) {
1300
+ for (const child of n.childNodes) {
1301
+ walker(child);
1302
+ }
1303
+ }
1304
+ };
1305
+ walker(node);
1306
+ return text;
1307
+ }
1308
+ case 2:
1309
+ // Attribute
1310
+ case 3:
1311
+ // Text
1312
+ case 4:
1313
+ // CDATA
1314
+ case 7:
1315
+ // Processing Instruction
1316
+ case 8:
1317
+ return node.nodeValue || "";
1318
+ default:
1319
+ return "";
1320
+ }
1321
+ }
1322
+ // Comparison helper
1323
+ compareValues(left, right, operator) {
1324
+ const leftIsNodeSet = Array.isArray(left);
1325
+ const rightIsNodeSet = Array.isArray(right);
1326
+ if (leftIsNodeSet && rightIsNodeSet) {
1327
+ for (const l of left) {
1328
+ for (const r of right) {
1329
+ if (this.comparePrimitive(
1330
+ this.getStringValue(l),
1331
+ this.getStringValue(r),
1332
+ operator
1333
+ )) {
1334
+ return true;
1335
+ }
1336
+ }
1337
+ }
1338
+ return false;
1339
+ }
1340
+ if (leftIsNodeSet) {
1341
+ for (const l of left) {
1342
+ if (this.comparePrimitive(this.getStringValue(l), right, operator)) {
1343
+ return true;
1344
+ }
1345
+ }
1346
+ return false;
1347
+ }
1348
+ if (rightIsNodeSet) {
1349
+ for (const r of right) {
1350
+ if (this.comparePrimitive(left, this.getStringValue(r), operator)) {
1351
+ return true;
1352
+ }
1353
+ }
1354
+ return false;
1355
+ }
1356
+ return this.comparePrimitive(left, right, operator);
1357
+ }
1358
+ comparePrimitive(left, right, operator) {
1359
+ if (operator === "=" || operator === "!=") {
1360
+ if (typeof left === "boolean" || typeof right === "boolean") {
1361
+ const result2 = this.toBoolean(left) === this.toBoolean(right);
1362
+ return operator === "=" ? result2 : !result2;
1363
+ }
1364
+ if (typeof left === "number" || typeof right === "number") {
1365
+ const result2 = this.toNumber(left) === this.toNumber(right);
1366
+ return operator === "=" ? result2 : !result2;
1367
+ }
1368
+ const result = this.toString(left) === this.toString(right);
1369
+ return operator === "=" ? result : !result;
1370
+ }
1371
+ const leftNum = this.toNumber(left);
1372
+ const rightNum = this.toNumber(right);
1373
+ switch (operator) {
1374
+ case "<":
1375
+ return leftNum < rightNum;
1376
+ case "<=":
1377
+ return leftNum <= rightNum;
1378
+ case ">":
1379
+ return leftNum > rightNum;
1380
+ case ">=":
1381
+ return leftNum >= rightNum;
1382
+ default:
1383
+ throw new Error(`Unknown comparison operator: ${operator}`);
1384
+ }
1385
+ }
1386
+ sortByDocumentOrder(nodes) {
1387
+ if (nodes.length <= 1) return nodes;
1388
+ return nodes.sort((a, b) => {
1389
+ if (a === b) return 0;
1390
+ const position = a.compareDocumentPosition ? a.compareDocumentPosition(b) : this.compareDocumentPositionFallback(a, b);
1391
+ if (position & 4) return -1;
1392
+ if (position & 2) return 1;
1393
+ return 0;
1394
+ });
1395
+ }
1396
+ compareDocumentPositionFallback(a, b) {
1397
+ const getPath = (node) => {
1398
+ const path = [];
1399
+ let current = node;
1400
+ while (current) {
1401
+ if (current.parentNode) {
1402
+ const siblings = Array.from(current.parentNode.childNodes);
1403
+ path.unshift(siblings.indexOf(current));
1404
+ }
1405
+ current = current.parentNode;
1406
+ }
1407
+ return path;
1408
+ };
1409
+ const pathA = getPath(a);
1410
+ const pathB = getPath(b);
1411
+ for (let i = 0; i < Math.min(pathA.length, pathB.length); i++) {
1412
+ if (pathA[i] < pathB[i]) return 4;
1413
+ if (pathA[i] > pathB[i]) return 2;
1414
+ }
1415
+ return pathA.length < pathB.length ? 4 : 2;
1416
+ }
1417
+ /**
1418
+ * Initialize XPath 1.0 core functions
1419
+ */
1420
+ initCoreFunctions() {
1421
+ return {
1422
+ // Node set functions
1423
+ last: (args, ctx) => ctx.size,
1424
+ position: (args, ctx) => ctx.position,
1425
+ count: (args, ctx) => {
1426
+ const nodeSet = this.evaluate(args[0], ctx);
1427
+ return Array.isArray(nodeSet) ? nodeSet.length : 1;
1428
+ },
1429
+ id: (args, ctx) => {
1430
+ const value = this.toString(this.evaluate(args[0], ctx));
1431
+ const doc = ctx.node.ownerDocument || ctx.node;
1432
+ const ids = value.split(/\s+/).filter((id) => id);
1433
+ const result = [];
1434
+ for (const id of ids) {
1435
+ const el = doc.getElementById(id);
1436
+ if (el) result.push(el);
1437
+ }
1438
+ return result;
1439
+ },
1440
+ "local-name": (args, ctx) => {
1441
+ let node;
1442
+ if (args.length === 0) {
1443
+ node = ctx.node;
1444
+ } else {
1445
+ const nodeSet = this.evaluate(args[0], ctx);
1446
+ node = Array.isArray(nodeSet) ? nodeSet[0] : nodeSet;
1447
+ }
1448
+ if (!node) return "";
1449
+ return node.localName || node.nodeName || "";
1450
+ },
1451
+ "namespace-uri": (args, ctx) => {
1452
+ let node;
1453
+ if (args.length === 0) {
1454
+ node = ctx.node;
1455
+ } else {
1456
+ const nodeSet = this.evaluate(args[0], ctx);
1457
+ node = Array.isArray(nodeSet) ? nodeSet[0] : nodeSet;
1458
+ }
1459
+ if (!node) return "";
1460
+ return node.namespaceURI || "";
1461
+ },
1462
+ name: (args, ctx) => {
1463
+ let node;
1464
+ if (args.length === 0) {
1465
+ node = ctx.node;
1466
+ } else {
1467
+ const nodeSet = this.evaluate(args[0], ctx);
1468
+ node = Array.isArray(nodeSet) ? nodeSet[0] : nodeSet;
1469
+ }
1470
+ if (!node) return "";
1471
+ return node.nodeName || "";
1472
+ },
1473
+ // String functions
1474
+ string: (args, ctx) => {
1475
+ if (args.length === 0) {
1476
+ return this.toString([ctx.node]);
1477
+ }
1478
+ return this.toString(this.evaluate(args[0], ctx));
1479
+ },
1480
+ concat: (args, ctx) => {
1481
+ return args.map((arg) => this.toString(this.evaluate(arg, ctx))).join("");
1482
+ },
1483
+ "starts-with": (args, ctx) => {
1484
+ const str = this.toString(this.evaluate(args[0], ctx));
1485
+ const prefix = this.toString(this.evaluate(args[1], ctx));
1486
+ return str.startsWith(prefix);
1487
+ },
1488
+ contains: (args, ctx) => {
1489
+ const str = this.toString(this.evaluate(args[0], ctx));
1490
+ const substr = this.toString(this.evaluate(args[1], ctx));
1491
+ return str.includes(substr);
1492
+ },
1493
+ "substring-before": (args, ctx) => {
1494
+ const str = this.toString(this.evaluate(args[0], ctx));
1495
+ const substr = this.toString(this.evaluate(args[1], ctx));
1496
+ const idx = str.indexOf(substr);
1497
+ return idx === -1 ? "" : str.substring(0, idx);
1498
+ },
1499
+ "substring-after": (args, ctx) => {
1500
+ const str = this.toString(this.evaluate(args[0], ctx));
1501
+ const substr = this.toString(this.evaluate(args[1], ctx));
1502
+ const idx = str.indexOf(substr);
1503
+ return idx === -1 ? "" : str.substring(idx + substr.length);
1504
+ },
1505
+ substring: (args, ctx) => {
1506
+ const str = this.toString(this.evaluate(args[0], ctx));
1507
+ let start = Math.round(this.toNumber(this.evaluate(args[1], ctx)));
1508
+ let length;
1509
+ if (args.length > 2) {
1510
+ length = Math.round(this.toNumber(this.evaluate(args[2], ctx)));
1511
+ }
1512
+ start = start - 1;
1513
+ if (isNaN(start)) return "";
1514
+ if (start < 0) {
1515
+ if (length !== void 0) {
1516
+ length = length + start;
1517
+ }
1518
+ start = 0;
1519
+ }
1520
+ if (length !== void 0) {
1521
+ if (isNaN(length) || length <= 0) return "";
1522
+ return str.substring(start, start + length);
1523
+ }
1524
+ return str.substring(start);
1525
+ },
1526
+ "string-length": (args, ctx) => {
1527
+ const str = args.length === 0 ? this.toString([ctx.node]) : this.toString(this.evaluate(args[0], ctx));
1528
+ return str.length;
1529
+ },
1530
+ "normalize-space": (args, ctx) => {
1531
+ const str = args.length === 0 ? this.toString([ctx.node]) : this.toString(this.evaluate(args[0], ctx));
1532
+ return str.trim().replace(/\s+/g, " ");
1533
+ },
1534
+ translate: (args, ctx) => {
1535
+ const str = this.toString(this.evaluate(args[0], ctx));
1536
+ const from = this.toString(this.evaluate(args[1], ctx));
1537
+ const to = this.toString(this.evaluate(args[2], ctx));
1538
+ let result = "";
1539
+ for (const char of str) {
1540
+ const idx = from.indexOf(char);
1541
+ if (idx === -1) {
1542
+ result += char;
1543
+ } else if (idx < to.length) {
1544
+ result += to[idx];
1545
+ }
1546
+ }
1547
+ return result;
1548
+ },
1549
+ // Boolean functions
1550
+ boolean: (args, ctx) => {
1551
+ return this.toBoolean(this.evaluate(args[0], ctx));
1552
+ },
1553
+ not: (args, ctx) => {
1554
+ return !this.toBoolean(this.evaluate(args[0], ctx));
1555
+ },
1556
+ true: () => true,
1557
+ false: () => false,
1558
+ lang: (args, ctx) => {
1559
+ const lang = this.toString(this.evaluate(args[0], ctx)).toLowerCase();
1560
+ let node = ctx.node;
1561
+ while (node && node.nodeType === 1) {
1562
+ const xmlLang = node.getAttribute("xml:lang") || node.getAttribute("lang");
1563
+ if (xmlLang) {
1564
+ const nodeLang = xmlLang.toLowerCase();
1565
+ return nodeLang === lang || nodeLang.startsWith(lang + "-");
1566
+ }
1567
+ node = node.parentNode;
1568
+ }
1569
+ return false;
1570
+ },
1571
+ // Number functions
1572
+ number: (args, ctx) => {
1573
+ if (args.length === 0) {
1574
+ return this.toNumber([ctx.node]);
1575
+ }
1576
+ return this.toNumber(this.evaluate(args[0], ctx));
1577
+ },
1578
+ sum: (args, ctx) => {
1579
+ const nodeSet = this.evaluate(args[0], ctx);
1580
+ if (!Array.isArray(nodeSet)) return NaN;
1581
+ return nodeSet.reduce(
1582
+ (sum, node) => sum + this.toNumber(this.getStringValue(node)),
1583
+ 0
1584
+ );
1585
+ },
1586
+ floor: (args, ctx) => {
1587
+ return Math.floor(this.toNumber(this.evaluate(args[0], ctx)));
1588
+ },
1589
+ ceiling: (args, ctx) => {
1590
+ return Math.ceil(this.toNumber(this.evaluate(args[0], ctx)));
1591
+ },
1592
+ round: (args, ctx) => {
1593
+ const num = this.toNumber(this.evaluate(args[0], ctx));
1594
+ if (isNaN(num)) return NaN;
1595
+ if (num === -0.5) return -0;
1596
+ return Math.round(num);
1597
+ }
1598
+ };
1599
+ }
1600
+ };
1601
+
1602
+ // src/xslt/engine.js
1603
+ var XSLT_NS = "http://www.w3.org/1999/XSL/Transform";
1604
+ var XsltContext = class _XsltContext {
1605
+ constructor(options = {}) {
1606
+ this.currentNode = options.currentNode;
1607
+ this.currentNodeList = options.currentNodeList || [];
1608
+ this.position = options.position || 1;
1609
+ this.variables = { ...options.variables };
1610
+ this.parameters = { ...options.parameters };
1611
+ this.outputDocument = options.outputDocument;
1612
+ this.stylesheet = options.stylesheet;
1613
+ this.namespaces = { ...options.namespaces };
1614
+ this.templates = options.templates || [];
1615
+ this.keys = options.keys || {};
1616
+ this.decimalFormats = options.decimalFormats || {};
1617
+ this.outputMethod = options.outputMethod || "xml";
1618
+ this.xpathEvaluator = options.xpathEvaluator || new XPathEvaluator();
1619
+ }
1620
+ clone(overrides = {}) {
1621
+ return new _XsltContext({
1622
+ currentNode: overrides.currentNode ?? this.currentNode,
1623
+ currentNodeList: overrides.currentNodeList ?? this.currentNodeList,
1624
+ position: overrides.position ?? this.position,
1625
+ variables: overrides.variables ? { ...this.variables, ...overrides.variables } : { ...this.variables },
1626
+ parameters: overrides.parameters ? { ...this.parameters, ...overrides.parameters } : { ...this.parameters },
1627
+ outputDocument: this.outputDocument,
1628
+ stylesheet: this.stylesheet,
1629
+ namespaces: overrides.namespaces ? { ...this.namespaces, ...overrides.namespaces } : { ...this.namespaces },
1630
+ templates: this.templates,
1631
+ keys: this.keys,
1632
+ decimalFormats: this.decimalFormats,
1633
+ outputMethod: this.outputMethod,
1634
+ xpathEvaluator: this.xpathEvaluator
1635
+ });
1636
+ }
1637
+ getVariable(name) {
1638
+ if (name in this.variables) {
1639
+ return this.variables[name];
1640
+ }
1641
+ if (name in this.parameters) {
1642
+ return this.parameters[name];
1643
+ }
1644
+ throw new Error(`Undefined variable: $${name}`);
1645
+ }
1646
+ setVariable(name, value) {
1647
+ this.variables[name] = value;
1648
+ }
1649
+ };
1650
+ var XsltEngine = class {
1651
+ constructor(options = {}) {
1652
+ this.xpathEvaluator = new XPathEvaluator();
1653
+ this.templates = [];
1654
+ this.keys = {};
1655
+ this.globalVariables = {};
1656
+ this.globalParameters = {};
1657
+ this.outputSettings = {
1658
+ method: "xml",
1659
+ encoding: "UTF-8",
1660
+ indent: "no",
1661
+ omitXmlDeclaration: "no",
1662
+ doctypePublic: null,
1663
+ doctypeSystem: null,
1664
+ mediaType: null,
1665
+ cdataSectionElements: []
1666
+ };
1667
+ this.namespaces = {};
1668
+ this.decimalFormats = {};
1669
+ this.stylesheetDoc = null;
1670
+ this.attributeSets = {};
1671
+ this.namespaceAliases = {};
1672
+ this.stripSpace = [];
1673
+ this.preserveSpace = [];
1674
+ this.stylesheetLoader = options.stylesheetLoader || null;
1675
+ this.currentImportPrecedence = 0;
1676
+ this.processedStylesheets = /* @__PURE__ */ new Set();
1677
+ this.baseUri = options.baseUri || "";
1678
+ }
1679
+ /**
1680
+ * Set the stylesheet loader function for xsl:import and xsl:include
1681
+ * @param {Function} loader - Function(href, baseUri) => Document or string (XML)
1682
+ */
1683
+ setStylesheetLoader(loader) {
1684
+ this.stylesheetLoader = loader;
1685
+ }
1686
+ /**
1687
+ * Resolve a relative URI against a base URI
1688
+ */
1689
+ resolveUri(href, baseUri) {
1690
+ if (!baseUri || href.startsWith("http://") || href.startsWith("https://") || href.startsWith("/")) {
1691
+ return href;
1692
+ }
1693
+ const lastSlash = baseUri.lastIndexOf("/");
1694
+ const baseDir = lastSlash >= 0 ? baseUri.substring(0, lastSlash + 1) : "";
1695
+ return baseDir + href;
1696
+ }
1697
+ /**
1698
+ * Load an external stylesheet document
1699
+ */
1700
+ loadStylesheet(href, baseUri) {
1701
+ if (!this.stylesheetLoader) {
1702
+ throw new Error(
1703
+ `Cannot load stylesheet "${href}": no stylesheetLoader configured. Use engine.setStylesheetLoader(fn) to provide a loader function.`
1704
+ );
1705
+ }
1706
+ const resolvedUri = this.resolveUri(href, baseUri);
1707
+ const result = this.stylesheetLoader(resolvedUri, baseUri);
1708
+ return { document: result, uri: resolvedUri };
1709
+ }
1710
+ /**
1711
+ * Parse XML string to document (helper for stylesheet loading)
1712
+ */
1713
+ parseXmlString(xmlString) {
1714
+ if (typeof DOMParser !== "undefined") {
1715
+ const parser = new DOMParser();
1716
+ const doc = parser.parseFromString(xmlString, "application/xml");
1717
+ const parseError = doc.querySelector("parsererror");
1718
+ if (parseError) {
1719
+ throw new Error(`XML parse error: ${parseError.textContent}`);
1720
+ }
1721
+ return doc;
1722
+ }
1723
+ throw new Error("XML parsing not available in this environment");
1724
+ }
1725
+ /**
1726
+ * Import and compile an XSLT stylesheet
1727
+ * @param {Document|Element} stylesheetNode - The stylesheet document or root element
1728
+ * @param {string} [stylesheetUri] - Optional URI of the stylesheet for resolving imports
1729
+ */
1730
+ importStylesheet(stylesheetNode, stylesheetUri) {
1731
+ const isMainStylesheet = this.stylesheetDoc === null;
1732
+ if (isMainStylesheet) {
1733
+ this.stylesheetDoc = stylesheetNode.ownerDocument || stylesheetNode;
1734
+ if (stylesheetUri) {
1735
+ this.baseUri = stylesheetUri;
1736
+ this.processedStylesheets.add(stylesheetUri);
1737
+ }
1738
+ }
1739
+ const root = stylesheetNode.documentElement || stylesheetNode;
1740
+ if (!this.isXsltElement(root, "stylesheet") && !this.isXsltElement(root, "transform")) {
1741
+ if (root.getAttribute && root.getAttribute("xsl:version")) {
1742
+ this.processLiteralResultStylesheet(root);
1743
+ return;
1744
+ }
1745
+ throw new Error(
1746
+ "Invalid XSLT stylesheet: root element must be xsl:stylesheet or xsl:transform"
1747
+ );
1748
+ }
1749
+ this.collectNamespaces(root);
1750
+ const imports = [];
1751
+ const otherElements = [];
1752
+ for (const child of root.childNodes) {
1753
+ if (child.nodeType !== 1) continue;
1754
+ if (this.isXsltElement(child, "import")) {
1755
+ imports.push(child);
1756
+ } else {
1757
+ otherElements.push(child);
1758
+ }
1759
+ }
1760
+ for (const importNode of imports) {
1761
+ this.processImport(importNode, stylesheetUri || this.baseUri);
1762
+ }
1763
+ for (const child of otherElements) {
1764
+ if (this.isXsltElement(child, "template")) {
1765
+ this.registerTemplate(child);
1766
+ } else if (this.isXsltElement(child, "output")) {
1767
+ this.processOutput(child);
1768
+ } else if (this.isXsltElement(child, "variable")) {
1769
+ this.processGlobalVariable(child);
1770
+ } else if (this.isXsltElement(child, "param")) {
1771
+ this.processGlobalParam(child);
1772
+ } else if (this.isXsltElement(child, "key")) {
1773
+ this.processKey(child);
1774
+ } else if (this.isXsltElement(child, "decimal-format")) {
1775
+ this.processDecimalFormat(child);
1776
+ } else if (this.isXsltElement(child, "namespace-alias")) {
1777
+ this.processNamespaceAlias(child);
1778
+ } else if (this.isXsltElement(child, "attribute-set")) {
1779
+ this.processAttributeSet(child);
1780
+ } else if (this.isXsltElement(child, "strip-space")) {
1781
+ this.processStripSpace(child);
1782
+ } else if (this.isXsltElement(child, "preserve-space")) {
1783
+ this.processPreserveSpace(child);
1784
+ } else if (this.isXsltElement(child, "include")) {
1785
+ this.processInclude(child, stylesheetUri || this.baseUri);
1786
+ }
1787
+ }
1788
+ if (isMainStylesheet) {
1789
+ this.currentImportPrecedence++;
1790
+ }
1791
+ }
1792
+ /**
1793
+ * Process xsl:include element
1794
+ * Includes are merged at the same import precedence level
1795
+ */
1796
+ processInclude(node, baseUri) {
1797
+ const href = node.getAttribute("href");
1798
+ if (!href) {
1799
+ throw new Error("xsl:include requires an href attribute");
1800
+ }
1801
+ const resolvedUri = this.resolveUri(href, baseUri);
1802
+ if (this.processedStylesheets.has(resolvedUri)) {
1803
+ throw new Error(`Circular stylesheet reference detected: ${resolvedUri}`);
1804
+ }
1805
+ this.processedStylesheets.add(resolvedUri);
1806
+ try {
1807
+ const { document: stylesheetDoc } = this.loadStylesheet(href, baseUri);
1808
+ let doc = stylesheetDoc;
1809
+ if (typeof stylesheetDoc === "string") {
1810
+ doc = this.parseXmlString(stylesheetDoc);
1811
+ }
1812
+ const savedPrecedence = this.currentImportPrecedence;
1813
+ this.processIncludedStylesheet(doc, resolvedUri);
1814
+ this.currentImportPrecedence = savedPrecedence;
1815
+ } catch (error) {
1816
+ throw new Error(
1817
+ `Failed to include stylesheet "${href}": ${error.message}`
1818
+ );
1819
+ }
1820
+ }
1821
+ /**
1822
+ * Process xsl:import element
1823
+ * Imports have lower precedence than the importing stylesheet
1824
+ */
1825
+ processImport(node, baseUri) {
1826
+ const href = node.getAttribute("href");
1827
+ if (!href) {
1828
+ throw new Error("xsl:import requires an href attribute");
1829
+ }
1830
+ const resolvedUri = this.resolveUri(href, baseUri);
1831
+ if (this.processedStylesheets.has(resolvedUri)) {
1832
+ throw new Error(`Circular stylesheet reference detected: ${resolvedUri}`);
1833
+ }
1834
+ this.processedStylesheets.add(resolvedUri);
1835
+ try {
1836
+ const { document: stylesheetDoc } = this.loadStylesheet(href, baseUri);
1837
+ let doc = stylesheetDoc;
1838
+ if (typeof stylesheetDoc === "string") {
1839
+ doc = this.parseXmlString(stylesheetDoc);
1840
+ }
1841
+ this.processIncludedStylesheet(doc, resolvedUri);
1842
+ this.currentImportPrecedence++;
1843
+ } catch (error) {
1844
+ throw new Error(
1845
+ `Failed to import stylesheet "${href}": ${error.message}`
1846
+ );
1847
+ }
1848
+ }
1849
+ /**
1850
+ * Process an included/imported stylesheet document
1851
+ */
1852
+ processIncludedStylesheet(stylesheetDoc, stylesheetUri) {
1853
+ const root = stylesheetDoc.documentElement || stylesheetDoc;
1854
+ if (!this.isXsltElement(root, "stylesheet") && !this.isXsltElement(root, "transform")) {
1855
+ throw new Error(
1856
+ "Included/imported document is not a valid XSLT stylesheet"
1857
+ );
1858
+ }
1859
+ this.collectNamespaces(root);
1860
+ const imports = [];
1861
+ const otherElements = [];
1862
+ for (const child of root.childNodes) {
1863
+ if (child.nodeType !== 1) continue;
1864
+ if (this.isXsltElement(child, "import")) {
1865
+ imports.push(child);
1866
+ } else {
1867
+ otherElements.push(child);
1868
+ }
1869
+ }
1870
+ for (const importNode of imports) {
1871
+ this.processImport(importNode, stylesheetUri);
1872
+ }
1873
+ for (const child of otherElements) {
1874
+ if (this.isXsltElement(child, "template")) {
1875
+ this.registerTemplate(child);
1876
+ } else if (this.isXsltElement(child, "output")) {
1877
+ this.processOutput(child);
1878
+ } else if (this.isXsltElement(child, "variable")) {
1879
+ this.processGlobalVariable(child);
1880
+ } else if (this.isXsltElement(child, "param")) {
1881
+ this.processGlobalParam(child);
1882
+ } else if (this.isXsltElement(child, "key")) {
1883
+ this.processKey(child);
1884
+ } else if (this.isXsltElement(child, "decimal-format")) {
1885
+ this.processDecimalFormat(child);
1886
+ } else if (this.isXsltElement(child, "namespace-alias")) {
1887
+ this.processNamespaceAlias(child);
1888
+ } else if (this.isXsltElement(child, "attribute-set")) {
1889
+ this.processAttributeSet(child);
1890
+ } else if (this.isXsltElement(child, "strip-space")) {
1891
+ this.processStripSpace(child);
1892
+ } else if (this.isXsltElement(child, "preserve-space")) {
1893
+ this.processPreserveSpace(child);
1894
+ } else if (this.isXsltElement(child, "include")) {
1895
+ this.processInclude(child, stylesheetUri);
1896
+ }
1897
+ }
1898
+ }
1899
+ processLiteralResultStylesheet(root) {
1900
+ this.templates.push({
1901
+ match: "/",
1902
+ name: null,
1903
+ mode: null,
1904
+ priority: 0.5,
1905
+ node: root
1906
+ });
1907
+ }
1908
+ collectNamespaces(node) {
1909
+ if (!node.attributes) return;
1910
+ for (const attr of node.attributes) {
1911
+ if (attr.name.startsWith("xmlns:")) {
1912
+ const prefix = attr.name.substring(6);
1913
+ if (attr.value !== XSLT_NS) {
1914
+ this.namespaces[prefix] = attr.value;
1915
+ }
1916
+ } else if (attr.name === "xmlns" && attr.value !== XSLT_NS) {
1917
+ this.namespaces[""] = attr.value;
1918
+ }
1919
+ }
1920
+ }
1921
+ registerTemplate(node) {
1922
+ const match = node.getAttribute("match");
1923
+ const name = node.getAttribute("name");
1924
+ const mode = node.getAttribute("mode") || null;
1925
+ const priorityAttr = node.getAttribute("priority");
1926
+ const priority = priorityAttr ? parseFloat(priorityAttr) : this.calculatePriority(match);
1927
+ this.templates.push({
1928
+ match,
1929
+ name,
1930
+ mode,
1931
+ priority,
1932
+ importPrecedence: this.currentImportPrecedence,
1933
+ node
1934
+ });
1935
+ }
1936
+ calculatePriority(matchPattern) {
1937
+ if (!matchPattern) return 0.5;
1938
+ if (matchPattern === "*" || matchPattern === "node()" || matchPattern === "text()" || matchPattern === "comment()" || matchPattern === "processing-instruction()") {
1939
+ return -0.5;
1940
+ }
1941
+ if (matchPattern.includes(":*")) {
1942
+ return -0.25;
1943
+ }
1944
+ if (/^[a-zA-Z_][\w.-]*$/.test(matchPattern)) {
1945
+ return 0;
1946
+ }
1947
+ return 0.5;
1948
+ }
1949
+ processOutput(node) {
1950
+ const method = node.getAttribute("method");
1951
+ if (method) this.outputSettings.method = method;
1952
+ const encoding = node.getAttribute("encoding");
1953
+ if (encoding) this.outputSettings.encoding = encoding;
1954
+ const indent = node.getAttribute("indent");
1955
+ if (indent) this.outputSettings.indent = indent;
1956
+ const omit = node.getAttribute("omit-xml-declaration");
1957
+ if (omit) this.outputSettings.omitXmlDeclaration = omit;
1958
+ const doctypePublic = node.getAttribute("doctype-public");
1959
+ if (doctypePublic) this.outputSettings.doctypePublic = doctypePublic;
1960
+ const doctypeSystem = node.getAttribute("doctype-system");
1961
+ if (doctypeSystem) this.outputSettings.doctypeSystem = doctypeSystem;
1962
+ const mediaType = node.getAttribute("media-type");
1963
+ if (mediaType) this.outputSettings.mediaType = mediaType;
1964
+ const cdataElements = node.getAttribute("cdata-section-elements");
1965
+ if (cdataElements) {
1966
+ this.outputSettings.cdataSectionElements = cdataElements.split(/\s+/).filter(Boolean);
1967
+ }
1968
+ }
1969
+ processGlobalVariable(node) {
1970
+ const name = node.getAttribute("name");
1971
+ const select2 = node.getAttribute("select");
1972
+ this.globalVariables[name] = { node, select: select2 };
1973
+ }
1974
+ processGlobalParam(node) {
1975
+ const name = node.getAttribute("name");
1976
+ const select2 = node.getAttribute("select");
1977
+ this.globalParameters[name] = { node, select: select2 };
1978
+ }
1979
+ processKey(node) {
1980
+ const name = node.getAttribute("name");
1981
+ const match = node.getAttribute("match");
1982
+ const use = node.getAttribute("use");
1983
+ this.keys[name] = { match, use };
1984
+ }
1985
+ processDecimalFormat(node) {
1986
+ const name = node.getAttribute("name") || "";
1987
+ this.decimalFormats[name] = {
1988
+ decimalSeparator: node.getAttribute("decimal-separator") || ".",
1989
+ groupingSeparator: node.getAttribute("grouping-separator") || ",",
1990
+ percent: node.getAttribute("percent") || "%",
1991
+ perMille: node.getAttribute("per-mille") || "\u2030",
1992
+ zeroDigit: node.getAttribute("zero-digit") || "0",
1993
+ digit: node.getAttribute("digit") || "#",
1994
+ patternSeparator: node.getAttribute("pattern-separator") || ";",
1995
+ infinity: node.getAttribute("infinity") || "Infinity",
1996
+ nan: node.getAttribute("NaN") || "NaN",
1997
+ minusSign: node.getAttribute("minus-sign") || "-"
1998
+ };
1999
+ }
2000
+ processNamespaceAlias(node) {
2001
+ const stylesheet = node.getAttribute("stylesheet-prefix");
2002
+ const result = node.getAttribute("result-prefix");
2003
+ this.namespaceAliases[stylesheet] = result;
2004
+ }
2005
+ processAttributeSet(node) {
2006
+ const name = node.getAttribute("name");
2007
+ const useAttributeSets = node.getAttribute("use-attribute-sets");
2008
+ this.attributeSets[name] = {
2009
+ node,
2010
+ useAttributeSets: useAttributeSets ? useAttributeSets.split(/\s+/).filter(Boolean) : []
2011
+ };
2012
+ }
2013
+ processStripSpace(node) {
2014
+ const elements = node.getAttribute("elements");
2015
+ if (elements) {
2016
+ this.stripSpace.push(...elements.split(/\s+/).filter(Boolean));
2017
+ }
2018
+ }
2019
+ processPreserveSpace(node) {
2020
+ const elements = node.getAttribute("elements");
2021
+ if (elements) {
2022
+ this.preserveSpace.push(...elements.split(/\s+/).filter(Boolean));
2023
+ }
2024
+ }
2025
+ /**
2026
+ * Transform a source document
2027
+ */
2028
+ transform(sourceNode, ownerDocument) {
2029
+ const doc = ownerDocument || (typeof document !== "undefined" ? document : null);
2030
+ if (!doc) {
2031
+ throw new Error("No output document available");
2032
+ }
2033
+ const context = new XsltContext({
2034
+ currentNode: sourceNode.documentElement || sourceNode,
2035
+ currentNodeList: [sourceNode.documentElement || sourceNode],
2036
+ position: 1,
2037
+ outputDocument: doc,
2038
+ stylesheet: this.stylesheetDoc,
2039
+ namespaces: { ...this.namespaces },
2040
+ templates: this.templates,
2041
+ keys: this.keys,
2042
+ decimalFormats: this.decimalFormats,
2043
+ outputMethod: this.outputSettings.method,
2044
+ xpathEvaluator: this.xpathEvaluator
2045
+ });
2046
+ for (const [name, def] of Object.entries(this.globalParameters)) {
2047
+ if (!(name in context.parameters)) {
2048
+ context.parameters[name] = this.evaluateVariable(def, context);
2049
+ }
2050
+ }
2051
+ for (const [name, def] of Object.entries(this.globalVariables)) {
2052
+ context.variables[name] = this.evaluateVariable(def, context);
2053
+ }
2054
+ const fragment = doc.createDocumentFragment();
2055
+ this.applyTemplates(
2056
+ [sourceNode.documentElement || sourceNode],
2057
+ null,
2058
+ context,
2059
+ fragment
2060
+ );
2061
+ return fragment;
2062
+ }
2063
+ /**
2064
+ * Transform to a complete document
2065
+ */
2066
+ transformToDocument(sourceNode) {
2067
+ const doc = this.createDocument();
2068
+ const fragment = this.transform(sourceNode, doc);
2069
+ while (fragment.firstChild) {
2070
+ doc.appendChild(fragment.firstChild);
2071
+ }
2072
+ return doc;
2073
+ }
2074
+ createDocument() {
2075
+ if (typeof document !== "undefined") {
2076
+ return document.implementation.createDocument(null, null, null);
2077
+ }
2078
+ throw new Error("Document creation not available in this environment");
2079
+ }
2080
+ evaluateVariable(def, context) {
2081
+ if (def.select) {
2082
+ return this.evaluateXPath(def.select, context);
2083
+ }
2084
+ const fragment = context.outputDocument.createDocumentFragment();
2085
+ this.processChildren(def.node, context, fragment);
2086
+ return fragment;
2087
+ }
2088
+ /**
2089
+ * Apply templates to a node list
2090
+ */
2091
+ applyTemplates(nodes, mode, context, output) {
2092
+ const nodeList = Array.isArray(nodes) ? nodes : [nodes];
2093
+ for (let i = 0; i < nodeList.length; i++) {
2094
+ const node = nodeList[i];
2095
+ const template = this.findMatchingTemplate(node, mode, context);
2096
+ if (template) {
2097
+ const newContext = context.clone({
2098
+ currentNode: node,
2099
+ currentNodeList: nodeList,
2100
+ position: i + 1
2101
+ });
2102
+ this.processTemplate(template.node, newContext, output);
2103
+ } else {
2104
+ this.applyBuiltinTemplate(node, mode, context, output);
2105
+ }
2106
+ }
2107
+ }
2108
+ /**
2109
+ * Find the best matching template for a node
2110
+ */
2111
+ findMatchingTemplate(node, mode, context) {
2112
+ let bestMatch = null;
2113
+ let bestPriority = -Infinity;
2114
+ let bestImportPrecedence = -Infinity;
2115
+ for (const template of this.templates) {
2116
+ if (template.mode !== mode) continue;
2117
+ if (!template.match) continue;
2118
+ if (this.matchesPattern(node, template.match, context)) {
2119
+ const priority = template.priority;
2120
+ const importPrecedence = template.importPrecedence || 0;
2121
+ if (importPrecedence > bestImportPrecedence || importPrecedence === bestImportPrecedence && priority > bestPriority) {
2122
+ bestMatch = template;
2123
+ bestPriority = priority;
2124
+ bestImportPrecedence = importPrecedence;
2125
+ }
2126
+ }
2127
+ }
2128
+ return bestMatch;
2129
+ }
2130
+ /**
2131
+ * Check if a node matches an XSLT pattern
2132
+ */
2133
+ matchesPattern(node, pattern, context) {
2134
+ const patterns = this.splitUnionPattern(pattern);
2135
+ for (const p of patterns) {
2136
+ if (this.matchesSinglePattern(node, p.trim(), context)) {
2137
+ return true;
2138
+ }
2139
+ }
2140
+ return false;
2141
+ }
2142
+ splitUnionPattern(pattern) {
2143
+ const parts = [];
2144
+ let current = "";
2145
+ let depth = 0;
2146
+ let inString = false;
2147
+ let stringChar = "";
2148
+ for (let i = 0; i < pattern.length; i++) {
2149
+ const char = pattern[i];
2150
+ if (inString) {
2151
+ current += char;
2152
+ if (char === stringChar) {
2153
+ inString = false;
2154
+ }
2155
+ } else if (char === '"' || char === "'") {
2156
+ inString = true;
2157
+ stringChar = char;
2158
+ current += char;
2159
+ } else if (char === "[") {
2160
+ depth++;
2161
+ current += char;
2162
+ } else if (char === "]") {
2163
+ depth--;
2164
+ current += char;
2165
+ } else if (char === "|" && depth === 0) {
2166
+ parts.push(current);
2167
+ current = "";
2168
+ } else {
2169
+ current += char;
2170
+ }
2171
+ }
2172
+ if (current) {
2173
+ parts.push(current);
2174
+ }
2175
+ return parts;
2176
+ }
2177
+ matchesSinglePattern(node, pattern, context) {
2178
+ var _a;
2179
+ try {
2180
+ if (pattern === "/") {
2181
+ return node.nodeType === 9 || node === ((_a = node.ownerDocument) == null ? void 0 : _a.documentElement);
2182
+ }
2183
+ const ast = parse(pattern);
2184
+ if (pattern.startsWith("/")) {
2185
+ const doc = node.ownerDocument || node;
2186
+ const xpathContext2 = new XPathContext(
2187
+ doc,
2188
+ 1,
2189
+ 1,
2190
+ { ...context.variables, ...context.parameters },
2191
+ context.namespaces
2192
+ );
2193
+ const result2 = this.xpathEvaluator.evaluate(ast, xpathContext2);
2194
+ const nodes2 = Array.isArray(result2) ? result2 : [result2];
2195
+ return nodes2.includes(node);
2196
+ }
2197
+ if (node.parentNode) {
2198
+ const xpathContext2 = new XPathContext(
2199
+ node.parentNode,
2200
+ 1,
2201
+ 1,
2202
+ { ...context.variables, ...context.parameters },
2203
+ context.namespaces
2204
+ );
2205
+ const result2 = this.xpathEvaluator.evaluate(ast, xpathContext2);
2206
+ const nodes2 = Array.isArray(result2) ? result2 : [result2];
2207
+ return nodes2.includes(node);
2208
+ }
2209
+ const xpathContext = new XPathContext(
2210
+ node,
2211
+ 1,
2212
+ 1,
2213
+ { ...context.variables, ...context.parameters },
2214
+ context.namespaces
2215
+ );
2216
+ const result = this.xpathEvaluator.evaluate(ast, xpathContext);
2217
+ const nodes = Array.isArray(result) ? result : [result];
2218
+ return nodes.includes(node);
2219
+ } catch {
2220
+ return false;
2221
+ }
2222
+ }
2223
+ /**
2224
+ * Apply built-in template rules
2225
+ */
2226
+ applyBuiltinTemplate(node, mode, context, output) {
2227
+ switch (node.nodeType) {
2228
+ case 1:
2229
+ // Element
2230
+ case 9:
2231
+ // Document
2232
+ case 11:
2233
+ this.applyTemplates(Array.from(node.childNodes), mode, context, output);
2234
+ break;
2235
+ case 3:
2236
+ // Text
2237
+ case 4: {
2238
+ const text = context.outputDocument.createTextNode(
2239
+ node.nodeValue || ""
2240
+ );
2241
+ output.appendChild(text);
2242
+ break;
2243
+ }
2244
+ case 2: {
2245
+ const attrText = context.outputDocument.createTextNode(
2246
+ node.nodeValue || ""
2247
+ );
2248
+ output.appendChild(attrText);
2249
+ break;
2250
+ }
2251
+ }
2252
+ }
2253
+ /**
2254
+ * Process template content
2255
+ */
2256
+ processTemplate(templateNode, context, output) {
2257
+ const localContext = context.clone();
2258
+ for (const child of templateNode.childNodes) {
2259
+ if (child.nodeType === 1 && this.isXsltElement(child, "param")) {
2260
+ const name = child.getAttribute("name");
2261
+ if (!(name in localContext.parameters)) {
2262
+ localContext.parameters[name] = this.evaluateVariable(
2263
+ { node: child, select: child.getAttribute("select") },
2264
+ localContext
2265
+ );
2266
+ }
2267
+ }
2268
+ }
2269
+ this.processChildren(templateNode, localContext, output);
2270
+ }
2271
+ /**
2272
+ * Process child nodes of an XSLT element
2273
+ */
2274
+ processChildren(node, context, output) {
2275
+ for (const child of node.childNodes) {
2276
+ this.processNode(child, context, output);
2277
+ }
2278
+ }
2279
+ /**
2280
+ * Process a single node in the stylesheet
2281
+ */
2282
+ processNode(node, context, output) {
2283
+ switch (node.nodeType) {
2284
+ case 1:
2285
+ this.processElement(node, context, output);
2286
+ break;
2287
+ case 3:
2288
+ // Text
2289
+ case 4: {
2290
+ const text = node.nodeValue;
2291
+ if (text && (text.trim() || this.shouldPreserveSpace(node))) {
2292
+ const textNode = context.outputDocument.createTextNode(text);
2293
+ output.appendChild(textNode);
2294
+ }
2295
+ break;
2296
+ }
2297
+ }
2298
+ }
2299
+ shouldPreserveSpace(node) {
2300
+ let current = node.parentNode;
2301
+ while (current && current.nodeType === 1) {
2302
+ const space = current.getAttribute("xml:space");
2303
+ if (space === "preserve") return true;
2304
+ if (space === "default") return false;
2305
+ current = current.parentNode;
2306
+ }
2307
+ return false;
2308
+ }
2309
+ /**
2310
+ * Process an element in the stylesheet
2311
+ */
2312
+ processElement(node, context, output) {
2313
+ if (this.isXsltNamespace(node)) {
2314
+ this.processXsltElement(node, context, output);
2315
+ } else {
2316
+ this.processLiteralResultElement(node, context, output);
2317
+ }
2318
+ }
2319
+ /**
2320
+ * Process an XSLT instruction element
2321
+ */
2322
+ processXsltElement(node, context, output) {
2323
+ const localName = node.localName || node.nodeName.replace(/^xsl:/, "");
2324
+ switch (localName) {
2325
+ case "apply-templates":
2326
+ this.xslApplyTemplates(node, context, output);
2327
+ break;
2328
+ case "call-template":
2329
+ this.xslCallTemplate(node, context, output);
2330
+ break;
2331
+ case "value-of":
2332
+ this.xslValueOf(node, context, output);
2333
+ break;
2334
+ case "text":
2335
+ this.xslText(node, context, output);
2336
+ break;
2337
+ case "element":
2338
+ this.xslElement(node, context, output);
2339
+ break;
2340
+ case "attribute":
2341
+ this.xslAttribute(node, context, output);
2342
+ break;
2343
+ case "if":
2344
+ this.xslIf(node, context, output);
2345
+ break;
2346
+ case "choose":
2347
+ this.xslChoose(node, context, output);
2348
+ break;
2349
+ case "for-each":
2350
+ this.xslForEach(node, context, output);
2351
+ break;
2352
+ case "copy":
2353
+ this.xslCopy(node, context, output);
2354
+ break;
2355
+ case "copy-of":
2356
+ this.xslCopyOf(node, context, output);
2357
+ break;
2358
+ case "variable":
2359
+ this.xslVariable(node, context, output);
2360
+ break;
2361
+ case "param":
2362
+ break;
2363
+ case "comment":
2364
+ this.xslComment(node, context, output);
2365
+ break;
2366
+ case "processing-instruction":
2367
+ this.xslProcessingInstruction(node, context, output);
2368
+ break;
2369
+ case "number":
2370
+ this.xslNumber(node, context, output);
2371
+ break;
2372
+ case "sort":
2373
+ break;
2374
+ case "with-param":
2375
+ break;
2376
+ case "message":
2377
+ this.xslMessage(node, context, output);
2378
+ break;
2379
+ case "fallback":
2380
+ break;
2381
+ default:
2382
+ console.warn(`Unknown XSLT element: ${localName}`);
2383
+ }
2384
+ }
2385
+ /**
2386
+ * Process a literal result element (non-XSLT)
2387
+ */
2388
+ processLiteralResultElement(node, context, output) {
2389
+ let outputElement;
2390
+ const namespaceURI = node.namespaceURI;
2391
+ const nodeName = node.nodeName;
2392
+ let resolvedNS = namespaceURI;
2393
+ if (namespaceURI) {
2394
+ for (const [from, to] of Object.entries(this.namespaceAliases)) {
2395
+ if (this.namespaces[from] === namespaceURI) {
2396
+ resolvedNS = this.namespaces[to] || to;
2397
+ break;
2398
+ }
2399
+ }
2400
+ }
2401
+ if (resolvedNS && context.outputDocument.createElementNS) {
2402
+ outputElement = context.outputDocument.createElementNS(
2403
+ resolvedNS,
2404
+ nodeName
2405
+ );
2406
+ } else {
2407
+ outputElement = context.outputDocument.createElement(nodeName);
2408
+ }
2409
+ if (node.attributes) {
2410
+ for (const attr of node.attributes) {
2411
+ if (attr.namespaceURI === XSLT_NS) continue;
2412
+ if (attr.name.startsWith("xmlns")) continue;
2413
+ const value = this.processAttributeValueTemplate(attr.value, context);
2414
+ outputElement.setAttribute(attr.name, value);
2415
+ }
2416
+ }
2417
+ this.processChildren(node, context, outputElement);
2418
+ output.appendChild(outputElement);
2419
+ }
2420
+ /**
2421
+ * Process attribute value templates (expressions in curly braces)
2422
+ */
2423
+ processAttributeValueTemplate(value, context) {
2424
+ if (!value.includes("{")) return value;
2425
+ let result = "";
2426
+ let i = 0;
2427
+ while (i < value.length) {
2428
+ if (value[i] === "{") {
2429
+ if (value[i + 1] === "{") {
2430
+ result += "{";
2431
+ i += 2;
2432
+ } else {
2433
+ let depth = 1;
2434
+ let j = i + 1;
2435
+ while (j < value.length && depth > 0) {
2436
+ if (value[j] === "{") depth++;
2437
+ else if (value[j] === "}") depth--;
2438
+ j++;
2439
+ }
2440
+ const expr = value.substring(i + 1, j - 1);
2441
+ const evalResult = this.evaluateXPath(expr, context);
2442
+ result += this.xpathEvaluator.toString(evalResult);
2443
+ i = j;
2444
+ }
2445
+ } else if (value[i] === "}") {
2446
+ if (value[i + 1] === "}") {
2447
+ result += "}";
2448
+ i += 2;
2449
+ } else {
2450
+ throw new Error("Unmatched } in attribute value template");
2451
+ }
2452
+ } else {
2453
+ result += value[i];
2454
+ i++;
2455
+ }
2456
+ }
2457
+ return result;
2458
+ }
2459
+ // XSLT Instructions
2460
+ xslApplyTemplates(node, context, output) {
2461
+ const select2 = node.getAttribute("select") || "node()";
2462
+ const mode = node.getAttribute("mode") || null;
2463
+ let nodes = this.evaluateXPath(select2, context);
2464
+ if (!Array.isArray(nodes)) {
2465
+ nodes = nodes ? [nodes] : [];
2466
+ }
2467
+ const sortSpecs = [];
2468
+ for (const child of node.childNodes) {
2469
+ if (child.nodeType === 1 && this.isXsltElement(child, "sort")) {
2470
+ sortSpecs.push({
2471
+ select: child.getAttribute("select") || ".",
2472
+ order: child.getAttribute("order") || "ascending",
2473
+ dataType: child.getAttribute("data-type") || "text",
2474
+ caseOrder: child.getAttribute("case-order") || "upper-first",
2475
+ lang: child.getAttribute("lang")
2476
+ });
2477
+ }
2478
+ }
2479
+ if (sortSpecs.length > 0) {
2480
+ nodes = this.sortNodes(nodes, sortSpecs, context);
2481
+ }
2482
+ const params = {};
2483
+ for (const child of node.childNodes) {
2484
+ if (child.nodeType === 1 && this.isXsltElement(child, "with-param")) {
2485
+ const name = child.getAttribute("name");
2486
+ const selectAttr = child.getAttribute("select");
2487
+ if (selectAttr) {
2488
+ params[name] = this.evaluateXPath(selectAttr, context);
2489
+ } else {
2490
+ const fragment = context.outputDocument.createDocumentFragment();
2491
+ this.processChildren(child, context, fragment);
2492
+ params[name] = fragment;
2493
+ }
2494
+ }
2495
+ }
2496
+ const newContext = context.clone({
2497
+ parameters: { ...context.parameters, ...params }
2498
+ });
2499
+ this.applyTemplates(nodes, mode, newContext, output);
2500
+ }
2501
+ xslCallTemplate(node, context, output) {
2502
+ const name = node.getAttribute("name");
2503
+ const template = this.templates.find((t) => t.name === name);
2504
+ if (!template) {
2505
+ throw new Error(`Template not found: ${name}`);
2506
+ }
2507
+ const params = {};
2508
+ for (const child of node.childNodes) {
2509
+ if (child.nodeType === 1 && this.isXsltElement(child, "with-param")) {
2510
+ const paramName = child.getAttribute("name");
2511
+ const selectAttr = child.getAttribute("select");
2512
+ if (selectAttr) {
2513
+ params[paramName] = this.evaluateXPath(selectAttr, context);
2514
+ } else {
2515
+ const fragment = context.outputDocument.createDocumentFragment();
2516
+ this.processChildren(child, context, fragment);
2517
+ params[paramName] = fragment;
2518
+ }
2519
+ }
2520
+ }
2521
+ const newContext = context.clone({
2522
+ parameters: { ...context.parameters, ...params }
2523
+ });
2524
+ this.processTemplate(template.node, newContext, output);
2525
+ }
2526
+ xslValueOf(node, context, output) {
2527
+ const select2 = node.getAttribute("select");
2528
+ const disableOutputEscaping = node.getAttribute("disable-output-escaping") === "yes";
2529
+ const result = this.evaluateXPath(select2, context);
2530
+ const text = this.xpathEvaluator.toString(result);
2531
+ if (text) {
2532
+ const textNode = context.outputDocument.createTextNode(text);
2533
+ if (disableOutputEscaping) {
2534
+ textNode._disableOutputEscaping = true;
2535
+ }
2536
+ output.appendChild(textNode);
2537
+ }
2538
+ }
2539
+ xslText(node, context, output) {
2540
+ const disableOutputEscaping = node.getAttribute("disable-output-escaping") === "yes";
2541
+ let text = "";
2542
+ for (const child of node.childNodes) {
2543
+ if (child.nodeType === 3 || child.nodeType === 4) {
2544
+ text += child.nodeValue || "";
2545
+ }
2546
+ }
2547
+ if (text) {
2548
+ const textNode = context.outputDocument.createTextNode(text);
2549
+ if (disableOutputEscaping) {
2550
+ textNode._disableOutputEscaping = true;
2551
+ }
2552
+ output.appendChild(textNode);
2553
+ }
2554
+ }
2555
+ xslElement(node, context, output) {
2556
+ const name = this.processAttributeValueTemplate(
2557
+ node.getAttribute("name"),
2558
+ context
2559
+ );
2560
+ const namespace = node.getAttribute("namespace");
2561
+ const useAttributeSets = node.getAttribute("use-attribute-sets");
2562
+ let element;
2563
+ if (namespace) {
2564
+ const ns = this.processAttributeValueTemplate(namespace, context);
2565
+ element = context.outputDocument.createElementNS(ns, name);
2566
+ } else {
2567
+ element = context.outputDocument.createElement(name);
2568
+ }
2569
+ if (useAttributeSets) {
2570
+ this.applyAttributeSets(useAttributeSets, context, element);
2571
+ }
2572
+ this.processChildren(node, context, element);
2573
+ output.appendChild(element);
2574
+ }
2575
+ xslAttribute(node, context, output) {
2576
+ const name = this.processAttributeValueTemplate(
2577
+ node.getAttribute("name"),
2578
+ context
2579
+ );
2580
+ const namespace = node.getAttribute("namespace");
2581
+ const fragment = context.outputDocument.createDocumentFragment();
2582
+ this.processChildren(node, context, fragment);
2583
+ let value = "";
2584
+ const getText = (n) => {
2585
+ if (n.nodeType === 3 || n.nodeType === 4) {
2586
+ value += n.nodeValue || "";
2587
+ } else if (n.childNodes) {
2588
+ for (const child of n.childNodes) {
2589
+ getText(child);
2590
+ }
2591
+ }
2592
+ };
2593
+ getText(fragment);
2594
+ if (output.nodeType === 1) {
2595
+ if (namespace) {
2596
+ const ns = this.processAttributeValueTemplate(namespace, context);
2597
+ output.setAttributeNS(ns, name, value);
2598
+ } else {
2599
+ output.setAttribute(name, value);
2600
+ }
2601
+ }
2602
+ }
2603
+ xslIf(node, context, output) {
2604
+ const test = node.getAttribute("test");
2605
+ const result = this.evaluateXPath(test, context);
2606
+ if (this.xpathEvaluator.toBoolean(result)) {
2607
+ this.processChildren(node, context, output);
2608
+ }
2609
+ }
2610
+ xslChoose(node, context, output) {
2611
+ for (const child of node.childNodes) {
2612
+ if (child.nodeType !== 1) continue;
2613
+ if (this.isXsltElement(child, "when")) {
2614
+ const test = child.getAttribute("test");
2615
+ const result = this.evaluateXPath(test, context);
2616
+ if (this.xpathEvaluator.toBoolean(result)) {
2617
+ this.processChildren(child, context, output);
2618
+ return;
2619
+ }
2620
+ } else if (this.isXsltElement(child, "otherwise")) {
2621
+ this.processChildren(child, context, output);
2622
+ return;
2623
+ }
2624
+ }
2625
+ }
2626
+ xslForEach(node, context, output) {
2627
+ const select2 = node.getAttribute("select");
2628
+ let nodes = this.evaluateXPath(select2, context);
2629
+ if (!Array.isArray(nodes)) {
2630
+ nodes = nodes ? [nodes] : [];
2631
+ }
2632
+ const sortSpecs = [];
2633
+ for (const child of node.childNodes) {
2634
+ if (child.nodeType === 1 && this.isXsltElement(child, "sort")) {
2635
+ sortSpecs.push({
2636
+ select: child.getAttribute("select") || ".",
2637
+ order: child.getAttribute("order") || "ascending",
2638
+ dataType: child.getAttribute("data-type") || "text",
2639
+ caseOrder: child.getAttribute("case-order") || "upper-first",
2640
+ lang: child.getAttribute("lang")
2641
+ });
2642
+ }
2643
+ }
2644
+ if (sortSpecs.length > 0) {
2645
+ nodes = this.sortNodes(nodes, sortSpecs, context);
2646
+ }
2647
+ for (let i = 0; i < nodes.length; i++) {
2648
+ const newContext = context.clone({
2649
+ currentNode: nodes[i],
2650
+ currentNodeList: nodes,
2651
+ position: i + 1
2652
+ });
2653
+ this.processChildren(node, newContext, output);
2654
+ }
2655
+ }
2656
+ xslCopy(node, context, output) {
2657
+ const currentNode = context.currentNode;
2658
+ const useAttributeSets = node.getAttribute("use-attribute-sets");
2659
+ switch (currentNode.nodeType) {
2660
+ case 1: {
2661
+ let copy;
2662
+ if (currentNode.namespaceURI) {
2663
+ copy = context.outputDocument.createElementNS(
2664
+ currentNode.namespaceURI,
2665
+ currentNode.nodeName
2666
+ );
2667
+ } else {
2668
+ copy = context.outputDocument.createElement(currentNode.nodeName);
2669
+ }
2670
+ if (useAttributeSets) {
2671
+ this.applyAttributeSets(useAttributeSets, context, copy);
2672
+ }
2673
+ this.processChildren(node, context, copy);
2674
+ output.appendChild(copy);
2675
+ break;
2676
+ }
2677
+ case 2:
2678
+ if (output.nodeType === 1) {
2679
+ output.setAttribute(currentNode.name, currentNode.value);
2680
+ }
2681
+ break;
2682
+ case 3:
2683
+ // Text
2684
+ case 4: {
2685
+ const textCopy = context.outputDocument.createTextNode(
2686
+ currentNode.nodeValue || ""
2687
+ );
2688
+ output.appendChild(textCopy);
2689
+ break;
2690
+ }
2691
+ case 7: {
2692
+ const piCopy = context.outputDocument.createProcessingInstruction(
2693
+ currentNode.target,
2694
+ currentNode.data
2695
+ );
2696
+ output.appendChild(piCopy);
2697
+ break;
2698
+ }
2699
+ case 8: {
2700
+ const commentCopy = context.outputDocument.createComment(
2701
+ currentNode.nodeValue || ""
2702
+ );
2703
+ output.appendChild(commentCopy);
2704
+ break;
2705
+ }
2706
+ case 9:
2707
+ // Document
2708
+ case 11:
2709
+ this.processChildren(node, context, output);
2710
+ break;
2711
+ }
2712
+ }
2713
+ xslCopyOf(node, context, output) {
2714
+ const select2 = node.getAttribute("select");
2715
+ const result = this.evaluateXPath(select2, context);
2716
+ this.copyToOutput(result, context, output);
2717
+ }
2718
+ copyToOutput(value, context, output) {
2719
+ if (Array.isArray(value)) {
2720
+ for (const item of value) {
2721
+ this.copyToOutput(item, context, output);
2722
+ }
2723
+ return;
2724
+ }
2725
+ if (value && value.nodeType) {
2726
+ const clone = this.deepCloneNode(value, context.outputDocument);
2727
+ output.appendChild(clone);
2728
+ } else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2729
+ const text = context.outputDocument.createTextNode(String(value));
2730
+ output.appendChild(text);
2731
+ }
2732
+ }
2733
+ deepCloneNode(node, targetDoc) {
2734
+ switch (node.nodeType) {
2735
+ case 1: {
2736
+ let clone;
2737
+ if (node.namespaceURI && targetDoc.createElementNS) {
2738
+ clone = targetDoc.createElementNS(node.namespaceURI, node.nodeName);
2739
+ } else {
2740
+ clone = targetDoc.createElement(node.nodeName);
2741
+ }
2742
+ if (node.attributes) {
2743
+ for (const attr of node.attributes) {
2744
+ clone.setAttribute(attr.name, attr.value);
2745
+ }
2746
+ }
2747
+ for (const child of node.childNodes) {
2748
+ clone.appendChild(this.deepCloneNode(child, targetDoc));
2749
+ }
2750
+ return clone;
2751
+ }
2752
+ case 3:
2753
+ // Text
2754
+ case 4:
2755
+ return targetDoc.createTextNode(node.nodeValue || "");
2756
+ case 7:
2757
+ return targetDoc.createProcessingInstruction(node.target, node.data);
2758
+ case 8:
2759
+ return targetDoc.createComment(node.nodeValue || "");
2760
+ case 11: {
2761
+ const frag = targetDoc.createDocumentFragment();
2762
+ for (const child of node.childNodes) {
2763
+ frag.appendChild(this.deepCloneNode(child, targetDoc));
2764
+ }
2765
+ return frag;
2766
+ }
2767
+ default:
2768
+ return targetDoc.createTextNode("");
2769
+ }
2770
+ }
2771
+ xslVariable(node, context, _output) {
2772
+ const name = node.getAttribute("name");
2773
+ const select2 = node.getAttribute("select");
2774
+ let value;
2775
+ if (select2) {
2776
+ value = this.evaluateXPath(select2, context);
2777
+ } else {
2778
+ const fragment = context.outputDocument.createDocumentFragment();
2779
+ this.processChildren(node, context, fragment);
2780
+ value = fragment;
2781
+ }
2782
+ context.setVariable(name, value);
2783
+ }
2784
+ xslComment(node, context, output) {
2785
+ const fragment = context.outputDocument.createDocumentFragment();
2786
+ this.processChildren(node, context, fragment);
2787
+ let text = "";
2788
+ const getText = (n) => {
2789
+ if (n.nodeType === 3 || n.nodeType === 4) {
2790
+ text += n.nodeValue || "";
2791
+ } else if (n.childNodes) {
2792
+ for (const child of n.childNodes) {
2793
+ getText(child);
2794
+ }
2795
+ }
2796
+ };
2797
+ getText(fragment);
2798
+ const comment = context.outputDocument.createComment(text);
2799
+ output.appendChild(comment);
2800
+ }
2801
+ xslProcessingInstruction(node, context, output) {
2802
+ const name = this.processAttributeValueTemplate(
2803
+ node.getAttribute("name"),
2804
+ context
2805
+ );
2806
+ const fragment = context.outputDocument.createDocumentFragment();
2807
+ this.processChildren(node, context, fragment);
2808
+ let data = "";
2809
+ const getText = (n) => {
2810
+ if (n.nodeType === 3 || n.nodeType === 4) {
2811
+ data += n.nodeValue || "";
2812
+ } else if (n.childNodes) {
2813
+ for (const child of n.childNodes) {
2814
+ getText(child);
2815
+ }
2816
+ }
2817
+ };
2818
+ getText(fragment);
2819
+ const pi = context.outputDocument.createProcessingInstruction(name, data);
2820
+ output.appendChild(pi);
2821
+ }
2822
+ xslNumber(node, context, output) {
2823
+ const value = node.getAttribute("value");
2824
+ const format = node.getAttribute("format") || "1";
2825
+ const level = node.getAttribute("level") || "single";
2826
+ let number;
2827
+ if (value) {
2828
+ number = Math.round(
2829
+ this.xpathEvaluator.toNumber(this.evaluateXPath(value, context))
2830
+ );
2831
+ } else {
2832
+ number = this.countNumber(context.currentNode, level, node, context);
2833
+ }
2834
+ const formatted = this.formatNumber(number, format);
2835
+ const text = context.outputDocument.createTextNode(formatted);
2836
+ output.appendChild(text);
2837
+ }
2838
+ countNumber(node, level, spec, context) {
2839
+ const count = spec.getAttribute("count");
2840
+ const _from = spec.getAttribute("from");
2841
+ if (level === "single") {
2842
+ let n = 1;
2843
+ let sibling = node.previousSibling;
2844
+ while (sibling) {
2845
+ if (sibling.nodeType === 1) {
2846
+ if (!count || this.matchesPattern(sibling, count, context)) {
2847
+ n++;
2848
+ }
2849
+ }
2850
+ sibling = sibling.previousSibling;
2851
+ }
2852
+ return n;
2853
+ }
2854
+ return 1;
2855
+ }
2856
+ formatNumber(number, format) {
2857
+ if (/^[0-9]+$/.test(format)) {
2858
+ return String(number).padStart(format.length, "0");
2859
+ }
2860
+ if (format === "a") {
2861
+ return String.fromCharCode(96 + (number - 1) % 26 + 1);
2862
+ }
2863
+ if (format === "A") {
2864
+ return String.fromCharCode(64 + (number - 1) % 26 + 1);
2865
+ }
2866
+ if (format === "i") {
2867
+ return this.toRoman(number).toLowerCase();
2868
+ }
2869
+ if (format === "I") {
2870
+ return this.toRoman(number);
2871
+ }
2872
+ return String(number);
2873
+ }
2874
+ toRoman(num) {
2875
+ const romanNumerals = [
2876
+ ["M", 1e3],
2877
+ ["CM", 900],
2878
+ ["D", 500],
2879
+ ["CD", 400],
2880
+ ["C", 100],
2881
+ ["XC", 90],
2882
+ ["L", 50],
2883
+ ["XL", 40],
2884
+ ["X", 10],
2885
+ ["IX", 9],
2886
+ ["V", 5],
2887
+ ["IV", 4],
2888
+ ["I", 1]
2889
+ ];
2890
+ let result = "";
2891
+ for (const [numeral, value] of romanNumerals) {
2892
+ while (num >= value) {
2893
+ result += numeral;
2894
+ num -= value;
2895
+ }
2896
+ }
2897
+ return result;
2898
+ }
2899
+ xslMessage(node, context, _output) {
2900
+ const terminate = node.getAttribute("terminate") === "yes";
2901
+ const fragment = context.outputDocument.createDocumentFragment();
2902
+ this.processChildren(node, context, fragment);
2903
+ let text = "";
2904
+ const getText = (n) => {
2905
+ if (n.nodeType === 3 || n.nodeType === 4) {
2906
+ text += n.nodeValue || "";
2907
+ } else if (n.childNodes) {
2908
+ for (const child of n.childNodes) {
2909
+ getText(child);
2910
+ }
2911
+ }
2912
+ };
2913
+ getText(fragment);
2914
+ console.log("XSLT Message:", text);
2915
+ if (terminate) {
2916
+ throw new Error(`XSLT terminated: ${text}`);
2917
+ }
2918
+ }
2919
+ applyAttributeSets(names, context, element) {
2920
+ const setNames = names.split(/\s+/).filter(Boolean);
2921
+ for (const name of setNames) {
2922
+ const attrSet = this.attributeSets[name];
2923
+ if (attrSet) {
2924
+ if (attrSet.useAttributeSets.length > 0) {
2925
+ this.applyAttributeSets(
2926
+ attrSet.useAttributeSets.join(" "),
2927
+ context,
2928
+ element
2929
+ );
2930
+ }
2931
+ for (const child of attrSet.node.childNodes) {
2932
+ if (child.nodeType === 1 && this.isXsltElement(child, "attribute")) {
2933
+ this.xslAttribute(child, context, element);
2934
+ }
2935
+ }
2936
+ }
2937
+ }
2938
+ }
2939
+ sortNodes(nodes, sortSpecs, context) {
2940
+ return [...nodes].sort((a, b) => {
2941
+ for (const spec of sortSpecs) {
2942
+ const contextA = context.clone({ currentNode: a });
2943
+ const contextB = context.clone({ currentNode: b });
2944
+ let valueA = this.evaluateXPath(spec.select, contextA);
2945
+ let valueB = this.evaluateXPath(spec.select, contextB);
2946
+ valueA = this.xpathEvaluator.toString(valueA) || "";
2947
+ valueB = this.xpathEvaluator.toString(valueB) || "";
2948
+ if (spec.dataType === "number") {
2949
+ valueA = parseFloat(valueA) || 0;
2950
+ valueB = parseFloat(valueB) || 0;
2951
+ } else {
2952
+ if (spec.caseOrder === "lower-first") {
2953
+ valueA = valueA.toLowerCase();
2954
+ valueB = valueB.toLowerCase();
2955
+ } else {
2956
+ valueA = valueA.toUpperCase();
2957
+ valueB = valueB.toUpperCase();
2958
+ }
2959
+ }
2960
+ let cmp;
2961
+ if (typeof valueA === "number") {
2962
+ cmp = valueA - valueB;
2963
+ } else {
2964
+ cmp = valueA.localeCompare(valueB, spec.lang || void 0);
2965
+ }
2966
+ if (spec.order === "descending") {
2967
+ cmp = -cmp;
2968
+ }
2969
+ if (cmp !== 0) return cmp;
2970
+ }
2971
+ return 0;
2972
+ });
2973
+ }
2974
+ evaluateXPath(expr, context) {
2975
+ const ast = parse(expr);
2976
+ const xpathContext = new XPathContext(
2977
+ context.currentNode,
2978
+ context.position,
2979
+ context.currentNodeList.length,
2980
+ { ...context.variables, ...context.parameters },
2981
+ context.namespaces
2982
+ );
2983
+ return this.xpathEvaluator.evaluate(ast, xpathContext);
2984
+ }
2985
+ isXsltNamespace(node) {
2986
+ return node.namespaceURI === XSLT_NS || node.nodeName && node.nodeName.startsWith("xsl:");
2987
+ }
2988
+ isXsltElement(node, localName) {
2989
+ if (node.nodeType !== 1) return false;
2990
+ const nodeName = node.localName || node.nodeName;
2991
+ return node.namespaceURI === XSLT_NS && nodeName === localName || node.nodeName === `xsl:${localName}`;
2992
+ }
2993
+ };
2994
+
2995
+ // src/XSLTProcessor.js
2996
+ var XSLTProcessor = class {
2997
+ constructor() {
2998
+ this._engine = null;
2999
+ this._stylesheet = null;
3000
+ this._parameters = /* @__PURE__ */ new Map();
3001
+ }
3002
+ /**
3003
+ * Imports the XSLT stylesheet.
3004
+ *
3005
+ * If the given node is a document node, you can pass in a full XSL Transform
3006
+ * or a literal result element transform; otherwise, it must be an
3007
+ * <xsl:stylesheet> or <xsl:transform> element.
3008
+ *
3009
+ * @param {Node} style - The XSLT stylesheet to import (Document or Element)
3010
+ * @returns {void}
3011
+ *
3012
+ * @example
3013
+ * const parser = new DOMParser();
3014
+ * const xslDoc = parser.parseFromString(xslText, 'application/xml');
3015
+ * processor.importStylesheet(xslDoc);
3016
+ */
3017
+ importStylesheet(style) {
3018
+ if (!style) {
3019
+ throw new TypeError(
3020
+ "Failed to execute 'importStylesheet' on 'XSLTProcessor': 1 argument required, but only 0 present."
3021
+ );
3022
+ }
3023
+ if (style.nodeType !== 1 && style.nodeType !== 9) {
3024
+ throw new TypeError(
3025
+ "Failed to execute 'importStylesheet' on 'XSLTProcessor': The node provided is not a Document or Element."
3026
+ );
3027
+ }
3028
+ const errorNode = style.querySelector ? style.querySelector("parsererror") : null;
3029
+ if (errorNode) {
3030
+ throw new Error("XSLT stylesheet contains parse errors");
3031
+ }
3032
+ this._stylesheet = style;
3033
+ this._engine = new XsltEngine();
3034
+ for (const [key, value] of this._parameters) {
3035
+ this._engine.globalParameters[key] = { value };
3036
+ }
3037
+ this._engine.importStylesheet(style);
3038
+ }
3039
+ /**
3040
+ * Transforms the node source by applying the XSLT stylesheet.
3041
+ * Returns a document fragment.
3042
+ *
3043
+ * @param {Node} source - The XML document to transform
3044
+ * @param {Document} output - The document that will own the generated fragment
3045
+ * @returns {DocumentFragment} The transformed result as a DocumentFragment
3046
+ *
3047
+ * @example
3048
+ * const fragment = processor.transformToFragment(xmlDoc, document);
3049
+ * document.getElementById('output').appendChild(fragment);
3050
+ */
3051
+ transformToFragment(source, output) {
3052
+ if (!source) {
3053
+ throw new TypeError(
3054
+ "Failed to execute 'transformToFragment' on 'XSLTProcessor': 2 arguments required, but only 0 present."
3055
+ );
3056
+ }
3057
+ if (!output) {
3058
+ throw new TypeError(
3059
+ "Failed to execute 'transformToFragment' on 'XSLTProcessor': 2 arguments required, but only 1 present."
3060
+ );
3061
+ }
3062
+ if (!this._engine || !this._stylesheet) {
3063
+ throw new Error(
3064
+ "Failed to execute 'transformToFragment' on 'XSLTProcessor': No stylesheet has been imported."
3065
+ );
3066
+ }
3067
+ if (source.nodeType !== 1 && source.nodeType !== 9 && source.nodeType !== 11) {
3068
+ throw new TypeError(
3069
+ "Failed to execute 'transformToFragment' on 'XSLTProcessor': The source is not a valid node type."
3070
+ );
3071
+ }
3072
+ if (output.nodeType !== 9) {
3073
+ throw new TypeError(
3074
+ "Failed to execute 'transformToFragment' on 'XSLTProcessor': The output is not a Document."
3075
+ );
3076
+ }
3077
+ try {
3078
+ return this._engine.transform(source, output);
3079
+ } catch (error) {
3080
+ console.error("XSLT transformation error:", error);
3081
+ return null;
3082
+ }
3083
+ }
3084
+ /**
3085
+ * Transforms the node source by applying the XSLT stylesheet.
3086
+ * Returns a full XML document.
3087
+ *
3088
+ * @param {Node} source - The XML document to transform
3089
+ * @returns {XMLDocument} The transformed result as an XMLDocument
3090
+ *
3091
+ * @example
3092
+ * const resultDoc = processor.transformToDocument(xmlDoc);
3093
+ * const serialized = new XMLSerializer().serializeToString(resultDoc);
3094
+ */
3095
+ transformToDocument(source) {
3096
+ if (!source) {
3097
+ throw new TypeError(
3098
+ "Failed to execute 'transformToDocument' on 'XSLTProcessor': 1 argument required, but only 0 present."
3099
+ );
3100
+ }
3101
+ if (!this._engine || !this._stylesheet) {
3102
+ throw new Error(
3103
+ "Failed to execute 'transformToDocument' on 'XSLTProcessor': No stylesheet has been imported."
3104
+ );
3105
+ }
3106
+ if (source.nodeType !== 1 && source.nodeType !== 9 && source.nodeType !== 11) {
3107
+ throw new TypeError(
3108
+ "Failed to execute 'transformToDocument' on 'XSLTProcessor': The source is not a valid node type."
3109
+ );
3110
+ }
3111
+ try {
3112
+ return this._engine.transformToDocument(source);
3113
+ } catch (error) {
3114
+ console.error("XSLT transformation error:", error);
3115
+ return null;
3116
+ }
3117
+ }
3118
+ /**
3119
+ * Sets a parameter in the XSLT stylesheet.
3120
+ *
3121
+ * @param {string|null} namespaceURI - The namespace URI of the XSLT parameter (use null for no namespace)
3122
+ * @param {string} localName - The local name of the parameter
3123
+ * @param {*} value - The value to set (string, number, boolean, or node-set)
3124
+ * @returns {void}
3125
+ *
3126
+ * @example
3127
+ * processor.setParameter(null, 'sortOrder', 'ascending');
3128
+ * processor.setParameter('http://example.com/ns', 'limit', 10);
3129
+ */
3130
+ setParameter(namespaceURI, localName, value) {
3131
+ if (arguments.length < 3) {
3132
+ throw new TypeError(
3133
+ `Failed to execute 'setParameter' on 'XSLTProcessor': 3 arguments required, but only ${arguments.length} present.`
3134
+ );
3135
+ }
3136
+ if (typeof localName !== "string" || localName === "") {
3137
+ throw new TypeError(
3138
+ "Failed to execute 'setParameter' on 'XSLTProcessor': The localName argument must be a non-empty string."
3139
+ );
3140
+ }
3141
+ const key = namespaceURI ? `{${namespaceURI}}${localName}` : localName;
3142
+ this._parameters.set(key, value);
3143
+ if (this._engine) {
3144
+ this._engine.globalParameters[key] = { value };
3145
+ }
3146
+ }
3147
+ /**
3148
+ * Gets the value of a parameter from the XSLT stylesheet.
3149
+ *
3150
+ * @param {string|null} namespaceURI - The namespace URI of the parameter
3151
+ * @param {string} localName - The local name of the parameter
3152
+ * @returns {*} The parameter value, or empty string if not set
3153
+ *
3154
+ * @example
3155
+ * const sortOrder = processor.getParameter(null, 'sortOrder');
3156
+ */
3157
+ getParameter(namespaceURI, localName) {
3158
+ if (arguments.length < 2) {
3159
+ throw new TypeError(
3160
+ `Failed to execute 'getParameter' on 'XSLTProcessor': 2 arguments required, but only ${arguments.length} present.`
3161
+ );
3162
+ }
3163
+ if (typeof localName !== "string") {
3164
+ throw new TypeError(
3165
+ "Failed to execute 'getParameter' on 'XSLTProcessor': The localName argument must be a string."
3166
+ );
3167
+ }
3168
+ const key = namespaceURI ? `{${namespaceURI}}${localName}` : localName;
3169
+ if (this._parameters.has(key)) {
3170
+ return this._parameters.get(key);
3171
+ }
3172
+ return "";
3173
+ }
3174
+ /**
3175
+ * Removes a parameter from the XSLT processor.
3176
+ *
3177
+ * The XSLTProcessor will use the default value for the parameter
3178
+ * as specified in the XSLT stylesheet.
3179
+ *
3180
+ * @param {string|null} namespaceURI - The namespace URI of the parameter
3181
+ * @param {string} localName - The local name of the parameter
3182
+ * @returns {void}
3183
+ *
3184
+ * @example
3185
+ * processor.removeParameter(null, 'sortOrder');
3186
+ */
3187
+ removeParameter(namespaceURI, localName) {
3188
+ if (arguments.length < 2) {
3189
+ throw new TypeError(
3190
+ `Failed to execute 'removeParameter' on 'XSLTProcessor': 2 arguments required, but only ${arguments.length} present.`
3191
+ );
3192
+ }
3193
+ if (typeof localName !== "string") {
3194
+ throw new TypeError(
3195
+ "Failed to execute 'removeParameter' on 'XSLTProcessor': The localName argument must be a string."
3196
+ );
3197
+ }
3198
+ const key = namespaceURI ? `{${namespaceURI}}${localName}` : localName;
3199
+ this._parameters.delete(key);
3200
+ if (this._engine) {
3201
+ delete this._engine.globalParameters[key];
3202
+ }
3203
+ }
3204
+ /**
3205
+ * Removes all set parameters from the XSLTProcessor.
3206
+ *
3207
+ * The processor will use default values specified in the XSLT stylesheet.
3208
+ *
3209
+ * @returns {void}
3210
+ *
3211
+ * @example
3212
+ * processor.clearParameters();
3213
+ */
3214
+ clearParameters() {
3215
+ this._parameters.clear();
3216
+ if (this._engine) {
3217
+ this._engine.globalParameters = {};
3218
+ }
3219
+ }
3220
+ /**
3221
+ * Removes all parameters and stylesheets from the XSLTProcessor.
3222
+ *
3223
+ * @returns {void}
3224
+ *
3225
+ * @example
3226
+ * processor.reset();
3227
+ * // Now need to call importStylesheet() again before transforming
3228
+ */
3229
+ reset() {
3230
+ this._engine = null;
3231
+ this._stylesheet = null;
3232
+ this._parameters.clear();
3233
+ }
3234
+ };
3235
+ function isNativeXSLTSupported() {
3236
+ if (typeof globalThis.XSLTProcessor === "undefined") {
3237
+ return false;
3238
+ }
3239
+ try {
3240
+ const processor = new globalThis.XSLTProcessor();
3241
+ const parser = new DOMParser();
3242
+ const xslt = parser.parseFromString(
3243
+ `<?xml version="1.0"?>
3244
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3245
+ <xsl:template match="/"><test/></xsl:template>
3246
+ </xsl:stylesheet>`,
3247
+ "application/xml"
3248
+ );
3249
+ processor.importStylesheet(xslt);
3250
+ const xml = parser.parseFromString("<root/>", "application/xml");
3251
+ const result = processor.transformToFragment(xml, document);
3252
+ return result !== null && result.childNodes.length > 0;
3253
+ } catch {
3254
+ return false;
3255
+ }
3256
+ }
3257
+ function installGlobal(force = false) {
3258
+ if (!force && isNativeXSLTSupported()) {
3259
+ return false;
3260
+ }
3261
+ globalThis.XSLTProcessor = XSLTProcessor;
3262
+ return true;
3263
+ }
3264
+ var XSLTProcessor_default = XSLTProcessor;
3265
+
3266
+ // src/xpath/index.js
3267
+ function evaluate(expression, contextNode, options = {}) {
3268
+ const ast = parse(expression);
3269
+ const evaluator = new XPathEvaluator();
3270
+ const context = new XPathContext(
3271
+ contextNode,
3272
+ 1,
3273
+ 1,
3274
+ options.variables || {},
3275
+ options.namespaces || {}
3276
+ );
3277
+ return evaluator.evaluate(ast, context);
3278
+ }
3279
+ function select(expression, contextNode, options = {}) {
3280
+ const result = evaluate(expression, contextNode, options);
3281
+ if (Array.isArray(result)) return result;
3282
+ if (result && result.nodeType) return [result];
3283
+ return [];
3284
+ }
3285
+ function selectFirst(expression, contextNode, options = {}) {
3286
+ const nodes = select(expression, contextNode, options);
3287
+ return nodes.length > 0 ? nodes[0] : null;
3288
+ }
3289
+
3290
+ // src/index.js
3291
+ var VERSION = "1.0.0";
3292
+ var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
3293
+ var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
3294
+ return __toCommonJS(index_exports);
3295
+ })();
3296
+
3297
+ // Auto-install as global XSLTProcessor replacement if native is not functional
3298
+ if (typeof window !== 'undefined') {
3299
+ XsltProcessorLib.installGlobal();
3300
+ }
3301
+
3302
+ //# sourceMappingURL=xslt-processor.browser.js.map