@btc-embedded/cdk-extensions 0.23.4 → 0.23.5
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/.jsii +5 -5
- package/CHANGELOG.md +7 -0
- package/assets/cli/catnip.js +154 -166
- package/lib/constructs/EventPipe.js +1 -1
- package/lib/constructs/ExportedService.js +1 -1
- package/lib/constructs/S3Bucket.js +1 -1
- package/lib/constructs/SecureRestApi.js +1 -1
- package/lib/constructs/SecureRestApiV2.js +1 -1
- package/lib/constructs/api-keys/ApiKeyClientAuthorization.js +1 -1
- package/lib/constructs/api-keys/ApiKeyManagement.js +1 -1
- package/lib/constructs/api-keys/ApiKeyPreTokenHandler.js +1 -1
- package/lib/constructs/api-keys/ApiKeyStore.js +1 -1
- package/lib/extensions/ApiGatewayExtension.js +1 -1
- package/lib/extensions/ApplicationContainer.js +1 -1
- package/lib/extensions/ApplicationLoadBalancerExtension.js +1 -1
- package/lib/extensions/ApplicationLoadBalancerExtensionV2.js +1 -1
- package/lib/extensions/CloudMapExtension.js +1 -1
- package/lib/extensions/DeactivatableServiceExtension.js +1 -1
- package/lib/extensions/DeploymentConfigExtension.js +1 -1
- package/lib/extensions/DocumentDbAccessExtension.js +1 -1
- package/lib/extensions/DomainEventMessagingExtension.js +1 -1
- package/lib/extensions/EfsMountExtension.js +1 -1
- package/lib/extensions/ExtraContainerExtension.js +1 -1
- package/lib/extensions/HTTPApiExtension.js +1 -1
- package/lib/extensions/LogExtension.js +1 -1
- package/lib/extensions/ModifyContainerDefinitionExtension.js +1 -1
- package/lib/extensions/ModifyTaskDefinitionExtension.js +1 -1
- package/lib/extensions/OpenIdExtension.js +1 -1
- package/lib/extensions/OpenTelemetryExtension.js +1 -1
- package/lib/extensions/PostgresDbAccessExtension.js +1 -1
- package/lib/extensions/SharedVolumeExtension.js +1 -1
- package/lib/extensions/TcpKeepAliveExtension.js +1 -1
- package/lib/platform/ApiGateway.js +1 -1
- package/lib/platform/ApiGatewayVpcLink.js +2 -2
- package/lib/platform/ApplicationLoadBalancer.js +1 -1
- package/lib/platform/ApplicationLoadBalancerV2.d.ts +1 -0
- package/lib/platform/ApplicationLoadBalancerV2.js +10 -3
- package/lib/platform/AuroraPostgresDB.js +2 -2
- package/lib/platform/BTCLogGroup.js +1 -1
- package/lib/platform/CognitoUserPool.js +2 -2
- package/lib/platform/DefaultUserPoolClients.js +1 -1
- package/lib/platform/DocumentDB.js +2 -2
- package/lib/platform/EcsCluster.js +1 -1
- package/lib/platform/EfsFileSystem.js +1 -1
- package/lib/platform/HostedZone.js +1 -1
- package/lib/platform/PrivateDnsNamespace.js +1 -1
- package/lib/platform/ResourceServer.js +1 -1
- package/lib/platform/Vpc.js +1 -1
- package/lib/platform/VpcV2.js +1 -1
- package/lib/stacks/ApplicationStack.js +1 -1
- package/lib/utils/BasePlatformStackResolver.js +1 -1
- package/lib/utils/StackParameter.js +1 -1
- package/node_modules/@nodable/entities/README.md +41 -0
- package/node_modules/@nodable/entities/package.json +54 -0
- package/node_modules/@nodable/entities/src/EntityDecoder.js +543 -0
- package/node_modules/@nodable/entities/src/EntityEncoder.js +194 -0
- package/node_modules/@nodable/entities/src/entities.js +1177 -0
- package/node_modules/@nodable/entities/src/entityTries.js +49 -0
- package/node_modules/@nodable/entities/src/index.d.ts +264 -0
- package/node_modules/@nodable/entities/src/index.js +29 -0
- package/node_modules/fast-xml-builder/CHANGELOG.md +40 -0
- package/node_modules/fast-xml-builder/LICENSE +21 -0
- package/node_modules/fast-xml-builder/README.md +74 -0
- package/node_modules/fast-xml-builder/lib/fxb.cjs +1 -0
- package/node_modules/fast-xml-builder/lib/fxb.d.cts +270 -0
- package/node_modules/fast-xml-builder/lib/fxb.min.js +2 -0
- package/node_modules/fast-xml-builder/lib/fxb.min.js.map +1 -0
- package/node_modules/fast-xml-builder/package.json +81 -0
- package/node_modules/fast-xml-builder/src/fxb.d.ts +270 -0
- package/node_modules/fast-xml-builder/src/fxb.js +599 -0
- package/node_modules/fast-xml-builder/src/ignoreAttributes.js +18 -0
- package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +359 -0
- package/node_modules/fast-xml-builder/src/util.js +16 -0
- package/node_modules/fast-xml-parser/CHANGELOG.md +165 -0
- package/node_modules/fast-xml-parser/README.md +21 -44
- package/node_modules/fast-xml-parser/lib/fxbuilder.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxbuilder.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.cjs +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.d.cts +343 -31
- package/node_modules/fast-xml-parser/lib/fxp.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxvalidator.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxvalidator.min.js.map +1 -1
- package/node_modules/fast-xml-parser/package.json +13 -8
- package/node_modules/fast-xml-parser/src/fxp.d.ts +335 -30
- package/node_modules/fast-xml-parser/src/fxp.js +1 -1
- package/node_modules/fast-xml-parser/src/util.js +18 -25
- package/node_modules/fast-xml-parser/src/v6/EntitiesParser.js +89 -87
- package/node_modules/fast-xml-parser/src/v6/OptionsBuilder.js +10 -10
- package/node_modules/fast-xml-parser/src/v6/OutputBuilders/BaseOutputBuilder.js +23 -23
- package/node_modules/fast-xml-parser/src/v6/OutputBuilders/JsArrBuilder.js +29 -29
- package/node_modules/fast-xml-parser/src/v6/OutputBuilders/JsMinArrBuilder.js +1 -1
- package/node_modules/fast-xml-parser/src/v6/OutputBuilders/JsObjBuilder.js +39 -39
- package/node_modules/fast-xml-parser/src/v6/OutputBuilders/ParserOptionsBuilder.js +21 -21
- package/node_modules/fast-xml-parser/src/v6/XMLParser.js +22 -22
- package/node_modules/fast-xml-parser/src/v6/valueParsers/EntitiesParser.js +85 -85
- package/node_modules/fast-xml-parser/src/validator.js +34 -34
- package/node_modules/fast-xml-parser/src/xmlbuilder/json2xml.js +5 -284
- package/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js +335 -293
- package/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js +160 -43
- package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +540 -308
- package/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js +26 -26
- package/node_modules/fast-xml-parser/src/xmlparser/node2json.js +99 -41
- package/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js +10 -10
- package/node_modules/path-expression-matcher/LICENSE +21 -0
- package/node_modules/path-expression-matcher/README.md +872 -0
- package/node_modules/path-expression-matcher/lib/pem.cjs +1 -0
- package/node_modules/path-expression-matcher/lib/pem.d.cts +634 -0
- package/node_modules/path-expression-matcher/lib/pem.min.js +2 -0
- package/node_modules/path-expression-matcher/lib/pem.min.js.map +1 -0
- package/node_modules/path-expression-matcher/package.json +78 -0
- package/node_modules/path-expression-matcher/src/Expression.js +232 -0
- package/node_modules/path-expression-matcher/src/ExpressionSet.js +209 -0
- package/node_modules/path-expression-matcher/src/Matcher.js +570 -0
- package/node_modules/path-expression-matcher/src/index.d.ts +523 -0
- package/node_modules/path-expression-matcher/src/index.js +29 -0
- package/node_modules/strnum/CHANGELOG.md +12 -2
- package/node_modules/strnum/README.md +1 -0
- package/node_modules/strnum/package.json +5 -4
- package/node_modules/strnum/strnum.js +99 -65
- package/node_modules/xml-naming/README.md +189 -0
- package/node_modules/xml-naming/package.json +54 -0
- package/node_modules/xml-naming/src/index.d.ts +74 -0
- package/node_modules/xml-naming/src/index.js +270 -0
- package/package.json +3 -2
- package/renovate.json5 +1 -0
- package/node_modules/fast-xml-parser/src/xmlbuilder/orderedJs2Xml.js +0 -134
- package/node_modules/strnum/.github/SECURITY.md +0 -5
- package/node_modules/strnum/.vscode/launch.json +0 -25
- package/node_modules/strnum/algo.stflow +0 -84
- package/node_modules/strnum/strnum.test.js +0 -173
- package/node_modules/strnum/test.js +0 -9
- /package/node_modules/{fast-xml-parser/src/xmlbuilder → fast-xml-builder/src}/prettifyJs2Xml.js +0 -0
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import ExpressionSet from "./ExpressionSet.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MatcherView - A lightweight read-only view over a Matcher's internal state.
|
|
5
|
+
*
|
|
6
|
+
* Created once by Matcher and reused across all callbacks. Holds a direct
|
|
7
|
+
* reference to the parent Matcher so it always reflects current parser state
|
|
8
|
+
* with zero copying or freezing overhead.
|
|
9
|
+
*
|
|
10
|
+
* Users receive this via {@link Matcher#readOnly} or directly from parser
|
|
11
|
+
* callbacks. It exposes all query and matching methods but has no mutation
|
|
12
|
+
* methods — misuse is caught at the TypeScript level rather than at runtime.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const matcher = new Matcher();
|
|
16
|
+
* const view = matcher.readOnly();
|
|
17
|
+
*
|
|
18
|
+
* matcher.push("root", {});
|
|
19
|
+
* view.getCurrentTag(); // "root"
|
|
20
|
+
* view.getDepth(); // 1
|
|
21
|
+
*/
|
|
22
|
+
export class MatcherView {
|
|
23
|
+
/**
|
|
24
|
+
* @param {Matcher} matcher - The parent Matcher instance to read from.
|
|
25
|
+
*/
|
|
26
|
+
constructor(matcher) {
|
|
27
|
+
this._matcher = matcher;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get the path separator used by the parent matcher.
|
|
32
|
+
* @returns {string}
|
|
33
|
+
*/
|
|
34
|
+
get separator() {
|
|
35
|
+
return this._matcher.separator;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get current tag name.
|
|
40
|
+
* @returns {string|undefined}
|
|
41
|
+
*/
|
|
42
|
+
getCurrentTag() {
|
|
43
|
+
const path = this._matcher.path;
|
|
44
|
+
return path.length > 0 ? path[path.length - 1].tag : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get current namespace.
|
|
49
|
+
* @returns {string|undefined}
|
|
50
|
+
*/
|
|
51
|
+
getCurrentNamespace() {
|
|
52
|
+
const path = this._matcher.path;
|
|
53
|
+
return path.length > 0 ? path[path.length - 1].namespace : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current node's attribute value.
|
|
58
|
+
* @param {string} attrName
|
|
59
|
+
* @returns {*}
|
|
60
|
+
*/
|
|
61
|
+
getAttrValue(attrName) {
|
|
62
|
+
const path = this._matcher.path;
|
|
63
|
+
if (path.length === 0) return undefined;
|
|
64
|
+
return path[path.length - 1].values?.[attrName];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if current node has an attribute.
|
|
69
|
+
* @param {string} attrName
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
hasAttr(attrName) {
|
|
73
|
+
const path = this._matcher.path;
|
|
74
|
+
if (path.length === 0) return false;
|
|
75
|
+
const current = path[path.length - 1];
|
|
76
|
+
return current.values !== undefined && attrName in current.values;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get current node's sibling position (child index in parent).
|
|
81
|
+
* @returns {number}
|
|
82
|
+
*/
|
|
83
|
+
getPosition() {
|
|
84
|
+
const path = this._matcher.path;
|
|
85
|
+
if (path.length === 0) return -1;
|
|
86
|
+
return path[path.length - 1].position ?? 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get current node's repeat counter (occurrence count of this tag name).
|
|
91
|
+
* @returns {number}
|
|
92
|
+
*/
|
|
93
|
+
getCounter() {
|
|
94
|
+
const path = this._matcher.path;
|
|
95
|
+
if (path.length === 0) return -1;
|
|
96
|
+
return path[path.length - 1].counter ?? 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get current node's sibling index (alias for getPosition).
|
|
101
|
+
* @returns {number}
|
|
102
|
+
* @deprecated Use getPosition() or getCounter() instead
|
|
103
|
+
*/
|
|
104
|
+
getIndex() {
|
|
105
|
+
return this.getPosition();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get current path depth.
|
|
110
|
+
* @returns {number}
|
|
111
|
+
*/
|
|
112
|
+
getDepth() {
|
|
113
|
+
return this._matcher.path.length;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get path as string.
|
|
118
|
+
* @param {string} [separator] - Optional separator (uses default if not provided)
|
|
119
|
+
* @param {boolean} [includeNamespace=true]
|
|
120
|
+
* @returns {string}
|
|
121
|
+
*/
|
|
122
|
+
toString(separator, includeNamespace = true) {
|
|
123
|
+
return this._matcher.toString(separator, includeNamespace);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get path as array of tag names.
|
|
128
|
+
* @returns {string[]}
|
|
129
|
+
*/
|
|
130
|
+
toArray() {
|
|
131
|
+
return this._matcher.path.map(n => n.tag);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Match current path against an Expression.
|
|
136
|
+
* @param {Expression} expression
|
|
137
|
+
* @returns {boolean}
|
|
138
|
+
*/
|
|
139
|
+
matches(expression) {
|
|
140
|
+
return this._matcher.matches(expression);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Match any expression in the given set against the current path.
|
|
145
|
+
* @param {ExpressionSet} exprSet
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
matchesAny(exprSet) {
|
|
149
|
+
return exprSet.matchesAny(this._matcher);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Matcher - Tracks current path in XML/JSON tree and matches against Expressions.
|
|
155
|
+
*
|
|
156
|
+
* The matcher maintains a stack of nodes representing the current path from root to
|
|
157
|
+
* current tag. It only stores attribute values for the current (top) node to minimize
|
|
158
|
+
* memory usage. Sibling tracking is used to auto-calculate position and counter.
|
|
159
|
+
*
|
|
160
|
+
* Use {@link Matcher#readOnly} to obtain a {@link MatcherView} safe to pass to
|
|
161
|
+
* user callbacks — it always reflects current state with no Proxy overhead.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* const matcher = new Matcher();
|
|
165
|
+
* matcher.push("root", {});
|
|
166
|
+
* matcher.push("users", {});
|
|
167
|
+
* matcher.push("user", { id: "123", type: "admin" });
|
|
168
|
+
*
|
|
169
|
+
* const expr = new Expression("root.users.user");
|
|
170
|
+
* matcher.matches(expr); // true
|
|
171
|
+
*/
|
|
172
|
+
export default class Matcher {
|
|
173
|
+
/**
|
|
174
|
+
* Create a new Matcher.
|
|
175
|
+
* @param {Object} [options={}]
|
|
176
|
+
* @param {string} [options.separator='.'] - Default path separator
|
|
177
|
+
*/
|
|
178
|
+
constructor(options = {}) {
|
|
179
|
+
this.separator = options.separator || '.';
|
|
180
|
+
this.path = [];
|
|
181
|
+
this.siblingStacks = [];
|
|
182
|
+
// Each path node: { tag, values, position, counter, namespace? }
|
|
183
|
+
// values only present for current (last) node
|
|
184
|
+
// Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level
|
|
185
|
+
this._pathStringCache = null;
|
|
186
|
+
this._view = new MatcherView(this);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Push a new tag onto the path.
|
|
191
|
+
* @param {string} tagName
|
|
192
|
+
* @param {Object|null} [attrValues=null]
|
|
193
|
+
* @param {string|null} [namespace=null]
|
|
194
|
+
*/
|
|
195
|
+
push(tagName, attrValues = null, namespace = null) {
|
|
196
|
+
this._pathStringCache = null;
|
|
197
|
+
|
|
198
|
+
// Remove values from previous current node (now becoming ancestor)
|
|
199
|
+
if (this.path.length > 0) {
|
|
200
|
+
this.path[this.path.length - 1].values = undefined;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Get or create sibling tracking for current level
|
|
204
|
+
const currentLevel = this.path.length;
|
|
205
|
+
if (!this.siblingStacks[currentLevel]) {
|
|
206
|
+
this.siblingStacks[currentLevel] = new Map();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const siblings = this.siblingStacks[currentLevel];
|
|
210
|
+
|
|
211
|
+
// Create a unique key for sibling tracking that includes namespace
|
|
212
|
+
const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;
|
|
213
|
+
|
|
214
|
+
// Calculate counter (how many times this tag appeared at this level)
|
|
215
|
+
const counter = siblings.get(siblingKey) || 0;
|
|
216
|
+
|
|
217
|
+
// Calculate position (total children at this level so far)
|
|
218
|
+
let position = 0;
|
|
219
|
+
for (const count of siblings.values()) {
|
|
220
|
+
position += count;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Update sibling count for this tag
|
|
224
|
+
siblings.set(siblingKey, counter + 1);
|
|
225
|
+
|
|
226
|
+
// Create new node
|
|
227
|
+
const node = {
|
|
228
|
+
tag: tagName,
|
|
229
|
+
position: position,
|
|
230
|
+
counter: counter
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
if (namespace !== null && namespace !== undefined) {
|
|
234
|
+
node.namespace = namespace;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (attrValues !== null && attrValues !== undefined) {
|
|
238
|
+
node.values = attrValues;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this.path.push(node);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Pop the last tag from the path.
|
|
246
|
+
* @returns {Object|undefined} The popped node
|
|
247
|
+
*/
|
|
248
|
+
pop() {
|
|
249
|
+
if (this.path.length === 0) return undefined;
|
|
250
|
+
this._pathStringCache = null;
|
|
251
|
+
|
|
252
|
+
const node = this.path.pop();
|
|
253
|
+
|
|
254
|
+
if (this.siblingStacks.length > this.path.length + 1) {
|
|
255
|
+
this.siblingStacks.length = this.path.length + 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return node;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Update current node's attribute values.
|
|
263
|
+
* Useful when attributes are parsed after push.
|
|
264
|
+
* @param {Object} attrValues
|
|
265
|
+
*/
|
|
266
|
+
updateCurrent(attrValues) {
|
|
267
|
+
if (this.path.length > 0) {
|
|
268
|
+
const current = this.path[this.path.length - 1];
|
|
269
|
+
if (attrValues !== null && attrValues !== undefined) {
|
|
270
|
+
current.values = attrValues;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get current tag name.
|
|
277
|
+
* @returns {string|undefined}
|
|
278
|
+
*/
|
|
279
|
+
getCurrentTag() {
|
|
280
|
+
return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get current namespace.
|
|
285
|
+
* @returns {string|undefined}
|
|
286
|
+
*/
|
|
287
|
+
getCurrentNamespace() {
|
|
288
|
+
return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get current node's attribute value.
|
|
293
|
+
* @param {string} attrName
|
|
294
|
+
* @returns {*}
|
|
295
|
+
*/
|
|
296
|
+
getAttrValue(attrName) {
|
|
297
|
+
if (this.path.length === 0) return undefined;
|
|
298
|
+
return this.path[this.path.length - 1].values?.[attrName];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Check if current node has an attribute.
|
|
303
|
+
* @param {string} attrName
|
|
304
|
+
* @returns {boolean}
|
|
305
|
+
*/
|
|
306
|
+
hasAttr(attrName) {
|
|
307
|
+
if (this.path.length === 0) return false;
|
|
308
|
+
const current = this.path[this.path.length - 1];
|
|
309
|
+
return current.values !== undefined && attrName in current.values;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get current node's sibling position (child index in parent).
|
|
314
|
+
* @returns {number}
|
|
315
|
+
*/
|
|
316
|
+
getPosition() {
|
|
317
|
+
if (this.path.length === 0) return -1;
|
|
318
|
+
return this.path[this.path.length - 1].position ?? 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Get current node's repeat counter (occurrence count of this tag name).
|
|
323
|
+
* @returns {number}
|
|
324
|
+
*/
|
|
325
|
+
getCounter() {
|
|
326
|
+
if (this.path.length === 0) return -1;
|
|
327
|
+
return this.path[this.path.length - 1].counter ?? 0;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Get current node's sibling index (alias for getPosition).
|
|
332
|
+
* @returns {number}
|
|
333
|
+
* @deprecated Use getPosition() or getCounter() instead
|
|
334
|
+
*/
|
|
335
|
+
getIndex() {
|
|
336
|
+
return this.getPosition();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get current path depth.
|
|
341
|
+
* @returns {number}
|
|
342
|
+
*/
|
|
343
|
+
getDepth() {
|
|
344
|
+
return this.path.length;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get path as string.
|
|
349
|
+
* @param {string} [separator] - Optional separator (uses default if not provided)
|
|
350
|
+
* @param {boolean} [includeNamespace=true]
|
|
351
|
+
* @returns {string}
|
|
352
|
+
*/
|
|
353
|
+
toString(separator, includeNamespace = true) {
|
|
354
|
+
const sep = separator || this.separator;
|
|
355
|
+
const isDefault = (sep === this.separator && includeNamespace === true);
|
|
356
|
+
|
|
357
|
+
if (isDefault) {
|
|
358
|
+
if (this._pathStringCache !== null) {
|
|
359
|
+
return this._pathStringCache;
|
|
360
|
+
}
|
|
361
|
+
const result = this.path.map(n =>
|
|
362
|
+
(n.namespace) ? `${n.namespace}:${n.tag}` : n.tag
|
|
363
|
+
).join(sep);
|
|
364
|
+
this._pathStringCache = result;
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return this.path.map(n =>
|
|
369
|
+
(includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag
|
|
370
|
+
).join(sep);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Get path as array of tag names.
|
|
375
|
+
* @returns {string[]}
|
|
376
|
+
*/
|
|
377
|
+
toArray() {
|
|
378
|
+
return this.path.map(n => n.tag);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Reset the path to empty.
|
|
383
|
+
*/
|
|
384
|
+
reset() {
|
|
385
|
+
this._pathStringCache = null;
|
|
386
|
+
this.path = [];
|
|
387
|
+
this.siblingStacks = [];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Match current path against an Expression.
|
|
392
|
+
* @param {Expression} expression
|
|
393
|
+
* @returns {boolean}
|
|
394
|
+
*/
|
|
395
|
+
matches(expression) {
|
|
396
|
+
const segments = expression.segments;
|
|
397
|
+
|
|
398
|
+
if (segments.length === 0) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (expression.hasDeepWildcard()) {
|
|
403
|
+
return this._matchWithDeepWildcard(segments);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return this._matchSimple(segments);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
_matchSimple(segments) {
|
|
413
|
+
if (this.path.length !== segments.length) {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (let i = 0; i < segments.length; i++) {
|
|
418
|
+
if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* @private
|
|
428
|
+
*/
|
|
429
|
+
_matchWithDeepWildcard(segments) {
|
|
430
|
+
let pathIdx = this.path.length - 1;
|
|
431
|
+
let segIdx = segments.length - 1;
|
|
432
|
+
|
|
433
|
+
while (segIdx >= 0 && pathIdx >= 0) {
|
|
434
|
+
const segment = segments[segIdx];
|
|
435
|
+
|
|
436
|
+
if (segment.type === 'deep-wildcard') {
|
|
437
|
+
segIdx--;
|
|
438
|
+
|
|
439
|
+
if (segIdx < 0) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const nextSeg = segments[segIdx];
|
|
444
|
+
let found = false;
|
|
445
|
+
|
|
446
|
+
for (let i = pathIdx; i >= 0; i--) {
|
|
447
|
+
if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {
|
|
448
|
+
pathIdx = i - 1;
|
|
449
|
+
segIdx--;
|
|
450
|
+
found = true;
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (!found) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
pathIdx--;
|
|
463
|
+
segIdx--;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return segIdx < 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
_matchSegment(segment, node, isCurrentNode) {
|
|
474
|
+
if (segment.tag !== '*' && segment.tag !== node.tag) {
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (segment.namespace !== undefined) {
|
|
479
|
+
if (segment.namespace !== '*' && segment.namespace !== node.namespace) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (segment.attrName !== undefined) {
|
|
485
|
+
if (!isCurrentNode) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (!node.values || !(segment.attrName in node.values)) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (segment.attrValue !== undefined) {
|
|
494
|
+
if (String(node.values[segment.attrName]) !== String(segment.attrValue)) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (segment.position !== undefined) {
|
|
501
|
+
if (!isCurrentNode) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const counter = node.counter ?? 0;
|
|
506
|
+
|
|
507
|
+
if (segment.position === 'first' && counter !== 0) {
|
|
508
|
+
return false;
|
|
509
|
+
} else if (segment.position === 'odd' && counter % 2 !== 1) {
|
|
510
|
+
return false;
|
|
511
|
+
} else if (segment.position === 'even' && counter % 2 !== 0) {
|
|
512
|
+
return false;
|
|
513
|
+
} else if (segment.position === 'nth' && counter !== segment.positionValue) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Match any expression in the given set against the current path.
|
|
523
|
+
* @param {ExpressionSet} exprSet
|
|
524
|
+
* @returns {boolean}
|
|
525
|
+
*/
|
|
526
|
+
matchesAny(exprSet) {
|
|
527
|
+
return exprSet.matchesAny(this);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Create a snapshot of current state.
|
|
532
|
+
* @returns {Object}
|
|
533
|
+
*/
|
|
534
|
+
snapshot() {
|
|
535
|
+
return {
|
|
536
|
+
path: this.path.map(node => ({ ...node })),
|
|
537
|
+
siblingStacks: this.siblingStacks.map(map => new Map(map))
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Restore state from snapshot.
|
|
543
|
+
* @param {Object} snapshot
|
|
544
|
+
*/
|
|
545
|
+
restore(snapshot) {
|
|
546
|
+
this._pathStringCache = null;
|
|
547
|
+
this.path = snapshot.path.map(node => ({ ...node }));
|
|
548
|
+
this.siblingStacks = snapshot.siblingStacks.map(map => new Map(map));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Return the read-only {@link MatcherView} for this matcher.
|
|
553
|
+
*
|
|
554
|
+
* The same instance is returned on every call — no allocation occurs.
|
|
555
|
+
* It always reflects the current parser state and is safe to pass to
|
|
556
|
+
* user callbacks without risk of accidental mutation.
|
|
557
|
+
*
|
|
558
|
+
* @returns {MatcherView}
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* const view = matcher.readOnly();
|
|
562
|
+
* // pass view to callbacks — it stays in sync automatically
|
|
563
|
+
* view.matches(expr); // ✓
|
|
564
|
+
* view.getCurrentTag(); // ✓
|
|
565
|
+
* // view.push(...) // ✗ method does not exist — caught by TypeScript
|
|
566
|
+
*/
|
|
567
|
+
readOnly() {
|
|
568
|
+
return this._view;
|
|
569
|
+
}
|
|
570
|
+
}
|