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