@ai-sdk-tool/rxml 0.1.1 → 0.1.2-canary.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.
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  var RXMLParseError = class extends Error {
3
3
  constructor(message, cause, line, column) {
4
4
  super(message);
5
+ this.name = "RXMLParseError";
5
6
  this.cause = cause;
6
7
  this.line = line;
7
8
  this.column = column;
8
- this.name = "RXMLParseError";
9
9
  }
10
10
  };
11
11
  var RXMLDuplicateStringTagError = class extends Error {
@@ -17,28 +17,443 @@ var RXMLDuplicateStringTagError = class extends Error {
17
17
  var RXMLCoercionError = class extends Error {
18
18
  constructor(message, cause) {
19
19
  super(message);
20
- this.cause = cause;
21
20
  this.name = "RXMLCoercionError";
21
+ this.cause = cause;
22
22
  }
23
23
  };
24
24
  var RXMLStringifyError = class extends Error {
25
25
  constructor(message, cause) {
26
26
  super(message);
27
- this.cause = cause;
28
27
  this.name = "RXMLStringifyError";
28
+ this.cause = cause;
29
29
  }
30
30
  };
31
31
  var RXMLStreamError = class extends Error {
32
32
  constructor(message, cause) {
33
33
  super(message);
34
- this.cause = cause;
35
34
  this.name = "RXMLStreamError";
35
+ this.cause = cause;
36
36
  }
37
37
  };
38
38
 
39
+ // src/core/types.ts
40
+ var CharCodes = {
41
+ OPEN_BRACKET: "<".charCodeAt(0),
42
+ CLOSE_BRACKET: ">".charCodeAt(0),
43
+ MINUS: "-".charCodeAt(0),
44
+ SLASH: "/".charCodeAt(0),
45
+ EXCLAMATION: "!".charCodeAt(0),
46
+ QUESTION: "?".charCodeAt(0),
47
+ SINGLE_QUOTE: "'".charCodeAt(0),
48
+ DOUBLE_QUOTE: '"'.charCodeAt(0),
49
+ OPEN_CORNER_BRACKET: "[".charCodeAt(0),
50
+ CLOSE_CORNER_BRACKET: "]".charCodeAt(0),
51
+ SPACE: " ".charCodeAt(0),
52
+ TAB: " ".charCodeAt(0),
53
+ NEWLINE: "\n".charCodeAt(0),
54
+ CARRIAGE_RETURN: "\r".charCodeAt(0)
55
+ };
56
+ var DEFAULT_NO_CHILD_NODES = [
57
+ "img",
58
+ "br",
59
+ "input",
60
+ "meta",
61
+ "link",
62
+ "hr",
63
+ "area",
64
+ "base",
65
+ "col",
66
+ "embed",
67
+ "param",
68
+ "source",
69
+ "track",
70
+ "wbr"
71
+ ];
72
+ var NAME_SPACER = "\r\n >/= ";
73
+
74
+ // src/utils/helpers.ts
75
+ var NAME_START_CHAR_REGEX = /[A-Za-z_:]/;
76
+ var NAME_CHAR_REGEX = /[A-Za-z0-9_.:-]/;
77
+ function isNameStartChar(ch) {
78
+ return NAME_START_CHAR_REGEX.test(ch);
79
+ }
80
+ function isNameChar(ch) {
81
+ return NAME_CHAR_REGEX.test(ch);
82
+ }
83
+ function skipQuoted(s, i) {
84
+ const quote = s[i];
85
+ let pos = i + 1;
86
+ while (pos < s.length) {
87
+ const ch = s[pos];
88
+ if (ch === "\\") {
89
+ pos += 2;
90
+ continue;
91
+ }
92
+ if (ch === quote) {
93
+ return pos + 1;
94
+ }
95
+ pos += 1;
96
+ }
97
+ return pos;
98
+ }
99
+ function parseName(s, pos) {
100
+ const start = pos;
101
+ let currentPos = pos;
102
+ while (NAME_SPACER.indexOf(s[currentPos]) === -1 && s[currentPos]) {
103
+ currentPos += 1;
104
+ }
105
+ return { name: s.slice(start, currentPos), newPos: currentPos };
106
+ }
107
+ function parseString(s, pos) {
108
+ const startChar = s[pos];
109
+ const startPos = pos + 1;
110
+ const endPos = s.indexOf(startChar, startPos);
111
+ if (endPos === -1) {
112
+ const tagEnd = s.indexOf(">", startPos);
113
+ if (tagEnd !== -1) {
114
+ return { value: s.slice(startPos, tagEnd), newPos: tagEnd };
115
+ }
116
+ return { value: s.slice(startPos), newPos: s.length };
117
+ }
118
+ return { value: s.slice(startPos, endPos), newPos: endPos + 1 };
119
+ }
120
+ function getLineColumn(s, pos) {
121
+ let line = 1;
122
+ let column = 1;
123
+ for (let i = 0; i < pos && i < s.length; i += 1) {
124
+ if (s[i] === "\n") {
125
+ line += 1;
126
+ column = 1;
127
+ } else {
128
+ column += 1;
129
+ }
130
+ }
131
+ return { line, column };
132
+ }
133
+ function escapeXml(text) {
134
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
135
+ }
136
+ function escapeXmlMinimalText(text) {
137
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/]]>/g, "]]&gt;");
138
+ }
139
+ function escapeXmlMinimalAttr(value, wrapper = '"') {
140
+ let escaped = value.replace(/&/g, "&amp;").replace(/</g, "&lt;");
141
+ if (wrapper === '"') {
142
+ escaped = escaped.replace(/"/g, "&quot;");
143
+ } else {
144
+ escaped = escaped.replace(/'/g, "&apos;");
145
+ }
146
+ return escaped;
147
+ }
148
+ function unescapeXml(text) {
149
+ return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
150
+ }
151
+
152
+ // src/builders/stringify.ts
153
+ function stringify(rootTag, obj, options = {}) {
154
+ var _a, _b, _c, _d;
155
+ try {
156
+ const format = (_a = options.format) != null ? _a : true;
157
+ const minimalEscaping = (_b = options.minimalEscaping) != null ? _b : false;
158
+ const suppressEmptyNode = (_c = options.suppressEmptyNode) != null ? _c : false;
159
+ const strictBooleanAttributes = (_d = options.strictBooleanAttributes) != null ? _d : false;
160
+ let result = "";
161
+ if (format) {
162
+ result += '<?xml version="1.0" encoding="UTF-8"?>\n';
163
+ }
164
+ result += stringifyValue(rootTag, obj, {
165
+ depth: 0,
166
+ format,
167
+ suppressEmptyNode,
168
+ minimalEscaping,
169
+ strictBooleanAttributes
170
+ });
171
+ return result;
172
+ } catch (error) {
173
+ throw new RXMLStringifyError("Failed to stringify XML", error);
174
+ }
175
+ }
176
+ function escapeContent(content, minimalEscaping) {
177
+ return minimalEscaping ? escapeXmlMinimalText(content) : escapeXml(content);
178
+ }
179
+ function createSelfClosingTag(tagName, indent, newline) {
180
+ return `${indent}<${tagName}/>${newline}`;
181
+ }
182
+ function createTextElement(tagName, content, indent, newline) {
183
+ return `${indent}<${tagName}>${content}</${tagName}>${newline}`;
184
+ }
185
+ function isPrimitive(value) {
186
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
187
+ }
188
+ function stringifyPrimitive(tagName, value, context, format) {
189
+ const { minimalEscaping, suppressEmptyNode } = context;
190
+ const content = escapeContent(String(value), minimalEscaping);
191
+ if (content === "" && suppressEmptyNode) {
192
+ return "";
193
+ }
194
+ return createTextElement(tagName, content, format.indent, format.newline);
195
+ }
196
+ function stringifyArray(tagName, value, context) {
197
+ let result = "";
198
+ for (const item of value) {
199
+ result += stringifyValue(tagName, item, context);
200
+ }
201
+ return result;
202
+ }
203
+ function stringifyValue(tagName, value, context) {
204
+ const { format, suppressEmptyNode, minimalEscaping } = context;
205
+ const indent = format ? " ".repeat(context.depth) : "";
206
+ const newline = format ? "\n" : "";
207
+ if (value === null || value === void 0) {
208
+ if (suppressEmptyNode) {
209
+ return "";
210
+ }
211
+ return createSelfClosingTag(tagName, indent, newline);
212
+ }
213
+ if (isPrimitive(value)) {
214
+ return stringifyPrimitive(tagName, value, context, { indent, newline });
215
+ }
216
+ if (Array.isArray(value)) {
217
+ return stringifyArray(tagName, value, context);
218
+ }
219
+ if (typeof value === "object") {
220
+ return stringifyObject(tagName, value, context);
221
+ }
222
+ const content = escapeContent(String(value), minimalEscaping);
223
+ if (content === "" && suppressEmptyNode) {
224
+ return "";
225
+ }
226
+ return createTextElement(tagName, content, indent, newline);
227
+ }
228
+ function extractObjectParts(obj) {
229
+ const attributes = {};
230
+ const elements = {};
231
+ let textContent;
232
+ for (const [key, value] of Object.entries(obj)) {
233
+ if (key.startsWith("@")) {
234
+ attributes[key.substring(1)] = value;
235
+ } else if (key === "#text" || key === "_text") {
236
+ textContent = String(value);
237
+ } else if (key === "_attributes") {
238
+ if (typeof value === "object" && value !== null) {
239
+ Object.assign(attributes, value);
240
+ }
241
+ } else {
242
+ elements[key] = value;
243
+ }
244
+ }
245
+ return { attributes, elements, textContent };
246
+ }
247
+ function formatAttribute(attrName, attrValue, minimalEscaping, strictBooleanAttributes) {
248
+ if (attrValue === null) {
249
+ return strictBooleanAttributes ? ` ${attrName}="${attrName}"` : ` ${attrName}`;
250
+ }
251
+ const valueStr = String(attrValue);
252
+ if (valueStr.indexOf('"') === -1) {
253
+ const escaped2 = minimalEscaping ? escapeXmlMinimalAttr(valueStr, '"') : escapeXml(valueStr);
254
+ return ` ${attrName}="${escaped2}"`;
255
+ }
256
+ const escaped = minimalEscaping ? escapeXmlMinimalAttr(valueStr, "'") : escapeXml(valueStr);
257
+ return ` ${attrName}='${escaped}'`;
258
+ }
259
+ function buildOpeningTag(tagName, attributes, context) {
260
+ let openTag = `<${tagName}`;
261
+ const { minimalEscaping, strictBooleanAttributes } = context;
262
+ for (const [attrName, attrValue] of Object.entries(attributes)) {
263
+ openTag += formatAttribute(
264
+ attrName,
265
+ attrValue,
266
+ minimalEscaping,
267
+ strictBooleanAttributes
268
+ );
269
+ }
270
+ return openTag;
271
+ }
272
+ function stringifyTextOnlyContent(options) {
273
+ const { tagName, textContent, openTag, format, minimalEscaping } = options;
274
+ const content = escapeContent(textContent, minimalEscaping);
275
+ return `${format.indent}${openTag}${content}</${tagName}>${format.newline}`;
276
+ }
277
+ function stringifyComplexContent(tagName, parts, context, options) {
278
+ const { format, minimalEscaping, depth } = context;
279
+ const { textContent, elements } = parts;
280
+ const hasElements = Object.keys(elements).length > 0;
281
+ let result = `${options.indent}${options.openTag}`;
282
+ if (textContent) {
283
+ const content = escapeContent(textContent, minimalEscaping);
284
+ result += format ? `${options.newline}${options.childIndent}${content}` : content;
285
+ }
286
+ if (hasElements) {
287
+ if (format) {
288
+ result += options.newline;
289
+ }
290
+ for (const [elementName, elementValue] of Object.entries(elements)) {
291
+ result += stringifyValue(elementName, elementValue, {
292
+ ...context,
293
+ depth: depth + 1
294
+ });
295
+ }
296
+ if (format) {
297
+ result += options.indent;
298
+ }
299
+ }
300
+ result += `</${tagName}>${options.newline}`;
301
+ return result;
302
+ }
303
+ function stringifyObject(tagName, obj, context) {
304
+ const { depth, format, suppressEmptyNode } = context;
305
+ const indent = format ? " ".repeat(depth) : "";
306
+ const newline = format ? "\n" : "";
307
+ const childIndent = format ? " ".repeat(depth + 1) : "";
308
+ const parts = extractObjectParts(obj);
309
+ const openTag = buildOpeningTag(tagName, parts.attributes, context);
310
+ const hasElements = Object.keys(parts.elements).length > 0;
311
+ const hasTextContent = parts.textContent !== void 0 && parts.textContent !== "";
312
+ if (!(hasElements || hasTextContent)) {
313
+ if (suppressEmptyNode) {
314
+ return "";
315
+ }
316
+ return `${indent}${openTag}/>${newline}`;
317
+ }
318
+ const fullOpenTag = `${openTag}>`;
319
+ if (!hasElements && hasTextContent && parts.textContent) {
320
+ return stringifyTextOnlyContent({
321
+ tagName,
322
+ textContent: parts.textContent,
323
+ openTag: fullOpenTag,
324
+ format: { indent, newline },
325
+ minimalEscaping: context.minimalEscaping
326
+ });
327
+ }
328
+ return stringifyComplexContent(tagName, parts, context, {
329
+ indent,
330
+ newline,
331
+ childIndent,
332
+ openTag: fullOpenTag
333
+ });
334
+ }
335
+ function stringifyNodes(nodes, format = true, options = {}) {
336
+ let result = "";
337
+ for (const node of nodes) {
338
+ if (typeof node === "string") {
339
+ result += node;
340
+ } else {
341
+ result += stringifyNode(node, 0, format, options);
342
+ }
343
+ }
344
+ return result;
345
+ }
346
+ function formatNodeAttribute(attrName, attrValue, minimalEscaping, strictBooleanAttributes) {
347
+ if (attrValue === null) {
348
+ if (strictBooleanAttributes) {
349
+ return ` ${attrName}="${attrName}"`;
350
+ }
351
+ return ` ${attrName}`;
352
+ }
353
+ if (attrValue.indexOf('"') === -1) {
354
+ const escaped2 = minimalEscaping ? escapeXmlMinimalAttr(attrValue, '"') : escapeXml(attrValue);
355
+ return ` ${attrName}="${escaped2}"`;
356
+ }
357
+ const escaped = minimalEscaping ? escapeXmlMinimalAttr(attrValue, "'") : escapeXml(attrValue);
358
+ return ` ${attrName}='${escaped}'`;
359
+ }
360
+ function buildNodeOpeningTag(node, opts) {
361
+ let result = `${opts.indent}<${node.tagName}`;
362
+ for (const [attrName, attrValue] of Object.entries(node.attributes)) {
363
+ result += formatNodeAttribute(
364
+ attrName,
365
+ attrValue,
366
+ opts.minimalEscaping,
367
+ opts.strictBooleanAttributes
368
+ );
369
+ }
370
+ return result;
371
+ }
372
+ function stringifyNodeChildren(options) {
373
+ const {
374
+ children,
375
+ depth,
376
+ format,
377
+ stringifyOptions,
378
+ minimalEscaping,
379
+ newline
380
+ } = options;
381
+ let content = "";
382
+ let hasElementChildren = false;
383
+ for (const child of children) {
384
+ if (typeof child === "string") {
385
+ content += minimalEscaping ? escapeXmlMinimalText(child) : escapeXml(child);
386
+ } else {
387
+ if (!hasElementChildren && format) {
388
+ content += newline;
389
+ hasElementChildren = true;
390
+ }
391
+ content += stringifyNode(child, depth + 1, format, stringifyOptions);
392
+ }
393
+ }
394
+ return { content, hasElementChildren };
395
+ }
396
+ function stringifyNode(node, depth = 0, format = true, options = {}) {
397
+ var _a, _b;
398
+ const indent = format ? " ".repeat(depth) : "";
399
+ const newline = format ? "\n" : "";
400
+ const minimalEscaping = (_a = options.minimalEscaping) != null ? _a : false;
401
+ const strictBooleanAttributes = (_b = options.strictBooleanAttributes) != null ? _b : false;
402
+ const opts = {
403
+ minimalEscaping,
404
+ strictBooleanAttributes,
405
+ indent,
406
+ newline
407
+ };
408
+ let result = buildNodeOpeningTag(node, opts);
409
+ if (node.tagName[0] === "?") {
410
+ return `${result}?>${newline}`;
411
+ }
412
+ if (node.children.length === 0) {
413
+ return `${result}/>${newline}`;
414
+ }
415
+ result += ">";
416
+ const { content, hasElementChildren } = stringifyNodeChildren({
417
+ children: node.children,
418
+ depth,
419
+ format,
420
+ stringifyOptions: options,
421
+ minimalEscaping,
422
+ newline
423
+ });
424
+ result += content;
425
+ if (hasElementChildren && format) {
426
+ result += indent;
427
+ }
428
+ result += `</${node.tagName}>`;
429
+ if (format) {
430
+ result += newline;
431
+ }
432
+ return result;
433
+ }
434
+ function toContentString(nodes) {
435
+ let result = "";
436
+ for (const node of nodes) {
437
+ if (typeof node === "string") {
438
+ result += ` ${node}`;
439
+ } else {
440
+ result += ` ${toContentString(node.children)}`;
441
+ }
442
+ result = result.trim();
443
+ }
444
+ return result;
445
+ }
446
+
39
447
  // src/schema/base-coercion.ts
448
+ var NUMERIC_REGEX = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
449
+ var EMPTY_OBJECT_REGEX = /^\{\s*\}$/s;
450
+ var NEWLINE_SPLIT_REGEX = /\n+/;
451
+ var COMMA_SPLIT_REGEX = /,\s*/;
452
+ var DIGIT_KEY_REGEX = /^\d+$/;
40
453
  function unwrapJsonSchema(schema) {
41
- if (!schema || typeof schema !== "object") return schema;
454
+ if (!schema || typeof schema !== "object") {
455
+ return schema;
456
+ }
42
457
  const s = schema;
43
458
  if (s.jsonSchema && typeof s.jsonSchema === "object") {
44
459
  return unwrapJsonSchema(s.jsonSchema);
@@ -47,9 +462,13 @@ function unwrapJsonSchema(schema) {
47
462
  }
48
463
  function getSchemaType(schema) {
49
464
  const unwrapped = unwrapJsonSchema(schema);
50
- if (!unwrapped || typeof unwrapped !== "object") return void 0;
465
+ if (!unwrapped || typeof unwrapped !== "object") {
466
+ return;
467
+ }
51
468
  const t = unwrapped.type;
52
- if (typeof t === "string") return t;
469
+ if (typeof t === "string") {
470
+ return t;
471
+ }
53
472
  if (Array.isArray(t)) {
54
473
  const preferred = [
55
474
  "object",
@@ -59,7 +478,11 @@ function getSchemaType(schema) {
59
478
  "integer",
60
479
  "string"
61
480
  ];
62
- for (const p of preferred) if (t.includes(p)) return p;
481
+ for (const p of preferred) {
482
+ if (t.includes(p)) {
483
+ return p;
484
+ }
485
+ }
63
486
  }
64
487
  const s = unwrapped;
65
488
  if (s && typeof s === "object" && (s.properties || s.additionalProperties)) {
@@ -68,150 +491,189 @@ function getSchemaType(schema) {
68
491
  if (s && typeof s === "object" && (s.items || s.prefixItems)) {
69
492
  return "array";
70
493
  }
71
- return void 0;
494
+ return;
72
495
  }
73
- function coerceBySchema(value, schema) {
74
- const unwrapped = unwrapJsonSchema(schema);
75
- if (!unwrapped || typeof unwrapped !== "object") {
76
- if (typeof value === "string") {
77
- const s = value.trim();
78
- const lower = s.toLowerCase();
79
- if (lower === "true") return true;
80
- if (lower === "false") return false;
81
- if (/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(s)) {
82
- const num = Number(s);
83
- if (Number.isFinite(num)) return num;
84
- }
85
- if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
86
- try {
87
- const parsed = JSON.parse(s);
88
- return coerceBySchema(parsed, void 0);
89
- } catch (e) {
90
- }
91
- }
496
+ function coerceStringWithoutSchema(value) {
497
+ const s = value.trim();
498
+ const lower = s.toLowerCase();
499
+ if (lower === "true") {
500
+ return true;
501
+ }
502
+ if (lower === "false") {
503
+ return false;
504
+ }
505
+ if (NUMERIC_REGEX.test(s)) {
506
+ const num = Number(s);
507
+ if (Number.isFinite(num)) {
508
+ return num;
92
509
  }
93
- return value;
94
510
  }
95
- const schemaType = getSchemaType(unwrapped);
96
- if (typeof value === "string") {
97
- const s = value.trim();
98
- if (schemaType === "object") {
99
- try {
100
- let normalized = s.replace(/'/g, '"');
101
- normalized = normalized.replace(/^\{\s*\}$/s, "{}");
102
- const obj = JSON.parse(normalized);
103
- if (obj && typeof obj === "object" && !Array.isArray(obj)) {
104
- const props = unwrapped.properties;
105
- const out = {};
106
- for (const [k, v] of Object.entries(obj)) {
107
- const propSchema = props ? props[k] : void 0;
108
- out[k] = typeof propSchema === "boolean" ? v : coerceBySchema(v, propSchema);
109
- }
110
- return out;
111
- }
112
- } catch (e) {
511
+ if (s.startsWith("{") && s.endsWith("}") || s.startsWith("[") && s.endsWith("]")) {
512
+ try {
513
+ const parsed = JSON.parse(s);
514
+ return coerceBySchema(parsed, void 0);
515
+ } catch (e) {
516
+ }
517
+ }
518
+ return value;
519
+ }
520
+ function coerceStringToObject(s, unwrapped) {
521
+ try {
522
+ let normalized = s.replace(/'/g, '"');
523
+ normalized = normalized.replace(EMPTY_OBJECT_REGEX, "{}");
524
+ const obj = JSON.parse(normalized);
525
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
526
+ const props = unwrapped.properties;
527
+ const out = {};
528
+ for (const [k, v] of Object.entries(obj)) {
529
+ const propSchema = props ? props[k] : void 0;
530
+ out[k] = typeof propSchema === "boolean" ? v : coerceBySchema(v, propSchema);
113
531
  }
532
+ return out;
114
533
  }
115
- if (schemaType === "array") {
116
- try {
117
- const normalized = s.replace(/'/g, '"');
118
- const arr = JSON.parse(normalized);
119
- if (Array.isArray(arr)) {
120
- const u = unwrapped;
121
- const prefixItems = Array.isArray(
122
- u.prefixItems
123
- ) ? u.prefixItems : void 0;
124
- const itemsSchema = u.items;
125
- if (prefixItems && arr.length === prefixItems.length) {
126
- return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
127
- }
128
- return arr.map((v) => coerceBySchema(v, itemsSchema));
129
- }
130
- } catch (e) {
131
- const csv = s.includes("\n") ? s.split(/\n+/) : s.split(/,\s*/);
132
- const trimmed = csv.map((x) => x.trim()).filter((x) => x.length > 0);
133
- const u = unwrapped;
134
- const prefixItems = Array.isArray(
135
- u.prefixItems
136
- ) ? u.prefixItems : void 0;
137
- const itemsSchema = u.items;
138
- if (prefixItems && trimmed.length === prefixItems.length) {
139
- return trimmed.map((x, i) => coerceBySchema(x, prefixItems[i]));
140
- }
141
- return trimmed.map((x) => coerceBySchema(x, itemsSchema));
534
+ } catch (e) {
535
+ }
536
+ return null;
537
+ }
538
+ function coerceStringToArray(s, unwrapped) {
539
+ const prefixItems = Array.isArray(unwrapped.prefixItems) ? unwrapped.prefixItems : void 0;
540
+ const itemsSchema = unwrapped.items;
541
+ try {
542
+ const normalized = s.replace(/'/g, '"');
543
+ const arr = JSON.parse(normalized);
544
+ if (Array.isArray(arr)) {
545
+ if (prefixItems && arr.length === prefixItems.length) {
546
+ return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
142
547
  }
548
+ return arr.map((v) => coerceBySchema(v, itemsSchema));
143
549
  }
550
+ } catch (e) {
551
+ const csv = s.includes("\n") ? s.split(NEWLINE_SPLIT_REGEX) : s.split(COMMA_SPLIT_REGEX);
552
+ const trimmed = csv.map((x) => x.trim()).filter((x) => x.length > 0);
553
+ if (prefixItems && trimmed.length === prefixItems.length) {
554
+ return trimmed.map((x, i) => coerceBySchema(x, prefixItems[i]));
555
+ }
556
+ return trimmed.map((x) => coerceBySchema(x, itemsSchema));
144
557
  }
145
- if (schemaType === "object" && value && typeof value === "object" && !Array.isArray(value)) {
146
- const out = {};
147
- const props = unwrapped.properties;
148
- for (const [k, v] of Object.entries(value)) {
149
- const propSchema = props ? props[k] : void 0;
150
- out[k] = typeof propSchema === "boolean" ? v : coerceBySchema(v, propSchema);
558
+ return null;
559
+ }
560
+ function coerceObjectToObject(value, unwrapped) {
561
+ const out = {};
562
+ const props = unwrapped.properties;
563
+ for (const [k, v] of Object.entries(value)) {
564
+ const propSchema = props ? props[k] : void 0;
565
+ out[k] = typeof propSchema === "boolean" ? v : coerceBySchema(v, propSchema);
566
+ }
567
+ return out;
568
+ }
569
+ function coerceArrayToArray(value, prefixItems, itemsSchema) {
570
+ if (prefixItems && value.length === prefixItems.length) {
571
+ return value.map((v, i) => coerceBySchema(v, prefixItems[i]));
572
+ }
573
+ return value.map((v) => coerceBySchema(v, itemsSchema));
574
+ }
575
+ function coerceObjectToArray(maybe, prefixItems, itemsSchema) {
576
+ if (Object.hasOwn(maybe, "item")) {
577
+ const items = maybe.item;
578
+ const arr = Array.isArray(items) ? items : [items];
579
+ return coerceArrayToArray(arr, prefixItems, itemsSchema);
580
+ }
581
+ const keys = Object.keys(maybe);
582
+ if (keys.length === 1) {
583
+ const singleValue = maybe[keys[0]];
584
+ if (Array.isArray(singleValue)) {
585
+ return singleValue.map((v) => coerceBySchema(v, itemsSchema));
586
+ }
587
+ }
588
+ if (keys.length > 0 && keys.every((k) => DIGIT_KEY_REGEX.test(k))) {
589
+ const arr = keys.sort((a, b) => Number(a) - Number(b)).map((k) => maybe[k]);
590
+ return coerceArrayToArray(arr, prefixItems, itemsSchema);
591
+ }
592
+ return null;
593
+ }
594
+ function coercePrimitiveToArray(value, prefixItems, itemsSchema) {
595
+ if (prefixItems && prefixItems.length > 0) {
596
+ return [coerceBySchema(value, prefixItems[0])];
597
+ }
598
+ return [coerceBySchema(value, itemsSchema)];
599
+ }
600
+ function coerceStringToPrimitive(s, schemaType) {
601
+ if (schemaType === "boolean") {
602
+ const lower = s.toLowerCase();
603
+ if (lower === "true") {
604
+ return true;
605
+ }
606
+ if (lower === "false") {
607
+ return false;
608
+ }
609
+ }
610
+ if ((schemaType === "number" || schemaType === "integer") && NUMERIC_REGEX.test(s)) {
611
+ const num = Number(s);
612
+ if (Number.isFinite(num)) {
613
+ return num;
614
+ }
615
+ }
616
+ return null;
617
+ }
618
+ function coerceStringValue(value, schemaType, u) {
619
+ const s = value.trim();
620
+ if (schemaType === "object") {
621
+ const result = coerceStringToObject(s, u);
622
+ if (result !== null) {
623
+ return result;
151
624
  }
152
- return out;
153
625
  }
154
626
  if (schemaType === "array") {
155
- const u = unwrapped;
156
- const itemsSchema = u.items;
157
- const prefixItems = Array.isArray(
158
- u.prefixItems
159
- ) ? u.prefixItems : void 0;
160
- if (Array.isArray(value)) {
161
- if (prefixItems && value.length === prefixItems.length) {
162
- return value.map((v, i) => coerceBySchema(v, prefixItems[i]));
163
- }
164
- return value.map((v) => coerceBySchema(v, itemsSchema));
165
- }
166
- if (value && typeof value === "object") {
167
- const maybe = value;
168
- if (Object.prototype.hasOwnProperty.call(maybe, "item")) {
169
- const items = maybe.item;
170
- const arr = Array.isArray(items) ? items : [items];
171
- if (prefixItems && arr.length === prefixItems.length) {
172
- return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
173
- }
174
- return arr.map((v) => coerceBySchema(v, itemsSchema));
175
- }
176
- const keys = Object.keys(maybe);
177
- if (keys.length === 1) {
178
- const singleKey = keys[0];
179
- const singleValue = maybe[singleKey];
180
- if (Array.isArray(singleValue)) {
181
- const coercedArray = singleValue.map(
182
- (v) => coerceBySchema(v, itemsSchema)
183
- );
184
- return coercedArray;
185
- }
186
- }
187
- if (keys.length > 0 && keys.every((k) => /^\d+$/.test(k))) {
188
- const arr = keys.sort((a, b) => Number(a) - Number(b)).map((k) => maybe[k]);
189
- if (prefixItems && arr.length === prefixItems.length) {
190
- return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
191
- }
192
- return arr.map((v) => coerceBySchema(v, itemsSchema));
193
- }
627
+ const result = coerceStringToArray(s, u);
628
+ if (result !== null) {
629
+ return result;
194
630
  }
195
- if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
196
- if (prefixItems && prefixItems.length > 0) {
197
- return [coerceBySchema(value, prefixItems[0])];
198
- }
199
- return [coerceBySchema(value, itemsSchema)];
631
+ }
632
+ const primitiveResult = coerceStringToPrimitive(s, schemaType);
633
+ if (primitiveResult !== null) {
634
+ return primitiveResult;
635
+ }
636
+ return value;
637
+ }
638
+ function coerceArrayValue(value, prefixItems, itemsSchema) {
639
+ if (Array.isArray(value)) {
640
+ return coerceArrayToArray(value, prefixItems, itemsSchema);
641
+ }
642
+ if (value && typeof value === "object") {
643
+ const result = coerceObjectToArray(
644
+ value,
645
+ prefixItems,
646
+ itemsSchema
647
+ );
648
+ if (result !== null) {
649
+ return result;
200
650
  }
201
651
  }
202
- if (typeof value === "string") {
203
- const s = value.trim();
204
- if (schemaType === "boolean") {
205
- const lower = s.toLowerCase();
206
- if (lower === "true") return true;
207
- if (lower === "false") return false;
208
- }
209
- if (schemaType === "number" || schemaType === "integer") {
210
- if (/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(s)) {
211
- const num = Number(s);
212
- if (Number.isFinite(num)) return num;
213
- }
652
+ if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
653
+ return coercePrimitiveToArray(value, prefixItems, itemsSchema);
654
+ }
655
+ return value;
656
+ }
657
+ function coerceBySchema(value, schema) {
658
+ const unwrapped = unwrapJsonSchema(schema);
659
+ if (!unwrapped || typeof unwrapped !== "object") {
660
+ if (typeof value === "string") {
661
+ return coerceStringWithoutSchema(value);
214
662
  }
663
+ return value;
664
+ }
665
+ const schemaType = getSchemaType(unwrapped);
666
+ const u = unwrapped;
667
+ if (typeof value === "string") {
668
+ return coerceStringValue(value, schemaType, u);
669
+ }
670
+ if (schemaType === "object" && value && typeof value === "object" && !Array.isArray(value)) {
671
+ return coerceObjectToObject(value, u);
672
+ }
673
+ if (schemaType === "array") {
674
+ const prefixItems = Array.isArray(u.prefixItems) ? u.prefixItems : void 0;
675
+ const itemsSchema = u.items;
676
+ return coerceArrayValue(value, prefixItems, itemsSchema);
215
677
  }
216
678
  return value;
217
679
  }
@@ -219,13 +681,56 @@ function coerceBySchema(value, schema) {
219
681
  // src/schema/coercion.ts
220
682
  function getPropertySchema(toolSchema, key) {
221
683
  const unwrapped = unwrapJsonSchema(toolSchema);
222
- if (!unwrapped || typeof unwrapped !== "object") return void 0;
684
+ if (!unwrapped || typeof unwrapped !== "object") {
685
+ return;
686
+ }
223
687
  const u = unwrapped;
224
688
  const props = u.properties;
225
- if (props && Object.prototype.hasOwnProperty.call(props, key)) {
689
+ if (props && Object.hasOwn(props, key)) {
226
690
  return props[key];
227
691
  }
228
- return void 0;
692
+ return;
693
+ }
694
+ function getNodeValue(children, schema, tagName, textNodeName) {
695
+ if (children.length === 0) {
696
+ return "";
697
+ }
698
+ if (children.length === 1 && typeof children[0] === "string") {
699
+ return children[0];
700
+ }
701
+ return processComplexContent(
702
+ children,
703
+ getPropertySchema(schema, tagName),
704
+ textNodeName
705
+ );
706
+ }
707
+ function addAttributesToValue(value, attributes, textNodeName) {
708
+ if (Object.keys(attributes).length === 0) {
709
+ return value;
710
+ }
711
+ if (typeof value === "string") {
712
+ const valueResult = { [textNodeName]: value };
713
+ for (const [attrName, attrValue] of Object.entries(attributes)) {
714
+ valueResult[`@_${attrName}`] = attrValue;
715
+ }
716
+ return valueResult;
717
+ }
718
+ if (value && typeof value === "object" && !Array.isArray(value)) {
719
+ for (const [attrName, attrValue] of Object.entries(attributes)) {
720
+ value[`@_${attrName}`] = attrValue;
721
+ }
722
+ }
723
+ return value;
724
+ }
725
+ function addToResult(result, tagName, value) {
726
+ if (result[tagName]) {
727
+ if (!Array.isArray(result[tagName])) {
728
+ result[tagName] = [result[tagName]];
729
+ }
730
+ result[tagName].push(value);
731
+ } else {
732
+ result[tagName] = value;
733
+ }
229
734
  }
230
735
  function domToObject(nodes, schema, textNodeName = "#text") {
231
736
  const result = {};
@@ -234,104 +739,57 @@ function domToObject(nodes, schema, textNodeName = "#text") {
234
739
  continue;
235
740
  }
236
741
  const { tagName, children, attributes } = node;
237
- let value;
238
- if (children.length === 0) {
239
- value = "";
240
- } else if (children.length === 1 && typeof children[0] === "string") {
241
- value = children[0];
242
- } else {
243
- value = processComplexContent(
244
- children,
245
- getPropertySchema(schema, tagName),
246
- textNodeName
247
- );
248
- }
249
- if (Object.keys(attributes).length > 0) {
250
- if (typeof value === "string") {
251
- const result2 = { [textNodeName]: value };
252
- for (const [attrName, attrValue] of Object.entries(attributes)) {
253
- result2[`@_${attrName}`] = attrValue;
254
- }
255
- value = result2;
256
- } else if (value && typeof value === "object" && !Array.isArray(value)) {
257
- for (const [attrName, attrValue] of Object.entries(attributes)) {
258
- value[`@_${attrName}`] = attrValue;
259
- }
260
- }
261
- }
262
- if (result[tagName]) {
263
- if (!Array.isArray(result[tagName])) {
264
- result[tagName] = [result[tagName]];
265
- }
266
- result[tagName].push(value);
267
- } else {
268
- result[tagName] = value;
269
- }
742
+ let value = getNodeValue(children, schema, tagName, textNodeName);
743
+ value = addAttributesToValue(value, attributes, textNodeName);
744
+ addToResult(result, tagName, value);
270
745
  }
271
746
  return result;
272
747
  }
273
- function processComplexContent(children, schema, textNodeName) {
274
- const textContent = [];
275
- const elements = {};
276
- for (const child of children) {
277
- if (typeof child === "string") {
278
- textContent.push(child);
279
- } else {
280
- let childValue;
281
- if (child.children.length === 0) {
282
- childValue = "";
283
- } else if (child.children.length === 1 && typeof child.children[0] === "string") {
284
- childValue = child.children[0];
285
- } else {
286
- childValue = processComplexContent(
287
- child.children,
288
- getPropertySchema(schema, child.tagName),
289
- textNodeName
290
- );
291
- }
292
- if (Object.keys(child.attributes).length > 0) {
293
- if (typeof childValue === "string") {
294
- const result = {
295
- [textNodeName]: childValue
296
- };
297
- for (const [attrName, attrValue] of Object.entries(
298
- child.attributes
299
- )) {
300
- result[`@_${attrName}`] = attrValue;
301
- }
302
- childValue = result;
303
- } else if (childValue && typeof childValue === "object" && !Array.isArray(childValue)) {
304
- for (const [attrName, attrValue] of Object.entries(
305
- child.attributes
306
- )) {
307
- childValue[`@_${attrName}`] = attrValue;
308
- }
309
- }
310
- }
311
- if (elements[child.tagName]) {
312
- if (!Array.isArray(elements[child.tagName])) {
313
- elements[child.tagName] = [elements[child.tagName]];
314
- }
315
- elements[child.tagName].push(childValue);
316
- } else {
317
- elements[child.tagName] = childValue;
318
- }
319
- }
748
+ function processChildElement(child, schema, textNodeName) {
749
+ let childValue;
750
+ if (child.children.length === 0) {
751
+ childValue = "";
752
+ } else if (child.children.length === 1 && typeof child.children[0] === "string") {
753
+ childValue = child.children[0];
754
+ } else {
755
+ childValue = processComplexContent(
756
+ child.children,
757
+ getPropertySchema(schema, child.tagName),
758
+ textNodeName
759
+ );
320
760
  }
321
- if (textContent.length > 0 && Object.keys(elements).length > 0) {
761
+ return addAttributesToValue(childValue, child.attributes, textNodeName);
762
+ }
763
+ function combineContent(textContent, elements, textNodeName) {
764
+ const hasText = textContent.length > 0;
765
+ const hasElements = Object.keys(elements).length > 0;
766
+ if (hasText && hasElements) {
322
767
  return {
323
768
  [textNodeName]: textContent.join("").trim(),
324
769
  ...elements
325
770
  };
326
771
  }
327
- if (textContent.length > 0 && Object.keys(elements).length === 0) {
772
+ if (hasText) {
328
773
  return textContent.join("").trim();
329
774
  }
330
- if (Object.keys(elements).length > 0) {
775
+ if (hasElements) {
331
776
  return elements;
332
777
  }
333
778
  return "";
334
779
  }
780
+ function processComplexContent(children, schema, textNodeName) {
781
+ const textContent = [];
782
+ const elements = {};
783
+ for (const child of children) {
784
+ if (typeof child === "string") {
785
+ textContent.push(child);
786
+ } else {
787
+ const childValue = processChildElement(child, schema, textNodeName);
788
+ addToResult(elements, child.tagName, childValue);
789
+ }
790
+ }
791
+ return combineContent(textContent, elements, textNodeName);
792
+ }
335
793
  function coerceDomBySchema(domObject, schema) {
336
794
  try {
337
795
  return coerceBySchema(domObject, schema);
@@ -339,41 +797,59 @@ function coerceDomBySchema(domObject, schema) {
339
797
  throw new RXMLCoercionError("Failed to coerce DOM object by schema", error);
340
798
  }
341
799
  }
800
+ function visitObjectProperties(props, collected, visit) {
801
+ for (const [key, propSchema] of Object.entries(props)) {
802
+ const t = getSchemaType(propSchema);
803
+ if (t === "string") {
804
+ collected.add(key);
805
+ } else if (t === "object" || t === "array") {
806
+ visit(propSchema);
807
+ }
808
+ }
809
+ }
810
+ function visitArrayItems(u, visit) {
811
+ const items = u.items;
812
+ if (items) {
813
+ visit(items);
814
+ }
815
+ const prefix = u.prefixItems;
816
+ if (Array.isArray(prefix)) {
817
+ for (const item of prefix) {
818
+ visit(item);
819
+ }
820
+ }
821
+ }
342
822
  function getStringTypedProperties(schema) {
343
823
  const collected = /* @__PURE__ */ new Set();
344
824
  const visit = (s) => {
345
825
  const unwrapped = unwrapJsonSchema(s);
346
- if (!unwrapped || typeof unwrapped !== "object") return;
826
+ if (!unwrapped || typeof unwrapped !== "object") {
827
+ return;
828
+ }
347
829
  const u = unwrapped;
348
830
  const type = getSchemaType(unwrapped);
349
831
  if (type === "object") {
350
832
  const props = u.properties;
351
833
  if (props && typeof props === "object") {
352
- for (const [key, propSchema] of Object.entries(props)) {
353
- const t = getSchemaType(propSchema);
354
- if (t === "string") {
355
- collected.add(key);
356
- } else if (t === "object" || t === "array") {
357
- visit(propSchema);
358
- }
359
- }
834
+ visitObjectProperties(props, collected, visit);
360
835
  }
361
836
  } else if (type === "array") {
362
- const items = u.items;
363
- if (items) visit(items);
364
- const prefix = u.prefixItems;
365
- if (Array.isArray(prefix)) prefix.forEach(visit);
837
+ visitArrayItems(u, visit);
366
838
  }
367
839
  };
368
840
  visit(schema);
369
841
  return collected;
370
842
  }
371
843
  function processArrayContent(value, schema, textNodeName) {
372
- if (!Array.isArray(value)) return value;
844
+ if (!Array.isArray(value)) {
845
+ return value;
846
+ }
373
847
  const schemaType = getSchemaType(schema);
374
848
  if (schemaType === "string") {
375
849
  return value.map((item) => {
376
- if (typeof item === "string") return item.trim();
850
+ if (typeof item === "string") {
851
+ return item.trim();
852
+ }
377
853
  if (item && typeof item === "object" && textNodeName in item) {
378
854
  const textVal = item[textNodeName];
379
855
  return typeof textVal === "string" ? textVal.trim() : String(textVal);
@@ -382,7 +858,9 @@ function processArrayContent(value, schema, textNodeName) {
382
858
  });
383
859
  }
384
860
  return value.map((item) => {
385
- if (typeof item === "string") return item.trim();
861
+ if (typeof item === "string") {
862
+ return item.trim();
863
+ }
386
864
  if (item && typeof item === "object" && textNodeName in item) {
387
865
  const textVal = item[textNodeName];
388
866
  return typeof textVal === "string" ? textVal.trim() : textVal;
@@ -392,10 +870,14 @@ function processArrayContent(value, schema, textNodeName) {
392
870
  }
393
871
  function processIndexedTuple(obj, textNodeName) {
394
872
  const keys = Object.keys(obj);
395
- const indices = keys.map((k) => parseInt(k, 10)).sort((a, b) => a - b);
873
+ const indices = keys.map((k) => Number.parseInt(k, 10)).sort((a, b) => a - b);
396
874
  const isValidTuple = indices[0] === 0 && indices.every((val, idx) => val === idx);
397
- if (!isValidTuple) return [obj];
398
- const sortedKeys = keys.sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
875
+ if (!isValidTuple) {
876
+ return [obj];
877
+ }
878
+ const sortedKeys = keys.sort(
879
+ (a, b) => Number.parseInt(a, 10) - Number.parseInt(b, 10)
880
+ );
399
881
  return sortedKeys.map((key) => {
400
882
  const item = obj[key];
401
883
  if (item && typeof item === "object" && textNodeName in item) {
@@ -406,115 +888,187 @@ function processIndexedTuple(obj, textNodeName) {
406
888
  });
407
889
  }
408
890
 
409
- // src/core/types.ts
410
- var CharCodes = {
411
- OPEN_BRACKET: "<".charCodeAt(0),
412
- CLOSE_BRACKET: ">".charCodeAt(0),
413
- MINUS: "-".charCodeAt(0),
414
- SLASH: "/".charCodeAt(0),
415
- EXCLAMATION: "!".charCodeAt(0),
416
- QUESTION: "?".charCodeAt(0),
417
- SINGLE_QUOTE: "'".charCodeAt(0),
418
- DOUBLE_QUOTE: '"'.charCodeAt(0),
419
- OPEN_CORNER_BRACKET: "[".charCodeAt(0),
420
- CLOSE_CORNER_BRACKET: "]".charCodeAt(0),
421
- SPACE: " ".charCodeAt(0),
422
- TAB: " ".charCodeAt(0),
423
- NEWLINE: "\n".charCodeAt(0),
424
- CARRIAGE_RETURN: "\r".charCodeAt(0)
425
- };
426
- var DEFAULT_NO_CHILD_NODES = [
427
- "img",
428
- "br",
429
- "input",
430
- "meta",
431
- "link",
432
- "hr",
433
- "area",
434
- "base",
435
- "col",
436
- "embed",
437
- "param",
438
- "source",
439
- "track",
440
- "wbr"
441
- ];
442
- var NAME_SPACER = "\r\n >/= ";
443
-
444
- // src/utils/helpers.ts
445
- function isNameStartChar(ch) {
446
- return /[A-Za-z_:]/.test(ch);
891
+ // src/schema/extraction.ts
892
+ function skipDoctype(xmlContent, i, len) {
893
+ const gt = xmlContent.indexOf(">", i + 1);
894
+ return gt === -1 ? len : gt + 1;
447
895
  }
448
- function isNameChar(ch) {
449
- return /[A-Za-z0-9_.:-]/.test(ch);
896
+ function skipComment(xmlContent, i, len) {
897
+ const close = xmlContent.indexOf("-->", i + 4);
898
+ return close === -1 ? len : close + 3;
450
899
  }
451
- function skipQuoted(s, i) {
452
- const quote = s[i];
453
- i++;
454
- while (i < s.length) {
455
- const ch = s[i];
456
- if (ch === "\\") {
457
- i += 2;
458
- continue;
900
+ function skipCdata(xmlContent, i, len) {
901
+ const close = xmlContent.indexOf("]]>", i + 9);
902
+ return close === -1 ? len : close + 3;
903
+ }
904
+ function skipProcessingInstruction(xmlContent, i, len) {
905
+ const close = xmlContent.indexOf("?>", i + 1);
906
+ return close === -1 ? len : close + 2;
907
+ }
908
+ function skipSpecialConstruct(xmlContent, i, len) {
909
+ const ch = xmlContent[i];
910
+ if (ch === "!") {
911
+ if (xmlContent.startsWith("!DOCTYPE", i + 1)) {
912
+ return skipDoctype(xmlContent, i, len);
913
+ }
914
+ if (xmlContent.startsWith("!--", i + 1)) {
915
+ return skipComment(xmlContent, i, len);
916
+ }
917
+ if (xmlContent.startsWith("![CDATA[", i + 1)) {
918
+ return skipCdata(xmlContent, i, len);
459
919
  }
460
- if (ch === quote) return i + 1;
461
- i++;
920
+ const gt = xmlContent.indexOf(">", i + 1);
921
+ return gt === -1 ? len : gt + 1;
462
922
  }
463
- return i;
923
+ if (ch === "?") {
924
+ return skipProcessingInstruction(xmlContent, i, len);
925
+ }
926
+ return -1;
464
927
  }
465
- function parseName(s, pos) {
466
- const start = pos;
467
- while (NAME_SPACER.indexOf(s[pos]) === -1 && s[pos]) {
468
- pos++;
928
+ function parseTagName(xmlContent, i, len) {
929
+ let j = i;
930
+ if (j < len && isNameStartChar(xmlContent[j])) {
931
+ j += 1;
932
+ while (j < len && isNameChar(xmlContent[j])) {
933
+ j += 1;
934
+ }
469
935
  }
470
- return { name: s.slice(start, pos), newPos: pos };
936
+ return { name: xmlContent.slice(i, j), pos: j };
471
937
  }
472
- function parseString(s, pos) {
473
- const startChar = s[pos];
474
- const startPos = pos + 1;
475
- const endPos = s.indexOf(startChar, startPos);
476
- if (endPos === -1) {
477
- const tagEnd = s.indexOf(">", startPos);
478
- if (tagEnd !== -1) {
479
- return { value: s.slice(startPos, tagEnd), newPos: tagEnd };
938
+ function skipToTagEnd(xmlContent, start, len) {
939
+ let k = start;
940
+ let isSelfClosing = false;
941
+ while (k < len) {
942
+ const c = xmlContent[k];
943
+ if (c === '"' || c === "'") {
944
+ k = skipQuoted(xmlContent, k);
945
+ continue;
480
946
  }
481
- return { value: s.slice(startPos), newPos: s.length };
947
+ if (c === ">") {
948
+ break;
949
+ }
950
+ if (c === "/" && xmlContent[k + 1] === ">") {
951
+ isSelfClosing = true;
952
+ k += 1;
953
+ break;
954
+ }
955
+ k += 1;
482
956
  }
483
- return { value: s.slice(startPos, endPos), newPos: endPos + 1 };
957
+ return { pos: k, isSelfClosing };
484
958
  }
485
- function getLineColumn(s, pos) {
486
- let line = 1;
487
- let column = 1;
488
- for (let i = 0; i < pos && i < s.length; i++) {
489
- if (s[i] === "\n") {
490
- line++;
491
- column = 1;
959
+ function processClosingTagMatch(options) {
960
+ const { xmlContent, nx, len, tagName, depth, nextLt } = options;
961
+ const tagInfo = parseTagName(xmlContent, nx + 1, len);
962
+ const gt = xmlContent.indexOf(">", tagInfo.pos);
963
+ if (tagInfo.name === tagName) {
964
+ const newDepth = depth - 1;
965
+ if (newDepth === 0) {
966
+ return { newPos: nextLt, newDepth, found: true };
967
+ }
968
+ return { newPos: gt === -1 ? len : gt + 1, newDepth, found: false };
969
+ }
970
+ return { newPos: gt === -1 ? len : gt + 1, newDepth: depth, found: false };
971
+ }
972
+ function processOpeningTagMatch(options) {
973
+ const { xmlContent, nx, len, tagName, depth } = options;
974
+ const tagInfo = parseTagName(xmlContent, nx, len);
975
+ const tagEndInfo = skipToTagEnd(xmlContent, tagInfo.pos, len);
976
+ const newDepth = tagInfo.name === tagName && !tagEndInfo.isSelfClosing ? depth + 1 : depth;
977
+ const newPos = xmlContent[tagEndInfo.pos] === ">" ? tagEndInfo.pos + 1 : tagEndInfo.pos + 1;
978
+ return { newPos, newDepth };
979
+ }
980
+ function findMatchingCloseTag(xmlContent, startPos, tagName, len) {
981
+ let pos = startPos;
982
+ let depth = 1;
983
+ while (pos < len) {
984
+ const nextLt = xmlContent.indexOf("<", pos);
985
+ if (nextLt === -1 || nextLt + 1 >= len) {
986
+ break;
987
+ }
988
+ const nx = nextLt + 1;
989
+ const h = xmlContent[nx];
990
+ const specialPos = skipSpecialConstruct(xmlContent, nx, len);
991
+ if (specialPos !== -1) {
992
+ pos = specialPos;
993
+ continue;
994
+ }
995
+ if (h === "/") {
996
+ const result = processClosingTagMatch({
997
+ xmlContent,
998
+ nx,
999
+ len,
1000
+ tagName,
1001
+ depth,
1002
+ nextLt
1003
+ });
1004
+ if (result.found) {
1005
+ return result.newPos;
1006
+ }
1007
+ pos = result.newPos;
1008
+ depth = result.newDepth;
492
1009
  } else {
493
- column++;
1010
+ const result = processOpeningTagMatch({
1011
+ xmlContent,
1012
+ nx,
1013
+ len,
1014
+ tagName,
1015
+ depth
1016
+ });
1017
+ pos = result.newPos;
1018
+ depth = result.newDepth;
494
1019
  }
495
1020
  }
496
- return { line, column };
497
- }
498
- function escapeXml(text) {
499
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1021
+ return -1;
500
1022
  }
501
- function escapeXmlMinimalText(text) {
502
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/]]>/g, "]]&gt;");
1023
+ function updateBestMatch(depth, bestDepth, contentStart, contentEnd) {
1024
+ if (depth < bestDepth) {
1025
+ return { start: contentStart, end: contentEnd, depth };
1026
+ }
1027
+ return null;
503
1028
  }
504
- function escapeXmlMinimalAttr(value, wrapper = '"') {
505
- let escaped = value.replace(/&/g, "&amp;").replace(/</g, "&lt;");
506
- if (wrapper === '"') {
507
- escaped = escaped.replace(/"/g, "&quot;");
508
- } else {
509
- escaped = escaped.replace(/'/g, "&apos;");
1029
+ function processTargetTag(options) {
1030
+ const { xmlContent, tagEnd, isSelfClosing, target, len, depth, bestDepth } = options;
1031
+ const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1032
+ if (isSelfClosing) {
1033
+ return updateBestMatch(depth, bestDepth, contentStart, contentStart);
510
1034
  }
511
- return escaped;
1035
+ const closePos = findMatchingCloseTag(xmlContent, contentStart, target, len);
1036
+ if (closePos !== -1) {
1037
+ return updateBestMatch(depth, bestDepth, contentStart, closePos);
1038
+ }
1039
+ return null;
512
1040
  }
513
- function unescapeXml(text) {
514
- return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
1041
+ function handleClosingTagInExtract(xmlContent, i, len, depth) {
1042
+ const gt = xmlContent.indexOf(">", i + 1);
1043
+ return {
1044
+ newPos: gt === -1 ? len : gt + 1,
1045
+ newDepth: Math.max(0, depth - 1)
1046
+ };
1047
+ }
1048
+ function processOpeningTagInExtract(options) {
1049
+ const { xmlContent, i, len, target, depth, bestDepth } = options;
1050
+ const tagInfo = parseTagName(xmlContent, i, len);
1051
+ const tagEndInfo = skipToTagEnd(xmlContent, tagInfo.pos, len);
1052
+ const tagEnd = tagEndInfo.pos;
1053
+ const isSelfClosing = tagEndInfo.isSelfClosing;
1054
+ let bestMatch = null;
1055
+ if (tagInfo.name === target) {
1056
+ bestMatch = processTargetTag({
1057
+ xmlContent,
1058
+ tagEnd,
1059
+ isSelfClosing,
1060
+ target,
1061
+ len,
1062
+ depth,
1063
+ bestDepth
1064
+ });
1065
+ }
1066
+ return {
1067
+ newPos: xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1,
1068
+ newDepth: depth + (isSelfClosing ? 0 : 1),
1069
+ bestMatch
1070
+ };
515
1071
  }
516
-
517
- // src/schema/extraction.ts
518
1072
  function extractRawInner(xmlContent, tagName) {
519
1073
  const len = xmlContent.length;
520
1074
  const target = tagName;
@@ -525,164 +1079,61 @@ function extractRawInner(xmlContent, tagName) {
525
1079
  let depth = 0;
526
1080
  while (i < len) {
527
1081
  const lt = xmlContent.indexOf("<", i);
528
- if (lt === -1) return void 0;
1082
+ if (lt === -1 || lt + 1 >= len) {
1083
+ return;
1084
+ }
529
1085
  i = lt + 1;
530
- if (i >= len) return void 0;
531
1086
  const ch = xmlContent[i];
532
- if (ch === "!") {
533
- if (xmlContent.startsWith("!DOCTYPE", i + 1)) {
534
- const gt2 = xmlContent.indexOf(">", i + 1);
535
- i = gt2 === -1 ? len : gt2 + 1;
536
- continue;
537
- }
538
- if (xmlContent.startsWith("!--", i + 1)) {
539
- const close = xmlContent.indexOf("-->", i + 4);
540
- i = close === -1 ? len : close + 3;
541
- continue;
542
- }
543
- if (xmlContent.startsWith("![CDATA[", i + 1)) {
544
- const close = xmlContent.indexOf("]]>", i + 9);
545
- i = close === -1 ? len : close + 3;
546
- continue;
547
- }
548
- const gt = xmlContent.indexOf(">", i + 1);
549
- i = gt === -1 ? len : gt + 1;
550
- continue;
551
- } else if (ch === "?") {
552
- const close = xmlContent.indexOf("?>", i + 1);
553
- i = close === -1 ? len : close + 2;
1087
+ const specialPos = skipSpecialConstruct(xmlContent, i, len);
1088
+ if (specialPos !== -1) {
1089
+ i = specialPos;
554
1090
  continue;
555
- } else if (ch === "/") {
556
- const gt = xmlContent.indexOf(">", i + 1);
557
- i = gt === -1 ? len : gt + 1;
558
- depth = Math.max(0, depth - 1);
559
- continue;
560
- } else {
561
- let j = i;
562
- if (j < len && isNameStartChar(xmlContent[j])) {
563
- j++;
564
- while (j < len && isNameChar(xmlContent[j])) j++;
565
- }
566
- const name = xmlContent.slice(i, j);
567
- let k = j;
568
- let isSelfClosing = false;
569
- while (k < len) {
570
- const c = xmlContent[k];
571
- if (c === '"' || c === "'") {
572
- k = skipQuoted(xmlContent, k);
573
- continue;
574
- }
575
- if (c === ">") break;
576
- if (c === "/" && xmlContent[k + 1] === ">") {
577
- isSelfClosing = true;
578
- k++;
579
- break;
580
- }
581
- k++;
582
- }
583
- const tagEnd = k;
584
- if (name === target) {
585
- const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
586
- if (isSelfClosing) {
587
- if (depth < bestDepth) {
588
- bestStart = contentStart;
589
- bestEnd = contentStart;
590
- bestDepth = depth;
591
- }
592
- } else {
593
- let pos = contentStart;
594
- let sameDepth = 1;
595
- while (pos < len) {
596
- const nextLt = xmlContent.indexOf("<", pos);
597
- if (nextLt === -1) break;
598
- const nx = nextLt + 1;
599
- if (nx >= len) break;
600
- const h = xmlContent[nx];
601
- if (h === "!") {
602
- if (xmlContent.startsWith("!DOCTYPE", nx + 1)) {
603
- const gt22 = xmlContent.indexOf(">", nx + 1);
604
- pos = gt22 === -1 ? len : gt22 + 1;
605
- continue;
606
- }
607
- if (xmlContent.startsWith("!--", nx + 1)) {
608
- const close = xmlContent.indexOf("-->", nx + 4);
609
- pos = close === -1 ? len : close + 3;
610
- continue;
611
- }
612
- if (xmlContent.startsWith("![CDATA[", nx + 1)) {
613
- const close = xmlContent.indexOf("]]>", nx + 9);
614
- pos = close === -1 ? len : close + 3;
615
- continue;
616
- }
617
- const gt2 = xmlContent.indexOf(">", nx + 1);
618
- pos = gt2 === -1 ? len : gt2 + 1;
619
- continue;
620
- } else if (h === "?") {
621
- const close = xmlContent.indexOf("?>", nx + 1);
622
- pos = close === -1 ? len : close + 2;
623
- continue;
624
- } else if (h === "/") {
625
- let t = nx + 1;
626
- if (t < len && isNameStartChar(xmlContent[t])) {
627
- t++;
628
- while (t < len && isNameChar(xmlContent[t])) t++;
629
- }
630
- const endName = xmlContent.slice(nx + 1, t);
631
- const gt2 = xmlContent.indexOf(">", t);
632
- if (endName === target) {
633
- sameDepth--;
634
- if (sameDepth === 0) {
635
- if (depth < bestDepth) {
636
- bestStart = contentStart;
637
- bestEnd = nextLt;
638
- bestDepth = depth;
639
- }
640
- break;
641
- }
642
- }
643
- pos = gt2 === -1 ? len : gt2 + 1;
644
- continue;
645
- } else {
646
- let t = nx;
647
- if (t < len && isNameStartChar(xmlContent[t])) {
648
- t++;
649
- while (t < len && isNameChar(xmlContent[t])) t++;
650
- }
651
- let u = t;
652
- let isSelfClosingNested = false;
653
- while (u < len) {
654
- const cu = xmlContent[u];
655
- if (cu === '"' || cu === "'") {
656
- u = skipQuoted(xmlContent, u);
657
- continue;
658
- }
659
- if (cu === ">") break;
660
- if (cu === "/" && xmlContent[u + 1] === ">") {
661
- isSelfClosingNested = true;
662
- u++;
663
- break;
664
- }
665
- u++;
666
- }
667
- const startName = xmlContent.slice(nx, t);
668
- if (startName === target && !isSelfClosingNested) {
669
- sameDepth++;
670
- }
671
- pos = xmlContent[u] === ">" ? u + 1 : u + 1;
672
- continue;
673
- }
674
- }
675
- }
676
- }
677
- i = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
678
- depth += isSelfClosing ? 0 : 1;
1091
+ }
1092
+ if (ch === "/") {
1093
+ const result2 = handleClosingTagInExtract(xmlContent, i, len, depth);
1094
+ i = result2.newPos;
1095
+ depth = result2.newDepth;
679
1096
  continue;
680
1097
  }
1098
+ const result = processOpeningTagInExtract({
1099
+ xmlContent,
1100
+ i,
1101
+ len,
1102
+ target,
1103
+ depth,
1104
+ bestDepth
1105
+ });
1106
+ if (result.bestMatch) {
1107
+ bestStart = result.bestMatch.start;
1108
+ bestEnd = result.bestMatch.end;
1109
+ bestDepth = result.bestMatch.depth;
1110
+ }
1111
+ i = result.newPos;
1112
+ depth = result.newDepth;
681
1113
  }
682
1114
  if (bestStart !== -1) {
683
1115
  return xmlContent.slice(bestStart, bestEnd);
684
1116
  }
685
- return void 0;
1117
+ return;
1118
+ }
1119
+ function processOpeningTag(options) {
1120
+ const { xmlContent, tagEnd, isSelfClosing, target, len, ranges } = options;
1121
+ const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1122
+ if (isSelfClosing) {
1123
+ ranges.push({ start: contentStart, end: contentStart });
1124
+ return contentStart;
1125
+ }
1126
+ const closePos = findMatchingCloseTag(xmlContent, contentStart, target, len);
1127
+ if (closePos !== -1) {
1128
+ ranges.push({ start: contentStart, end: closePos });
1129
+ const gt = xmlContent.indexOf(">", closePos);
1130
+ return gt === -1 ? len : gt + 1;
1131
+ }
1132
+ return -1;
1133
+ }
1134
+ function handleClosingTagInFindAll(xmlContent, i, len) {
1135
+ const gt = xmlContent.indexOf(">", i + 1);
1136
+ return gt === -1 ? len : gt + 1;
686
1137
  }
687
1138
  function findAllInnerRanges(xmlContent, tagName) {
688
1139
  const len = xmlContent.length;
@@ -691,148 +1142,62 @@ function findAllInnerRanges(xmlContent, tagName) {
691
1142
  let i = 0;
692
1143
  while (i < len) {
693
1144
  const lt = xmlContent.indexOf("<", i);
694
- if (lt === -1) break;
1145
+ if (lt === -1 || lt + 1 >= len) {
1146
+ break;
1147
+ }
695
1148
  i = lt + 1;
696
- if (i >= len) break;
697
1149
  const ch = xmlContent[i];
698
- if (ch === "!") {
699
- if (xmlContent.startsWith("!--", i + 1)) {
700
- const close = xmlContent.indexOf("-->", i + 4);
701
- i = close === -1 ? len : close + 3;
702
- continue;
703
- }
704
- if (xmlContent.startsWith("![CDATA[", i + 1)) {
705
- const close = xmlContent.indexOf("]]>", i + 9);
706
- i = close === -1 ? len : close + 3;
707
- continue;
708
- }
709
- const gt = xmlContent.indexOf(">", i + 1);
710
- i = gt === -1 ? len : gt + 1;
711
- continue;
712
- }
713
- if (ch === "?") {
714
- const close = xmlContent.indexOf("?>", i + 1);
715
- i = close === -1 ? len : close + 2;
1150
+ const specialPos = skipSpecialConstruct(xmlContent, i, len);
1151
+ if (specialPos !== -1) {
1152
+ i = specialPos;
716
1153
  continue;
717
1154
  }
718
1155
  if (ch === "/") {
719
- const gt = xmlContent.indexOf(">", i + 1);
720
- i = gt === -1 ? len : gt + 1;
1156
+ i = handleClosingTagInFindAll(xmlContent, i, len);
721
1157
  continue;
722
1158
  }
723
- let j = i;
724
- if (j < len && isNameStartChar(xmlContent[j])) {
725
- j++;
726
- while (j < len && isNameChar(xmlContent[j])) j++;
727
- }
728
- const name = xmlContent.slice(i, j);
729
- let k = j;
730
- let isSelfClosing = false;
731
- while (k < len) {
732
- const c = xmlContent[k];
733
- if (c === '"' || c === "'") {
734
- k = skipQuoted(xmlContent, k);
735
- continue;
736
- }
737
- if (c === ">") break;
738
- if (c === "/" && xmlContent[k + 1] === ">") {
739
- isSelfClosing = true;
740
- k++;
741
- break;
742
- }
743
- k++;
744
- }
745
- const tagEnd = k;
746
- if (name !== target) {
1159
+ const tagInfo = parseTagName(xmlContent, i, len);
1160
+ const tagEndInfo = skipToTagEnd(xmlContent, tagInfo.pos, len);
1161
+ const tagEnd = tagEndInfo.pos;
1162
+ const isSelfClosing = tagEndInfo.isSelfClosing;
1163
+ if (tagInfo.name !== target) {
747
1164
  i = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
748
1165
  continue;
749
1166
  }
750
- const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
751
- if (isSelfClosing) {
752
- ranges.push({ start: contentStart, end: contentStart });
753
- i = contentStart;
754
- continue;
755
- }
756
- let pos = contentStart;
757
- let sameDepth = 1;
758
- while (pos < len) {
759
- const nextLt = xmlContent.indexOf("<", pos);
760
- if (nextLt === -1) break;
761
- const nx = nextLt + 1;
762
- if (nx >= len) break;
763
- const h = xmlContent[nx];
764
- if (h === "!") {
765
- if (xmlContent.startsWith("!--", nx + 1)) {
766
- const close = xmlContent.indexOf("-->", nx + 4);
767
- pos = close === -1 ? len : close + 3;
768
- continue;
769
- }
770
- if (xmlContent.startsWith("![CDATA[", nx + 1)) {
771
- const close = xmlContent.indexOf("]]>", nx + 9);
772
- pos = close === -1 ? len : close + 3;
773
- continue;
774
- }
775
- const gt2 = xmlContent.indexOf(">", nx + 1);
776
- pos = gt2 === -1 ? len : gt2 + 1;
777
- continue;
778
- } else if (h === "?") {
779
- const close = xmlContent.indexOf("?>", nx + 1);
780
- pos = close === -1 ? len : close + 2;
781
- continue;
782
- } else if (h === "/") {
783
- let t = nx + 1;
784
- if (t < len && isNameStartChar(xmlContent[t])) {
785
- t++;
786
- while (t < len && isNameChar(xmlContent[t])) t++;
787
- }
788
- const endName = xmlContent.slice(nx + 1, t);
789
- const gt2 = xmlContent.indexOf(">", t);
790
- if (endName === target) {
791
- sameDepth--;
792
- if (sameDepth === 0) {
793
- ranges.push({ start: contentStart, end: nextLt });
794
- i = gt2 === -1 ? len : gt2 + 1;
795
- break;
796
- }
797
- }
798
- pos = gt2 === -1 ? len : gt2 + 1;
799
- continue;
800
- } else {
801
- let t = nx;
802
- if (t < len && isNameStartChar(xmlContent[t])) {
803
- t++;
804
- while (t < len && isNameChar(xmlContent[t])) t++;
805
- }
806
- let u = t;
807
- let isSelfClosingNested = false;
808
- while (u < len) {
809
- const cu = xmlContent[u];
810
- if (cu === '"' || cu === "'") {
811
- u = skipQuoted(xmlContent, u);
812
- continue;
813
- }
814
- if (cu === ">") break;
815
- if (cu === "/" && xmlContent[u + 1] === ">") {
816
- isSelfClosingNested = true;
817
- u++;
818
- break;
819
- }
820
- u++;
821
- }
822
- const startName = xmlContent.slice(nx, t);
823
- if (startName === target && !isSelfClosingNested) {
824
- sameDepth++;
825
- }
826
- pos = xmlContent[u] === ">" ? u + 1 : u + 1;
827
- continue;
828
- }
829
- }
830
- if (sameDepth !== 0) {
1167
+ const nextPos = processOpeningTag({
1168
+ xmlContent,
1169
+ tagEnd,
1170
+ isSelfClosing,
1171
+ target,
1172
+ len,
1173
+ ranges
1174
+ });
1175
+ if (nextPos === -1) {
831
1176
  break;
832
1177
  }
1178
+ i = nextPos;
833
1179
  }
834
1180
  return ranges;
835
1181
  }
1182
+ function findTopLevelTargetRange(options) {
1183
+ const { xmlContent, tagEnd, isSelfClosing, target, len } = options;
1184
+ const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1185
+ if (isSelfClosing) {
1186
+ return { start: contentStart, end: contentStart };
1187
+ }
1188
+ const closePos = findMatchingCloseTag(xmlContent, contentStart, target, len);
1189
+ if (closePos !== -1) {
1190
+ return { start: contentStart, end: closePos };
1191
+ }
1192
+ return;
1193
+ }
1194
+ function handleClosingTagInFindFirst(xmlContent, i, len, depth) {
1195
+ const gt = xmlContent.indexOf(">", i + 1);
1196
+ return {
1197
+ newPos: gt === -1 ? len : gt + 1,
1198
+ newDepth: Math.max(0, depth - 1)
1199
+ };
1200
+ }
836
1201
  function findFirstTopLevelRange(xmlContent, tagName) {
837
1202
  const len = xmlContent.length;
838
1203
  const target = tagName;
@@ -840,150 +1205,108 @@ function findFirstTopLevelRange(xmlContent, tagName) {
840
1205
  let depth = 0;
841
1206
  while (i < len) {
842
1207
  const lt = xmlContent.indexOf("<", i);
843
- if (lt === -1) return void 0;
1208
+ if (lt === -1 || lt + 1 >= len) {
1209
+ return;
1210
+ }
844
1211
  i = lt + 1;
845
- if (i >= len) return void 0;
846
1212
  const ch = xmlContent[i];
847
- if (ch === "!") {
848
- if (xmlContent.startsWith("!DOCTYPE", i + 1)) {
849
- const gt2 = xmlContent.indexOf(">", i + 1);
850
- i = gt2 === -1 ? len : gt2 + 1;
851
- continue;
852
- }
853
- if (xmlContent.startsWith("!--", i + 1)) {
854
- const close = xmlContent.indexOf("-->", i + 4);
855
- i = close === -1 ? len : close + 3;
856
- continue;
857
- }
858
- if (xmlContent.startsWith("![CDATA[", i + 1)) {
859
- const close = xmlContent.indexOf("]]>", i + 9);
860
- i = close === -1 ? len : close + 3;
861
- continue;
862
- }
863
- const gt = xmlContent.indexOf(">", i + 1);
864
- i = gt === -1 ? len : gt + 1;
865
- continue;
866
- } else if (ch === "?") {
867
- const close = xmlContent.indexOf("?>", i + 1);
868
- i = close === -1 ? len : close + 2;
1213
+ const specialPos = skipSpecialConstruct(xmlContent, i, len);
1214
+ if (specialPos !== -1) {
1215
+ i = specialPos;
869
1216
  continue;
870
- } else if (ch === "/") {
871
- const gt = xmlContent.indexOf(">", i + 1);
872
- i = gt === -1 ? len : gt + 1;
873
- depth = Math.max(0, depth - 1);
1217
+ }
1218
+ if (ch === "/") {
1219
+ const result = handleClosingTagInFindFirst(xmlContent, i, len, depth);
1220
+ i = result.newPos;
1221
+ depth = result.newDepth;
874
1222
  continue;
875
- } else {
876
- let j = i;
877
- if (j < len && isNameStartChar(xmlContent[j])) {
878
- j++;
879
- while (j < len && isNameChar(xmlContent[j])) j++;
880
- }
881
- const name = xmlContent.slice(i, j);
882
- let k = j;
883
- let isSelfClosing = false;
884
- while (k < len) {
885
- const c = xmlContent[k];
886
- if (c === '"' || c === "'") {
887
- k = skipQuoted(xmlContent, k);
888
- continue;
889
- }
890
- if (c === ">") break;
891
- if (c === "/" && xmlContent[k + 1] === ">") {
892
- isSelfClosing = true;
893
- k++;
894
- break;
895
- }
896
- k++;
897
- }
898
- const tagEnd = k;
899
- if (depth === 0 && name === target) {
900
- const contentStart = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
901
- if (isSelfClosing) return { start: contentStart, end: contentStart };
902
- let pos = contentStart;
903
- let sameDepth = 1;
904
- while (pos < len) {
905
- const nextLt = xmlContent.indexOf("<", pos);
906
- if (nextLt === -1) break;
907
- const nx = nextLt + 1;
908
- if (nx >= len) break;
909
- const h = xmlContent[nx];
910
- if (h === "!") {
911
- if (xmlContent.startsWith("!DOCTYPE", nx + 1)) {
912
- const gt22 = xmlContent.indexOf(">", nx + 1);
913
- pos = gt22 === -1 ? len : gt22 + 1;
914
- continue;
915
- }
916
- if (xmlContent.startsWith("!--", nx + 1)) {
917
- const close = xmlContent.indexOf("-->", nx + 4);
918
- pos = close === -1 ? len : close + 3;
919
- continue;
920
- }
921
- if (xmlContent.startsWith("![CDATA[", nx + 1)) {
922
- const close = xmlContent.indexOf("]]>", nx + 9);
923
- pos = close === -1 ? len : close + 3;
924
- continue;
925
- }
926
- const gt2 = xmlContent.indexOf(">", nx + 1);
927
- pos = gt2 === -1 ? len : gt2 + 1;
928
- continue;
929
- } else if (h === "?") {
930
- const close = xmlContent.indexOf("?>", nx + 1);
931
- pos = close === -1 ? len : close + 2;
932
- continue;
933
- } else if (h === "/") {
934
- let t = nx + 1;
935
- if (t < len && isNameStartChar(xmlContent[t])) {
936
- t++;
937
- while (t < len && isNameChar(xmlContent[t])) t++;
938
- }
939
- const endName = xmlContent.slice(nx + 1, t);
940
- const gt2 = xmlContent.indexOf(">", t);
941
- if (endName === target) {
942
- sameDepth--;
943
- if (sameDepth === 0) {
944
- return { start: contentStart, end: nextLt };
945
- }
946
- }
947
- pos = gt2 === -1 ? len : gt2 + 1;
948
- continue;
949
- } else {
950
- let t = nx;
951
- if (t < len && isNameStartChar(xmlContent[t])) {
952
- t++;
953
- while (t < len && isNameChar(xmlContent[t])) t++;
954
- }
955
- const startName = xmlContent.slice(nx, t);
956
- let u = t;
957
- let isSelfClosingNested = false;
958
- while (u < len) {
959
- const cu = xmlContent[u];
960
- if (cu === '"' || cu === "'") {
961
- u = skipQuoted(xmlContent, u);
962
- continue;
963
- }
964
- if (cu === ">") break;
965
- if (cu === "/" && xmlContent[u + 1] === ">") {
966
- isSelfClosingNested = true;
967
- u++;
968
- break;
969
- }
970
- u++;
971
- }
972
- if (startName === target && !isSelfClosingNested) {
973
- sameDepth++;
974
- }
975
- pos = xmlContent[u] === ">" ? u + 1 : u + 1;
976
- continue;
977
- }
978
- }
979
- return void 0;
980
- }
981
- i = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
982
- depth += isSelfClosing ? 0 : 1;
1223
+ }
1224
+ const tagInfo = parseTagName(xmlContent, i, len);
1225
+ const tagEndInfo = skipToTagEnd(xmlContent, tagInfo.pos, len);
1226
+ const tagEnd = tagEndInfo.pos;
1227
+ const isSelfClosing = tagEndInfo.isSelfClosing;
1228
+ if (depth === 0 && tagInfo.name === target) {
1229
+ return findTopLevelTargetRange({
1230
+ xmlContent,
1231
+ tagEnd,
1232
+ isSelfClosing,
1233
+ target,
1234
+ len
1235
+ });
1236
+ }
1237
+ i = xmlContent[tagEnd] === ">" ? tagEnd + 1 : tagEnd + 1;
1238
+ depth += isSelfClosing ? 0 : 1;
1239
+ }
1240
+ return;
1241
+ }
1242
+ function isPositionExcluded(pos, excludeRanges) {
1243
+ if (!excludeRanges || excludeRanges.length === 0) {
1244
+ return false;
1245
+ }
1246
+ for (const r of excludeRanges) {
1247
+ if (pos >= r.start && pos < r.end) {
1248
+ return true;
1249
+ }
1250
+ }
1251
+ return false;
1252
+ }
1253
+ function skipCommentInCounting(xmlContent, i, len) {
1254
+ const close = xmlContent.indexOf("-->", i + 4);
1255
+ return close === -1 ? len : close + 3;
1256
+ }
1257
+ function skipCdataInCounting(xmlContent, i, len) {
1258
+ const close = xmlContent.indexOf("]]>", i + 9);
1259
+ return close === -1 ? len : close + 3;
1260
+ }
1261
+ function skipSpecialInCounting(xmlContent, ch, i, len) {
1262
+ if (ch === "!") {
1263
+ if (xmlContent.startsWith("!--", i + 1)) {
1264
+ return skipCommentInCounting(xmlContent, i, len);
1265
+ }
1266
+ if (xmlContent.startsWith("![CDATA[", i + 1)) {
1267
+ return skipCdataInCounting(xmlContent, i, len);
1268
+ }
1269
+ const gt = xmlContent.indexOf(">", i + 1);
1270
+ return gt === -1 ? len : gt + 1;
1271
+ }
1272
+ if (ch === "?") {
1273
+ const close = xmlContent.indexOf("?>", i + 1);
1274
+ return close === -1 ? len : close + 2;
1275
+ }
1276
+ if (ch === "/") {
1277
+ const gt = xmlContent.indexOf(">", i + 1);
1278
+ return gt === -1 ? len : gt + 1;
1279
+ }
1280
+ return -1;
1281
+ }
1282
+ function parseAndCountTag(options) {
1283
+ const { xmlContent, i, len, target, lt, excludeRanges } = options;
1284
+ let j = i;
1285
+ if (j < len && isNameStartChar(xmlContent[j])) {
1286
+ j += 1;
1287
+ while (j < len && isNameChar(xmlContent[j])) {
1288
+ j += 1;
1289
+ }
1290
+ }
1291
+ const name = xmlContent.slice(i, j);
1292
+ let k = j;
1293
+ while (k < len) {
1294
+ const c = xmlContent[k];
1295
+ if (c === '"' || c === "'") {
1296
+ k = skipQuoted(xmlContent, k);
983
1297
  continue;
984
1298
  }
1299
+ if (c === ">") {
1300
+ break;
1301
+ }
1302
+ if (c === "/" && xmlContent[k + 1] === ">") {
1303
+ k += 1;
1304
+ break;
1305
+ }
1306
+ k += 1;
985
1307
  }
986
- return void 0;
1308
+ const shouldCount = name === target && !isPositionExcluded(lt, excludeRanges);
1309
+ return { nextPos: k + 1, shouldCount };
987
1310
  }
988
1311
  function countTagOccurrences(xmlContent, tagName, excludeRanges, shouldSkipFirst = true) {
989
1312
  const len = xmlContent.length;
@@ -991,74 +1314,146 @@ function countTagOccurrences(xmlContent, tagName, excludeRanges, shouldSkipFirst
991
1314
  let i = 0;
992
1315
  let count = 0;
993
1316
  let skipFirstLocal = shouldSkipFirst;
994
- const isExcluded = (pos) => {
995
- if (!excludeRanges || excludeRanges.length === 0) return false;
996
- for (const r of excludeRanges) {
997
- if (pos >= r.start && pos < r.end) return true;
998
- }
999
- return false;
1000
- };
1001
1317
  while (i < len) {
1002
1318
  const lt = xmlContent.indexOf("<", i);
1003
- if (lt === -1) break;
1319
+ if (lt === -1) {
1320
+ break;
1321
+ }
1004
1322
  i = lt + 1;
1005
- if (i >= len) break;
1323
+ if (i >= len) {
1324
+ break;
1325
+ }
1006
1326
  const ch = xmlContent[i];
1007
- if (ch === "!") {
1008
- if (xmlContent.startsWith("!--", i + 1)) {
1009
- const close = xmlContent.indexOf("-->", i + 4);
1010
- i = close === -1 ? len : close + 3;
1011
- continue;
1012
- }
1013
- if (xmlContent.startsWith("![CDATA[", i + 1)) {
1014
- const close = xmlContent.indexOf("]]>", i + 9);
1015
- i = close === -1 ? len : close + 3;
1016
- continue;
1017
- }
1018
- const gt = xmlContent.indexOf(">", i + 1);
1019
- i = gt === -1 ? len : gt + 1;
1327
+ const skipPos = skipSpecialInCounting(xmlContent, ch, i, len);
1328
+ if (skipPos !== -1) {
1329
+ i = skipPos;
1020
1330
  continue;
1021
- } else if (ch === "?") {
1022
- const close = xmlContent.indexOf("?>", i + 1);
1023
- i = close === -1 ? len : close + 2;
1024
- continue;
1025
- } else if (ch === "/") {
1026
- const gt = xmlContent.indexOf(">", i + 1);
1027
- i = gt === -1 ? len : gt + 1;
1028
- continue;
1029
- } else {
1030
- let j = i;
1031
- if (j < len && isNameStartChar(xmlContent[j])) {
1032
- j++;
1033
- while (j < len && isNameChar(xmlContent[j])) j++;
1034
- }
1035
- const name = xmlContent.slice(i, j);
1036
- let k = j;
1037
- while (k < len) {
1038
- const c = xmlContent[k];
1039
- if (c === '"' || c === "'") {
1040
- k = skipQuoted(xmlContent, k);
1041
- continue;
1042
- }
1043
- if (c === ">") break;
1044
- if (c === "/" && xmlContent[k + 1] === ">") {
1045
- k++;
1046
- break;
1047
- }
1048
- k++;
1049
- }
1050
- if (name === target && !isExcluded(lt)) {
1051
- if (skipFirstLocal) {
1052
- skipFirstLocal = false;
1053
- } else {
1054
- count++;
1055
- }
1331
+ }
1332
+ const result = parseAndCountTag({
1333
+ xmlContent,
1334
+ i,
1335
+ len,
1336
+ target,
1337
+ lt,
1338
+ excludeRanges
1339
+ });
1340
+ if (result.shouldCount) {
1341
+ if (skipFirstLocal) {
1342
+ skipFirstLocal = false;
1343
+ } else {
1344
+ count += 1;
1056
1345
  }
1057
- i = k + 1;
1346
+ }
1347
+ i = result.nextPos;
1348
+ }
1349
+ return count;
1350
+ }
1351
+ function skipAttributes(xmlContent, i, len) {
1352
+ let k = i;
1353
+ while (k < len && xmlContent[k] !== ">") {
1354
+ const c = xmlContent[k];
1355
+ if (c === '"' || c === "'") {
1356
+ k = skipQuoted(xmlContent, k);
1058
1357
  continue;
1059
1358
  }
1359
+ if (c === "/" && xmlContent[k + 1] === ">") {
1360
+ k += 1;
1361
+ break;
1362
+ }
1363
+ k += 1;
1364
+ }
1365
+ return k;
1366
+ }
1367
+ function updateDepthForClosingTag(xmlContent, nextLt, target, closeDepth) {
1368
+ const { name: closeName } = parseName(xmlContent, nextLt + 2);
1369
+ return closeName === target ? closeDepth - 1 : closeDepth;
1370
+ }
1371
+ function updateDepthForOpeningTag(xmlContent, nextLt, target, closeDepth) {
1372
+ const { name: openName } = parseName(xmlContent, nextLt + 1);
1373
+ return openName === target ? closeDepth + 1 : closeDepth;
1374
+ }
1375
+ function findClosingTagForRange(xmlContent, k, len, target) {
1376
+ let closeDepth = 1;
1377
+ let j = k + 1;
1378
+ while (j < len && closeDepth > 0) {
1379
+ const nextLt = xmlContent.indexOf("<", j);
1380
+ if (nextLt === -1) {
1381
+ break;
1382
+ }
1383
+ if (xmlContent[nextLt + 1] === "/") {
1384
+ closeDepth = updateDepthForClosingTag(
1385
+ xmlContent,
1386
+ nextLt,
1387
+ target,
1388
+ closeDepth
1389
+ );
1390
+ } else if (xmlContent[nextLt + 1] !== "!" && xmlContent[nextLt + 1] !== "?") {
1391
+ closeDepth = updateDepthForOpeningTag(
1392
+ xmlContent,
1393
+ nextLt,
1394
+ target,
1395
+ closeDepth
1396
+ );
1397
+ }
1398
+ j = xmlContent.indexOf(">", nextLt + 1);
1399
+ if (j === -1) {
1400
+ break;
1401
+ }
1402
+ j += 1;
1060
1403
  }
1061
- return count;
1404
+ return closeDepth === 0 ? j : -1;
1405
+ }
1406
+ function processTopLevelTarget(options) {
1407
+ const { xmlContent, tagStart, k, len, target, ranges } = options;
1408
+ const isSelfClosing = xmlContent[k] === "/" || xmlContent.startsWith("/>", k);
1409
+ if (isSelfClosing) {
1410
+ ranges.push({
1411
+ start: tagStart,
1412
+ end: k + (xmlContent[k] === "/" ? 2 : 1)
1413
+ });
1414
+ return { newDepth: 0 };
1415
+ }
1416
+ const endPos = findClosingTagForRange(xmlContent, k, len, target);
1417
+ if (endPos !== -1) {
1418
+ ranges.push({ start: tagStart, end: endPos });
1419
+ }
1420
+ return { newDepth: 0 };
1421
+ }
1422
+ function skipDoctypeInSpecial(xmlContent, i, len) {
1423
+ const gt = xmlContent.indexOf(">", i + 1);
1424
+ return gt === -1 ? len : gt + 1;
1425
+ }
1426
+ function handleSpecialConstructs(xmlContent, ch, i, len) {
1427
+ if (ch === "!") {
1428
+ if (xmlContent.startsWith("!DOCTYPE", i + 1)) {
1429
+ return skipDoctypeInSpecial(xmlContent, i, len);
1430
+ }
1431
+ if (xmlContent.startsWith("!--", i + 1)) {
1432
+ return skipCommentInCounting(xmlContent, i, len);
1433
+ }
1434
+ if (xmlContent.startsWith("![CDATA[", i + 1)) {
1435
+ return skipCdataInCounting(xmlContent, i, len);
1436
+ }
1437
+ const gt = xmlContent.indexOf(">", i + 1);
1438
+ return gt === -1 ? len : gt + 1;
1439
+ }
1440
+ if (ch === "?") {
1441
+ const close = xmlContent.indexOf("?>", i + 1);
1442
+ return close === -1 ? len : close + 2;
1443
+ }
1444
+ return -1;
1445
+ }
1446
+ function handleClosingTagInFindAllTop(xmlContent, i, target, depth) {
1447
+ const { name: closingName, newPos: closingPos } = parseName(
1448
+ xmlContent,
1449
+ i + 1
1450
+ );
1451
+ const newDepth = closingName === target ? depth - 1 : depth;
1452
+ const gt = xmlContent.indexOf(">", closingPos);
1453
+ return {
1454
+ newPos: gt === -1 ? -1 : gt + 1,
1455
+ newDepth
1456
+ };
1062
1457
  }
1063
1458
  function findAllTopLevelRanges(xmlContent, tagName) {
1064
1459
  const ranges = [];
@@ -1068,88 +1463,39 @@ function findAllTopLevelRanges(xmlContent, tagName) {
1068
1463
  let depth = 0;
1069
1464
  while (i < len) {
1070
1465
  const lt = xmlContent.indexOf("<", i);
1071
- if (lt === -1) break;
1466
+ if (lt === -1 || lt + 1 >= len) {
1467
+ break;
1468
+ }
1072
1469
  i = lt + 1;
1073
- if (i >= len) break;
1074
1470
  const ch = xmlContent[i];
1075
- if (ch === "!") {
1076
- if (xmlContent.startsWith("!DOCTYPE", i + 1)) {
1077
- const gt2 = xmlContent.indexOf(">", i + 1);
1078
- i = gt2 === -1 ? len : gt2 + 1;
1079
- continue;
1080
- }
1081
- if (xmlContent.startsWith("!--", i + 1)) {
1082
- const close = xmlContent.indexOf("-->", i + 4);
1083
- i = close === -1 ? len : close + 3;
1084
- continue;
1085
- }
1086
- if (xmlContent.startsWith("![CDATA[", i + 1)) {
1087
- const close = xmlContent.indexOf("]]>", i + 9);
1088
- i = close === -1 ? len : close + 3;
1089
- continue;
1090
- }
1091
- const gt = xmlContent.indexOf(">", i + 1);
1092
- i = gt === -1 ? len : gt + 1;
1093
- continue;
1094
- } else if (ch === "?") {
1095
- const close = xmlContent.indexOf("?>", i + 1);
1096
- i = close === -1 ? len : close + 2;
1097
- continue;
1098
- } else if (ch === "/") {
1099
- i++;
1100
- const { name: name2, newPos: newPos2 } = parseName(xmlContent, i);
1101
- if (name2 === target) depth--;
1102
- i = xmlContent.indexOf(">", newPos2);
1103
- if (i === -1) break;
1104
- i++;
1471
+ const specialPos = handleSpecialConstructs(xmlContent, ch, i, len);
1472
+ if (specialPos !== -1) {
1473
+ i = specialPos;
1105
1474
  continue;
1106
1475
  }
1107
- const { name, newPos } = parseName(xmlContent, i);
1108
- i = newPos;
1109
- let k = i;
1110
- while (k < len && xmlContent[k] !== ">") {
1111
- const c = xmlContent[k];
1112
- if (c === '"' || c === "'") {
1113
- k = skipQuoted(xmlContent, k);
1114
- continue;
1115
- }
1116
- if (c === "/" && xmlContent[k + 1] === ">") {
1117
- k++;
1476
+ if (ch === "/") {
1477
+ const result = handleClosingTagInFindAllTop(xmlContent, i, target, depth);
1478
+ if (result.newPos === -1) {
1118
1479
  break;
1119
1480
  }
1120
- k++;
1481
+ i = result.newPos;
1482
+ depth = result.newDepth;
1483
+ continue;
1121
1484
  }
1485
+ const { name, newPos } = parseName(xmlContent, i);
1486
+ i = newPos;
1487
+ const k = skipAttributes(xmlContent, i, len);
1122
1488
  if (name === target && depth === 0) {
1123
- const tagStart = lt;
1124
- const isSelfClosing = xmlContent[k] === "/" || xmlContent.startsWith("/>", k);
1125
- if (isSelfClosing) {
1126
- ranges.push({
1127
- start: tagStart,
1128
- end: k + (xmlContent[k] === "/" ? 2 : 1)
1129
- });
1130
- } else {
1131
- depth++;
1132
- let closeDepth = 1;
1133
- let j = k + 1;
1134
- while (j < len && closeDepth > 0) {
1135
- const nextLt = xmlContent.indexOf("<", j);
1136
- if (nextLt === -1) break;
1137
- if (xmlContent[nextLt + 1] === "/") {
1138
- const { name: closeName } = parseName(xmlContent, nextLt + 2);
1139
- if (closeName === target) closeDepth--;
1140
- } else if (xmlContent[nextLt + 1] !== "!" && xmlContent[nextLt + 1] !== "?") {
1141
- const { name: openName } = parseName(xmlContent, nextLt + 1);
1142
- if (openName === target) closeDepth++;
1143
- }
1144
- j = xmlContent.indexOf(">", nextLt + 1);
1145
- if (j === -1) break;
1146
- j++;
1147
- }
1148
- if (closeDepth === 0) {
1149
- ranges.push({ start: tagStart, end: j });
1150
- }
1151
- depth--;
1152
- }
1489
+ depth += 1;
1490
+ const result = processTopLevelTarget({
1491
+ xmlContent,
1492
+ tagStart: lt,
1493
+ k,
1494
+ len,
1495
+ target,
1496
+ ranges
1497
+ });
1498
+ depth += result.newDepth;
1153
1499
  }
1154
1500
  i = k + 1;
1155
1501
  }
@@ -1172,57 +1518,30 @@ var XMLTokenizer = class {
1172
1518
  this.pos = options.pos || 0;
1173
1519
  }
1174
1520
  /**
1175
- * Parse XML children recursively
1521
+ * Handle closing tag parsing
1176
1522
  */
1177
- parseChildren(tagName) {
1178
- const children = [];
1179
- let consumedToEnd = false;
1180
- while (this.xmlString[this.pos]) {
1181
- if (this.xmlString.charCodeAt(this.pos) === CharCodes.OPEN_BRACKET) {
1182
- if (this.xmlString.charCodeAt(this.pos + 1) === CharCodes.SLASH) {
1183
- const closeStart = this.pos + 2;
1184
- this.pos = this.xmlString.indexOf(">", this.pos);
1185
- const closeTag = this.xmlString.substring(closeStart, this.pos);
1186
- if (tagName && closeTag.trim() !== tagName) {
1187
- const { line, column } = getLineColumn(this.xmlString, this.pos);
1188
- throw new RXMLParseError(
1189
- `Unexpected close tag at line ${line}, column ${column}. Expected </${tagName}>, found </${closeTag}>`,
1190
- void 0,
1191
- line,
1192
- column
1193
- );
1194
- }
1195
- if (this.pos !== -1) this.pos += 1;
1196
- return children;
1197
- } else if (this.xmlString.charCodeAt(this.pos + 1) === CharCodes.EXCLAMATION) {
1198
- const prevPos = this.pos;
1199
- this.handleSpecialContent(children);
1200
- if (this.pos >= this.xmlString.length && prevPos < this.xmlString.length) {
1201
- consumedToEnd = true;
1202
- }
1203
- } else {
1204
- const node = this.parseNode();
1205
- children.push(node);
1206
- if (node.tagName[0] === "?") {
1207
- children.push(...node.children);
1208
- node.children = [];
1209
- }
1210
- }
1211
- } else {
1212
- const text = this.parseText();
1213
- if (this.options.keepWhitespace) {
1214
- if (text.length > 0) {
1215
- children.push(text);
1216
- }
1217
- } else {
1218
- const trimmed = text.trim();
1219
- if (trimmed.length > 0) {
1220
- children.push(trimmed);
1221
- }
1222
- }
1223
- this.pos++;
1224
- }
1523
+ handleClosingTag(tagName, children) {
1524
+ const closeStart = this.pos + 2;
1525
+ this.pos = this.xmlString.indexOf(">", this.pos);
1526
+ const closeTag = this.xmlString.substring(closeStart, this.pos);
1527
+ if (tagName && closeTag.trim() !== tagName) {
1528
+ const { line, column } = getLineColumn(this.xmlString, this.pos);
1529
+ throw new RXMLParseError(
1530
+ `Unexpected close tag at line ${line}, column ${column}. Expected </${tagName}>, found </${closeTag}>`,
1531
+ void 0,
1532
+ line,
1533
+ column
1534
+ );
1535
+ }
1536
+ if (this.pos !== -1) {
1537
+ this.pos += 1;
1225
1538
  }
1539
+ return children;
1540
+ }
1541
+ /**
1542
+ * Check if we're at end of string and should throw unclosed tag error
1543
+ */
1544
+ checkUnclosedTag(tagName, consumedToEnd) {
1226
1545
  if (tagName && this.pos >= this.xmlString.length && !consumedToEnd) {
1227
1546
  const { line, column } = getLineColumn(this.xmlString, this.pos - 1);
1228
1547
  throw new RXMLParseError(
@@ -1232,93 +1551,205 @@ var XMLTokenizer = class {
1232
1551
  column
1233
1552
  );
1234
1553
  }
1554
+ }
1555
+ /**
1556
+ * Process special content (comments, CDATA, DOCTYPE) and track if we consumed to end
1557
+ */
1558
+ processSpecialContent(children) {
1559
+ const prevPos = this.pos;
1560
+ this.handleSpecialContent(children);
1561
+ return this.pos >= this.xmlString.length && prevPos < this.xmlString.length;
1562
+ }
1563
+ /**
1564
+ * Handle text content parsing
1565
+ */
1566
+ handleTextContent(children) {
1567
+ const text = this.parseText();
1568
+ if (this.options.keepWhitespace) {
1569
+ if (text.length > 0) {
1570
+ children.push(text);
1571
+ }
1572
+ } else {
1573
+ const trimmed = text.trim();
1574
+ if (trimmed.length > 0) {
1575
+ children.push(trimmed);
1576
+ }
1577
+ }
1578
+ this.pos += 1;
1579
+ }
1580
+ /**
1581
+ * Handle regular element parsing
1582
+ */
1583
+ handleRegularElement(children) {
1584
+ const node = this.parseNode();
1585
+ children.push(node);
1586
+ if (node.tagName[0] === "?") {
1587
+ children.push(...node.children);
1588
+ node.children = [];
1589
+ }
1590
+ }
1591
+ /**
1592
+ * Process a single child element based on the current character
1593
+ */
1594
+ processSingleChild(children, tagName) {
1595
+ if (this.xmlString.charCodeAt(this.pos) !== CharCodes.OPEN_BRACKET) {
1596
+ this.handleTextContent(children);
1597
+ return { shouldReturn: false, consumedToEnd: false };
1598
+ }
1599
+ const nextChar = this.xmlString.charCodeAt(this.pos + 1);
1600
+ if (nextChar === CharCodes.SLASH) {
1601
+ const result = this.handleClosingTag(tagName, children);
1602
+ if (result !== null) {
1603
+ return { shouldReturn: true, consumedToEnd: false };
1604
+ }
1605
+ return { shouldReturn: false, consumedToEnd: false };
1606
+ }
1607
+ if (nextChar === CharCodes.EXCLAMATION) {
1608
+ const wasConsumedToEnd = this.processSpecialContent(children);
1609
+ return { shouldReturn: false, consumedToEnd: wasConsumedToEnd };
1610
+ }
1611
+ this.handleRegularElement(children);
1612
+ return { shouldReturn: false, consumedToEnd: false };
1613
+ }
1614
+ /**
1615
+ * Parse XML children recursively
1616
+ */
1617
+ parseChildren(tagName) {
1618
+ const children = [];
1619
+ let consumedToEnd = false;
1620
+ while (this.xmlString[this.pos]) {
1621
+ const result = this.processSingleChild(children, tagName);
1622
+ if (result.shouldReturn) {
1623
+ return children;
1624
+ }
1625
+ if (result.consumedToEnd) {
1626
+ consumedToEnd = true;
1627
+ }
1628
+ }
1629
+ this.checkUnclosedTag(tagName, consumedToEnd);
1235
1630
  return children;
1236
1631
  }
1237
1632
  /**
1238
- * Parse a single XML node
1633
+ * Check if character is whitespace
1239
1634
  */
1240
- parseNode() {
1241
- var _a;
1242
- this.pos++;
1243
- const { name: tagName, newPos } = parseName(this.xmlString, this.pos);
1244
- this.pos = newPos;
1635
+ isWhitespace(code) {
1636
+ return code === CharCodes.SPACE || code === CharCodes.TAB || code === CharCodes.NEWLINE || code === CharCodes.CARRIAGE_RETURN;
1637
+ }
1638
+ /**
1639
+ * Skip whitespace characters
1640
+ */
1641
+ skipWhitespace() {
1642
+ while (this.pos < this.xmlString.length && this.isWhitespace(this.xmlString.charCodeAt(this.pos))) {
1643
+ this.pos += 1;
1644
+ }
1645
+ }
1646
+ /**
1647
+ * Parse attribute value
1648
+ */
1649
+ parseAttributeValue() {
1650
+ if (this.pos >= this.xmlString.length || this.xmlString[this.pos] !== "=") {
1651
+ return null;
1652
+ }
1653
+ this.pos += 1;
1654
+ this.skipWhitespace();
1655
+ const code = this.xmlString.charCodeAt(this.pos);
1656
+ if (code === CharCodes.SINGLE_QUOTE || code === CharCodes.DOUBLE_QUOTE) {
1657
+ const { value: parsedValue, newPos: valueEnd } = parseString(
1658
+ this.xmlString,
1659
+ this.pos
1660
+ );
1661
+ this.pos = valueEnd;
1662
+ return parsedValue;
1663
+ }
1664
+ return null;
1665
+ }
1666
+ /**
1667
+ * Parse single attribute
1668
+ */
1669
+ parseAttribute(attributes) {
1670
+ const { name: attrName, newPos: nameEnd } = parseName(
1671
+ this.xmlString,
1672
+ this.pos
1673
+ );
1674
+ this.pos = nameEnd;
1675
+ this.skipWhitespace();
1676
+ const value = this.parseAttributeValue();
1677
+ attributes[attrName] = value;
1678
+ }
1679
+ /**
1680
+ * Parse all attributes
1681
+ */
1682
+ parseAttributes() {
1245
1683
  const attributes = {};
1246
- let children = [];
1247
1684
  while (this.xmlString.charCodeAt(this.pos) !== CharCodes.CLOSE_BRACKET && this.xmlString[this.pos]) {
1248
1685
  const c = this.xmlString.charCodeAt(this.pos);
1249
- if (c === CharCodes.SPACE || c === CharCodes.TAB || c === CharCodes.NEWLINE || c === CharCodes.CARRIAGE_RETURN) {
1250
- this.pos++;
1686
+ if (this.isWhitespace(c)) {
1687
+ this.pos += 1;
1251
1688
  continue;
1252
1689
  }
1253
1690
  if (c > 64 && c < 91 || c > 96 && c < 123) {
1254
- const { name: attrName, newPos: nameEnd } = parseName(
1255
- this.xmlString,
1256
- this.pos
1257
- );
1258
- this.pos = nameEnd;
1259
- while (this.pos < this.xmlString.length && (this.xmlString.charCodeAt(this.pos) === CharCodes.SPACE || this.xmlString.charCodeAt(this.pos) === CharCodes.TAB || this.xmlString.charCodeAt(this.pos) === CharCodes.NEWLINE || this.xmlString.charCodeAt(this.pos) === CharCodes.CARRIAGE_RETURN)) {
1260
- this.pos++;
1261
- }
1262
- let value = null;
1263
- if (this.pos < this.xmlString.length && this.xmlString[this.pos] === "=") {
1264
- this.pos++;
1265
- while (this.pos < this.xmlString.length && (this.xmlString.charCodeAt(this.pos) === CharCodes.SPACE || this.xmlString.charCodeAt(this.pos) === CharCodes.TAB || this.xmlString.charCodeAt(this.pos) === CharCodes.NEWLINE || this.xmlString.charCodeAt(this.pos) === CharCodes.CARRIAGE_RETURN)) {
1266
- this.pos++;
1267
- }
1268
- const code = this.xmlString.charCodeAt(this.pos);
1269
- if (code === CharCodes.SINGLE_QUOTE || code === CharCodes.DOUBLE_QUOTE) {
1270
- const { value: parsedValue, newPos: valueEnd } = parseString(
1271
- this.xmlString,
1272
- this.pos
1273
- );
1274
- value = parsedValue;
1275
- this.pos = valueEnd;
1276
- }
1277
- }
1278
- attributes[attrName] = value;
1691
+ this.parseAttribute(attributes);
1279
1692
  } else {
1280
- this.pos++;
1693
+ this.pos += 1;
1281
1694
  }
1282
1695
  }
1283
- const isSelfClosing = this.xmlString.charCodeAt(this.pos - 1) === CharCodes.SLASH || tagName[0] === "?" && this.xmlString.charCodeAt(this.pos - 1) === CharCodes.QUESTION;
1284
- if (!isSelfClosing) {
1285
- if (tagName === "script") {
1286
- const start = this.pos + 1;
1287
- this.pos = this.xmlString.indexOf("</script>", this.pos);
1288
- if (this.pos === -1) {
1289
- children = [this.xmlString.slice(start)];
1290
- this.pos = this.xmlString.length;
1291
- } else {
1292
- children = [this.xmlString.slice(start, this.pos)];
1293
- this.pos += 9;
1294
- }
1295
- } else if (tagName === "style") {
1296
- const start = this.pos + 1;
1297
- this.pos = this.xmlString.indexOf("</style>", this.pos);
1298
- if (this.pos === -1) {
1299
- children = [this.xmlString.slice(start)];
1300
- this.pos = this.xmlString.length;
1301
- } else {
1302
- children = [this.xmlString.slice(start, this.pos)];
1303
- this.pos += 8;
1304
- }
1305
- } else if (((_a = this.options.noChildNodes) == null ? void 0 : _a.indexOf(tagName)) === -1) {
1306
- this.pos++;
1307
- children = this.parseChildren(tagName);
1308
- } else {
1309
- this.pos++;
1310
- if (DEFAULT_NO_CHILD_NODES.includes(tagName)) {
1311
- } else {
1312
- const closingTag = `</${tagName}>`;
1313
- const closingPos = this.xmlString.indexOf(closingTag, this.pos);
1314
- if (closingPos !== -1) {
1315
- this.pos = closingPos + closingTag.length;
1316
- }
1317
- }
1318
- }
1319
- } else {
1320
- this.pos++;
1696
+ return attributes;
1697
+ }
1698
+ /**
1699
+ * Parse special tag content (script, style)
1700
+ */
1701
+ parseSpecialTagContent(_tagName, closingTag) {
1702
+ const start = this.pos + 1;
1703
+ this.pos = this.xmlString.indexOf(closingTag, this.pos);
1704
+ if (this.pos === -1) {
1705
+ const children2 = [this.xmlString.slice(start)];
1706
+ this.pos = this.xmlString.length;
1707
+ return children2;
1708
+ }
1709
+ const children = [this.xmlString.slice(start, this.pos)];
1710
+ this.pos += closingTag.length;
1711
+ return children;
1712
+ }
1713
+ /**
1714
+ * Parse node children based on tag type
1715
+ */
1716
+ parseNodeChildren(tagName, isSelfClosing) {
1717
+ var _a;
1718
+ if (isSelfClosing) {
1719
+ this.pos += 1;
1720
+ return [];
1321
1721
  }
1722
+ if (tagName === "script") {
1723
+ return this.parseSpecialTagContent(tagName, "</script>");
1724
+ }
1725
+ if (tagName === "style") {
1726
+ return this.parseSpecialTagContent(tagName, "</style>");
1727
+ }
1728
+ if (((_a = this.options.noChildNodes) == null ? void 0 : _a.indexOf(tagName)) === -1) {
1729
+ this.pos += 1;
1730
+ return this.parseChildren(tagName);
1731
+ }
1732
+ this.pos += 1;
1733
+ if (DEFAULT_NO_CHILD_NODES.includes(tagName)) {
1734
+ return [];
1735
+ }
1736
+ const closingTag = `</${tagName}>`;
1737
+ const closingPos = this.xmlString.indexOf(closingTag, this.pos);
1738
+ if (closingPos !== -1) {
1739
+ this.pos = closingPos + closingTag.length;
1740
+ }
1741
+ return [];
1742
+ }
1743
+ /**
1744
+ * Parse a single XML node
1745
+ */
1746
+ parseNode() {
1747
+ this.pos += 1;
1748
+ const { name: tagName, newPos } = parseName(this.xmlString, this.pos);
1749
+ this.pos = newPos;
1750
+ const attributes = this.parseAttributes();
1751
+ const isSelfClosing = this.xmlString.charCodeAt(this.pos - 1) === CharCodes.SLASH || tagName[0] === "?" && this.xmlString.charCodeAt(this.pos - 1) === CharCodes.QUESTION;
1752
+ const children = this.parseNodeChildren(tagName, isSelfClosing);
1322
1753
  return { tagName, attributes, children };
1323
1754
  }
1324
1755
  /**
@@ -1358,7 +1789,7 @@ var XMLTokenizer = class {
1358
1789
  if (this.options.keepComments) {
1359
1790
  children.push(this.xmlString.substring(startCommentPos, this.pos + 1));
1360
1791
  }
1361
- this.pos++;
1792
+ this.pos += 1;
1362
1793
  }
1363
1794
  /**
1364
1795
  * Handle CDATA sections
@@ -1386,10 +1817,10 @@ var XMLTokenizer = class {
1386
1817
  } else if (encapsulated && this.xmlString.charCodeAt(this.pos) === CharCodes.CLOSE_CORNER_BRACKET) {
1387
1818
  encapsulated = false;
1388
1819
  }
1389
- this.pos++;
1820
+ this.pos += 1;
1390
1821
  }
1391
1822
  children.push(this.xmlString.substring(startDoctype, this.pos));
1392
- this.pos++;
1823
+ this.pos += 1;
1393
1824
  }
1394
1825
  /**
1395
1826
  * Get current position
@@ -1406,9 +1837,123 @@ var XMLTokenizer = class {
1406
1837
  };
1407
1838
 
1408
1839
  // src/core/parser.ts
1840
+ var WHITESPACE_REGEX = /\s/;
1841
+ var NUMERIC_STRING_REGEX = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
1842
+ var DIGIT_KEY_REGEX2 = /^\d+$/;
1843
+ function getTopLevelStringProps(s) {
1844
+ const set = /* @__PURE__ */ new Set();
1845
+ const unwrapped = unwrapJsonSchema(s);
1846
+ if (unwrapped && typeof unwrapped === "object") {
1847
+ const props = unwrapped.properties;
1848
+ if (props && typeof props === "object") {
1849
+ for (const [k, v] of Object.entries(props)) {
1850
+ if (getSchemaType(v) === "string") {
1851
+ set.add(k);
1852
+ }
1853
+ }
1854
+ }
1855
+ }
1856
+ return set;
1857
+ }
1858
+ function restorePlaceholderString(val, placeholderMap) {
1859
+ if (val.startsWith("__RXML_PLACEHOLDER_")) {
1860
+ const orig = placeholderMap.get(val);
1861
+ return orig !== void 0 ? orig : val;
1862
+ }
1863
+ return val;
1864
+ }
1865
+ function restorePlaceholdersInObject(obj, _placeholderMap, textNodeName, restorer) {
1866
+ const out = {};
1867
+ for (const [k, v] of Object.entries(obj)) {
1868
+ const restored = restorer(v);
1869
+ if (k === textNodeName && typeof restored === "string") {
1870
+ out[k] = restored.trim();
1871
+ } else {
1872
+ out[k] = restored;
1873
+ }
1874
+ }
1875
+ return out;
1876
+ }
1877
+ function createPlaceholderRestorer(placeholderMap, textNodeName) {
1878
+ const restorer = (val) => {
1879
+ if (val == null) {
1880
+ return val;
1881
+ }
1882
+ if (typeof val === "string") {
1883
+ return restorePlaceholderString(val, placeholderMap);
1884
+ }
1885
+ if (Array.isArray(val)) {
1886
+ return val.map(restorer);
1887
+ }
1888
+ if (typeof val === "object") {
1889
+ return restorePlaceholdersInObject(
1890
+ val,
1891
+ placeholderMap,
1892
+ textNodeName,
1893
+ restorer
1894
+ );
1895
+ }
1896
+ return val;
1897
+ };
1898
+ return restorer;
1899
+ }
1900
+ function tryConvertToNumber(val) {
1901
+ if (typeof val !== "string") {
1902
+ return val;
1903
+ }
1904
+ const trimmed = val.trim();
1905
+ if (NUMERIC_STRING_REGEX.test(trimmed)) {
1906
+ const num = Number(trimmed);
1907
+ if (Number.isFinite(num)) {
1908
+ return num;
1909
+ }
1910
+ }
1911
+ return trimmed;
1912
+ }
1913
+ function processItemValue(item, textNodeName) {
1914
+ let currentVal = item;
1915
+ if (item && typeof item === "object" && Object.hasOwn(item, textNodeName)) {
1916
+ currentVal = item[textNodeName];
1917
+ }
1918
+ const trimmed = typeof currentVal === "string" ? currentVal.trim() : currentVal;
1919
+ return tryConvertToNumber(trimmed);
1920
+ }
1921
+ function processItemWrapper(itemValue, textNodeName) {
1922
+ if (Array.isArray(itemValue)) {
1923
+ return itemValue.map((item) => processItemValue(item, textNodeName));
1924
+ }
1925
+ const trimmed = typeof itemValue === "string" ? itemValue.trim() : itemValue;
1926
+ return tryConvertToNumber(trimmed);
1927
+ }
1928
+ function shouldRethrowParseError(error, xmlString) {
1929
+ if (!(error instanceof RXMLParseError)) {
1930
+ return false;
1931
+ }
1932
+ const isSimple = xmlString.split("<").length < 6;
1933
+ return error.message.includes("Unexpected close tag") && isSimple || error.message.includes("Unclosed tag") && isSimple;
1934
+ }
1935
+ function extractPartialXmlResults(xmlString, options) {
1936
+ const partialResults = [];
1937
+ const xmlPattern = /<([a-zA-Z_][\w.-]*)[^>]*>.*?<\/\1>/gs;
1938
+ let match = null;
1939
+ match = xmlPattern.exec(xmlString);
1940
+ while (match !== null) {
1941
+ try {
1942
+ const elementXml = match[0];
1943
+ const tokenizer = new XMLTokenizer(elementXml, options);
1944
+ const parsed = tokenizer.parseChildren();
1945
+ partialResults.push(...parsed);
1946
+ } catch (e) {
1947
+ }
1948
+ match = xmlPattern.exec(xmlString);
1949
+ }
1950
+ return partialResults;
1951
+ }
1409
1952
  function deepDecodeStringsBySchema(input, schema) {
1410
1953
  var _a;
1411
- if (input == null || schema == null) return input;
1954
+ if (input == null || schema == null) {
1955
+ return input;
1956
+ }
1412
1957
  const type = getSchemaType(schema);
1413
1958
  if (type === "string" && typeof input === "string") {
1414
1959
  return unescapeXml(input);
@@ -1427,7 +1972,9 @@ function deepDecodeStringsBySchema(input, schema) {
1427
1972
  }
1428
1973
  return out;
1429
1974
  }
1430
- if (typeof input === "string") return unescapeXml(input);
1975
+ if (typeof input === "string") {
1976
+ return unescapeXml(input);
1977
+ }
1431
1978
  return input;
1432
1979
  }
1433
1980
  function parse(xmlInner, schema, options = {}) {
@@ -1442,7 +1989,9 @@ function parse(xmlInner, schema, options = {}) {
1442
1989
  let rootName = "";
1443
1990
  while (i < s.length) {
1444
1991
  const lt = s.indexOf("<", i);
1445
- if (lt === -1) break;
1992
+ if (lt === -1) {
1993
+ break;
1994
+ }
1446
1995
  const next = s[lt + 1];
1447
1996
  if (next === "?") {
1448
1997
  const end = s.indexOf("?>", lt + 2);
@@ -1469,7 +2018,7 @@ function parse(xmlInner, schema, options = {}) {
1469
2018
  }
1470
2019
  let j = lt + 1;
1471
2020
  while (j < s.length && s[j] !== " " && s[j] !== "\n" && s[j] !== "\r" && s[j] !== " " && s[j] !== "/" && s[j] !== ">") {
1472
- j++;
2021
+ j += 1;
1473
2022
  }
1474
2023
  rootStart = lt;
1475
2024
  rootName = s.slice(lt + 1, j);
@@ -1482,41 +2031,36 @@ function parse(xmlInner, schema, options = {}) {
1482
2031
  const closeHead = s.indexOf(`</${rootName}`, range.end);
1483
2032
  if (closeHead === range.end) {
1484
2033
  let p = closeHead + 2 + rootName.length;
1485
- while (p < s.length && /\s/.test(s[p])) p++;
1486
- if (s[p] === ">") fullEnd = p + 1;
2034
+ while (p < s.length && WHITESPACE_REGEX.test(s[p])) {
2035
+ p += 1;
2036
+ }
2037
+ if (s[p] === ">") {
2038
+ fullEnd = p + 1;
2039
+ }
1487
2040
  }
1488
2041
  if (fullEnd === s.length) {
1489
2042
  const unwrapped = unwrapJsonSchema(schema);
1490
2043
  const schemaProps = unwrapped && typeof unwrapped === "object" ? unwrapped.properties : void 0;
1491
- if (schemaProps && !Object.prototype.hasOwnProperty.call(schemaProps, rootName)) {
2044
+ if (schemaProps && !Object.hasOwn(schemaProps, rootName)) {
1492
2045
  actualXmlInner = s.slice(range.start, range.end);
1493
2046
  }
1494
2047
  }
1495
2048
  }
1496
2049
  }
1497
2050
  }
1498
- const getTopLevelStringProps = (s) => {
1499
- const set = /* @__PURE__ */ new Set();
1500
- const unwrapped = unwrapJsonSchema(s);
1501
- if (unwrapped && typeof unwrapped === "object") {
1502
- const props = unwrapped.properties;
1503
- if (props && typeof props === "object") {
1504
- for (const [k, v] of Object.entries(props)) {
1505
- if (getSchemaType(v) === "string") set.add(k);
1506
- }
1507
- }
1508
- }
1509
- return set;
1510
- };
1511
2051
  const topLevelStringProps = getTopLevelStringProps(schema);
1512
2052
  const deepStringTypedProps = getStringTypedProperties(schema);
1513
2053
  const duplicateKeys = /* @__PURE__ */ new Set();
1514
2054
  for (const key of topLevelStringProps) {
1515
2055
  const excludeRanges = [];
1516
2056
  for (const other of topLevelStringProps) {
1517
- if (other === key) continue;
2057
+ if (other === key) {
2058
+ continue;
2059
+ }
1518
2060
  const range = findFirstTopLevelRange(actualXmlInner, other);
1519
- if (range) excludeRanges.push(range);
2061
+ if (range) {
2062
+ excludeRanges.push(range);
2063
+ }
1520
2064
  }
1521
2065
  const occurrences = countTagOccurrences(
1522
2066
  actualXmlInner,
@@ -1546,7 +2090,9 @@ function parse(xmlInner, schema, options = {}) {
1546
2090
  for (const key of deepStringTypedProps) {
1547
2091
  const innerRanges = findAllInnerRanges(actualXmlInner, key);
1548
2092
  for (const r of innerRanges) {
1549
- if (r.end > r.start) ranges.push({ ...r, key });
2093
+ if (r.end > r.start) {
2094
+ ranges.push({ ...r, key });
2095
+ }
1550
2096
  }
1551
2097
  }
1552
2098
  if (ranges.length > 0) {
@@ -1557,15 +2103,18 @@ function parse(xmlInner, schema, options = {}) {
1557
2103
  if (r.start < cursor) {
1558
2104
  continue;
1559
2105
  }
1560
- if (cursor < r.start) rebuilt += actualXmlInner.slice(cursor, r.start);
2106
+ if (cursor < r.start) {
2107
+ rebuilt += actualXmlInner.slice(cursor, r.start);
2108
+ }
1561
2109
  const placeholder = `__RXML_PLACEHOLDER_${r.key}_${r.start}_${r.end}__`;
1562
2110
  const originalContent = actualXmlInner.slice(r.start, r.end);
1563
2111
  originalContentMap.set(placeholder, originalContent);
1564
2112
  rebuilt += placeholder;
1565
2113
  cursor = r.end;
1566
2114
  }
1567
- if (cursor < actualXmlInner.length)
2115
+ if (cursor < actualXmlInner.length) {
1568
2116
  rebuilt += actualXmlInner.slice(cursor);
2117
+ }
1569
2118
  xmlInnerForParsing = rebuilt;
1570
2119
  }
1571
2120
  } catch (error) {
@@ -1577,44 +2126,23 @@ function parse(xmlInner, schema, options = {}) {
1577
2126
  }
1578
2127
  xmlInnerForParsing = actualXmlInner;
1579
2128
  }
1580
- let parsedNodes;
1581
- try {
1582
- const wrappedXml = `<root>${xmlInnerForParsing}</root>`;
1583
- const tokenizer = new XMLTokenizer(wrappedXml, {
1584
- ...options,
1585
- textNodeName
1586
- });
1587
- const rootNode = tokenizer.parseNode();
1588
- parsedNodes = rootNode.children;
1589
- } catch (cause) {
1590
- throw new RXMLParseError("Failed to parse XML", cause);
1591
- }
1592
- const parsedArgs = domToObject(parsedNodes, schema, textNodeName);
1593
- const restorePlaceholdersDeep = (val) => {
1594
- if (val == null) return val;
1595
- if (typeof val === "string") {
1596
- if (val.startsWith("__RXML_PLACEHOLDER_")) {
1597
- const orig = originalContentMap.get(val);
1598
- return orig !== void 0 ? orig : val;
1599
- }
1600
- return val;
1601
- }
1602
- if (Array.isArray(val)) return val.map(restorePlaceholdersDeep);
1603
- if (typeof val === "object") {
1604
- const obj = val;
1605
- const out = {};
1606
- for (const [k, v] of Object.entries(obj)) {
1607
- const restored = restorePlaceholdersDeep(v);
1608
- if (k === textNodeName && typeof restored === "string") {
1609
- out[k] = restored.trim();
1610
- } else {
1611
- out[k] = restored;
1612
- }
1613
- }
1614
- return out;
1615
- }
1616
- return val;
1617
- };
2129
+ let parsedNodes;
2130
+ try {
2131
+ const wrappedXml = `<root>${xmlInnerForParsing}</root>`;
2132
+ const tokenizer = new XMLTokenizer(wrappedXml, {
2133
+ ...options,
2134
+ textNodeName
2135
+ });
2136
+ const rootNode = tokenizer.parseNode();
2137
+ parsedNodes = rootNode.children;
2138
+ } catch (cause) {
2139
+ throw new RXMLParseError("Failed to parse XML", cause);
2140
+ }
2141
+ const parsedArgs = domToObject(parsedNodes, schema, textNodeName);
2142
+ const restorePlaceholdersDeep = createPlaceholderRestorer(
2143
+ originalContentMap,
2144
+ textNodeName
2145
+ );
1618
2146
  const parsedArgsRestored = restorePlaceholdersDeep(parsedArgs);
1619
2147
  const args = {};
1620
2148
  for (const k of Object.keys(parsedArgsRestored || {})) {
@@ -1636,7 +2164,7 @@ function parse(xmlInner, schema, options = {}) {
1636
2164
  }
1637
2165
  }
1638
2166
  if (propType === "string" && !Array.isArray(v)) {
1639
- const placeholderUsed = typeof v === "string" && v.startsWith("__RXML_PLACEHOLDER_") || v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, textNodeName) && typeof v[textNodeName] === "string" && v[textNodeName].startsWith(
2167
+ const placeholderUsed = typeof v === "string" && v.startsWith("__RXML_PLACEHOLDER_") || v && typeof v === "object" && Object.hasOwn(v, textNodeName) && typeof v[textNodeName] === "string" && v[textNodeName].startsWith(
1640
2168
  "__RXML_PLACEHOLDER_"
1641
2169
  );
1642
2170
  if (placeholderUsed) {
@@ -1658,13 +2186,13 @@ function parse(xmlInner, schema, options = {}) {
1658
2186
  continue;
1659
2187
  }
1660
2188
  }
1661
- if (v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, textNodeName)) {
2189
+ if (v && typeof v === "object" && Object.hasOwn(v, textNodeName)) {
1662
2190
  val = v[textNodeName];
1663
2191
  }
1664
2192
  if (Array.isArray(v)) {
1665
2193
  if (propType === "string") {
1666
2194
  const mapped = v.map((item) => {
1667
- if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, textNodeName)) {
2195
+ if (item && typeof item === "object" && Object.hasOwn(item, textNodeName)) {
1668
2196
  const textVal = item[textNodeName];
1669
2197
  return typeof textVal === "string" ? textVal : String(textVal);
1670
2198
  }
@@ -1683,41 +2211,18 @@ function parse(xmlInner, schema, options = {}) {
1683
2211
  }
1684
2212
  args[k] = (_c = mapped[0]) != null ? _c : "";
1685
2213
  continue;
1686
- } else {
1687
- val = processArrayContent(v, propSchema, textNodeName);
1688
2214
  }
1689
- } else if (v && typeof v === "object" && !Object.prototype.hasOwnProperty.call(v, textNodeName)) {
2215
+ val = processArrayContent(v, propSchema, textNodeName);
2216
+ } else if (v && typeof v === "object" && !Object.hasOwn(v, textNodeName)) {
1690
2217
  const obj = v;
1691
2218
  const keys2 = Object.keys(obj);
1692
2219
  if (keys2.length === 1 && keys2[0] === "item") {
1693
- const itemValue = obj.item;
1694
- if (Array.isArray(itemValue)) {
1695
- val = itemValue.map((item) => {
1696
- let currentVal = item;
1697
- if (item && typeof item === "object" && Object.prototype.hasOwnProperty.call(item, textNodeName)) {
1698
- currentVal = item[textNodeName];
1699
- }
1700
- const trimmed = typeof currentVal === "string" ? currentVal.trim() : currentVal;
1701
- if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1702
- const num = Number(trimmed);
1703
- if (Number.isFinite(num)) return num;
1704
- }
1705
- return trimmed;
1706
- });
1707
- } else {
1708
- const trimmed = typeof itemValue === "string" ? itemValue.trim() : itemValue;
1709
- if (typeof trimmed === "string" && /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(trimmed)) {
1710
- const num = Number(trimmed);
1711
- val = Number.isFinite(num) ? num : trimmed;
1712
- } else {
1713
- val = trimmed;
1714
- }
1715
- }
2220
+ val = processItemWrapper(obj.item, textNodeName);
1716
2221
  } else {
1717
2222
  let isIndexedTuple = false;
1718
- if (keys2.length > 0 && keys2.every((key) => /^\d+$/.test(key))) {
1719
- const indices = keys2.map((k2) => parseInt(k2, 10)).sort((a, b) => a - b);
1720
- isIndexedTuple = indices[0] === 0 && indices.every((val2, idx) => val2 === idx);
2223
+ if (keys2.length > 0 && keys2.every((key) => DIGIT_KEY_REGEX2.test(key))) {
2224
+ const indices = keys2.map((keyStr) => Number.parseInt(keyStr, 10)).sort((a, b) => a - b);
2225
+ isIndexedTuple = indices[0] === 0 && indices.every((indexVal, idx) => indexVal === idx);
1721
2226
  }
1722
2227
  if (isIndexedTuple) {
1723
2228
  val = processIndexedTuple(obj, textNodeName);
@@ -1729,7 +2234,7 @@ function parse(xmlInner, schema, options = {}) {
1729
2234
  args[k] = typeof val === "string" ? val.trim() : val;
1730
2235
  }
1731
2236
  for (const key of topLevelStringProps) {
1732
- if (!Object.prototype.hasOwnProperty.call(args, key)) {
2237
+ if (!Object.hasOwn(args, key)) {
1733
2238
  const raw = extractRawInner(actualXmlInner, key);
1734
2239
  if (typeof raw === "string") {
1735
2240
  args[key] = raw;
@@ -1744,7 +2249,7 @@ function parse(xmlInner, schema, options = {}) {
1744
2249
  const unwrapped = unwrapJsonSchema(schema);
1745
2250
  if (unwrapped && typeof unwrapped === "object") {
1746
2251
  const schemaProps = unwrapped.properties;
1747
- if (schemaProps && !Object.prototype.hasOwnProperty.call(schemaProps, rootKey)) {
2252
+ if (schemaProps && !Object.hasOwn(schemaProps, rootKey)) {
1748
2253
  dataToCoerce = rootValue;
1749
2254
  }
1750
2255
  }
@@ -1762,34 +2267,19 @@ function parseWithoutSchema(xmlString, options = {}) {
1762
2267
  const tokenizer = new XMLTokenizer(xmlString, options);
1763
2268
  return tokenizer.parseChildren();
1764
2269
  } catch (error) {
1765
- if (error instanceof RXMLParseError) {
1766
- const isSimple = xmlString.split("<").length < 6;
1767
- if (error.message.includes("Unexpected close tag") && isSimple || error.message.includes("Unclosed tag") && isSimple) {
1768
- throw new RXMLParseError(
1769
- error.message,
1770
- error.cause,
1771
- error.line,
1772
- error.column
1773
- );
1774
- }
2270
+ if (shouldRethrowParseError(error, xmlString)) {
2271
+ throw new RXMLParseError(
2272
+ error.message,
2273
+ error.cause,
2274
+ error.line,
2275
+ error.column
2276
+ );
1775
2277
  }
1776
2278
  if (options.onError) {
1777
2279
  options.onError("Failed to parse XML without schema", { error });
1778
2280
  }
1779
2281
  try {
1780
- const partialResults = [];
1781
- const xmlPattern = /<([a-zA-Z_][\w.-]*)[^>]*>.*?<\/\1>/gs;
1782
- let match;
1783
- while ((match = xmlPattern.exec(xmlString)) !== null) {
1784
- try {
1785
- const elementXml = match[0];
1786
- const tokenizer = new XMLTokenizer(elementXml, options);
1787
- const parsed = tokenizer.parseChildren();
1788
- partialResults.push(...parsed);
1789
- } catch (e) {
1790
- continue;
1791
- }
1792
- }
2282
+ const partialResults = extractPartialXmlResults(xmlString, options);
1793
2283
  if (partialResults.length > 0) {
1794
2284
  return partialResults;
1795
2285
  }
@@ -1806,6 +2296,26 @@ function parseNode(xmlString, options = {}) {
1806
2296
  throw new RXMLParseError("Failed to parse XML node", error);
1807
2297
  }
1808
2298
  }
2299
+ function buildNodeValue(child) {
2300
+ const kids = simplify(child.children);
2301
+ let nodeValue = kids;
2302
+ if (Object.keys(child.attributes).length) {
2303
+ if (typeof kids === "string") {
2304
+ nodeValue = kids;
2305
+ if (kids !== "") {
2306
+ nodeValue = { _attributes: child.attributes, value: kids };
2307
+ } else {
2308
+ nodeValue = { _attributes: child.attributes };
2309
+ }
2310
+ } else if (typeof kids === "object" && kids !== null) {
2311
+ kids._attributes = child.attributes;
2312
+ nodeValue = kids;
2313
+ } else {
2314
+ nodeValue = { _attributes: child.attributes };
2315
+ }
2316
+ }
2317
+ return nodeValue;
2318
+ }
1809
2319
  function simplify(children) {
1810
2320
  if (!children.length) {
1811
2321
  return "";
@@ -1814,33 +2324,20 @@ function simplify(children) {
1814
2324
  return children[0];
1815
2325
  }
1816
2326
  const out = {};
1817
- children.forEach((child) => {
2327
+ for (const child of children) {
1818
2328
  if (typeof child !== "object") {
1819
- return;
2329
+ continue;
1820
2330
  }
1821
2331
  if (!out[child.tagName]) {
1822
2332
  out[child.tagName] = [];
1823
2333
  }
1824
- const kids = simplify(child.children);
1825
- let nodeValue = kids;
1826
- if (Object.keys(child.attributes).length) {
1827
- if (typeof kids === "string") {
1828
- nodeValue = kids;
1829
- if (kids !== "") {
1830
- nodeValue = { _attributes: child.attributes, value: kids };
1831
- } else {
1832
- nodeValue = { _attributes: child.attributes };
1833
- }
1834
- } else if (typeof kids === "object" && kids !== null) {
1835
- kids._attributes = child.attributes;
1836
- nodeValue = kids;
1837
- } else {
1838
- nodeValue = { _attributes: child.attributes };
1839
- }
1840
- }
2334
+ const nodeValue = buildNodeValue(child);
1841
2335
  out[child.tagName].push(nodeValue);
1842
- });
2336
+ }
1843
2337
  for (const key in out) {
2338
+ if (!Object.hasOwn(out, key)) {
2339
+ continue;
2340
+ }
1844
2341
  const value = out[key];
1845
2342
  if (Array.isArray(value) && value.length === 1) {
1846
2343
  out[key] = value[0];
@@ -1850,46 +2347,42 @@ function simplify(children) {
1850
2347
  }
1851
2348
  function filter(children, filterFn, depth = 0, path = "") {
1852
2349
  const out = [];
1853
- children.forEach((child, i) => {
2350
+ for (let i = 0; i < children.length; i += 1) {
2351
+ const child = children[i];
1854
2352
  if (typeof child === "object" && filterFn(child, i, depth, path)) {
1855
2353
  out.push(child);
1856
2354
  }
1857
2355
  if (typeof child === "object" && child.children) {
1858
- const kids = filter(
1859
- child.children,
1860
- filterFn,
1861
- depth + 1,
1862
- (path ? path + "." : "") + i + "." + child.tagName
1863
- );
2356
+ const childPath = `${path ? `${path}.` : ""}${i}.${child.tagName}`;
2357
+ const kids = filter(child.children, filterFn, depth + 1, childPath);
1864
2358
  out.push(...kids);
1865
2359
  }
1866
- });
2360
+ }
1867
2361
  return out;
1868
2362
  }
1869
2363
 
1870
2364
  // src/core/stream.ts
1871
2365
  import { Transform } from "stream";
2366
+ var TAG_NAME_REGEX = /^([a-zA-Z_][\w.-]*)/;
2367
+ var WHITESPACE_REGEX2 = /\s/;
1872
2368
  var XMLTransformStream = class extends Transform {
1873
- constructor(offset, parseOptions = {}) {
2369
+ constructor(_offset, parseOptions = {}) {
1874
2370
  super({ readableObjectMode: true });
1875
2371
  this.buffer = "";
1876
2372
  this.emittedCount = 0;
1877
2373
  this.sawTagChar = false;
1878
- if (typeof offset === "string") {
1879
- this.position = offset.length;
1880
- } else {
1881
- this.position = offset || 0;
1882
- }
1883
2374
  this.parseOptions = {
1884
2375
  keepComments: false,
1885
2376
  keepWhitespace: false,
1886
2377
  ...parseOptions
1887
2378
  };
1888
2379
  }
1889
- _transform(chunk, encoding, callback) {
2380
+ _transform(chunk, _encoding, callback) {
1890
2381
  try {
1891
2382
  const incoming = chunk.toString();
1892
- if (incoming.includes("<")) this.sawTagChar = true;
2383
+ if (incoming.includes("<")) {
2384
+ this.sawTagChar = true;
2385
+ }
1893
2386
  this.buffer += incoming;
1894
2387
  this.processBuffer();
1895
2388
  callback();
@@ -1915,121 +2408,190 @@ var XMLTransformStream = class extends Transform {
1915
2408
  }
1916
2409
  processBuffer(isFlush = false) {
1917
2410
  while (this.buffer.length > 0) {
1918
- const openBracket = this.buffer.indexOf("<");
1919
- if (openBracket === -1) {
1920
- if (isFlush) this.buffer = "";
2411
+ if (!this.trimToNextTag(isFlush)) {
1921
2412
  break;
1922
2413
  }
1923
- if (openBracket > 0) {
1924
- this.buffer = this.buffer.slice(openBracket);
2414
+ if (this.tryProcessSpecialNode(isFlush)) {
2415
+ continue;
1925
2416
  }
1926
- if (this.buffer.startsWith("<?") || this.buffer.startsWith("<!--") || this.buffer.startsWith("<![CDATA[")) {
1927
- const endMarkers = {
1928
- "<?": "?>",
1929
- "<!--": "-->",
1930
- "<![CDATA[": "]]>"
1931
- };
1932
- let endMarker = "";
1933
- for (const [start, end] of Object.entries(endMarkers)) {
1934
- if (this.buffer.startsWith(start)) {
1935
- endMarker = end;
1936
- break;
1937
- }
1938
- }
1939
- const endPos = endMarker ? this.buffer.indexOf(endMarker) : -1;
1940
- if (endPos === -1) {
1941
- if (!isFlush) break;
1942
- this.buffer = "";
1943
- break;
1944
- }
1945
- if (this.parseOptions.keepComments && this.buffer.startsWith("<!--")) {
1946
- this.push(this.buffer.slice(0, endPos + endMarker.length));
1947
- }
1948
- this.buffer = this.buffer.slice(endPos + endMarker.length);
2417
+ if (this.trySkipStrayClosingTag(isFlush)) {
1949
2418
  continue;
1950
2419
  }
1951
- if (this.buffer.startsWith("</")) {
1952
- const closeEnd = this.buffer.indexOf(">");
1953
- if (closeEnd === -1) {
1954
- if (!isFlush) break;
1955
- this.buffer = "";
1956
- break;
1957
- }
1958
- this.buffer = this.buffer.slice(closeEnd + 1);
2420
+ const tagInfo = this.extractTagInfo(isFlush);
2421
+ if (!tagInfo) {
2422
+ break;
2423
+ }
2424
+ if (this.tryProcessSelfClosingTag(tagInfo)) {
1959
2425
  continue;
1960
2426
  }
1961
- const openTagEnd = this.buffer.indexOf(">");
1962
- if (openTagEnd === -1) {
1963
- if (!isFlush) break;
2427
+ if (!this.tryProcessRegularElement(tagInfo, isFlush)) {
2428
+ break;
2429
+ }
2430
+ }
2431
+ }
2432
+ trimToNextTag(isFlush) {
2433
+ const openBracket = this.buffer.indexOf("<");
2434
+ if (openBracket === -1) {
2435
+ if (isFlush) {
1964
2436
  this.buffer = "";
2437
+ }
2438
+ return false;
2439
+ }
2440
+ if (openBracket > 0) {
2441
+ this.buffer = this.buffer.slice(openBracket);
2442
+ }
2443
+ return true;
2444
+ }
2445
+ tryProcessSpecialNode(isFlush) {
2446
+ if (!(this.buffer.startsWith("<?") || this.buffer.startsWith("<!--") || this.buffer.startsWith("<![CDATA["))) {
2447
+ return false;
2448
+ }
2449
+ const endMarkers = {
2450
+ "<?": "?>",
2451
+ "<!--": "-->",
2452
+ "<![CDATA[": "]]>"
2453
+ };
2454
+ let endMarker = "";
2455
+ for (const [start, end] of Object.entries(endMarkers)) {
2456
+ if (this.buffer.startsWith(start)) {
2457
+ endMarker = end;
1965
2458
  break;
1966
2459
  }
1967
- const openTagContent = this.buffer.slice(1, openTagEnd);
1968
- const nameMatch = openTagContent.match(/^([a-zA-Z_][\w.-]*)/);
1969
- if (!nameMatch) {
1970
- this.buffer = this.buffer.slice(1);
1971
- continue;
2460
+ }
2461
+ const endPos = endMarker ? this.buffer.indexOf(endMarker) : -1;
2462
+ if (endPos === -1) {
2463
+ if (isFlush) {
2464
+ this.buffer = "";
1972
2465
  }
1973
- const tagName = nameMatch[1];
1974
- const isSelfClosing = this.buffer[openTagEnd - 1] === "/";
1975
- if (isSelfClosing) {
1976
- const elementEnd2 = openTagEnd + 1;
1977
- const elementXml2 = this.buffer.slice(0, elementEnd2);
1978
- try {
1979
- const tokenizer = new XMLTokenizer(elementXml2, this.parseOptions);
1980
- const node = tokenizer.parseNode();
1981
- this.emitElementAndChildren(node);
1982
- this.buffer = this.buffer.slice(elementEnd2);
1983
- continue;
1984
- } catch (e) {
1985
- this.buffer = this.buffer.slice(1);
1986
- continue;
1987
- }
2466
+ return false;
2467
+ }
2468
+ if (this.parseOptions.keepComments && this.buffer.startsWith("<!--")) {
2469
+ this.push(this.buffer.slice(0, endPos + endMarker.length));
2470
+ }
2471
+ this.buffer = this.buffer.slice(endPos + endMarker.length);
2472
+ return true;
2473
+ }
2474
+ trySkipStrayClosingTag(isFlush) {
2475
+ if (!this.buffer.startsWith("</")) {
2476
+ return false;
2477
+ }
2478
+ const closeEnd = this.buffer.indexOf(">");
2479
+ if (closeEnd === -1) {
2480
+ if (isFlush) {
2481
+ this.buffer = "";
1988
2482
  }
1989
- let depth = 1;
1990
- let searchStart = openTagEnd + 1;
1991
- let elementEnd = -1;
1992
- while (searchStart < this.buffer.length) {
1993
- let nextOpen = this.buffer.indexOf(`<${tagName}`, searchStart);
1994
- while (nextOpen !== -1) {
1995
- const after = this.buffer[nextOpen + tagName.length + 1];
1996
- if (after === void 0 || after === ">" || /\s/.test(after)) break;
1997
- nextOpen = this.buffer.indexOf(`<${tagName}`, nextOpen + 1);
1998
- }
1999
- const nextCloseStart = this.buffer.indexOf(`</${tagName}`, searchStart);
2000
- if (nextCloseStart === -1) break;
2001
- if (nextOpen !== -1 && nextOpen < nextCloseStart) {
2002
- depth++;
2003
- searchStart = nextOpen + 1;
2004
- } else {
2005
- depth--;
2006
- let p = nextCloseStart + 2 + tagName.length;
2007
- while (p < this.buffer.length && /\s/.test(this.buffer[p])) p++;
2008
- if (this.buffer[p] !== ">") break;
2009
- const closeAdvance = p + 1;
2010
- searchStart = closeAdvance;
2011
- if (depth === 0) {
2012
- elementEnd = searchStart;
2013
- break;
2014
- }
2015
- }
2483
+ return true;
2484
+ }
2485
+ this.buffer = this.buffer.slice(closeEnd + 1);
2486
+ return true;
2487
+ }
2488
+ extractTagInfo(isFlush) {
2489
+ const openTagEnd = this.buffer.indexOf(">");
2490
+ if (openTagEnd === -1) {
2491
+ if (isFlush) {
2492
+ this.buffer = "";
2016
2493
  }
2017
- if (elementEnd === -1) {
2018
- if (!isFlush) break;
2494
+ return null;
2495
+ }
2496
+ const openTagContent = this.buffer.slice(1, openTagEnd);
2497
+ const nameMatch = openTagContent.match(TAG_NAME_REGEX);
2498
+ if (!nameMatch) {
2499
+ this.buffer = this.buffer.slice(1);
2500
+ return null;
2501
+ }
2502
+ return { openTagEnd, tagName: nameMatch[1] };
2503
+ }
2504
+ tryProcessSelfClosingTag(tagInfo) {
2505
+ const isSelfClosing = this.buffer[tagInfo.openTagEnd - 1] === "/";
2506
+ if (!isSelfClosing) {
2507
+ return false;
2508
+ }
2509
+ const elementEnd = tagInfo.openTagEnd + 1;
2510
+ const elementXml = this.buffer.slice(0, elementEnd);
2511
+ try {
2512
+ const tokenizer = new XMLTokenizer(elementXml, this.parseOptions);
2513
+ const node = tokenizer.parseNode();
2514
+ this.emitElementAndChildren(node);
2515
+ this.buffer = this.buffer.slice(elementEnd);
2516
+ return true;
2517
+ } catch (e) {
2518
+ this.buffer = this.buffer.slice(1);
2519
+ return true;
2520
+ }
2521
+ }
2522
+ tryProcessRegularElement(tagInfo, isFlush) {
2523
+ const elementEnd = this.findMatchingClosingTag(
2524
+ tagInfo.tagName,
2525
+ tagInfo.openTagEnd
2526
+ );
2527
+ if (elementEnd === -1) {
2528
+ if (isFlush) {
2019
2529
  this.buffer = this.buffer.slice(1);
2020
- continue;
2530
+ return true;
2531
+ }
2532
+ return false;
2533
+ }
2534
+ const elementXml = this.buffer.slice(0, elementEnd);
2535
+ try {
2536
+ const tokenizer = new XMLTokenizer(elementXml, this.parseOptions);
2537
+ const node = tokenizer.parseNode();
2538
+ this.emitElementAndChildren(node);
2539
+ this.buffer = this.buffer.slice(elementEnd);
2540
+ return true;
2541
+ } catch (e) {
2542
+ this.emit("error", new RXMLStreamError("Parse error", e));
2543
+ return false;
2544
+ }
2545
+ }
2546
+ findMatchingClosingTag(tagName, openTagEnd) {
2547
+ let depth = 1;
2548
+ let searchStart = openTagEnd + 1;
2549
+ while (searchStart < this.buffer.length) {
2550
+ const nextOpen = this.findNextOpeningTag(tagName, searchStart);
2551
+ const nextCloseStart = this.buffer.indexOf(`</${tagName}`, searchStart);
2552
+ if (nextCloseStart === -1) {
2553
+ return -1;
2554
+ }
2555
+ if (nextOpen !== -1 && nextOpen < nextCloseStart) {
2556
+ depth += 1;
2557
+ searchStart = nextOpen + 1;
2558
+ } else {
2559
+ depth -= 1;
2560
+ const closeAdvance = this.advancePastClosingTag(
2561
+ tagName,
2562
+ nextCloseStart
2563
+ );
2564
+ if (closeAdvance === -1) {
2565
+ return -1;
2566
+ }
2567
+ searchStart = closeAdvance;
2568
+ if (depth === 0) {
2569
+ return searchStart;
2570
+ }
2021
2571
  }
2022
- const elementXml = this.buffer.slice(0, elementEnd);
2023
- try {
2024
- const tokenizer = new XMLTokenizer(elementXml, this.parseOptions);
2025
- const node = tokenizer.parseNode();
2026
- this.emitElementAndChildren(node);
2027
- this.buffer = this.buffer.slice(elementEnd);
2028
- } catch (e) {
2029
- this.emit("error", new RXMLStreamError("Parse error", e));
2030
- return;
2572
+ }
2573
+ return -1;
2574
+ }
2575
+ findNextOpeningTag(tagName, searchStart) {
2576
+ let nextOpen = this.buffer.indexOf(`<${tagName}`, searchStart);
2577
+ while (nextOpen !== -1) {
2578
+ const after = this.buffer[nextOpen + tagName.length + 1];
2579
+ if (after === void 0 || after === ">" || WHITESPACE_REGEX2.test(after)) {
2580
+ break;
2031
2581
  }
2582
+ nextOpen = this.buffer.indexOf(`<${tagName}`, nextOpen + 1);
2583
+ }
2584
+ return nextOpen;
2585
+ }
2586
+ advancePastClosingTag(tagName, nextCloseStart) {
2587
+ let p = nextCloseStart + 2 + tagName.length;
2588
+ while (p < this.buffer.length && WHITESPACE_REGEX2.test(this.buffer[p])) {
2589
+ p += 1;
2590
+ }
2591
+ if (this.buffer[p] !== ">") {
2592
+ return -1;
2032
2593
  }
2594
+ return p + 1;
2033
2595
  }
2034
2596
  /**
2035
2597
  * Emit an element and recursively emit its children as separate events
@@ -2038,12 +2600,12 @@ var XMLTransformStream = class extends Transform {
2038
2600
  if (typeof node === "string") {
2039
2601
  if (this.parseOptions.keepComments && node.includes("<!--")) {
2040
2602
  this.push(node);
2041
- this.emittedCount++;
2603
+ this.emittedCount += 1;
2042
2604
  }
2043
2605
  return;
2044
2606
  }
2045
2607
  this.push(node);
2046
- this.emittedCount++;
2608
+ this.emittedCount += 1;
2047
2609
  for (const child of node.children) {
2048
2610
  this.emitElementAndChildren(child);
2049
2611
  }
@@ -2052,7 +2614,7 @@ var XMLTransformStream = class extends Transform {
2052
2614
  function createXMLStream(offset, parseOptions) {
2053
2615
  return new XMLTransformStream(offset, parseOptions);
2054
2616
  }
2055
- async function parseFromStream(stream, offset, parseOptions) {
2617
+ function parseFromStream(stream, offset, parseOptions) {
2056
2618
  return new Promise((resolve, reject) => {
2057
2619
  const results = [];
2058
2620
  const transformStream = createXMLStream(offset, parseOptions);
@@ -2115,7 +2677,10 @@ async function* processXMLStream(stream, offset, parseOptions) {
2115
2677
  throw new RXMLStreamError("Stream processing error", error);
2116
2678
  }
2117
2679
  if (queue.length > 0) {
2118
- yield queue.shift();
2680
+ const item = queue.shift();
2681
+ if (item !== void 0) {
2682
+ yield item;
2683
+ }
2119
2684
  continue;
2120
2685
  }
2121
2686
  if (ended) {
@@ -2150,224 +2715,6 @@ async function* findElementsByClassStream(stream, className, offset, parseOption
2150
2715
  }
2151
2716
  }
2152
2717
  }
2153
-
2154
- // src/builders/stringify.ts
2155
- function stringify(rootTag, obj, options = {}) {
2156
- var _a, _b, _c, _d;
2157
- try {
2158
- const format = (_a = options.format) != null ? _a : true;
2159
- const minimalEscaping = (_b = options.minimalEscaping) != null ? _b : false;
2160
- const suppressEmptyNode = (_c = options.suppressEmptyNode) != null ? _c : false;
2161
- const strictBooleanAttributes = (_d = options.strictBooleanAttributes) != null ? _d : false;
2162
- let result = "";
2163
- if (format) {
2164
- result += '<?xml version="1.0" encoding="UTF-8"?>\n';
2165
- }
2166
- result += stringifyValue(
2167
- rootTag,
2168
- obj,
2169
- 0,
2170
- format,
2171
- suppressEmptyNode,
2172
- minimalEscaping,
2173
- strictBooleanAttributes
2174
- );
2175
- return result;
2176
- } catch (error) {
2177
- throw new RXMLStringifyError("Failed to stringify XML", error);
2178
- }
2179
- }
2180
- function stringifyValue(tagName, value, depth, format, suppressEmptyNode, minimalEscaping, strictBooleanAttributes) {
2181
- const indent = format ? " ".repeat(depth) : "";
2182
- const newline = format ? "\n" : "";
2183
- if (value === null || value === void 0) {
2184
- if (suppressEmptyNode) return "";
2185
- return `${indent}<${tagName}/>${newline}`;
2186
- }
2187
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
2188
- const content2 = minimalEscaping ? escapeXmlMinimalText(String(value)) : escapeXml(String(value));
2189
- if (content2 === "" && suppressEmptyNode) return "";
2190
- return `${indent}<${tagName}>${content2}</${tagName}>${newline}`;
2191
- }
2192
- if (Array.isArray(value)) {
2193
- let result = "";
2194
- for (const item of value) {
2195
- result += stringifyValue(
2196
- tagName,
2197
- item,
2198
- depth,
2199
- format,
2200
- suppressEmptyNode,
2201
- minimalEscaping,
2202
- strictBooleanAttributes
2203
- );
2204
- }
2205
- return result;
2206
- }
2207
- if (typeof value === "object") {
2208
- return stringifyObject(
2209
- tagName,
2210
- value,
2211
- depth,
2212
- format,
2213
- suppressEmptyNode,
2214
- minimalEscaping,
2215
- strictBooleanAttributes
2216
- );
2217
- }
2218
- const content = minimalEscaping ? escapeXmlMinimalText(String(value)) : escapeXml(String(value));
2219
- if (content === "" && suppressEmptyNode) return "";
2220
- return `${indent}<${tagName}>${content}</${tagName}>${newline}`;
2221
- }
2222
- function stringifyObject(tagName, obj, depth, format, suppressEmptyNode, minimalEscaping, strictBooleanAttributes) {
2223
- const indent = format ? " ".repeat(depth) : "";
2224
- const newline = format ? "\n" : "";
2225
- const childIndent = format ? " ".repeat(depth + 1) : "";
2226
- const attributes = {};
2227
- const elements = {};
2228
- let textContent;
2229
- for (const [key, value] of Object.entries(obj)) {
2230
- if (key.startsWith("@")) {
2231
- attributes[key.substring(1)] = value;
2232
- } else if (key === "#text" || key === "_text") {
2233
- textContent = String(value);
2234
- } else if (key === "_attributes") {
2235
- if (typeof value === "object" && value !== null) {
2236
- Object.assign(attributes, value);
2237
- }
2238
- } else {
2239
- elements[key] = value;
2240
- }
2241
- }
2242
- let openTag = `<${tagName}`;
2243
- for (const [attrName, attrValue] of Object.entries(attributes)) {
2244
- if (attrValue === null) {
2245
- if (strictBooleanAttributes) {
2246
- openTag += ` ${attrName}="${attrName}"`;
2247
- } else {
2248
- openTag += ` ${attrName}`;
2249
- }
2250
- } else {
2251
- const valueStr = String(attrValue);
2252
- if (valueStr.indexOf('"') === -1) {
2253
- const escaped = minimalEscaping ? escapeXmlMinimalAttr(valueStr, '"') : escapeXml(valueStr);
2254
- openTag += ` ${attrName}="${escaped}"`;
2255
- } else {
2256
- const escaped = minimalEscaping ? escapeXmlMinimalAttr(valueStr, "'") : escapeXml(valueStr);
2257
- openTag += ` ${attrName}='${escaped}'`;
2258
- }
2259
- }
2260
- }
2261
- const hasElements = Object.keys(elements).length > 0;
2262
- const hasTextContent = textContent !== void 0 && textContent !== "";
2263
- if (!hasElements && !hasTextContent) {
2264
- if (suppressEmptyNode) return "";
2265
- return `${indent}${openTag}/>${newline}`;
2266
- }
2267
- openTag += ">";
2268
- if (!hasElements && hasTextContent && textContent) {
2269
- const content = minimalEscaping ? escapeXmlMinimalText(textContent) : escapeXml(textContent);
2270
- return `${indent}${openTag}${content}</${tagName}>${newline}`;
2271
- }
2272
- let result = `${indent}${openTag}`;
2273
- if (hasTextContent && textContent) {
2274
- const content = minimalEscaping ? escapeXmlMinimalText(textContent) : escapeXml(textContent);
2275
- if (format) result += `${newline}${childIndent}${content}`;
2276
- else result += content;
2277
- }
2278
- if (hasElements) {
2279
- if (format) result += newline;
2280
- for (const [elementName, elementValue] of Object.entries(elements)) {
2281
- result += stringifyValue(
2282
- elementName,
2283
- elementValue,
2284
- depth + 1,
2285
- format,
2286
- suppressEmptyNode,
2287
- minimalEscaping,
2288
- strictBooleanAttributes
2289
- );
2290
- }
2291
- if (format) result += indent;
2292
- }
2293
- result += `</${tagName}>${newline}`;
2294
- return result;
2295
- }
2296
- function stringifyNodes(nodes, format = true, options = {}) {
2297
- let result = "";
2298
- for (const node of nodes) {
2299
- if (typeof node === "string") {
2300
- result += node;
2301
- } else {
2302
- result += stringifyNode(node, 0, format, options);
2303
- }
2304
- }
2305
- return result;
2306
- }
2307
- function stringifyNode(node, depth = 0, format = true, options = {}) {
2308
- var _a, _b;
2309
- const indent = format ? " ".repeat(depth) : "";
2310
- const newline = format ? "\n" : "";
2311
- const minimalEscaping = (_a = options.minimalEscaping) != null ? _a : false;
2312
- const strictBooleanAttributes = (_b = options.strictBooleanAttributes) != null ? _b : false;
2313
- let result = `${indent}<${node.tagName}`;
2314
- for (const [attrName, attrValue] of Object.entries(node.attributes)) {
2315
- if (attrValue === null) {
2316
- if (strictBooleanAttributes) {
2317
- result += ` ${attrName}="${attrName}"`;
2318
- } else {
2319
- result += ` ${attrName}`;
2320
- }
2321
- } else if (attrValue.indexOf('"') === -1) {
2322
- const escaped = minimalEscaping ? escapeXmlMinimalAttr(attrValue, '"') : escapeXml(attrValue);
2323
- result += ` ${attrName}="${escaped}"`;
2324
- } else {
2325
- const escaped = minimalEscaping ? escapeXmlMinimalAttr(attrValue, "'") : escapeXml(attrValue);
2326
- result += ` ${attrName}='${escaped}'`;
2327
- }
2328
- }
2329
- if (node.tagName[0] === "?") {
2330
- result += "?>";
2331
- return result + newline;
2332
- }
2333
- if (node.children.length === 0) {
2334
- result += "/>";
2335
- return result + newline;
2336
- }
2337
- result += ">";
2338
- let hasElementChildren = false;
2339
- for (const child of node.children) {
2340
- if (typeof child === "string") {
2341
- result += minimalEscaping ? escapeXmlMinimalText(child) : escapeXml(child);
2342
- } else {
2343
- if (!hasElementChildren && format) {
2344
- result += newline;
2345
- hasElementChildren = true;
2346
- }
2347
- result += stringifyNode(child, depth + 1, format, options);
2348
- }
2349
- }
2350
- if (hasElementChildren && format) {
2351
- result += indent;
2352
- }
2353
- result += `</${node.tagName}>`;
2354
- if (format) {
2355
- result += newline;
2356
- }
2357
- return result;
2358
- }
2359
- function toContentString(nodes) {
2360
- let result = "";
2361
- for (const node of nodes) {
2362
- if (typeof node === "string") {
2363
- result += " " + node;
2364
- } else {
2365
- result += " " + toContentString(node.children);
2366
- }
2367
- result = result.trim();
2368
- }
2369
- return result;
2370
- }
2371
2718
  export {
2372
2719
  RXMLCoercionError,
2373
2720
  RXMLDuplicateStringTagError,