@poe-platform/safe-fs 0.1.816 → 0.1.818
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 +3 -0
- package/dist/safe-fs/core.d.ts +1 -1
- package/dist/safe-fs/storage.d.ts +2 -0
- package/dist/safe-fs/storage.js +5 -0
- package/dist/xml-ast/attributes.d.ts +47 -0
- package/dist/xml-ast/attributes.js +62 -0
- package/dist/xml-ast/frames.d.ts +21 -0
- package/dist/xml-ast/frames.js +20 -0
- package/dist/xml-ast/index.d.ts +12 -3
- package/dist/xml-ast/index.js +48 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,9 @@ To bound aggregate pages across multiple live stores, pass the same
|
|
|
15
15
|
This shares a 256 KiB resident-page budget; `cache.residentBytes` reports current
|
|
16
16
|
page retention. Eviction uses each store's filesystem, and each store still owns
|
|
17
17
|
its `close()` cleanup. The budget excludes caller buffers and backend storage.
|
|
18
|
+
Call `await storage.prepare()` before opening a directory iterator when its
|
|
19
|
+
snapshot includes the scratch directory. It acquires and unlinks the backing
|
|
20
|
+
handle up front, so later spills do not mutate that directory's namespace.
|
|
18
21
|
`IntegerTable.entries()` replays records in unsigned key order with a fixed
|
|
19
22
|
16-level node stack and batches of at most 128 records. It accepts zero-filled
|
|
20
23
|
caller storage with `allocate`, `read` and `write`; mutation during traversal
|
package/dist/safe-fs/core.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from "./fs/webdav/index.js";
|
|
|
9
9
|
export * from "./bridge/index.js";
|
|
10
10
|
export * from "./python/index.js";
|
|
11
11
|
export { parseXml, parseXmlSteps, parseXmlSourceSteps, normalizeXmlChunks, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
|
-
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest } from "./xml.js";
|
|
12
|
+
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest, XmlAttributeRequest, XmlAttributeRecord } from "./xml.js";
|
|
13
13
|
export * from "./contracts/object.js";
|
|
14
14
|
export { ObjectAuthority } from "./fs/object-authority.js";
|
|
15
15
|
export * from "./fs/s3/index.js";
|
|
@@ -44,6 +44,8 @@ export declare class PagedStorage {
|
|
|
44
44
|
private closing;
|
|
45
45
|
constructor(context: PagedStorageContext, maxPages?: number, cache?: PagedStorageCache | undefined);
|
|
46
46
|
allocate(length: number): number;
|
|
47
|
+
/** Acquire and unlink backing storage before observing a directory snapshot. */
|
|
48
|
+
prepare(): Promise<void>;
|
|
47
49
|
private operation;
|
|
48
50
|
private spill;
|
|
49
51
|
private flush;
|
package/dist/safe-fs/storage.js
CHANGED
|
@@ -73,6 +73,11 @@ export class PagedStorage {
|
|
|
73
73
|
this.end += length;
|
|
74
74
|
return offset;
|
|
75
75
|
}
|
|
76
|
+
/** Acquire and unlink backing storage before observing a directory snapshot. */
|
|
77
|
+
async prepare() {
|
|
78
|
+
await this.operation(async () => { if (!this.descriptor)
|
|
79
|
+
await this.spill(); });
|
|
80
|
+
}
|
|
76
81
|
operation(action) {
|
|
77
82
|
this.signal.throwIfAborted();
|
|
78
83
|
const execute = () => {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface XmlAttributeRecord {
|
|
2
|
+
name: string;
|
|
3
|
+
prefix: string;
|
|
4
|
+
localName: string;
|
|
5
|
+
value: string;
|
|
6
|
+
next: number;
|
|
7
|
+
}
|
|
8
|
+
export interface XmlAttributeState {
|
|
9
|
+
reference: number;
|
|
10
|
+
first: number;
|
|
11
|
+
length: number;
|
|
12
|
+
size: number;
|
|
13
|
+
}
|
|
14
|
+
export type XmlAttributeRequest = {
|
|
15
|
+
attributeOperation: 'has' | 'expanded';
|
|
16
|
+
state: XmlAttributeState;
|
|
17
|
+
name: string;
|
|
18
|
+
found?: boolean;
|
|
19
|
+
} | {
|
|
20
|
+
attributeOperation: 'append';
|
|
21
|
+
state: XmlAttributeState;
|
|
22
|
+
attribute: Omit<XmlAttributeRecord, 'next'>;
|
|
23
|
+
result?: XmlAttributeState;
|
|
24
|
+
} | {
|
|
25
|
+
attributeOperation: 'read';
|
|
26
|
+
state: XmlAttributeState;
|
|
27
|
+
reference: number;
|
|
28
|
+
values: boolean;
|
|
29
|
+
result?: XmlAttributeRecord;
|
|
30
|
+
};
|
|
31
|
+
/** Buffered APIs use local collections; external parsing retains only a store
|
|
32
|
+
* handle and one current attribute. Hosts preserve order and last-value recovery. */
|
|
33
|
+
export declare class XmlAttributes {
|
|
34
|
+
private readonly external;
|
|
35
|
+
private state;
|
|
36
|
+
private readonly names;
|
|
37
|
+
private readonly records;
|
|
38
|
+
private readonly expandedNames;
|
|
39
|
+
constructor(external: boolean);
|
|
40
|
+
get length(): number;
|
|
41
|
+
get size(): number;
|
|
42
|
+
get first(): number;
|
|
43
|
+
has(name: string): Generator<XmlAttributeRequest, boolean, void>;
|
|
44
|
+
append(name: string, prefix: string, localName: string, value: string): Generator<XmlAttributeRequest, void, void>;
|
|
45
|
+
read(reference: number, values: boolean): Generator<XmlAttributeRequest, XmlAttributeRecord, void>;
|
|
46
|
+
expanded(name: string): Generator<XmlAttributeRequest, boolean, void>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Buffered APIs use local collections; external parsing retains only a store
|
|
2
|
+
* handle and one current attribute. Hosts preserve order and last-value recovery. */
|
|
3
|
+
export class XmlAttributes {
|
|
4
|
+
external;
|
|
5
|
+
state = { reference: 0, first: 0, length: 0, size: 0 };
|
|
6
|
+
names = new Map();
|
|
7
|
+
records = [];
|
|
8
|
+
expandedNames = new Set();
|
|
9
|
+
constructor(external) {
|
|
10
|
+
this.external = external;
|
|
11
|
+
}
|
|
12
|
+
get length() { return this.state.length; }
|
|
13
|
+
get size() { return this.state.size; }
|
|
14
|
+
get first() { return this.state.first; }
|
|
15
|
+
*has(name) {
|
|
16
|
+
if (!this.length)
|
|
17
|
+
return false;
|
|
18
|
+
if (!this.external)
|
|
19
|
+
return this.names.has(name);
|
|
20
|
+
const request = { attributeOperation: 'has', state: this.state, name };
|
|
21
|
+
yield request;
|
|
22
|
+
if (request.found === undefined)
|
|
23
|
+
throw new TypeError('Incomplete XML attribute lookup');
|
|
24
|
+
return request.found;
|
|
25
|
+
}
|
|
26
|
+
*append(name, prefix, localName, value) {
|
|
27
|
+
if (!this.external) {
|
|
28
|
+
this.names.set(name, value);
|
|
29
|
+
this.records.push({ name, prefix, localName });
|
|
30
|
+
this.state = { reference: 0, first: 1, length: this.records.length, size: this.names.size };
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const request = { attributeOperation: 'append', state: this.state, attribute: { name, prefix, localName, value } };
|
|
34
|
+
yield request;
|
|
35
|
+
if (!request.result)
|
|
36
|
+
throw new TypeError('Incomplete XML attribute append');
|
|
37
|
+
this.state = request.result;
|
|
38
|
+
}
|
|
39
|
+
*read(reference, values) {
|
|
40
|
+
if (!this.external) {
|
|
41
|
+
const attribute = this.records[reference - 1];
|
|
42
|
+
return { ...attribute, value: values ? this.names.get(attribute.name) : '', next: reference < this.length ? reference + 1 : 0 };
|
|
43
|
+
}
|
|
44
|
+
const request = { attributeOperation: 'read', state: this.state, reference, values };
|
|
45
|
+
yield request;
|
|
46
|
+
if (!request.result)
|
|
47
|
+
throw new TypeError('Incomplete XML attribute read');
|
|
48
|
+
return request.result;
|
|
49
|
+
}
|
|
50
|
+
*expanded(name) {
|
|
51
|
+
if (!this.external) {
|
|
52
|
+
const found = this.expandedNames.has(name);
|
|
53
|
+
this.expandedNames.add(name);
|
|
54
|
+
return found;
|
|
55
|
+
}
|
|
56
|
+
const request = { attributeOperation: 'expanded', state: this.state, name };
|
|
57
|
+
yield request;
|
|
58
|
+
if (request.found === undefined)
|
|
59
|
+
throw new TypeError('Incomplete XML expanded attribute lookup');
|
|
60
|
+
return request.found;
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/xml-ast/frames.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface XmlParserFrame {
|
|
|
4
4
|
content: XmlContent[] | undefined;
|
|
5
5
|
name: string;
|
|
6
6
|
namespaces: Map<string, string>;
|
|
7
|
+
namespaceScope?: XmlNamespaceScope;
|
|
7
8
|
}
|
|
8
9
|
export type XmlFrameRequest = {
|
|
9
10
|
readonly frameOperation: 'push';
|
|
@@ -26,3 +27,23 @@ export declare class XmlFrames {
|
|
|
26
27
|
push(frame: XmlParserFrame): Generator<XmlFrameRequest, void, void>;
|
|
27
28
|
pop(): Generator<XmlFrameRequest, XmlParserFrame | undefined, void>;
|
|
28
29
|
}
|
|
30
|
+
/** A host-owned immutable namespace scope; count includes the built-in xml prefix. */
|
|
31
|
+
export interface XmlNamespaceScope {
|
|
32
|
+
readonly reference: number;
|
|
33
|
+
readonly size: number;
|
|
34
|
+
}
|
|
35
|
+
export type XmlNamespaceRequest = {
|
|
36
|
+
readonly namespaceOperation: 'get';
|
|
37
|
+
readonly scope: XmlNamespaceScope;
|
|
38
|
+
readonly prefix: string;
|
|
39
|
+
value?: string;
|
|
40
|
+
complete?: true;
|
|
41
|
+
} | {
|
|
42
|
+
readonly namespaceOperation: 'set';
|
|
43
|
+
readonly scope: XmlNamespaceScope;
|
|
44
|
+
readonly prefix: string;
|
|
45
|
+
readonly value: string;
|
|
46
|
+
result?: XmlNamespaceScope;
|
|
47
|
+
};
|
|
48
|
+
export declare function namespaceValue(scope: Map<string, string> | XmlNamespaceScope, prefix: string): Generator<XmlNamespaceRequest, string | undefined, void>;
|
|
49
|
+
export declare function bindNamespace(scope: Map<string, string> | XmlNamespaceScope, prefix: string, value: string): Generator<XmlNamespaceRequest, Map<string, string> | XmlNamespaceScope, void>;
|
package/dist/xml-ast/frames.js
CHANGED
|
@@ -43,3 +43,23 @@ export class XmlFrames {
|
|
|
43
43
|
return frame;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
export function* namespaceValue(scope, prefix) {
|
|
47
|
+
if (scope instanceof Map)
|
|
48
|
+
return scope.get(prefix);
|
|
49
|
+
const request = { namespaceOperation: 'get', scope, prefix };
|
|
50
|
+
yield request;
|
|
51
|
+
if (!request.complete)
|
|
52
|
+
throw new TypeError('Incomplete XML namespace read');
|
|
53
|
+
return request.value;
|
|
54
|
+
}
|
|
55
|
+
export function* bindNamespace(scope, prefix, value) {
|
|
56
|
+
if (scope instanceof Map) {
|
|
57
|
+
scope.set(prefix, value);
|
|
58
|
+
return scope;
|
|
59
|
+
}
|
|
60
|
+
const request = { namespaceOperation: 'set', scope, prefix, value };
|
|
61
|
+
yield request;
|
|
62
|
+
if (!request.result)
|
|
63
|
+
throw new TypeError('Incomplete XML namespace update');
|
|
64
|
+
return request.result;
|
|
65
|
+
}
|
package/dist/xml-ast/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export type {
|
|
1
|
+
import { type XmlAttributeRequest } from "./attributes.js";
|
|
2
|
+
export type { XmlAttributeRequest, XmlAttributeRecord, XmlAttributeState } from "./attributes.js";
|
|
3
|
+
import { type XmlNamespaceRequest, type XmlFrameRequest } from "./frames.js";
|
|
4
|
+
export type { XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest } from "./frames.js";
|
|
3
5
|
export { normalizeXmlChunks } from "./characters.js";
|
|
4
6
|
import { type XmlSourceStep } from "./source.js";
|
|
5
7
|
export type { XmlSourceRead, XmlSourceStep } from "./source.js";
|
|
@@ -61,11 +63,18 @@ export interface XmlSourceLimits extends XmlStepLimits {
|
|
|
61
63
|
readonly fragmentContent?: boolean;
|
|
62
64
|
/** Store active parser frames through host-serviced requests. Requires retainTree: false. */
|
|
63
65
|
readonly storeFrames?: boolean;
|
|
66
|
+
/** Resolve namespace scopes through host-serviced requests. Requires retainTree: false. */
|
|
67
|
+
readonly storeNamespaces?: boolean;
|
|
68
|
+
/** Store attribute collections through host requests and emit resolved attributes individually. */
|
|
69
|
+
readonly storeAttributes?: boolean;
|
|
70
|
+
readonly onAttribute?: (attribute: XmlAttribute, element: XmlElement) => void;
|
|
64
71
|
}
|
|
65
|
-
export type XmlParseStep = XmlSourceStep | XmlFrameRequest;
|
|
72
|
+
export type XmlParseStep = XmlSourceStep | XmlFrameRequest | XmlNamespaceRequest | XmlAttributeRequest;
|
|
66
73
|
/** Parse a validated, BOM-free, line-normalized UTF-16 source via bounded read requests. */
|
|
67
74
|
export declare function parseXmlSourceSteps(length: number | undefined, limits?: XmlStepLimits & {
|
|
68
75
|
readonly storeFrames?: false;
|
|
76
|
+
readonly storeNamespaces?: false;
|
|
77
|
+
readonly storeAttributes?: false;
|
|
69
78
|
}): Generator<XmlSourceStep, XmlElement, void>;
|
|
70
79
|
export declare function parseXmlSourceSteps(length: number | undefined, limits: XmlSourceLimits): Generator<XmlParseStep, XmlElement, void>;
|
|
71
80
|
export declare function parseXmlSteps(input: string, limits?: XmlStepLimits): Generator<number, XmlElement, void>;
|
package/dist/xml-ast/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { XmlAttributes } from "./attributes.js";
|
|
2
|
+
import { XmlFrames, bindNamespace, namespaceValue } from "./frames.js";
|
|
2
3
|
import { validCharacter } from "./characters.js";
|
|
3
4
|
export { normalizeXmlChunks } from "./characters.js";
|
|
4
5
|
import { XmlSource } from "./source.js";
|
|
@@ -255,6 +256,10 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
255
256
|
validateLimits(limits);
|
|
256
257
|
if (limits.storeFrames && limits.retainTree !== false)
|
|
257
258
|
throw new TypeError("Stored XML frames require retainTree: false");
|
|
259
|
+
if (limits.storeAttributes && limits.retainTree !== false)
|
|
260
|
+
throw new TypeError("Stored XML attributes require retainTree: false");
|
|
261
|
+
if (limits.storeNamespaces && limits.retainTree !== false)
|
|
262
|
+
throw new TypeError("Stored XML namespaces require retainTree: false");
|
|
258
263
|
if (limits.fragmentContent && limits.retainTree !== false)
|
|
259
264
|
throw new TypeError("XML content fragments require retainTree: false");
|
|
260
265
|
const source = new XmlSource(length);
|
|
@@ -277,6 +282,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
277
282
|
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
278
283
|
textLength += text.length;
|
|
279
284
|
};
|
|
285
|
+
const initialNamespaces = limits.storeNamespaces ? yield* bindNamespace({ reference: 0, size: 0 }, "xml", xmlNamespace) : new Map([["xml", xmlNamespace]]);
|
|
280
286
|
const stack = new XmlFrames(limits.storeFrames === true);
|
|
281
287
|
let root;
|
|
282
288
|
const prolog = [];
|
|
@@ -552,9 +558,9 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
552
558
|
yield 512;
|
|
553
559
|
pendingWork -= 512;
|
|
554
560
|
}
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
let namespaces =
|
|
561
|
+
const attributes = new XmlAttributes(limits.storeAttributes === true);
|
|
562
|
+
const namespaceParent = yield* stack.peek();
|
|
563
|
+
let namespaces = namespaceParent?.namespaceScope ?? namespaceParent?.namespaces ?? (initialNamespaces instanceof Map ? new Map(initialNamespaces) : initialNamespaces);
|
|
558
564
|
let ownsNamespaces = stack.length === 0;
|
|
559
565
|
while (!repeated) {
|
|
560
566
|
const ws = (yield* skipWhitespace());
|
|
@@ -572,11 +578,11 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
572
578
|
yield 512;
|
|
573
579
|
pendingWork -= 512;
|
|
574
580
|
}
|
|
575
|
-
if (attributes
|
|
581
|
+
if ((yield* attributes.has(attribute)))
|
|
576
582
|
invalid("duplicate attribute");
|
|
577
583
|
if (++attributeCount > maxAttributes)
|
|
578
584
|
throw new XmlLimitError("maxAttributes", "XML attribute limit exceeded");
|
|
579
|
-
if (
|
|
585
|
+
if (attributes.size >= maxAttributesPerElement)
|
|
580
586
|
throw new XmlLimitError("maxAttributesPerElement", "XML attribute limit exceeded");
|
|
581
587
|
yield* skipWhitespace();
|
|
582
588
|
if ((yield* source.slice(offset++, offset)) !== "=")
|
|
@@ -609,8 +615,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
609
615
|
pendingWork -= 512;
|
|
610
616
|
}
|
|
611
617
|
admitText(value);
|
|
612
|
-
|
|
613
|
-
attrMeta.push([attribute, attrPrefix, attrLocal]);
|
|
618
|
+
yield* attributes.append(attribute, attrPrefix, attrLocal, value);
|
|
614
619
|
offset = end + 1;
|
|
615
620
|
if (attribute === "xmlns" || attribute.startsWith("xmlns:")) {
|
|
616
621
|
const nsPrefix = attribute === "xmlns" ? "" : attribute.slice(6);
|
|
@@ -618,7 +623,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
618
623
|
|| (nsPrefix === "xml") !== (value === xmlNamespace)
|
|
619
624
|
|| (nsPrefix !== "" && value === ""))
|
|
620
625
|
invalid("invalid namespace binding");
|
|
621
|
-
if (!ownsNamespaces) {
|
|
626
|
+
if (!ownsNamespaces && namespaces instanceof Map) {
|
|
622
627
|
const copy = new Map();
|
|
623
628
|
for (const [key, uri] of namespaces) {
|
|
624
629
|
copy.set(key, uri);
|
|
@@ -627,25 +632,25 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
627
632
|
namespaces = copy;
|
|
628
633
|
ownsNamespaces = true;
|
|
629
634
|
}
|
|
630
|
-
namespaces
|
|
635
|
+
namespaces = yield* bindNamespace(namespaces, nsPrefix, value);
|
|
631
636
|
if (namespaces.size > maxNamespaces)
|
|
632
637
|
throw new XmlLimitError("maxNamespaces", "XML namespace scope limit exceeded");
|
|
633
638
|
}
|
|
634
639
|
}
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
640
|
+
if (attributes.length > 0) {
|
|
641
|
+
for (let cursor = attributes.first; cursor;) {
|
|
642
|
+
const record = yield* attributes.read(cursor, false);
|
|
643
|
+
cursor = record.next;
|
|
644
|
+
const { name: attribute, prefix: attrPrefix, localName: attrLocal } = record;
|
|
639
645
|
if (attribute === "xmlns" || attribute.startsWith("xmlns:"))
|
|
640
646
|
continue;
|
|
641
647
|
pendingWork += attribute.length;
|
|
642
|
-
if (attrPrefix &&
|
|
648
|
+
if (attrPrefix && (yield* namespaceValue(namespaces, attrPrefix)) === undefined)
|
|
643
649
|
invalid("unbound attribute prefix");
|
|
644
|
-
if (
|
|
645
|
-
const key = `${attrPrefix ? namespaces
|
|
646
|
-
if (expanded
|
|
650
|
+
if (attributes.length > 1) {
|
|
651
|
+
const key = `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
|
|
652
|
+
if (yield* attributes.expanded(key))
|
|
647
653
|
invalid("duplicate expanded attribute");
|
|
648
|
-
expanded.add(key);
|
|
649
654
|
}
|
|
650
655
|
}
|
|
651
656
|
}
|
|
@@ -654,23 +659,25 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
654
659
|
yield 512;
|
|
655
660
|
pendingWork -= 512;
|
|
656
661
|
}
|
|
657
|
-
if (prefix === "xmlns" || (prefix &&
|
|
662
|
+
if (prefix === "xmlns" || (prefix && (yield* namespaceValue(namespaces, prefix)) === undefined))
|
|
658
663
|
invalid("unbound element prefix");
|
|
659
664
|
if (++nodes > maxNodes)
|
|
660
665
|
throw new XmlLimitError("maxNodes", "XML resource limit exceeded");
|
|
661
666
|
if (stack.length + 1 > maxDepth)
|
|
662
667
|
throw new XmlLimitError("maxDepth", "XML resource limit exceeded");
|
|
663
|
-
const namespace = namespaces
|
|
668
|
+
const namespace = (yield* namespaceValue(namespaces, prefix)) ?? "";
|
|
664
669
|
limits.onElement?.({ name, namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
|
|
665
670
|
admitContent();
|
|
666
671
|
const retainedAttributes = [];
|
|
667
|
-
if (retainContent
|
|
668
|
-
for (let
|
|
669
|
-
const
|
|
670
|
-
|
|
672
|
+
if (retainContent) {
|
|
673
|
+
for (let cursor = attributes.first; cursor;) {
|
|
674
|
+
const record = yield* attributes.read(cursor, !limits.storeAttributes);
|
|
675
|
+
cursor = record.next;
|
|
676
|
+
const { name: attribute, prefix: attrPrefix, localName: attrLocal, value } = record;
|
|
671
677
|
admitContent();
|
|
672
|
-
|
|
673
|
-
|
|
678
|
+
if (!limits.storeAttributes)
|
|
679
|
+
retainedAttributes.push({ name: attribute, localName: attrLocal,
|
|
680
|
+
namespace: attribute === "xmlns" || attrPrefix === "xmlns" ? xmlnsNamespace : attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : "", value });
|
|
674
681
|
pendingWork += attribute.length + 1;
|
|
675
682
|
}
|
|
676
683
|
}
|
|
@@ -679,7 +686,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
679
686
|
pendingWork -= 512;
|
|
680
687
|
}
|
|
681
688
|
const content = retain ? [] : undefined;
|
|
682
|
-
const element = { kind: "element", name, namespace, localName, children: [], text: "", content: content ?? emptyContent, attributes: retainContent ? retainedAttributes : emptyAttributes, namespaces: retainContent ? namespaces : emptyNamespaces, ...(root === undefined && retainContent ? { prolog, epilog, ...(declaration === undefined ? {} : { declaration }) } : {}) };
|
|
689
|
+
const element = { kind: "element", name, namespace, localName, 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
690
|
const parent = (yield* stack.peek());
|
|
684
691
|
if (parent) {
|
|
685
692
|
if (retainTree) {
|
|
@@ -701,11 +708,22 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
701
708
|
}
|
|
702
709
|
// Only cache compact, attribute-free spellings. Namespace resolution and
|
|
703
710
|
// 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}/>`)
|
|
711
|
+
if (empty && !attributes.length && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
|
|
705
712
|
previousEmpty = { suffix: name + "/>", name, prefix, localName };
|
|
706
713
|
limits.events?.({ type: "open", element, parent: parent?.element });
|
|
714
|
+
if (limits.storeAttributes && limits.onAttribute && retainContent) {
|
|
715
|
+
yield 1;
|
|
716
|
+
for (let cursor = attributes.first; cursor;) {
|
|
717
|
+
const attribute = yield* attributes.read(cursor, true);
|
|
718
|
+
cursor = attribute.next;
|
|
719
|
+
const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? xmlnsNamespace
|
|
720
|
+
: attribute.prefix ? (yield* namespaceValue(namespaces, attribute.prefix)) : "";
|
|
721
|
+
limits.onAttribute({ name: attribute.name, localName: attribute.localName, namespace, value: attribute.value }, element);
|
|
722
|
+
yield 1;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
707
725
|
if (!empty)
|
|
708
|
-
yield* stack.push({ element, content, name, namespaces });
|
|
726
|
+
yield* stack.push({ element, content, name, namespaces: namespaces instanceof Map ? namespaces : new Map(), ...(namespaces instanceof Map ? {} : { namespaceScope: namespaces }) });
|
|
709
727
|
else
|
|
710
728
|
limits.events?.({ type: "close", element, parent: parent?.element });
|
|
711
729
|
}
|