@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,232 @@
1
+ /**
2
+ * Expression - Parses and stores a tag pattern expression
3
+ *
4
+ * Patterns are parsed once and stored in an optimized structure for fast matching.
5
+ *
6
+ * @example
7
+ * const expr = new Expression("root.users.user");
8
+ * const expr2 = new Expression("..user[id]:first");
9
+ * const expr3 = new Expression("root/users/user", { separator: '/' });
10
+ */
11
+ export default class Expression {
12
+ /**
13
+ * Create a new Expression
14
+ * @param {string} pattern - Pattern string (e.g., "root.users.user", "..user[id]")
15
+ * @param {Object} options - Configuration options
16
+ * @param {string} options.separator - Path separator (default: '.')
17
+ */
18
+ constructor(pattern, options = {}) {
19
+ this.pattern = pattern;
20
+ this.separator = options.separator || '.';
21
+ this.segments = this._parse(pattern);
22
+
23
+ // Cache expensive checks for performance (O(1) instead of O(n))
24
+ this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');
25
+ this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);
26
+ this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);
27
+ }
28
+
29
+ /**
30
+ * Parse pattern string into segments
31
+ * @private
32
+ * @param {string} pattern - Pattern to parse
33
+ * @returns {Array} Array of segment objects
34
+ */
35
+ _parse(pattern) {
36
+ const segments = [];
37
+
38
+ // Split by separator but handle ".." specially
39
+ let i = 0;
40
+ let currentPart = '';
41
+
42
+ while (i < pattern.length) {
43
+ if (pattern[i] === this.separator) {
44
+ // Check if next char is also separator (deep wildcard)
45
+ if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {
46
+ // Flush current part if any
47
+ if (currentPart.trim()) {
48
+ segments.push(this._parseSegment(currentPart.trim()));
49
+ currentPart = '';
50
+ }
51
+ // Add deep wildcard
52
+ segments.push({ type: 'deep-wildcard' });
53
+ i += 2; // Skip both separators
54
+ } else {
55
+ // Regular separator
56
+ if (currentPart.trim()) {
57
+ segments.push(this._parseSegment(currentPart.trim()));
58
+ }
59
+ currentPart = '';
60
+ i++;
61
+ }
62
+ } else {
63
+ currentPart += pattern[i];
64
+ i++;
65
+ }
66
+ }
67
+
68
+ // Flush remaining part
69
+ if (currentPart.trim()) {
70
+ segments.push(this._parseSegment(currentPart.trim()));
71
+ }
72
+
73
+ return segments;
74
+ }
75
+
76
+ /**
77
+ * Parse a single segment
78
+ * @private
79
+ * @param {string} part - Segment string (e.g., "user", "ns::user", "user[id]", "ns::user:first")
80
+ * @returns {Object} Segment object
81
+ */
82
+ _parseSegment(part) {
83
+ const segment = { type: 'tag' };
84
+
85
+ // NEW NAMESPACE SYNTAX (v2.0):
86
+ // ============================
87
+ // Namespace uses DOUBLE colon (::)
88
+ // Position uses SINGLE colon (:)
89
+ //
90
+ // Examples:
91
+ // "user" → tag
92
+ // "user:first" → tag + position
93
+ // "user[id]" → tag + attribute
94
+ // "user[id]:first" → tag + attribute + position
95
+ // "ns::user" → namespace + tag
96
+ // "ns::user:first" → namespace + tag + position
97
+ // "ns::user[id]" → namespace + tag + attribute
98
+ // "ns::user[id]:first" → namespace + tag + attribute + position
99
+ // "ns::first" → namespace + tag named "first" (NO ambiguity!)
100
+ //
101
+ // This eliminates all ambiguity:
102
+ // :: = namespace separator
103
+ // : = position selector
104
+ // [] = attributes
105
+
106
+ // Step 1: Extract brackets [attr] or [attr=value]
107
+ let bracketContent = null;
108
+ let withoutBrackets = part;
109
+
110
+ const bracketMatch = part.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);
111
+ if (bracketMatch) {
112
+ withoutBrackets = bracketMatch[1] + bracketMatch[3];
113
+ if (bracketMatch[2]) {
114
+ const content = bracketMatch[2].slice(1, -1);
115
+ if (content) {
116
+ bracketContent = content;
117
+ }
118
+ }
119
+ }
120
+
121
+ // Step 2: Check for namespace (double colon ::)
122
+ let namespace = undefined;
123
+ let tagAndPosition = withoutBrackets;
124
+
125
+ if (withoutBrackets.includes('::')) {
126
+ const nsIndex = withoutBrackets.indexOf('::');
127
+ namespace = withoutBrackets.substring(0, nsIndex).trim();
128
+ tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::
129
+
130
+ if (!namespace) {
131
+ throw new Error(`Invalid namespace in pattern: ${part}`);
132
+ }
133
+ }
134
+
135
+ // Step 3: Parse tag and position (single colon :)
136
+ let tag = undefined;
137
+ let positionMatch = null;
138
+
139
+ if (tagAndPosition.includes(':')) {
140
+ const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position
141
+ const tagPart = tagAndPosition.substring(0, colonIndex).trim();
142
+ const posPart = tagAndPosition.substring(colonIndex + 1).trim();
143
+
144
+ // Verify position is a valid keyword
145
+ const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||
146
+ /^nth\(\d+\)$/.test(posPart);
147
+
148
+ if (isPositionKeyword) {
149
+ tag = tagPart;
150
+ positionMatch = posPart;
151
+ } else {
152
+ // Not a valid position keyword, treat whole thing as tag
153
+ tag = tagAndPosition;
154
+ }
155
+ } else {
156
+ tag = tagAndPosition;
157
+ }
158
+
159
+ if (!tag) {
160
+ throw new Error(`Invalid segment pattern: ${part}`);
161
+ }
162
+
163
+ segment.tag = tag;
164
+ if (namespace) {
165
+ segment.namespace = namespace;
166
+ }
167
+
168
+ // Step 4: Parse attributes
169
+ if (bracketContent) {
170
+ if (bracketContent.includes('=')) {
171
+ const eqIndex = bracketContent.indexOf('=');
172
+ segment.attrName = bracketContent.substring(0, eqIndex).trim();
173
+ segment.attrValue = bracketContent.substring(eqIndex + 1).trim();
174
+ } else {
175
+ segment.attrName = bracketContent.trim();
176
+ }
177
+ }
178
+
179
+ // Step 5: Parse position selector
180
+ if (positionMatch) {
181
+ const nthMatch = positionMatch.match(/^nth\((\d+)\)$/);
182
+ if (nthMatch) {
183
+ segment.position = 'nth';
184
+ segment.positionValue = parseInt(nthMatch[1], 10);
185
+ } else {
186
+ segment.position = positionMatch;
187
+ }
188
+ }
189
+
190
+ return segment;
191
+ }
192
+
193
+ /**
194
+ * Get the number of segments
195
+ * @returns {number}
196
+ */
197
+ get length() {
198
+ return this.segments.length;
199
+ }
200
+
201
+ /**
202
+ * Check if expression contains deep wildcard
203
+ * @returns {boolean}
204
+ */
205
+ hasDeepWildcard() {
206
+ return this._hasDeepWildcard;
207
+ }
208
+
209
+ /**
210
+ * Check if expression has attribute conditions
211
+ * @returns {boolean}
212
+ */
213
+ hasAttributeCondition() {
214
+ return this._hasAttributeCondition;
215
+ }
216
+
217
+ /**
218
+ * Check if expression has position selectors
219
+ * @returns {boolean}
220
+ */
221
+ hasPositionSelector() {
222
+ return this._hasPositionSelector;
223
+ }
224
+
225
+ /**
226
+ * Get string representation
227
+ * @returns {string}
228
+ */
229
+ toString() {
230
+ return this.pattern;
231
+ }
232
+ }
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Matcher - Tracks current path in XML/JSON tree and matches against Expressions
3
+ *
4
+ * The matcher maintains a stack of nodes representing the current path from root to
5
+ * current tag. It only stores attribute values for the current (top) node to minimize
6
+ * memory usage. Sibling tracking is used to auto-calculate position and counter.
7
+ *
8
+ * @example
9
+ * const matcher = new Matcher();
10
+ * matcher.push("root", {});
11
+ * matcher.push("users", {});
12
+ * matcher.push("user", { id: "123", type: "admin" });
13
+ *
14
+ * const expr = new Expression("root.users.user");
15
+ * matcher.matches(expr); // true
16
+ */
17
+ export default class Matcher {
18
+ /**
19
+ * Create a new Matcher
20
+ * @param {Object} options - Configuration options
21
+ * @param {string} options.separator - Default path separator (default: '.')
22
+ */
23
+ constructor(options = {}) {
24
+ this.separator = options.separator || '.';
25
+ this.path = [];
26
+ this.siblingStacks = [];
27
+ // Each path node: { tag: string, values: object, position: number, counter: number }
28
+ // values only present for current (last) node
29
+ // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level
30
+ }
31
+
32
+ /**
33
+ * Push a new tag onto the path
34
+ * @param {string} tagName - Name of the tag
35
+ * @param {Object} attrValues - Attribute key-value pairs for current node (optional)
36
+ * @param {string} namespace - Namespace for the tag (optional)
37
+ */
38
+ push(tagName, attrValues = null, namespace = null) {
39
+ // Remove values from previous current node (now becoming ancestor)
40
+ if (this.path.length > 0) {
41
+ const prev = this.path[this.path.length - 1];
42
+ prev.values = undefined;
43
+ }
44
+
45
+ // Get or create sibling tracking for current level
46
+ const currentLevel = this.path.length;
47
+ if (!this.siblingStacks[currentLevel]) {
48
+ this.siblingStacks[currentLevel] = new Map();
49
+ }
50
+
51
+ const siblings = this.siblingStacks[currentLevel];
52
+
53
+ // Create a unique key for sibling tracking that includes namespace
54
+ const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;
55
+
56
+ // Calculate counter (how many times this tag appeared at this level)
57
+ const counter = siblings.get(siblingKey) || 0;
58
+
59
+ // Calculate position (total children at this level so far)
60
+ let position = 0;
61
+ for (const count of siblings.values()) {
62
+ position += count;
63
+ }
64
+
65
+ // Update sibling count for this tag
66
+ siblings.set(siblingKey, counter + 1);
67
+
68
+ // Create new node
69
+ const node = {
70
+ tag: tagName,
71
+ position: position,
72
+ counter: counter
73
+ };
74
+
75
+ // Store namespace if provided
76
+ if (namespace !== null && namespace !== undefined) {
77
+ node.namespace = namespace;
78
+ }
79
+
80
+ // Store values only for current node
81
+ if (attrValues !== null && attrValues !== undefined) {
82
+ node.values = attrValues;
83
+ }
84
+
85
+ this.path.push(node);
86
+ }
87
+
88
+ /**
89
+ * Pop the last tag from the path
90
+ * @returns {Object|undefined} The popped node
91
+ */
92
+ pop() {
93
+ if (this.path.length === 0) {
94
+ return undefined;
95
+ }
96
+
97
+ const node = this.path.pop();
98
+
99
+ // Clean up sibling tracking for levels deeper than current
100
+ // After pop, path.length is the new depth
101
+ // We need to clean up siblingStacks[path.length + 1] and beyond
102
+ if (this.siblingStacks.length > this.path.length + 1) {
103
+ this.siblingStacks.length = this.path.length + 1;
104
+ }
105
+
106
+ return node;
107
+ }
108
+
109
+ /**
110
+ * Update current node's attribute values
111
+ * Useful when attributes are parsed after push
112
+ * @param {Object} attrValues - Attribute values
113
+ */
114
+ updateCurrent(attrValues) {
115
+ if (this.path.length > 0) {
116
+ const current = this.path[this.path.length - 1];
117
+ if (attrValues !== null && attrValues !== undefined) {
118
+ current.values = attrValues;
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Get current tag name
125
+ * @returns {string|undefined}
126
+ */
127
+ getCurrentTag() {
128
+ return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;
129
+ }
130
+
131
+ /**
132
+ * Get current namespace
133
+ * @returns {string|undefined}
134
+ */
135
+ getCurrentNamespace() {
136
+ return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;
137
+ }
138
+
139
+ /**
140
+ * Get current node's attribute value
141
+ * @param {string} attrName - Attribute name
142
+ * @returns {*} Attribute value or undefined
143
+ */
144
+ getAttrValue(attrName) {
145
+ if (this.path.length === 0) return undefined;
146
+ const current = this.path[this.path.length - 1];
147
+ return current.values?.[attrName];
148
+ }
149
+
150
+ /**
151
+ * Check if current node has an attribute
152
+ * @param {string} attrName - Attribute name
153
+ * @returns {boolean}
154
+ */
155
+ hasAttr(attrName) {
156
+ if (this.path.length === 0) return false;
157
+ const current = this.path[this.path.length - 1];
158
+ return current.values !== undefined && attrName in current.values;
159
+ }
160
+
161
+ /**
162
+ * Get current node's sibling position (child index in parent)
163
+ * @returns {number}
164
+ */
165
+ getPosition() {
166
+ if (this.path.length === 0) return -1;
167
+ return this.path[this.path.length - 1].position ?? 0;
168
+ }
169
+
170
+ /**
171
+ * Get current node's repeat counter (occurrence count of this tag name)
172
+ * @returns {number}
173
+ */
174
+ getCounter() {
175
+ if (this.path.length === 0) return -1;
176
+ return this.path[this.path.length - 1].counter ?? 0;
177
+ }
178
+
179
+ /**
180
+ * Get current node's sibling index (alias for getPosition for backward compatibility)
181
+ * @returns {number}
182
+ * @deprecated Use getPosition() or getCounter() instead
183
+ */
184
+ getIndex() {
185
+ return this.getPosition();
186
+ }
187
+
188
+ /**
189
+ * Get current path depth
190
+ * @returns {number}
191
+ */
192
+ getDepth() {
193
+ return this.path.length;
194
+ }
195
+
196
+ /**
197
+ * Get path as string
198
+ * @param {string} separator - Optional separator (uses default if not provided)
199
+ * @param {boolean} includeNamespace - Whether to include namespace in output (default: true)
200
+ * @returns {string}
201
+ */
202
+ toString(separator, includeNamespace = true) {
203
+ const sep = separator || this.separator;
204
+ return this.path.map(n => {
205
+ if (includeNamespace && n.namespace) {
206
+ return `${n.namespace}:${n.tag}`;
207
+ }
208
+ return n.tag;
209
+ }).join(sep);
210
+ }
211
+
212
+ /**
213
+ * Get path as array of tag names
214
+ * @returns {string[]}
215
+ */
216
+ toArray() {
217
+ return this.path.map(n => n.tag);
218
+ }
219
+
220
+ /**
221
+ * Reset the path to empty
222
+ */
223
+ reset() {
224
+ this.path = [];
225
+ this.siblingStacks = [];
226
+ }
227
+
228
+ /**
229
+ * Match current path against an Expression
230
+ * @param {Expression} expression - The expression to match against
231
+ * @returns {boolean} True if current path matches the expression
232
+ */
233
+ matches(expression) {
234
+ const segments = expression.segments;
235
+
236
+ if (segments.length === 0) {
237
+ return false;
238
+ }
239
+
240
+ // Handle deep wildcard patterns
241
+ if (expression.hasDeepWildcard()) {
242
+ return this._matchWithDeepWildcard(segments);
243
+ }
244
+
245
+ // Simple path matching (no deep wildcards)
246
+ return this._matchSimple(segments);
247
+ }
248
+
249
+ /**
250
+ * Match simple path (no deep wildcards)
251
+ * @private
252
+ */
253
+ _matchSimple(segments) {
254
+ // Path must be same length as segments
255
+ if (this.path.length !== segments.length) {
256
+ return false;
257
+ }
258
+
259
+ // Match each segment bottom-to-top
260
+ for (let i = 0; i < segments.length; i++) {
261
+ const segment = segments[i];
262
+ const node = this.path[i];
263
+ const isCurrentNode = (i === this.path.length - 1);
264
+
265
+ if (!this._matchSegment(segment, node, isCurrentNode)) {
266
+ return false;
267
+ }
268
+ }
269
+
270
+ return true;
271
+ }
272
+
273
+ /**
274
+ * Match path with deep wildcards
275
+ * @private
276
+ */
277
+ _matchWithDeepWildcard(segments) {
278
+ let pathIdx = this.path.length - 1; // Start from current node (bottom)
279
+ let segIdx = segments.length - 1; // Start from last segment
280
+
281
+ while (segIdx >= 0 && pathIdx >= 0) {
282
+ const segment = segments[segIdx];
283
+
284
+ if (segment.type === 'deep-wildcard') {
285
+ // ".." matches zero or more levels
286
+ segIdx--;
287
+
288
+ if (segIdx < 0) {
289
+ // Pattern ends with "..", always matches
290
+ return true;
291
+ }
292
+
293
+ // Find where next segment matches in the path
294
+ const nextSeg = segments[segIdx];
295
+ let found = false;
296
+
297
+ for (let i = pathIdx; i >= 0; i--) {
298
+ const isCurrentNode = (i === this.path.length - 1);
299
+ if (this._matchSegment(nextSeg, this.path[i], isCurrentNode)) {
300
+ pathIdx = i - 1;
301
+ segIdx--;
302
+ found = true;
303
+ break;
304
+ }
305
+ }
306
+
307
+ if (!found) {
308
+ return false;
309
+ }
310
+ } else {
311
+ // Regular segment
312
+ const isCurrentNode = (pathIdx === this.path.length - 1);
313
+ if (!this._matchSegment(segment, this.path[pathIdx], isCurrentNode)) {
314
+ return false;
315
+ }
316
+ pathIdx--;
317
+ segIdx--;
318
+ }
319
+ }
320
+
321
+ // All segments must be consumed
322
+ return segIdx < 0;
323
+ }
324
+
325
+ /**
326
+ * Match a single segment against a node
327
+ * @private
328
+ * @param {Object} segment - Segment from Expression
329
+ * @param {Object} node - Node from path
330
+ * @param {boolean} isCurrentNode - Whether this is the current (last) node
331
+ * @returns {boolean}
332
+ */
333
+ _matchSegment(segment, node, isCurrentNode) {
334
+ // Match tag name (* is wildcard)
335
+ if (segment.tag !== '*' && segment.tag !== node.tag) {
336
+ return false;
337
+ }
338
+
339
+ // Match namespace if specified in segment
340
+ if (segment.namespace !== undefined) {
341
+ // Segment has namespace - node must match it
342
+ if (segment.namespace !== '*' && segment.namespace !== node.namespace) {
343
+ return false;
344
+ }
345
+ }
346
+ // If segment has no namespace, it matches nodes with or without namespace
347
+
348
+ // Match attribute name (check if node has this attribute)
349
+ // Can only check for current node since ancestors don't have values
350
+ if (segment.attrName !== undefined) {
351
+ if (!isCurrentNode) {
352
+ // Can't check attributes for ancestor nodes (values not stored)
353
+ return false;
354
+ }
355
+
356
+ if (!node.values || !(segment.attrName in node.values)) {
357
+ return false;
358
+ }
359
+
360
+ // Match attribute value (only possible for current node)
361
+ if (segment.attrValue !== undefined) {
362
+ const actualValue = node.values[segment.attrName];
363
+ // Both should be strings
364
+ if (String(actualValue) !== String(segment.attrValue)) {
365
+ return false;
366
+ }
367
+ }
368
+ }
369
+
370
+ // Match position (only for current node)
371
+ if (segment.position !== undefined) {
372
+ if (!isCurrentNode) {
373
+ // Can't check position for ancestor nodes
374
+ return false;
375
+ }
376
+
377
+ const counter = node.counter ?? 0;
378
+
379
+ if (segment.position === 'first' && counter !== 0) {
380
+ return false;
381
+ } else if (segment.position === 'odd' && counter % 2 !== 1) {
382
+ return false;
383
+ } else if (segment.position === 'even' && counter % 2 !== 0) {
384
+ return false;
385
+ } else if (segment.position === 'nth') {
386
+ if (counter !== segment.positionValue) {
387
+ return false;
388
+ }
389
+ }
390
+ }
391
+
392
+ return true;
393
+ }
394
+
395
+ /**
396
+ * Create a snapshot of current state
397
+ * @returns {Object} State snapshot
398
+ */
399
+ snapshot() {
400
+ return {
401
+ path: this.path.map(node => ({ ...node })),
402
+ siblingStacks: this.siblingStacks.map(map => new Map(map))
403
+ };
404
+ }
405
+
406
+ /**
407
+ * Restore state from snapshot
408
+ * @param {Object} snapshot - State snapshot
409
+ */
410
+ restore(snapshot) {
411
+ this.path = snapshot.path.map(node => ({ ...node }));
412
+ this.siblingStacks = snapshot.siblingStacks.map(map => new Map(map));
413
+ }
414
+ }