@cdk8s/awscdk-resolver 0.0.509 โ†’ 0.0.511

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 (31) hide show
  1. package/.jsii +3 -3
  2. package/lib/resolve.js +1 -1
  3. package/node_modules/@aws-sdk/client-cloudformation/package.json +2 -2
  4. package/node_modules/@smithy/util-waiter/dist-cjs/index.js +1 -1
  5. package/node_modules/@smithy/util-waiter/dist-es/poller.js +1 -1
  6. package/node_modules/@smithy/util-waiter/package.json +1 -1
  7. package/node_modules/fast-xml-builder/CHANGELOG.md +13 -0
  8. package/node_modules/fast-xml-builder/README.md +1 -1
  9. package/node_modules/fast-xml-builder/lib/fxb.cjs +1 -0
  10. package/node_modules/fast-xml-builder/lib/fxb.d.cts +13 -9
  11. package/node_modules/fast-xml-builder/lib/fxb.min.js +2 -0
  12. package/node_modules/fast-xml-builder/lib/fxb.min.js.map +1 -0
  13. package/node_modules/fast-xml-builder/package.json +7 -5
  14. package/node_modules/fast-xml-builder/src/fxb.d.ts +17 -3
  15. package/node_modules/fast-xml-builder/src/fxb.js +262 -21
  16. package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +161 -18
  17. package/node_modules/path-expression-matcher/LICENSE +21 -0
  18. package/node_modules/path-expression-matcher/README.md +635 -0
  19. package/node_modules/path-expression-matcher/lib/pem.cjs +1 -0
  20. package/node_modules/path-expression-matcher/lib/pem.d.cts +335 -0
  21. package/node_modules/path-expression-matcher/lib/pem.min.js +2 -0
  22. package/node_modules/path-expression-matcher/lib/pem.min.js.map +1 -0
  23. package/node_modules/path-expression-matcher/package.json +78 -0
  24. package/node_modules/path-expression-matcher/src/Expression.js +232 -0
  25. package/node_modules/path-expression-matcher/src/Matcher.js +414 -0
  26. package/node_modules/path-expression-matcher/src/index.d.ts +366 -0
  27. package/node_modules/path-expression-matcher/src/index.js +28 -0
  28. package/package.json +2 -2
  29. package/node_modules/fast-xml-builder/lib/builder.cjs +0 -1
  30. package/node_modules/fast-xml-builder/lib/builder.min.js +0 -2
  31. package/node_modules/fast-xml-builder/lib/builder.min.js.map +0 -1
