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