@poe-platform/safe-fs 0.1.817 → 0.1.819
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/README.md +1 -1
- package/dist/safe-fs/contracts/filesystem.d.ts +9 -1
- package/dist/safe-fs/core.d.ts +1 -1
- package/dist/safe-fs/fs/capabilities.js +7 -4
- package/dist/safe-fs/fs/devices/index.d.ts +3 -1
- package/dist/safe-fs/fs/devices/index.js +7 -0
- package/dist/safe-fs/fs/devices/path.js +6 -0
- package/dist/safe-fs/fs/devices/plain-path.d.ts +2 -0
- package/dist/safe-fs/fs/devices/plain-path.js +3 -0
- package/dist/safe-fs/fs/memory/index.d.ts +2 -1
- package/dist/safe-fs/fs/memory/index.js +3 -0
- package/dist/safe-fs/fs/mount/index.d.ts +4 -1
- package/dist/safe-fs/fs/mount/index.js +56 -25
- package/dist/safe-fs/fs/overlay/index.d.ts +2 -1
- package/dist/safe-fs/fs/overlay/index.js +2 -0
- package/dist/safe-fs/fs/real/index.d.ts +3 -0
- package/dist/safe-fs/fs/real/index.js +110 -5
- package/dist/safe-fs/fs/scoped.js +2 -2
- package/dist/xml-ast/attributes.d.ts +69 -0
- package/dist/xml-ast/attributes.js +62 -0
- package/dist/xml-ast/frames.d.ts +40 -1
- package/dist/xml-ast/frames.js +42 -0
- package/dist/xml-ast/index.d.ts +33 -5
- package/dist/xml-ast/index.js +212 -64
- package/dist/xml-ast/source.d.ts +4 -0
- package/package.json +1 -1
package/dist/xml-ast/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { XmlAttributes } from "./attributes.js";
|
|
2
|
+
import { XmlFrames, bindNamespace, namespaceValue, namespaceMetadata, hasNamespace } from "./frames.js";
|
|
2
3
|
import { validCharacter } from "./characters.js";
|
|
3
4
|
export { normalizeXmlChunks } from "./characters.js";
|
|
4
5
|
import { XmlSource } from "./source.js";
|
|
@@ -67,6 +68,41 @@ function qualifiedNameSync(name, cache) {
|
|
|
67
68
|
}
|
|
68
69
|
return parts;
|
|
69
70
|
}
|
|
71
|
+
/** Validate a name span without retaining its spelling. */
|
|
72
|
+
function* validateNameSpan(source, start, end) {
|
|
73
|
+
let first = true, colon = -1, work = 0;
|
|
74
|
+
for (let offset = start; offset < end;) {
|
|
75
|
+
let point = yield* source.charCodeAt(offset);
|
|
76
|
+
if (point >= 0xd800 && point <= 0xdbff && offset + 1 < end) {
|
|
77
|
+
const low = yield* source.charCodeAt(offset + 1);
|
|
78
|
+
if (low >= 0xdc00 && low <= 0xdfff)
|
|
79
|
+
point = 0x10000 + (point - 0xd800) * 1024 + low - 0xdc00;
|
|
80
|
+
}
|
|
81
|
+
if (point === 58) {
|
|
82
|
+
if (colon >= 0 || first)
|
|
83
|
+
invalid("invalid qualified name");
|
|
84
|
+
colon = offset;
|
|
85
|
+
first = true;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
if (!(first ? nameStart(point) : namePart(point)))
|
|
89
|
+
invalid("invalid qualified name");
|
|
90
|
+
first = false;
|
|
91
|
+
}
|
|
92
|
+
const size = point > 0xffff ? 2 : 1;
|
|
93
|
+
offset += size;
|
|
94
|
+
work += size;
|
|
95
|
+
if (work >= 512) {
|
|
96
|
+
yield 512;
|
|
97
|
+
work -= 512;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (first)
|
|
101
|
+
invalid("invalid qualified name");
|
|
102
|
+
if (work)
|
|
103
|
+
yield work;
|
|
104
|
+
return colon;
|
|
105
|
+
}
|
|
70
106
|
function* entity(source, start, end, recover) {
|
|
71
107
|
// Inspect only a bounded prefix, even for arbitrarily long numeric references.
|
|
72
108
|
const prefix = typeof source === "string" ? source.slice(start, Math.min(end, start + 4))
|
|
@@ -101,7 +137,7 @@ function* entity(source, start, end, recover) {
|
|
|
101
137
|
return String.fromCodePoint(point);
|
|
102
138
|
}
|
|
103
139
|
/** Decoded text fragments; source read/work requests remain host-serviced. */
|
|
104
|
-
function* textParts(source, start, end, decode, recover) {
|
|
140
|
+
function* textParts(source, start, end, decode, recover, attribute = false) {
|
|
105
141
|
let cursor = start;
|
|
106
142
|
while (cursor < end) {
|
|
107
143
|
let finish = Math.min(end, cursor + 512);
|
|
@@ -110,7 +146,13 @@ function* textParts(source, start, end, decode, recover) {
|
|
|
110
146
|
if (last >= 0xd800 && last <= 0xdbff)
|
|
111
147
|
finish--;
|
|
112
148
|
}
|
|
113
|
-
|
|
149
|
+
let raw = yield* source.slice(cursor, finish);
|
|
150
|
+
if (attribute) {
|
|
151
|
+
let normalized = "";
|
|
152
|
+
for (const character of raw)
|
|
153
|
+
normalized += "\t\n\r".includes(character) ? " " : character;
|
|
154
|
+
raw = normalized;
|
|
155
|
+
}
|
|
114
156
|
const found = decode ? raw.indexOf("&") : -1;
|
|
115
157
|
if (found < 0) {
|
|
116
158
|
yield raw;
|
|
@@ -139,9 +181,9 @@ function* textParts(source, start, end, decode, recover) {
|
|
|
139
181
|
cursor = semicolon + 1;
|
|
140
182
|
}
|
|
141
183
|
}
|
|
142
|
-
function* textFragments(source, start, end, decode, recover) {
|
|
184
|
+
function* textFragments(source, start, end, decode, recover, attribute = false) {
|
|
143
185
|
let pending = "";
|
|
144
|
-
for (const part of textParts(source, start, end, decode, recover)) {
|
|
186
|
+
for (const part of textParts(source, start, end, decode, recover, attribute)) {
|
|
145
187
|
if (typeof part !== "string") {
|
|
146
188
|
yield part;
|
|
147
189
|
continue;
|
|
@@ -253,8 +295,22 @@ function validateLimits(limits) {
|
|
|
253
295
|
}
|
|
254
296
|
export function* parseXmlSourceSteps(length, limits = {}) {
|
|
255
297
|
validateLimits(limits);
|
|
298
|
+
if (limits.deferAttributeNames && (!limits.storeAttributes || !limits.storeNamespaces))
|
|
299
|
+
throw new TypeError("Deferred attribute names require stored attributes and namespaces");
|
|
300
|
+
if (limits.deferElementNames && limits.retainTree !== false)
|
|
301
|
+
throw new TypeError("Deferred element names require retainTree: false");
|
|
302
|
+
if (limits.deferContentNames && !limits.fragmentContent)
|
|
303
|
+
throw new TypeError("Deferred XML content names require fragmentContent: true");
|
|
304
|
+
if (limits.fragmentAttributes && !limits.storeAttributes)
|
|
305
|
+
throw new TypeError("XML attribute fragments require storeAttributes: true");
|
|
256
306
|
if (limits.storeFrames && limits.retainTree !== false)
|
|
257
307
|
throw new TypeError("Stored XML frames require retainTree: false");
|
|
308
|
+
if (limits.storeAttributes && limits.retainTree !== false)
|
|
309
|
+
throw new TypeError("Stored XML attributes require retainTree: false");
|
|
310
|
+
if (limits.deferNamespaces && !limits.storeNamespaces)
|
|
311
|
+
throw new TypeError("Deferred XML namespaces require stored namespaces");
|
|
312
|
+
if (limits.storeNamespaces && limits.retainTree !== false)
|
|
313
|
+
throw new TypeError("Stored XML namespaces require retainTree: false");
|
|
258
314
|
if (limits.fragmentContent && limits.retainTree !== false)
|
|
259
315
|
throw new TypeError("XML content fragments require retainTree: false");
|
|
260
316
|
const source = new XmlSource(length);
|
|
@@ -277,6 +333,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
277
333
|
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
278
334
|
textLength += text.length;
|
|
279
335
|
};
|
|
336
|
+
const initialNamespaces = limits.storeNamespaces ? yield* bindNamespace({ reference: 0, size: 0 }, "xml", xmlNamespace) : new Map([["xml", xmlNamespace]]);
|
|
280
337
|
const stack = new XmlFrames(limits.storeFrames === true);
|
|
281
338
|
let root;
|
|
282
339
|
const prolog = [];
|
|
@@ -308,7 +365,24 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
308
365
|
}
|
|
309
366
|
return offset - start;
|
|
310
367
|
};
|
|
311
|
-
const
|
|
368
|
+
const sameName = function* (left, right, span) {
|
|
369
|
+
if (!left.nameSource && !span)
|
|
370
|
+
return left.name === right;
|
|
371
|
+
const leftLength = left.nameSource ? left.nameSource.end - left.nameSource.start : left.name.length;
|
|
372
|
+
const rightLength = span ? span.end - span.start : right.length;
|
|
373
|
+
if (leftLength !== rightLength)
|
|
374
|
+
return false;
|
|
375
|
+
for (let index = 0; index < leftLength; index += 512) {
|
|
376
|
+
const end = Math.min(leftLength, index + 512);
|
|
377
|
+
const a = left.nameSource ? yield* source.slice(left.nameSource.start + index, left.nameSource.start + end) : left.name.slice(index, end);
|
|
378
|
+
const b = span ? yield* source.slice(span.start + index, span.start + end) : right.slice(index, end);
|
|
379
|
+
yield end - index;
|
|
380
|
+
if (a !== b)
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
return true;
|
|
384
|
+
};
|
|
385
|
+
const scanName = function* (defer) {
|
|
312
386
|
const start = offset;
|
|
313
387
|
while (yield* source.has(offset)) {
|
|
314
388
|
const c = (yield* source.charCodeAt(offset));
|
|
@@ -321,6 +395,21 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
321
395
|
pendingWork -= 512;
|
|
322
396
|
}
|
|
323
397
|
}
|
|
398
|
+
if (defer && offset - start > 512) {
|
|
399
|
+
const colon = yield* validateNameSpan(source, start, offset);
|
|
400
|
+
if (defer === "content")
|
|
401
|
+
return ["", "", "", { start, end: offset }];
|
|
402
|
+
if (colon - start > 512 && !limits.storeNamespaces) {
|
|
403
|
+
const name = yield* source.slice(start, offset);
|
|
404
|
+
return [name, name.slice(0, colon - start), name.slice(colon - start + 1)];
|
|
405
|
+
}
|
|
406
|
+
const localStart = colon < 0 ? start : colon + 1;
|
|
407
|
+
const prefixSource = colon - start > 512 ? { start, end: colon } : undefined;
|
|
408
|
+
const prefix = colon < 0 || prefixSource ? "" : yield* source.slice(start, colon);
|
|
409
|
+
const localNameSource = offset - localStart > 512 ? { start: localStart, end: offset } : undefined;
|
|
410
|
+
const local = localNameSource ? "" : yield* source.slice(localStart, offset);
|
|
411
|
+
return ["", prefix, local, { start, end: offset }, localNameSource, prefixSource];
|
|
412
|
+
}
|
|
324
413
|
const name = (yield* source.slice(start, offset));
|
|
325
414
|
pendingWork += offset - start;
|
|
326
415
|
while (pendingWork >= 512) {
|
|
@@ -330,7 +419,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
330
419
|
const [prefix, localName] = qualifiedNameSync(name, qualifiedNames);
|
|
331
420
|
return [name, prefix, localName];
|
|
332
421
|
};
|
|
333
|
-
const rawContent = function* (kind, start, end, parent, target = "") {
|
|
422
|
+
const rawContent = function* (kind, start, end, parent, target = "", targetSource) {
|
|
334
423
|
if (end - start > maxTextLength - textLength)
|
|
335
424
|
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
336
425
|
textLength += end - start;
|
|
@@ -344,7 +433,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
344
433
|
finish--;
|
|
345
434
|
}
|
|
346
435
|
const text = yield* source.slice(cursor, finish);
|
|
347
|
-
const content = kind === "processing-instruction" ? { kind, target, text } : { kind, text };
|
|
436
|
+
const content = kind === "processing-instruction" ? { kind, target, text, ...(targetSource ? { targetSource } : {}) } : { kind, text };
|
|
348
437
|
if (kind === "cdata" && retainTree)
|
|
349
438
|
parent.element.text += text;
|
|
350
439
|
if (retain)
|
|
@@ -480,7 +569,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
480
569
|
else if ((yield* source.startsWith("<?", offset))) {
|
|
481
570
|
const start = offset;
|
|
482
571
|
offset += 2;
|
|
483
|
-
const [target] = yield* scanName();
|
|
572
|
+
const [target, , , targetSource] = yield* scanName(limits.deferContentNames ? "content" : undefined);
|
|
484
573
|
while (pendingWork >= 512) {
|
|
485
574
|
yield 512;
|
|
486
575
|
pendingWork -= 512;
|
|
@@ -513,7 +602,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
513
602
|
yield 512;
|
|
514
603
|
pendingWork -= 512;
|
|
515
604
|
}
|
|
516
|
-
yield* rawContent("processing-instruction", body, end, yield* stack.peek(), target);
|
|
605
|
+
yield* rawContent("processing-instruction", body, end, yield* stack.peek(), target, targetSource);
|
|
517
606
|
}
|
|
518
607
|
offset = end + 2;
|
|
519
608
|
}
|
|
@@ -522,14 +611,14 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
522
611
|
}
|
|
523
612
|
else if ((yield* source.charCodeAt(offset + 1)) === 47) {
|
|
524
613
|
offset += 2;
|
|
525
|
-
const [name] = yield* scanName();
|
|
614
|
+
const [name, , , nameSource] = yield* scanName(limits.deferElementNames ? "element" : undefined);
|
|
526
615
|
yield* skipWhitespace();
|
|
527
616
|
while (pendingWork >= 512) {
|
|
528
617
|
yield 512;
|
|
529
618
|
pendingWork -= 512;
|
|
530
619
|
}
|
|
531
620
|
const closed = (yield* source.slice(offset++, offset)) === ">" ? (yield* stack.pop()) : undefined;
|
|
532
|
-
if (closed
|
|
621
|
+
if (!closed || !(yield* sameName(closed.element, name, nameSource))) {
|
|
533
622
|
if (!limits.recover)
|
|
534
623
|
invalid("mismatched closing tag");
|
|
535
624
|
limits.recover("mismatched closing tag");
|
|
@@ -540,9 +629,9 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
540
629
|
else {
|
|
541
630
|
offset++;
|
|
542
631
|
const repeated = previousEmpty && (yield* source.startsWith(previousEmpty.suffix, offset)) ? previousEmpty : undefined;
|
|
543
|
-
const [name, prefix, localName] = repeated
|
|
632
|
+
const [name, prefix, localName, nameSource, localNameSource, prefixSource] = repeated
|
|
544
633
|
? [repeated.name, repeated.prefix, repeated.localName]
|
|
545
|
-
: (yield* scanName());
|
|
634
|
+
: (yield* scanName(limits.deferElementNames ? "element" : undefined));
|
|
546
635
|
if (repeated)
|
|
547
636
|
offset += name.length;
|
|
548
637
|
// Preserve scan/validation work while reusing the admitted spelling.
|
|
@@ -552,9 +641,9 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
552
641
|
yield 512;
|
|
553
642
|
pendingWork -= 512;
|
|
554
643
|
}
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
let namespaces =
|
|
644
|
+
const attributes = new XmlAttributes(limits.storeAttributes === true);
|
|
645
|
+
const namespaceParent = yield* stack.peek();
|
|
646
|
+
let namespaces = namespaceParent?.namespaceScope ?? namespaceParent?.namespaces ?? (initialNamespaces instanceof Map ? new Map(initialNamespaces) : initialNamespaces);
|
|
558
647
|
let ownsNamespaces = stack.length === 0;
|
|
559
648
|
while (!repeated) {
|
|
560
649
|
const ws = (yield* skipWhitespace());
|
|
@@ -567,16 +656,16 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
567
656
|
break;
|
|
568
657
|
if (ws === 0)
|
|
569
658
|
invalid("attributes require whitespace");
|
|
570
|
-
const [attribute, attrPrefix, attrLocal] = yield* scanName();
|
|
659
|
+
const [attribute, attrPrefix, attrLocal, nameSource, localNameSource, attrPrefixSource] = yield* scanName(limits.deferAttributeNames ? "attribute" : undefined);
|
|
571
660
|
while (pendingWork >= 512) {
|
|
572
661
|
yield 512;
|
|
573
662
|
pendingWork -= 512;
|
|
574
663
|
}
|
|
575
|
-
if (attributes
|
|
664
|
+
if ((yield* attributes.has(attribute, nameSource)))
|
|
576
665
|
invalid("duplicate attribute");
|
|
577
666
|
if (++attributeCount > maxAttributes)
|
|
578
667
|
throw new XmlLimitError("maxAttributes", "XML attribute limit exceeded");
|
|
579
|
-
if (
|
|
668
|
+
if (attributes.size >= maxAttributesPerElement)
|
|
580
669
|
throw new XmlLimitError("maxAttributesPerElement", "XML attribute limit exceeded");
|
|
581
670
|
yield* skipWhitespace();
|
|
582
671
|
if ((yield* source.slice(offset++, offset)) !== "=")
|
|
@@ -594,31 +683,59 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
594
683
|
}
|
|
595
684
|
if (end < 0)
|
|
596
685
|
invalid("unterminated attribute");
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
686
|
+
const fragmented = limits.fragmentAttributes && (limits.storeNamespaces || attribute !== "xmlns" && attrPrefix !== "xmlns");
|
|
687
|
+
const valueStart = offset;
|
|
688
|
+
let value = "", size = 0, matchesXml = true, matchesXmlns = true;
|
|
689
|
+
if (fragmented) {
|
|
690
|
+
if ((yield* source.indexOf("<", offset, end)) >= 0)
|
|
691
|
+
invalid("less-than in attribute");
|
|
692
|
+
for (const part of textFragments(source, offset, end, true, limits.recover, true)) {
|
|
693
|
+
if (typeof part !== "string")
|
|
694
|
+
yield part;
|
|
695
|
+
else {
|
|
696
|
+
matchesXml = matchesXml && part === xmlNamespace.slice(size, size + part.length);
|
|
697
|
+
matchesXmlns = matchesXmlns && part === xmlnsNamespace.slice(size, size + part.length);
|
|
698
|
+
size += part.length;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
matchesXml = matchesXml && size === xmlNamespace.length;
|
|
702
|
+
matchesXmlns = matchesXmlns && size === xmlnsNamespace.length;
|
|
703
|
+
if (size > maxTextLength - textLength)
|
|
704
|
+
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
705
|
+
textLength += size;
|
|
602
706
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
707
|
+
else {
|
|
708
|
+
const raw = (yield* source.slice(offset, end));
|
|
709
|
+
pendingWork += raw.length * 2;
|
|
710
|
+
while (pendingWork >= 512) {
|
|
711
|
+
yield 512;
|
|
712
|
+
pendingWork -= 512;
|
|
713
|
+
}
|
|
714
|
+
if (raw.indexOf("<") >= 0)
|
|
715
|
+
invalid("less-than in attribute");
|
|
716
|
+
let normalized = raw;
|
|
717
|
+
if (raw.includes("\t") || raw.includes("\n") || raw.includes("\r")) {
|
|
718
|
+
normalized = "";
|
|
719
|
+
for (const character of raw)
|
|
720
|
+
normalized += "\t\n\r".includes(character) ? " " : character;
|
|
721
|
+
}
|
|
722
|
+
value = normalized.indexOf("&") < 0 ? (pendingWork += normalized.length, normalized) : yield* entities(normalized, limits.recover);
|
|
723
|
+
while (pendingWork >= 512) {
|
|
724
|
+
yield 512;
|
|
725
|
+
pendingWork -= 512;
|
|
726
|
+
}
|
|
727
|
+
admitText(value);
|
|
610
728
|
}
|
|
611
|
-
|
|
612
|
-
(attributes ??= new Map()).set(attribute, value);
|
|
613
|
-
attrMeta.push([attribute, attrPrefix, attrLocal]);
|
|
729
|
+
yield* attributes.append(attribute, attrPrefix, attrLocal, value, limits.fragmentAttributes ? { start: offset, end } : undefined, nameSource, localNameSource, attrPrefixSource);
|
|
614
730
|
offset = end + 1;
|
|
615
|
-
if (attribute === "xmlns" ||
|
|
616
|
-
const nsPrefix = attribute === "xmlns" ? "" :
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|| (nsPrefix
|
|
731
|
+
if (attribute === "xmlns" || attrPrefix === "xmlns") {
|
|
732
|
+
const nsPrefix = attribute === "xmlns" ? "" : attrLocal;
|
|
733
|
+
const nsPrefixSource = attrPrefix === "xmlns" ? localNameSource : undefined;
|
|
734
|
+
if (nsPrefix === "xmlns" || (fragmented ? matchesXmlns : value === xmlnsNamespace)
|
|
735
|
+
|| (nsPrefix === "xml") !== (fragmented ? matchesXml : value === xmlNamespace)
|
|
736
|
+
|| ((nsPrefix !== "" || nsPrefixSource) && (fragmented ? size === 0 : value === "")))
|
|
620
737
|
invalid("invalid namespace binding");
|
|
621
|
-
if (!ownsNamespaces) {
|
|
738
|
+
if (!ownsNamespaces && namespaces instanceof Map) {
|
|
622
739
|
const copy = new Map();
|
|
623
740
|
for (const [key, uri] of namespaces) {
|
|
624
741
|
copy.set(key, uri);
|
|
@@ -627,25 +744,27 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
627
744
|
namespaces = copy;
|
|
628
745
|
ownsNamespaces = true;
|
|
629
746
|
}
|
|
630
|
-
namespaces
|
|
747
|
+
namespaces = yield* bindNamespace(namespaces, nsPrefix, fragmented
|
|
748
|
+
? textFragments(source, valueStart, end, true, limits.recover ? () => { } : undefined, true) : value, nsPrefixSource);
|
|
631
749
|
if (namespaces.size > maxNamespaces)
|
|
632
750
|
throw new XmlLimitError("maxNamespaces", "XML namespace scope limit exceeded");
|
|
633
751
|
}
|
|
634
752
|
}
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
753
|
+
if (attributes.length > 0) {
|
|
754
|
+
for (let cursor = attributes.first; cursor;) {
|
|
755
|
+
const record = yield* attributes.read(cursor, false);
|
|
756
|
+
cursor = record.next;
|
|
757
|
+
const { name: attribute, prefix: attrPrefix, localName: attrLocal } = record;
|
|
758
|
+
if (attribute === "xmlns" || attrPrefix === "xmlns")
|
|
640
759
|
continue;
|
|
641
760
|
pendingWork += attribute.length;
|
|
642
|
-
if (attrPrefix && !namespaces.
|
|
761
|
+
if ((attrPrefix || record.prefixSource) && !(yield* hasNamespace(namespaces, attrPrefix, record.prefixSource)))
|
|
643
762
|
invalid("unbound attribute prefix");
|
|
644
|
-
if (
|
|
645
|
-
const
|
|
646
|
-
|
|
763
|
+
if (attributes.length > 1) {
|
|
764
|
+
const backed = limits.storeAttributes && !(namespaces instanceof Map) ? namespaces : undefined;
|
|
765
|
+
const key = backed ? attrLocal : `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
|
|
766
|
+
if (yield* attributes.expanded(key, backed ? { scope: backed, prefix: attrPrefix, ...(record.prefixSource ? { prefixSource: record.prefixSource } : {}) } : undefined, record.localNameSource))
|
|
647
767
|
invalid("duplicate expanded attribute");
|
|
648
|
-
expanded.add(key);
|
|
649
768
|
}
|
|
650
769
|
}
|
|
651
770
|
}
|
|
@@ -654,23 +773,26 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
654
773
|
yield 512;
|
|
655
774
|
pendingWork -= 512;
|
|
656
775
|
}
|
|
657
|
-
if (prefix === "xmlns" || (prefix && !namespaces
|
|
776
|
+
if (prefix === "xmlns" || ((prefix || prefixSource) && !(yield* hasNamespace(namespaces, prefix, prefixSource))))
|
|
658
777
|
invalid("unbound element prefix");
|
|
659
778
|
if (++nodes > maxNodes)
|
|
660
779
|
throw new XmlLimitError("maxNodes", "XML resource limit exceeded");
|
|
661
780
|
if (stack.length + 1 > maxDepth)
|
|
662
781
|
throw new XmlLimitError("maxDepth", "XML resource limit exceeded");
|
|
663
|
-
const namespace = namespaces
|
|
664
|
-
|
|
782
|
+
const namespace = yield* namespaceMetadata(namespaces, prefix, limits.deferNamespaces, prefixSource);
|
|
783
|
+
const elementName = { name, localName, ...(nameSource ? { nameSource, prefix } : {}), ...(localNameSource ? { localNameSource } : {}), ...(prefixSource ? { prefixSource } : {}) };
|
|
784
|
+
limits.onElement?.({ ...elementName, ...namespace }, (yield* stack.peek())?.element, stack.length + 1);
|
|
665
785
|
admitContent();
|
|
666
786
|
const retainedAttributes = [];
|
|
667
|
-
if (retainContent
|
|
668
|
-
for (let
|
|
669
|
-
const
|
|
670
|
-
|
|
787
|
+
if (retainContent) {
|
|
788
|
+
for (let cursor = attributes.first; cursor;) {
|
|
789
|
+
const record = yield* attributes.read(cursor, !limits.storeAttributes);
|
|
790
|
+
cursor = record.next;
|
|
791
|
+
const { name: attribute, prefix: attrPrefix, localName: attrLocal, value } = record;
|
|
671
792
|
admitContent();
|
|
672
|
-
|
|
673
|
-
|
|
793
|
+
if (!limits.storeAttributes)
|
|
794
|
+
retainedAttributes.push({ name: attribute, localName: attrLocal,
|
|
795
|
+
namespace: attribute === "xmlns" || attrPrefix === "xmlns" ? xmlnsNamespace : attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : "", value });
|
|
674
796
|
pendingWork += attribute.length + 1;
|
|
675
797
|
}
|
|
676
798
|
}
|
|
@@ -679,7 +801,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
679
801
|
pendingWork -= 512;
|
|
680
802
|
}
|
|
681
803
|
const content = retain ? [] : undefined;
|
|
682
|
-
const element = { kind: "element",
|
|
804
|
+
const element = { kind: "element", ...elementName, ...namespace, children: [], text: "", content: content ?? emptyContent, attributes: retainContent ? retainedAttributes : emptyAttributes, namespaces: retainContent && namespaces instanceof Map ? namespaces : emptyNamespaces, ...(root === undefined && retainContent ? { prolog, epilog, ...(declaration === undefined ? {} : { declaration }) } : {}) };
|
|
683
805
|
const parent = (yield* stack.peek());
|
|
684
806
|
if (parent) {
|
|
685
807
|
if (retainTree) {
|
|
@@ -701,11 +823,37 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
701
823
|
}
|
|
702
824
|
// Only cache compact, attribute-free spellings. Namespace resolution and
|
|
703
825
|
// every admission counter still run for each distinct physical element.
|
|
704
|
-
if (empty && !attributes && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
|
|
826
|
+
if (empty && !nameSource && !attributes.length && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
|
|
705
827
|
previousEmpty = { suffix: name + "/>", name, prefix, localName };
|
|
706
828
|
limits.events?.({ type: "open", element, parent: parent?.element });
|
|
829
|
+
if (limits.storeAttributes && limits.onAttribute && retainContent) {
|
|
830
|
+
yield 1;
|
|
831
|
+
for (let cursor = attributes.first; cursor;) {
|
|
832
|
+
const attribute = yield* attributes.read(cursor, true);
|
|
833
|
+
cursor = attribute.next;
|
|
834
|
+
const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? { namespace: xmlnsNamespace }
|
|
835
|
+
: attribute.prefix || attribute.prefixSource ? yield* namespaceMetadata(namespaces, attribute.prefix, limits.deferNamespaces, attribute.prefixSource) : { namespace: "" };
|
|
836
|
+
const nameMetadata = { name: attribute.name, localName: attribute.localName, ...(attribute.nameSource ? { nameSource: attribute.nameSource, prefix: attribute.prefix } : {}), ...(attribute.localNameSource ? { localNameSource: attribute.localNameSource } : {}), ...(attribute.prefixSource ? { prefixSource: attribute.prefixSource } : {}) };
|
|
837
|
+
let continuation = false;
|
|
838
|
+
if (attribute.source) {
|
|
839
|
+
for (const part of textFragments(source, attribute.source.start, attribute.source.end, true, limits.recover ? () => { } : undefined, true)) {
|
|
840
|
+
if (typeof part !== "string") {
|
|
841
|
+
yield part;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
limits.onAttribute({ ...nameMetadata, ...namespace, value: part }, element, continuation);
|
|
845
|
+
continuation = true;
|
|
846
|
+
yield 1;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (!continuation) {
|
|
850
|
+
limits.onAttribute({ ...nameMetadata, ...namespace, value: attribute.value }, element);
|
|
851
|
+
yield 1;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
707
855
|
if (!empty)
|
|
708
|
-
yield* stack.push({ element, content, name, namespaces });
|
|
856
|
+
yield* stack.push({ element, content, name, namespaces: namespaces instanceof Map ? namespaces : new Map(), ...(namespaces instanceof Map ? {} : { namespaceScope: namespaces }) });
|
|
709
857
|
else
|
|
710
858
|
limits.events?.({ type: "close", element, parent: parent?.element });
|
|
711
859
|
}
|
package/dist/xml-ast/source.d.ts
CHANGED