@@ -0,0 +1,635 @@
1
+ # path-expression-matcher
2
+
3
+ Efficient path tracking and pattern matching for XML, JSON, YAML or any other parsers.
4
+
5
+ ## ๐ŸŽฏ Purpose
6
+
7
+ `path-expression-matcher` provides two core classes for tracking and matching paths:
8
+
9
+ - **`Expression`**: Parses and stores pattern expressions (e.g., `"root.users.user[id]"`)
10
+ - **`Matcher`**: Tracks current path during parsing and matches against expressions
11
+
12
+ Compatible with [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and similar tools.
13
+
14
+ ## ๐Ÿ“ฆ Installation
15
+
16
+ ```bash
17
+ npm install path-expression-matcher
18
+ ```
19
+
20
+ ## ๐Ÿš€ Quick Start
21
+
22
+ ```javascript
23
+ import { Expression, Matcher } from 'path-expression-matcher';
24
+
25
+ // Create expression (parse once, reuse many times)
26
+ const expr = new Expression("root.users.user");
27
+
28
+ // Create matcher (tracks current path)
29
+ const matcher = new Matcher();
30
+
31
+ matcher.push("root");
32
+ matcher.push("users");
33
+ matcher.push("user", { id: "123" });
34
+
35
+ // Match current path against expression
36
+ if (matcher.matches(expr)) {
37
+ console.log("Match found!");
38
+ console.log("Current path:", matcher.toString()); // "root.users.user"
39
+ }
40
+
41
+ // Namespace support
42
+ const nsExpr = new Expression("soap::Envelope.soap::Body..ns::UserId");
43
+ matcher.push("Envelope", null, "soap");
44
+ matcher.push("Body", null, "soap");
45
+ matcher.push("UserId", null, "ns");
46
+ console.log(matcher.toString()); // "soap:Envelope.soap:Body.ns:UserId"
47
+ ```
48
+
49
+ ## ๐Ÿ“– Pattern Syntax
50
+
51
+ ### Basic Paths
52
+
53
+ ```javascript
54
+ "root.users.user" // Exact path match
55
+ "*.users.user" // Wildcard: any parent
56
+ "root.*.user" // Wildcard: any middle
57
+ "root.users.*" // Wildcard: any child
58
+ ```
59
+
60
+ ### Deep Wildcard
61
+
62
+ ```javascript
63
+ "..user" // user anywhere in tree
64
+ "root..user" // user anywhere under root
65
+ "..users..user" // users somewhere, then user below it
66
+ ```
67
+
68
+ ### Attribute Matching
69
+
70
+ ```javascript
71
+ "user[id]" // user with "id" attribute
72
+ "user[type=admin]" // user with type="admin" (current node only)
73
+ "root[lang]..user" // user under root that has "lang" attribute
74
+ ```
75
+
76
+ ### Position Selectors
77
+
78
+ ```javascript
79
+ "user:first" // First user (counter=0)
80
+ "user:nth(2)" // Third user (counter=2, zero-based)
81
+ "user:odd" // Odd-numbered users (counter=1,3,5...)
82
+ "user:even" // Even-numbered users (counter=0,2,4...)
83
+ "root.users.user:first" // First user under users
84
+ ```
85
+
86
+ **Note:** Position selectors use the **counter** (occurrence count of the tag name), not the position (child index). For example, in `<root><a/><b/><a/></root>`, the second `<a/>` has position=2 but counter=1.
87
+
88
+ ### Namespaces
89
+
90
+ ```javascript
91
+ "ns::user" // user with namespace "ns"
92
+ "soap::Envelope" // Envelope with namespace "soap"
93
+ "ns::user[id]" // user with namespace "ns" and "id" attribute
94
+ "ns::user:first" // First user with namespace "ns"
95
+ "*::user" // user with any namespace
96
+ "..ns::item" // item with namespace "ns" anywhere in tree
97
+ "soap::Envelope.soap::Body" // Nested namespaced elements
98
+ "ns::first" // Tag named "first" with namespace "ns" (NO ambiguity!)
99
+ ```
100
+
101
+ **Namespace syntax:**
102
+ - Use **double colon (::)** for namespace: `ns::tag`
103
+ - Use **single colon (:)** for position: `tag:first`
104
+ - Combined: `ns::tag:first` (namespace + tag + position)
105
+
106
+ **Namespace matching rules:**
107
+ - Pattern `ns::user` matches only nodes with namespace "ns" and tag "user"
108
+ - Pattern `user` (no namespace) matches nodes with tag "user" regardless of namespace
109
+ - Pattern `*::user` matches tag "user" with any namespace (wildcard namespace)
110
+ - Namespaces are tracked separately for counter/position (e.g., `ns1::item` and `ns2::item` have independent counters)
111
+
112
+ ### Wildcard Differences
113
+
114
+ **Single wildcard (`*`)** - Matches exactly ONE level:
115
+ - `"*.fix1"` matches `root.fix1` (2 levels) โœ…
116
+ - `"*.fix1"` does NOT match `root.another.fix1` (3 levels) โŒ
117
+ - Path depth MUST equal pattern depth
118
+
119
+ **Deep wildcard (`..`)** - Matches ZERO or MORE levels:
120
+ - `"..fix1"` matches `root.fix1` โœ…
121
+ - `"..fix1"` matches `root.another.fix1` โœ…
122
+ - `"..fix1"` matches `a.b.c.d.fix1` โœ…
123
+ - Works at any depth
124
+
125
+ ### Combined Patterns
126
+
127
+ ```javascript
128
+ "..user[id]:first" // First user with id, anywhere
129
+ "root..user[type=admin]" // Admin user under root
130
+ "ns::user[id]:first" // First namespaced user with id
131
+ "soap::Envelope..ns::UserId" // UserId with namespace ns under SOAP envelope
132
+ ```
133
+
134
+ ## ๐Ÿ”ง API Reference
135
+
136
+ ### Expression
137
+
138
+ #### Constructor
139
+
140
+ ```javascript
141
+ new Expression(pattern, options)
142
+ ```
143
+
144
+ **Parameters:**
145
+ - `pattern` (string): Pattern to parse
146
+ - `options.separator` (string): Path separator (default: `'.'`)
147
+
148
+ **Example:**
149
+ ```javascript
150
+ const expr1 = new Expression("root.users.user");
151
+ const expr2 = new Expression("root/users/user", { separator: '/' });
152
+ ```
153
+
154
+ #### Methods
155
+
156
+ - `hasDeepWildcard()` โ†’ boolean
157
+ - `hasAttributeCondition()` โ†’ boolean
158
+ - `hasPositionSelector()` โ†’ boolean
159
+ - `toString()` โ†’ string
160
+
161
+ ### Matcher
162
+
163
+ #### Constructor
164
+
165
+ ```javascript
166
+ new Matcher(options)
167
+ ```
168
+
169
+ **Parameters:**
170
+ - `options.separator` (string): Default path separator (default: `'.'`)
171
+
172
+ #### Path Tracking Methods
173
+
174
+ ##### `push(tagName, attrValues, namespace)`
175
+
176
+ Add a tag to the current path. Position and counter are automatically calculated.
177
+
178
+ **Parameters:**
179
+ - `tagName` (string): Tag name
180
+ - `attrValues` (object, optional): Attribute key-value pairs (current node only)
181
+ - `namespace` (string, optional): Namespace for the tag
182
+
183
+ **Example:**
184
+ ```javascript
185
+ matcher.push("user", { id: "123", type: "admin" });
186
+ matcher.push("item"); // No attributes
187
+ matcher.push("Envelope", null, "soap"); // With namespace
188
+ matcher.push("Body", { version: "1.1" }, "soap"); // With both
189
+ ```
190
+
191
+ **Position vs Counter:**
192
+ - **Position**: The child index in the parent (0, 1, 2, 3...)
193
+ - **Counter**: How many times this tag name appeared at this level (0, 1, 2...)
194
+
195
+ Example:
196
+ ```xml
197
+ <root>
198
+ <a/> <!-- position=0, counter=0 -->
199
+ <b/> <!-- position=1, counter=0 -->
200
+ <a/> <!-- position=2, counter=1 -->
201
+ </root>
202
+ ```
203
+
204
+ ##### `pop()`
205
+
206
+ Remove the last tag from the path.
207
+
208
+ ```javascript
209
+ matcher.pop();
210
+ ```
211
+
212
+ ##### `updateCurrent(attrValues)`
213
+
214
+ Update current node's attributes (useful when attributes are parsed after push).
215
+
216
+ ```javascript
217
+ matcher.push("user"); // Don't know values yet
218
+ // ... parse attributes ...
219
+ matcher.updateCurrent({ id: "123" });
220
+ ```
221
+
222
+ ##### `reset()`
223
+
224
+ Clear the entire path.
225
+
226
+ ```javascript
227
+ matcher.reset();
228
+ ```
229
+
230
+ #### Query Methods
231
+
232
+ ##### `matches(expression)`
233
+
234
+ Check if current path matches an Expression.
235
+
236
+ ```javascript
237
+ const expr = new Expression("root.users.user");
238
+ if (matcher.matches(expr)) {
239
+ // Current path matches
240
+ }
241
+ ```
242
+
243
+ ##### `getCurrentTag()`
244
+
245
+ Get current tag name.
246
+
247
+ ```javascript
248
+ const tag = matcher.getCurrentTag(); // "user"
249
+ ```
250
+
251
+ ##### `getCurrentNamespace()`
252
+
253
+ Get current namespace.
254
+
255
+ ```javascript
256
+ const ns = matcher.getCurrentNamespace(); // "soap" or undefined
257
+ ```
258
+
259
+ ##### `getAttrValue(attrName)`
260
+
261
+ Get attribute value of current node.
262
+
263
+ ```javascript
264
+ const id = matcher.getAttrValue("id"); // "123"
265
+ ```
266
+
267
+ ##### `hasAttr(attrName)`
268
+
269
+ Check if current node has an attribute.
270
+
271
+ ```javascript
272
+ if (matcher.hasAttr("id")) {
273
+ // Current node has "id" attribute
274
+ }
275
+ ```
276
+
277
+ ##### `getPosition()`
278
+
279
+ Get sibling position of current node (child index in parent).
280
+
281
+ ```javascript
282
+ const position = matcher.getPosition(); // 0, 1, 2, ...
283
+ ```
284
+
285
+ ##### `getCounter()`
286
+
287
+ Get repeat counter of current node (occurrence count of this tag name).
288
+
289
+ ```javascript
290
+ const counter = matcher.getCounter(); // 0, 1, 2, ...
291
+ ```
292
+
293
+ ##### `getIndex()` (deprecated)
294
+
295
+ Alias for `getPosition()`. Use `getPosition()` or `getCounter()` instead for clarity.
296
+
297
+ ```javascript
298
+ const index = matcher.getIndex(); // Same as getPosition()
299
+ ```
300
+
301
+ ##### `getDepth()`
302
+
303
+ Get current path depth.
304
+
305
+ ```javascript
306
+ const depth = matcher.getDepth(); // 3 for "root.users.user"
307
+ ```
308
+
309
+ ##### `toString(separator?, includeNamespace?)`
310
+
311
+ Get path as string.
312
+
313
+ **Parameters:**
314
+ - `separator` (string, optional): Path separator (uses default if not provided)
315
+ - `includeNamespace` (boolean, optional): Whether to include namespaces (default: true)
316
+
317
+ ```javascript
318
+ const path = matcher.toString(); // "root.ns:user.item"
319
+ const path2 = matcher.toString('/'); // "root/ns:user/item"
320
+ const path3 = matcher.toString('.', false); // "root.user.item" (no namespaces)
321
+ ```
322
+
323
+ ##### `toArray()`
324
+
325
+ Get path as array.
326
+
327
+ ```javascript
328
+ const arr = matcher.toArray(); // ["root", "users", "user"]
329
+ ```
330
+
331
+ #### State Management
332
+
333
+ ##### `snapshot()`
334
+
335
+ Create a snapshot of current state.
336
+
337
+ ```javascript
338
+ const snapshot = matcher.snapshot();
339
+ ```
340
+
341
+ ##### `restore(snapshot)`
342
+
343
+ Restore from a snapshot.
344
+
345
+ ```javascript
346
+ matcher.restore(snapshot);
347
+ ```
348
+
349
+ ## ๐Ÿ’ก Usage Examples
350
+
351
+ ### Example 1: XML Parser with stopNodes
352
+
353
+ ```javascript
354
+ import { XMLParser } from 'fast-xml-parser';
355
+ import { Expression, Matcher } from 'path-expression-matcher';
356
+
357
+ class MyParser {
358
+ constructor() {
359
+ this.matcher = new Matcher();
360
+
361
+ // Pre-compile stop node patterns
362
+ this.stopNodeExpressions = [
363
+ new Expression("html.body.script"),
364
+ new Expression("html.body.style"),
365
+ new Expression("..svg"),
366
+ ];
367
+ }
368
+
369
+ parseTag(tagName, attrs) {
370
+ this.matcher.push(tagName, attrs);
371
+
372
+ // Check if this is a stop node
373
+ for (const expr of this.stopNodeExpressions) {
374
+ if (this.matcher.matches(expr)) {
375
+ // Don't parse children, read as raw text
376
+ return this.readRawContent();
377
+ }
378
+ }
379
+
380
+ // Continue normal parsing
381
+ this.parseChildren();
382
+
383
+ this.matcher.pop();
384
+ }
385
+ }
386
+ ```
387
+
388
+ ### Example 2: Conditional Processing
389
+
390
+ ```javascript
391
+ const matcher = new Matcher();
392
+ const userExpr = new Expression("..user[type=admin]");
393
+ const firstItemExpr = new Expression("..item:first");
394
+
395
+ function processTag(tagName, value, attrs) {
396
+ matcher.push(tagName, attrs);
397
+
398
+ if (matcher.matches(userExpr)) {
399
+ value = enhanceAdminUser(value);
400
+ }
401
+
402
+ if (matcher.matches(firstItemExpr)) {
403
+ value = markAsFirst(value);
404
+ }
405
+
406
+ matcher.pop();
407
+ return value;
408
+ }
409
+ ```
410
+
411
+ ### Example 3: Path-based Filtering
412
+
413
+ ```javascript
414
+ const patterns = [
415
+ new Expression("data.users.user"),
416
+ new Expression("data.posts.post"),
417
+ new Expression("..comment[approved=true]"),
418
+ ];
419
+
420
+ function shouldInclude(matcher) {
421
+ return patterns.some(expr => matcher.matches(expr));
422
+ }
423
+ ```
424
+
425
+ ### Example 4: Custom Separator
426
+
427
+ ```javascript
428
+ const matcher = new Matcher({ separator: '/' });
429
+ const expr = new Expression("root/config/database", { separator: '/' });
430
+
431
+ matcher.push("root");
432
+ matcher.push("config");
433
+ matcher.push("database");
434
+
435
+ console.log(matcher.toString()); // "root/config/database"
436
+ console.log(matcher.matches(expr)); // true
437
+ ```
438
+
439
+ ### Example 5: Attribute Checking
440
+
441
+ ```javascript
442
+ const matcher = new Matcher();
443
+ matcher.push("root");
444
+ matcher.push("user", { id: "123", type: "admin", status: "active" });
445
+
446
+ // Check attribute existence (current node only)
447
+ console.log(matcher.hasAttr("id")); // true
448
+ console.log(matcher.hasAttr("email")); // false
449
+
450
+ // Get attribute value (current node only)
451
+ console.log(matcher.getAttrValue("type")); // "admin"
452
+
453
+ // Match by attribute
454
+ const expr1 = new Expression("user[id]");
455
+ console.log(matcher.matches(expr1)); // true
456
+
457
+ const expr2 = new Expression("user[type=admin]");
458
+ console.log(matcher.matches(expr2)); // true
459
+ ```
460
+
461
+ ### Example 6: Position vs Counter
462
+
463
+ ```javascript
464
+ const matcher = new Matcher();
465
+ matcher.push("root");
466
+
467
+ // Mixed tags at same level
468
+ matcher.push("item"); // position=0, counter=0 (first item)
469
+ matcher.pop();
470
+
471
+ matcher.push("div"); // position=1, counter=0 (first div)
472
+ matcher.pop();
473
+
474
+ matcher.push("item"); // position=2, counter=1 (second item)
475
+
476
+ console.log(matcher.getPosition()); // 2 (third child overall)
477
+ console.log(matcher.getCounter()); // 1 (second "item" specifically)
478
+
479
+ // :first uses counter, not position
480
+ const expr = new Expression("root.item:first");
481
+ console.log(matcher.matches(expr)); // false (counter=1, not 0)
482
+ ```
483
+
484
+ ### Example 7: Namespace Support (XML/SOAP)
485
+
486
+ ```javascript
487
+ const matcher = new Matcher();
488
+ const soapExpr = new Expression("soap::Envelope.soap::Body..ns::UserId");
489
+
490
+ // Parse SOAP document
491
+ matcher.push("Envelope", { xmlns: "..." }, "soap");
492
+ matcher.push("Body", null, "soap");
493
+ matcher.push("GetUserRequest", null, "ns");
494
+ matcher.push("UserId", null, "ns");
495
+
496
+ // Match namespaced pattern
497
+ if (matcher.matches(soapExpr)) {
498
+ console.log("Found UserId in SOAP body");
499
+ console.log(matcher.toString()); // "soap:Envelope.soap:Body.ns:GetUserRequest.ns:UserId"
500
+ }
501
+
502
+ // Namespace-specific counters
503
+ matcher.reset();
504
+ matcher.push("root");
505
+ matcher.push("item", null, "ns1"); // ns1::item counter=0
506
+ matcher.pop();
507
+ matcher.push("item", null, "ns2"); // ns2::item counter=0 (different namespace)
508
+ matcher.pop();
509
+ matcher.push("item", null, "ns1"); // ns1::item counter=1
510
+
511
+ const firstNs1Item = new Expression("root.ns1::item:first");
512
+ console.log(matcher.matches(firstNs1Item)); // false (counter=1)
513
+
514
+ const secondNs1Item = new Expression("root.ns1::item:nth(1)");
515
+ console.log(matcher.matches(secondNs1Item)); // true
516
+
517
+ // NO AMBIGUITY: Tags named after position keywords
518
+ matcher.reset();
519
+ matcher.push("root");
520
+ matcher.push("first", null, "ns"); // Tag named "first" with namespace
521
+
522
+ const expr = new Expression("root.ns::first");
523
+ console.log(matcher.matches(expr)); // true - matches namespace "ns", tag "first"
524
+ ```
525
+
526
+ ## ๐Ÿ—๏ธ Architecture
527
+
528
+ ### Data Storage Strategy
529
+
530
+ **Ancestor nodes:** Store only tag name, position, and counter (minimal memory)
531
+ **Current node:** Store tag name, position, counter, and attribute values
532
+
533
+ This design minimizes memory usage:
534
+ - No attribute names stored (derived from values object when needed)
535
+ - Attribute values only for current node, not ancestors
536
+ - Attribute checking for ancestors is not supported (acceptable trade-off)
537
+ - For 1M nodes with 3 attributes each, saves ~50MB vs storing attribute names
538
+
539
+ ### Matching Strategy
540
+
541
+ Matching is performed **bottom-to-top** (from current node toward root):
542
+ 1. Start at current node
543
+ 2. Match segments from pattern end to start
544
+ 3. Attribute checking only works for current node (ancestors have no attribute data)
545
+ 4. Position selectors use **counter** (occurrence count), not position (child index)
546
+
547
+ ### Performance
548
+
549
+ - **Expression parsing:** One-time cost when Expression is created
550
+ - **Expression analysis:** Cached (hasDeepWildcard, hasAttributeCondition, hasPositionSelector)
551
+ - **Path tracking:** O(1) for push/pop operations
552
+ - **Pattern matching:** O(n*m) where n = path depth, m = pattern segments
553
+ - **Memory per ancestor node:** ~40-60 bytes (tag, position, counter only)
554
+ - **Memory per current node:** ~80-120 bytes (adds attribute values)
555
+
556
+ ## ๐ŸŽ“ Design Patterns
557
+
558
+ ### Pre-compile Patterns (Recommended)
559
+
560
+ ```javascript
561
+ // โœ… GOOD: Parse once, reuse many times
562
+ const expr = new Expression("..user[id]");
563
+
564
+ for (let i = 0; i < 1000; i++) {
565
+ if (matcher.matches(expr)) {
566
+ // ...
567
+ }
568
+ }
569
+ ```
570
+
571
+ ```javascript
572
+ // โŒ BAD: Parse on every iteration
573
+ for (let i = 0; i < 1000; i++) {
574
+ if (matcher.matches(new Expression("..user[id]"))) {
575
+ // ...
576
+ }
577
+ }
578
+ ```
579
+
580
+ ### Batch Pattern Checking
581
+
582
+ ```javascript
583
+ // For multiple patterns, check all at once
584
+ const patterns = [
585
+ new Expression("..user"),
586
+ new Expression("..post"),
587
+ new Expression("..comment"),
588
+ ];
589
+
590
+ function matchesAny(matcher, patterns) {
591
+ return patterns.some(expr => matcher.matches(expr));
592
+ }
593
+ ```
594
+
595
+ ## ๐Ÿ”— Integration with fast-xml-parser
596
+
597
+ **Basic integration:**
598
+
599
+ ```javascript
600
+ import { XMLParser } from 'fast-xml-parser';
601
+ import { Expression, Matcher } from 'path-expression-matcher';
602
+
603
+ const parser = new XMLParser({
604
+ // Custom options using path-expression-matcher
605
+ stopNodes: ["script", "style"].map(tag => new Expression(`..${tag}`)),
606
+
607
+ tagValueProcessor: (tagName, value, jPath, hasAttrs, isLeaf, matcher) => {
608
+ // matcher is available in callbacks
609
+ if (matcher.matches(new Expression("..user[type=admin]"))) {
610
+ return enhanceValue(value);
611
+ }
612
+ return value;
613
+ }
614
+ });
615
+ ```
616
+
617
+ ## ๐Ÿงช Testing
618
+
619
+ ```bash
620
+ npm test
621
+ ```
622
+
623
+ All 77 tests covering:
624
+ - Pattern parsing (exact, wildcards, attributes, position)
625
+ - Path tracking (push, pop, update)
626
+ - Pattern matching (all combinations)
627
+ - Edge cases and error conditions
628
+
629
+ ## ๐Ÿ“„ License
630
+
631
+ MIT
632
+
633
+ ## ๐Ÿค Contributing
634
+
635
+ Issues and PRs welcome! This package is designed to be used by XML/JSON parsers like fast-xml-parser.
@@ -0,0 +1 @@
1
+ (()=>{"use strict";var t={d:(e,s)=>{for(var i in s)t.o(s,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:s[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{Expression:()=>s,Matcher:()=>i,default:()=>n});class s{constructor(t,e={}){this.pattern=t,this.separator=e.separator||".",this.segments=this._parse(t),this._hasDeepWildcard=this.segments.some(t=>"deep-wildcard"===t.type),this._hasAttributeCondition=this.segments.some(t=>void 0!==t.attrName),this._hasPositionSelector=this.segments.some(t=>void 0!==t.position)}_parse(t){const e=[];let s=0,i="";for(;s<t.length;)t[s]===this.separator?s+1<t.length&&t[s+1]===this.separator?(i.trim()&&(e.push(this._parseSegment(i.trim())),i=""),e.push({type:"deep-wildcard"}),s+=2):(i.trim()&&e.push(this._parseSegment(i.trim())),i="",s++):(i+=t[s],s++);return i.trim()&&e.push(this._parseSegment(i.trim())),e}_parseSegment(t){const e={type:"tag"};let s=null,i=t;const n=t.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);if(n&&(i=n[1]+n[3],n[2])){const t=n[2].slice(1,-1);t&&(s=t)}let r,a,h=i;if(i.includes("::")){const e=i.indexOf("::");if(r=i.substring(0,e).trim(),h=i.substring(e+2).trim(),!r)throw new Error(`Invalid namespace in pattern: ${t}`)}let o=null;if(h.includes(":")){const t=h.lastIndexOf(":"),e=h.substring(0,t).trim(),s=h.substring(t+1).trim();["first","last","odd","even"].includes(s)||/^nth\(\d+\)$/.test(s)?(a=e,o=s):a=h}else a=h;if(!a)throw new Error(`Invalid segment pattern: ${t}`);if(e.tag=a,r&&(e.namespace=r),s)if(s.includes("=")){const t=s.indexOf("=");e.attrName=s.substring(0,t).trim(),e.attrValue=s.substring(t+1).trim()}else e.attrName=s.trim();if(o){const t=o.match(/^nth\((\d+)\)$/);t?(e.position="nth",e.positionValue=parseInt(t[1],10)):e.position=o}return e}get length(){return this.segments.length}hasDeepWildcard(){return this._hasDeepWildcard}hasAttributeCondition(){return this._hasAttributeCondition}hasPositionSelector(){return this._hasPositionSelector}toString(){return this.pattern}}class i{constructor(t={}){this.separator=t.separator||".",this.path=[],this.siblingStacks=[]}push(t,e=null,s=null){this.path.length>0&&(this.path[this.path.length-1].values=void 0);const i=this.path.length;this.siblingStacks[i]||(this.siblingStacks[i]=new Map);const n=this.siblingStacks[i],r=s?`${s}:${t}`:t,a=n.get(r)||0;let h=0;for(const t of n.values())h+=t;n.set(r,a+1);const o={tag:t,position:h,counter:a};null!=s&&(o.namespace=s),null!=e&&(o.values=e),this.path.push(o)}pop(){if(0===this.path.length)return;const t=this.path.pop();return this.siblingStacks.length>this.path.length+1&&(this.siblingStacks.length=this.path.length+1),t}updateCurrent(t){if(this.path.length>0){const e=this.path[this.path.length-1];null!=t&&(e.values=t)}}getCurrentTag(){return this.path.length>0?this.path[this.path.length-1].tag:void 0}getCurrentNamespace(){return this.path.length>0?this.path[this.path.length-1].namespace:void 0}getAttrValue(t){if(0===this.path.length)return;const e=this.path[this.path.length-1];return e.values?.[t]}hasAttr(t){if(0===this.path.length)return!1;const e=this.path[this.path.length-1];return void 0!==e.values&&t in e.values}getPosition(){return 0===this.path.length?-1:this.path[this.path.length-1].position??0}getCounter(){return 0===this.path.length?-1:this.path[this.path.length-1].counter??0}getIndex(){return this.getPosition()}getDepth(){return this.path.length}toString(t,e=!0){const s=t||this.separator;return this.path.map(t=>e&&t.namespace?`${t.namespace}:${t.tag}`:t.tag).join(s)}toArray(){return this.path.map(t=>t.tag)}reset(){this.path=[],this.siblingStacks=[]}matches(t){const e=t.segments;return 0!==e.length&&(t.hasDeepWildcard()?this._matchWithDeepWildcard(e):this._matchSimple(e))}_matchSimple(t){if(this.path.length!==t.length)return!1;for(let e=0;e<t.length;e++){const s=t[e],i=this.path[e],n=e===this.path.length-1;if(!this._matchSegment(s,i,n))return!1}return!0}_matchWithDeepWildcard(t){let e=this.path.length-1,s=t.length-1;for(;s>=0&&e>=0;){const i=t[s];if("deep-wildcard"===i.type){if(s--,s<0)return!0;const i=t[s];let n=!1;for(let t=e;t>=0;t--){const r=t===this.path.length-1;if(this._matchSegment(i,this.path[t],r)){e=t-1,s--,n=!0;break}}if(!n)return!1}else{const t=e===this.path.length-1;if(!this._matchSegment(i,this.path[e],t))return!1;e--,s--}}return s<0}_matchSegment(t,e,s){if("*"!==t.tag&&t.tag!==e.tag)return!1;if(void 0!==t.namespace&&"*"!==t.namespace&&t.namespace!==e.namespace)return!1;if(void 0!==t.attrName){if(!s)return!1;if(!e.values||!(t.attrName in e.values))return!1;if(void 0!==t.attrValue){const s=e.values[t.attrName];if(String(s)!==String(t.attrValue))return!1}}if(void 0!==t.position){if(!s)return!1;const i=e.counter??0;if("first"===t.position&&0!==i)return!1;if("odd"===t.position&&i%2!=1)return!1;if("even"===t.position&&i%2!=0)return!1;if("nth"===t.position&&i!==t.positionValue)return!1}return!0}snapshot(){return{path:this.path.map(t=>({...t})),siblingStacks:this.siblingStacks.map(t=>new Map(t))}}restore(t){this.path=t.path.map(t=>({...t})),this.siblingStacks=t.siblingStacks.map(t=>new Map(t))}}const n={Expression:s,Matcher:i};module.exports=e})();