@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.
Files changed (135) hide show
  1. package/.jsii +5 -5
  2. package/CHANGELOG.md +7 -0
  3. package/assets/cli/catnip.js +154 -166
  4. package/lib/constructs/EventPipe.js +1 -1
  5. package/lib/constructs/ExportedService.js +1 -1
  6. package/lib/constructs/S3Bucket.js +1 -1
  7. package/lib/constructs/SecureRestApi.js +1 -1
  8. package/lib/constructs/SecureRestApiV2.js +1 -1
  9. package/lib/constructs/api-keys/ApiKeyClientAuthorization.js +1 -1
  10. package/lib/constructs/api-keys/ApiKeyManagement.js +1 -1
  11. package/lib/constructs/api-keys/ApiKeyPreTokenHandler.js +1 -1
  12. package/lib/constructs/api-keys/ApiKeyStore.js +1 -1
  13. package/lib/extensions/ApiGatewayExtension.js +1 -1
  14. package/lib/extensions/ApplicationContainer.js +1 -1
  15. package/lib/extensions/ApplicationLoadBalancerExtension.js +1 -1
  16. package/lib/extensions/ApplicationLoadBalancerExtensionV2.js +1 -1
  17. package/lib/extensions/CloudMapExtension.js +1 -1
  18. package/lib/extensions/DeactivatableServiceExtension.js +1 -1
  19. package/lib/extensions/DeploymentConfigExtension.js +1 -1
  20. package/lib/extensions/DocumentDbAccessExtension.js +1 -1
  21. package/lib/extensions/DomainEventMessagingExtension.js +1 -1
  22. package/lib/extensions/EfsMountExtension.js +1 -1
  23. package/lib/extensions/ExtraContainerExtension.js +1 -1
  24. package/lib/extensions/HTTPApiExtension.js +1 -1
  25. package/lib/extensions/LogExtension.js +1 -1
  26. package/lib/extensions/ModifyContainerDefinitionExtension.js +1 -1
  27. package/lib/extensions/ModifyTaskDefinitionExtension.js +1 -1
  28. package/lib/extensions/OpenIdExtension.js +1 -1
  29. package/lib/extensions/OpenTelemetryExtension.js +1 -1
  30. package/lib/extensions/PostgresDbAccessExtension.js +1 -1
  31. package/lib/extensions/SharedVolumeExtension.js +1 -1
  32. package/lib/extensions/TcpKeepAliveExtension.js +1 -1
  33. package/lib/platform/ApiGateway.js +1 -1
  34. package/lib/platform/ApiGatewayVpcLink.js +2 -2
  35. package/lib/platform/ApplicationLoadBalancer.js +1 -1
  36. package/lib/platform/ApplicationLoadBalancerV2.d.ts +1 -0
  37. package/lib/platform/ApplicationLoadBalancerV2.js +10 -3
  38. package/lib/platform/AuroraPostgresDB.js +2 -2
  39. package/lib/platform/BTCLogGroup.js +1 -1
  40. package/lib/platform/CognitoUserPool.js +2 -2
  41. package/lib/platform/DefaultUserPoolClients.js +1 -1
  42. package/lib/platform/DocumentDB.js +2 -2
  43. package/lib/platform/EcsCluster.js +1 -1
  44. package/lib/platform/EfsFileSystem.js +1 -1
  45. package/lib/platform/HostedZone.js +1 -1
  46. package/lib/platform/PrivateDnsNamespace.js +1 -1
  47. package/lib/platform/ResourceServer.js +1 -1
  48. package/lib/platform/Vpc.js +1 -1
  49. package/lib/platform/VpcV2.js +1 -1
  50. package/lib/stacks/ApplicationStack.js +1 -1
  51. package/lib/utils/BasePlatformStackResolver.js +1 -1
  52. package/lib/utils/StackParameter.js +1 -1
  53. package/node_modules/@nodable/entities/README.md +41 -0
  54. package/node_modules/@nodable/entities/package.json +54 -0
  55. package/node_modules/@nodable/entities/src/EntityDecoder.js +543 -0
  56. package/node_modules/@nodable/entities/src/EntityEncoder.js +194 -0
  57. package/node_modules/@nodable/entities/src/entities.js +1177 -0
  58. package/node_modules/@nodable/entities/src/entityTries.js +49 -0
  59. package/node_modules/@nodable/entities/src/index.d.ts +264 -0
  60. package/node_modules/@nodable/entities/src/index.js +29 -0
  61. package/node_modules/fast-xml-builder/CHANGELOG.md +40 -0
  62. package/node_modules/fast-xml-builder/LICENSE +21 -0
  63. package/node_modules/fast-xml-builder/README.md +74 -0
  64. package/node_modules/fast-xml-builder/lib/fxb.cjs +1 -0
  65. package/node_modules/fast-xml-builder/lib/fxb.d.cts +270 -0
  66. package/node_modules/fast-xml-builder/lib/fxb.min.js +2 -0
  67. package/node_modules/fast-xml-builder/lib/fxb.min.js.map +1 -0
  68. package/node_modules/fast-xml-builder/package.json +81 -0
  69. package/node_modules/fast-xml-builder/src/fxb.d.ts +270 -0
  70. package/node_modules/fast-xml-builder/src/fxb.js +599 -0
  71. package/node_modules/fast-xml-builder/src/ignoreAttributes.js +18 -0
  72. package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +359 -0
  73. package/node_modules/fast-xml-builder/src/util.js +16 -0
  74. package/node_modules/fast-xml-parser/CHANGELOG.md +165 -0
  75. package/node_modules/fast-xml-parser/README.md +21 -44
  76. package/node_modules/fast-xml-parser/lib/fxbuilder.min.js +1 -1
  77. package/node_modules/fast-xml-parser/lib/fxbuilder.min.js.map +1 -1
  78. package/node_modules/fast-xml-parser/lib/fxp.cjs +1 -1
  79. package/node_modules/fast-xml-parser/lib/fxp.d.cts +343 -31
  80. package/node_modules/fast-xml-parser/lib/fxp.min.js +1 -1
  81. package/node_modules/fast-xml-parser/lib/fxp.min.js.map +1 -1
  82. package/node_modules/fast-xml-parser/lib/fxparser.min.js +1 -1
  83. package/node_modules/fast-xml-parser/lib/fxparser.min.js.map +1 -1
  84. package/node_modules/fast-xml-parser/lib/fxvalidator.min.js +1 -1
  85. package/node_modules/fast-xml-parser/lib/fxvalidator.min.js.map +1 -1
  86. package/node_modules/fast-xml-parser/package.json +13 -8
  87. package/node_modules/fast-xml-parser/src/fxp.d.ts +335 -30
  88. package/node_modules/fast-xml-parser/src/fxp.js +1 -1
  89. package/node_modules/fast-xml-parser/src/util.js +18 -25
  90. package/node_modules/fast-xml-parser/src/v6/EntitiesParser.js +89 -87
  91. package/node_modules/fast-xml-parser/src/v6/OptionsBuilder.js +10 -10
  92. package/node_modules/fast-xml-parser/src/v6/OutputBuilders/BaseOutputBuilder.js +23 -23
  93. package/node_modules/fast-xml-parser/src/v6/OutputBuilders/JsArrBuilder.js +29 -29
  94. package/node_modules/fast-xml-parser/src/v6/OutputBuilders/JsMinArrBuilder.js +1 -1
  95. package/node_modules/fast-xml-parser/src/v6/OutputBuilders/JsObjBuilder.js +39 -39
  96. package/node_modules/fast-xml-parser/src/v6/OutputBuilders/ParserOptionsBuilder.js +21 -21
  97. package/node_modules/fast-xml-parser/src/v6/XMLParser.js +22 -22
  98. package/node_modules/fast-xml-parser/src/v6/valueParsers/EntitiesParser.js +85 -85
  99. package/node_modules/fast-xml-parser/src/validator.js +34 -34
  100. package/node_modules/fast-xml-parser/src/xmlbuilder/json2xml.js +5 -284
  101. package/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js +335 -293
  102. package/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js +160 -43
  103. package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +540 -308
  104. package/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js +26 -26
  105. package/node_modules/fast-xml-parser/src/xmlparser/node2json.js +99 -41
  106. package/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js +10 -10
  107. package/node_modules/path-expression-matcher/LICENSE +21 -0
  108. package/node_modules/path-expression-matcher/README.md +872 -0
  109. package/node_modules/path-expression-matcher/lib/pem.cjs +1 -0
  110. package/node_modules/path-expression-matcher/lib/pem.d.cts +634 -0
  111. package/node_modules/path-expression-matcher/lib/pem.min.js +2 -0
  112. package/node_modules/path-expression-matcher/lib/pem.min.js.map +1 -0
  113. package/node_modules/path-expression-matcher/package.json +78 -0
  114. package/node_modules/path-expression-matcher/src/Expression.js +232 -0
  115. package/node_modules/path-expression-matcher/src/ExpressionSet.js +209 -0
  116. package/node_modules/path-expression-matcher/src/Matcher.js +570 -0
  117. package/node_modules/path-expression-matcher/src/index.d.ts +523 -0
  118. package/node_modules/path-expression-matcher/src/index.js +29 -0
  119. package/node_modules/strnum/CHANGELOG.md +12 -2
  120. package/node_modules/strnum/README.md +1 -0
  121. package/node_modules/strnum/package.json +5 -4
  122. package/node_modules/strnum/strnum.js +99 -65
  123. package/node_modules/xml-naming/README.md +189 -0
  124. package/node_modules/xml-naming/package.json +54 -0
  125. package/node_modules/xml-naming/src/index.d.ts +74 -0
  126. package/node_modules/xml-naming/src/index.js +270 -0
  127. package/package.json +3 -2
  128. package/renovate.json5 +1 -0
  129. package/node_modules/fast-xml-parser/src/xmlbuilder/orderedJs2Xml.js +0 -134
  130. package/node_modules/strnum/.github/SECURITY.md +0 -5
  131. package/node_modules/strnum/.vscode/launch.json +0 -25
  132. package/node_modules/strnum/algo.stflow +0 -84
  133. package/node_modules/strnum/strnum.test.js +0 -173
  134. package/node_modules/strnum/test.js +0 -9
  135. /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
+ }