@btc-embedded/cdk-extensions 0.23.3 → 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 +41 -41
  2. package/CHANGELOG.md +14 -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 +5 -4
  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,599 @@
1
+ 'use strict';
2
+ //parse Empty Node as self closing node
3
+ import buildFromOrderedJs from './orderedJs2Xml.js';
4
+ import getIgnoreAttributesFn from "./ignoreAttributes.js";
5
+ import { Expression, Matcher } from 'path-expression-matcher';
6
+ import { safeComment, safeCdata, escapeAttribute } from './util.js';
7
+ import { qName } from 'xml-naming';
8
+
9
+ const defaultOptions = {
10
+ attributeNamePrefix: '@_',
11
+ attributesGroupName: false,
12
+ textNodeName: '#text',
13
+ ignoreAttributes: true,
14
+ cdataPropName: false,
15
+ format: false,
16
+ indentBy: ' ',
17
+ suppressEmptyNode: false,
18
+ suppressUnpairedNode: true,
19
+ suppressBooleanAttributes: true,
20
+ tagValueProcessor: function (key, a) {
21
+ return a;
22
+ },
23
+ attributeValueProcessor: function (attrName, a) {
24
+ return a;
25
+ },
26
+ preserveOrder: false,
27
+ commentPropName: false,
28
+ unpairedTags: [],
29
+ entities: [
30
+ { regex: new RegExp("&", "g"), val: "&" },//it must be on top
31
+ { regex: new RegExp(">", "g"), val: ">" },
32
+ { regex: new RegExp("<", "g"), val: "&lt;" },
33
+ { regex: new RegExp("\'", "g"), val: "&apos;" },
34
+ { regex: new RegExp("\"", "g"), val: "&quot;" }
35
+ ],
36
+ processEntities: true,
37
+ stopNodes: [],
38
+ // transformTagName: false,
39
+ // transformAttributeName: false,
40
+ oneListGroup: false,
41
+ maxNestedTags: 100,
42
+ jPath: true, // When true, callbacks receive string jPath; when false, receive Matcher instance
43
+ sanitizeName: false // false = allow all names as-is (default, backward-compatible).
44
+ // Set to a function (name, { isAttribute, matcher }) => string to
45
+ // validate/sanitize tag and attribute names. Throw inside the function
46
+ // to reject an invalid name.
47
+ };
48
+
49
+ export default function Builder(options) {
50
+ this.options = Object.assign({}, defaultOptions, options);
51
+
52
+ // Convert old-style stopNodes for backward compatibility
53
+ // Old syntax: "*.tag" meant "tag anywhere in tree"
54
+ // New syntax: "..tag" means "tag anywhere in tree"
55
+ if (this.options.stopNodes && Array.isArray(this.options.stopNodes)) {
56
+ this.options.stopNodes = this.options.stopNodes.map(node => {
57
+ if (typeof node === 'string' && node.startsWith('*.')) {
58
+ // Convert old wildcard syntax to deep wildcard
59
+ return '..' + node.substring(2);
60
+ }
61
+ return node;
62
+ });
63
+ }
64
+
65
+ // Pre-compile stopNode expressions for pattern matching
66
+ this.stopNodeExpressions = [];
67
+ if (this.options.stopNodes && Array.isArray(this.options.stopNodes)) {
68
+ for (let i = 0; i < this.options.stopNodes.length; i++) {
69
+ const node = this.options.stopNodes[i];
70
+ if (typeof node === 'string') {
71
+ this.stopNodeExpressions.push(new Expression(node));
72
+ } else if (node instanceof Expression) {
73
+ this.stopNodeExpressions.push(node);
74
+ }
75
+ }
76
+ }
77
+
78
+ if (this.options.ignoreAttributes === true || this.options.attributesGroupName) {
79
+ this.isAttribute = function (/*a*/) {
80
+ return false;
81
+ };
82
+ } else {
83
+ this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
84
+ this.attrPrefixLen = this.options.attributeNamePrefix.length;
85
+ this.isAttribute = isAttribute;
86
+ }
87
+
88
+ this.processTextOrObjNode = processTextOrObjNode
89
+
90
+ if (this.options.format) {
91
+ this.indentate = indentate;
92
+ this.tagEndChar = '>\n';
93
+ this.newLine = '\n';
94
+ } else {
95
+ this.indentate = function () {
96
+ return '';
97
+ };
98
+ this.tagEndChar = '>';
99
+ this.newLine = '';
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Detect XML version from the ?xml declaration at the root of a plain-object input.
105
+ * Checks both attributesGroupName and flat attribute forms.
106
+ * Returns '1.0' if no declaration is found.
107
+ */
108
+ function detectXmlVersionFromObj(jObj, options) {
109
+ const decl = jObj['?xml'];
110
+ if (decl && typeof decl === 'object') {
111
+ // attributesGroupName path e.g. { '$$': { '@_version': '1.1' } }
112
+ if (options.attributesGroupName && decl[options.attributesGroupName]) {
113
+ const v = decl[options.attributesGroupName][options.attributeNamePrefix + 'version'];
114
+ if (v) return v;
115
+ }
116
+ // flat attribute path e.g. { '@_version': '1.1' }
117
+ const v = decl[options.attributeNamePrefix + 'version'];
118
+ if (v) return v;
119
+ }
120
+ return '1.0';
121
+ }
122
+
123
+ /**
124
+ * Resolve a tag or attribute name through sanitizeName if configured.
125
+ * Validation via xml-naming's qName is performed first; the sanitizeName
126
+ * callback is invoked only when the name is invalid. If sanitizeName is
127
+ * false (default), no validation occurs and the name is used as-is.
128
+ *
129
+ * @param {string} name - raw name from the JS object
130
+ * @param {boolean} isAttribute - true when resolving an attribute name
131
+ * @param {object} options
132
+ * @param {Matcher} matcher - current matcher state (readonly from callback perspective)
133
+ * @param {string} xmlVersion - '1.0' or '1.1', forwarded to xml-naming
134
+ */
135
+ function resolveTagName(name, isAttribute, options, matcher, xmlVersion) {
136
+ if (!options.sanitizeName) return name;
137
+ if (qName(name, { xmlVersion })) return name;
138
+ return options.sanitizeName(name, { isAttribute, matcher: matcher.readOnly() });
139
+ }
140
+
141
+ Builder.prototype.build = function (jObj) {
142
+ if (this.options.preserveOrder) {
143
+ return buildFromOrderedJs(jObj, this.options);
144
+ } else {
145
+ if (Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1) {
146
+ jObj = {
147
+ [this.options.arrayNodeName]: jObj
148
+ }
149
+ }
150
+ // Initialize matcher for path tracking
151
+ const matcher = new Matcher();
152
+ const xmlVersion = detectXmlVersionFromObj(jObj, this.options);
153
+ return this.j2x(jObj, 0, matcher, xmlVersion).val;
154
+ }
155
+ };
156
+
157
+ Builder.prototype.j2x = function (jObj, level, matcher, xmlVersion) {
158
+ let attrStr = '';
159
+ let val = '';
160
+ if (this.options.maxNestedTags && matcher.getDepth() >= this.options.maxNestedTags) {
161
+ throw new Error("Maximum nested tags exceeded");
162
+ }
163
+ // Get jPath based on option: string for backward compatibility, or Matcher for new features
164
+ const jPath = this.options.jPath ? matcher.toString() : matcher;
165
+
166
+ // Check if current node is a stopNode (will be used for attribute encoding)
167
+ const isCurrentStopNode = this.checkStopNode(matcher);
168
+
169
+ for (let key in jObj) {
170
+ if (!Object.prototype.hasOwnProperty.call(jObj, key)) continue;
171
+
172
+ // Resolve the key through sanitizeName before any use.
173
+ // Special keys (textNodeName, cdataPropName, commentPropName, attributeNamePrefix,
174
+ // attributesGroupName, "?" PI tags) are exempt — they are builder-internal conventions,
175
+ // not user-supplied XML names.
176
+ const isSpecialKey = key === this.options.textNodeName
177
+ || key === this.options.cdataPropName
178
+ || key === this.options.commentPropName
179
+ || (this.options.attributesGroupName && key === this.options.attributesGroupName)
180
+ || this.isAttribute(key)
181
+ || key[0] === '?';
182
+
183
+ const resolvedKey = isSpecialKey
184
+ ? key
185
+ : resolveTagName(key, false, this.options, matcher, xmlVersion);
186
+
187
+ if (typeof jObj[key] === 'undefined') {
188
+ // supress undefined node only if it is not an attribute
189
+ if (this.isAttribute(key)) {
190
+ val += '';
191
+ }
192
+ } else if (jObj[key] === null) {
193
+ // null attribute should be ignored by the attribute list, but should not cause the tag closing
194
+ if (this.isAttribute(key)) {
195
+ val += '';
196
+ } else if (resolvedKey === this.options.cdataPropName || resolvedKey === this.options.commentPropName) {
197
+ val += '';
198
+ } else if (resolvedKey[0] === '?') {
199
+ val += this.indentate(level) + '<' + resolvedKey + '?' + this.tagEndChar;
200
+ } else {
201
+ val += this.indentate(level) + '<' + resolvedKey + '/' + this.tagEndChar;
202
+ }
203
+ } else if (jObj[key] instanceof Date) {
204
+ val += this.buildTextValNode(jObj[key], resolvedKey, '', level, matcher);
205
+ } else if (typeof jObj[key] !== 'object') {
206
+ //premitive type
207
+ const attr = this.isAttribute(key);
208
+ if (attr && !this.ignoreAttributesFn(attr, jPath)) {
209
+ // Resolve the attribute name through sanitizeName
210
+ const resolvedAttr = resolveTagName(attr, true, this.options, matcher, xmlVersion);
211
+ attrStr += this.buildAttrPairStr(resolvedAttr, '' + jObj[key], isCurrentStopNode);
212
+ } else if (!attr) {
213
+ //tag value
214
+ if (key === this.options.textNodeName) {
215
+ let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
216
+ val += this.replaceEntitiesValue(newval);
217
+ } else {
218
+ // Check if this is a stopNode before building
219
+ matcher.push(resolvedKey);
220
+ const isStopNode = this.checkStopNode(matcher);
221
+ matcher.pop();
222
+
223
+ if (isStopNode) {
224
+ // Build as raw content without encoding
225
+ const textValue = '' + jObj[key];
226
+ if (textValue === '') {
227
+ val += this.indentate(level) + '<' + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
228
+ } else {
229
+ val += this.indentate(level) + '<' + resolvedKey + '>' + textValue + '</' + resolvedKey + this.tagEndChar;
230
+ }
231
+ } else {
232
+ val += this.buildTextValNode(jObj[key], resolvedKey, '', level, matcher);
233
+ }
234
+ }
235
+ }
236
+ } else if (Array.isArray(jObj[key])) {
237
+ //repeated nodes
238
+ const arrLen = jObj[key].length;
239
+ let listTagVal = "";
240
+ let listTagAttr = "";
241
+ for (let j = 0; j < arrLen; j++) {
242
+ const item = jObj[key][j];
243
+ if (typeof item === 'undefined') {
244
+ // supress undefined node
245
+ } else if (item === null) {
246
+ if (resolvedKey[0] === "?") val += this.indentate(level) + '<' + resolvedKey + '?' + this.tagEndChar;
247
+ else val += this.indentate(level) + '<' + resolvedKey + '/' + this.tagEndChar;
248
+ } else if (typeof item === 'object') {
249
+ if (this.options.oneListGroup) {
250
+ // Push tag to matcher before recursive call
251
+ matcher.push(resolvedKey);
252
+ const result = this.j2x(item, level + 1, matcher, xmlVersion);
253
+ // Pop tag from matcher after recursive call
254
+ matcher.pop();
255
+
256
+ listTagVal += result.val;
257
+ if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) {
258
+ listTagAttr += result.attrStr
259
+ }
260
+ } else {
261
+ listTagVal += this.processTextOrObjNode(item, resolvedKey, level, matcher, xmlVersion)
262
+ }
263
+ } else {
264
+ if (this.options.oneListGroup) {
265
+ let textValue = this.options.tagValueProcessor(resolvedKey, item);
266
+ textValue = this.replaceEntitiesValue(textValue);
267
+ listTagVal += textValue;
268
+ } else {
269
+ // Check if this is a stopNode before building
270
+ matcher.push(resolvedKey);
271
+ const isStopNode = this.checkStopNode(matcher);
272
+ matcher.pop();
273
+
274
+ if (isStopNode) {
275
+ // Build as raw content without encoding
276
+ const textValue = '' + item;
277
+ if (textValue === '') {
278
+ listTagVal += this.indentate(level) + '<' + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
279
+ } else {
280
+ listTagVal += this.indentate(level) + '<' + resolvedKey + '>' + textValue + '</' + resolvedKey + this.tagEndChar;
281
+ }
282
+ } else {
283
+ listTagVal += this.buildTextValNode(item, resolvedKey, '', level, matcher);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ if (this.options.oneListGroup) {
289
+ listTagVal = this.buildObjectNode(listTagVal, resolvedKey, listTagAttr, level);
290
+ }
291
+ val += listTagVal;
292
+ } else {
293
+ //nested node
294
+ if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
295
+ const Ks = Object.keys(jObj[key]);
296
+ const L = Ks.length;
297
+ for (let j = 0; j < L; j++) {
298
+ // Resolve attribute names inside attributesGroupName
299
+ const resolvedAttr = resolveTagName(Ks[j], true, this.options, matcher, xmlVersion);
300
+ attrStr += this.buildAttrPairStr(resolvedAttr, '' + jObj[key][Ks[j]], isCurrentStopNode);
301
+ }
302
+ } else {
303
+ val += this.processTextOrObjNode(jObj[key], resolvedKey, level, matcher, xmlVersion)
304
+ }
305
+ }
306
+ }
307
+ return { attrStr: attrStr, val: val };
308
+ };
309
+
310
+ Builder.prototype.buildAttrPairStr = function (attrName, val, isStopNode) {
311
+ if (!isStopNode) {
312
+ val = this.options.attributeValueProcessor(attrName, '' + val);
313
+ val = this.replaceEntitiesValue(val);
314
+ }
315
+ if (this.options.suppressBooleanAttributes && val === "true") {
316
+ return ' ' + attrName;
317
+ } else return ' ' + attrName + '="' + escapeAttribute(val) + '"';
318
+ }
319
+
320
+ function processTextOrObjNode(object, key, level, matcher, xmlVersion) {
321
+ // Extract attributes to pass to matcher
322
+ const attrValues = this.extractAttributes(object);
323
+
324
+ // Push tag to matcher before recursion WITH attributes
325
+ matcher.push(key, attrValues);
326
+
327
+ // Check if this entire node is a stopNode
328
+ const isStopNode = this.checkStopNode(matcher);
329
+
330
+ if (isStopNode) {
331
+ // For stopNodes, build raw content without entity encoding
332
+ const rawContent = this.buildRawContent(object);
333
+ const attrStr = this.buildAttributesForStopNode(object);
334
+ matcher.pop();
335
+ return this.buildObjectNode(rawContent, key, attrStr, level);
336
+ }
337
+
338
+ const result = this.j2x(object, level + 1, matcher, xmlVersion);
339
+ // Pop tag from matcher after recursion
340
+ matcher.pop();
341
+
342
+ // PI/XML-declaration tags must never emit text content — route through
343
+ // buildTextValNode which correctly ignores the text node for "?" tags.
344
+ if (key[0] === '?') {
345
+ return this.buildTextValNode('', key, result.attrStr, level, matcher);
346
+ } else if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
347
+ return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level, matcher);
348
+ } else {
349
+ return this.buildObjectNode(result.val, key, result.attrStr, level);
350
+ }
351
+ }
352
+
353
+ // Helper method to extract attributes from an object
354
+ Builder.prototype.extractAttributes = function (obj) {
355
+ if (!obj || typeof obj !== 'object') return null;
356
+
357
+ const attrValues = {};
358
+ let hasAttrs = false;
359
+
360
+ // Check for attributesGroupName (when attributes are grouped)
361
+ if (this.options.attributesGroupName && obj[this.options.attributesGroupName]) {
362
+ const attrGroup = obj[this.options.attributesGroupName];
363
+ for (let attrKey in attrGroup) {
364
+ if (!Object.prototype.hasOwnProperty.call(attrGroup, attrKey)) continue;
365
+ // Remove attribute prefix if present
366
+ const cleanKey = attrKey.startsWith(this.options.attributeNamePrefix)
367
+ ? attrKey.substring(this.options.attributeNamePrefix.length)
368
+ : attrKey;
369
+ attrValues[cleanKey] = escapeAttribute(attrGroup[attrKey]);
370
+ hasAttrs = true;
371
+ }
372
+ } else {
373
+ // Look for individual attributes (prefixed with attributeNamePrefix)
374
+ for (let key in obj) {
375
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
376
+ const attr = this.isAttribute(key);
377
+ if (attr) {
378
+ attrValues[attr] = escapeAttribute(obj[key]);
379
+ hasAttrs = true;
380
+ }
381
+ }
382
+ }
383
+
384
+ return hasAttrs ? attrValues : null;
385
+ };
386
+
387
+ // Build raw content for stopNode without entity encoding
388
+ Builder.prototype.buildRawContent = function (obj) {
389
+ if (typeof obj === 'string') {
390
+ return obj; // Already a string, return as-is
391
+ }
392
+
393
+ if (typeof obj !== 'object' || obj === null) {
394
+ return String(obj);
395
+ }
396
+
397
+ // Check if this is a stopNode data from parser: { "#text": "raw xml", "@_attr": "val" }
398
+ if (obj[this.options.textNodeName] !== undefined) {
399
+ return obj[this.options.textNodeName]; // Return raw text without encoding
400
+ }
401
+
402
+ // Build raw XML from nested structure
403
+ let content = '';
404
+
405
+ for (let key in obj) {
406
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
407
+
408
+ // Skip attributes
409
+ if (this.isAttribute(key)) continue;
410
+ if (this.options.attributesGroupName && key === this.options.attributesGroupName) continue;
411
+
412
+ const value = obj[key];
413
+
414
+ if (key === this.options.textNodeName) {
415
+ content += value; // Raw text
416
+ } else if (Array.isArray(value)) {
417
+ // Array of same tag
418
+ for (let item of value) {
419
+ if (typeof item === 'string' || typeof item === 'number') {
420
+ content += `<${key}>${item}</${key}>`;
421
+ } else if (typeof item === 'object' && item !== null) {
422
+ const nestedContent = this.buildRawContent(item);
423
+ const nestedAttrs = this.buildAttributesForStopNode(item);
424
+ if (nestedContent === '') {
425
+ content += `<${key}${nestedAttrs}/>`;
426
+ } else {
427
+ content += `<${key}${nestedAttrs}>${nestedContent}</${key}>`;
428
+ }
429
+ }
430
+ }
431
+ } else if (typeof value === 'object' && value !== null) {
432
+ // Nested object
433
+ const nestedContent = this.buildRawContent(value);
434
+ const nestedAttrs = this.buildAttributesForStopNode(value);
435
+ if (nestedContent === '') {
436
+ content += `<${key}${nestedAttrs}/>`;
437
+ } else {
438
+ content += `<${key}${nestedAttrs}>${nestedContent}</${key}>`;
439
+ }
440
+ } else {
441
+ // Primitive value
442
+ content += `<${key}>${value}</${key}>`;
443
+ }
444
+ }
445
+
446
+ return content;
447
+ };
448
+
449
+ // Build attribute string for stopNode (no entity encoding)
450
+ Builder.prototype.buildAttributesForStopNode = function (obj) {
451
+ if (!obj || typeof obj !== 'object') return '';
452
+
453
+ let attrStr = '';
454
+
455
+ // Check for attributesGroupName (when attributes are grouped)
456
+ if (this.options.attributesGroupName && obj[this.options.attributesGroupName]) {
457
+ const attrGroup = obj[this.options.attributesGroupName];
458
+ for (let attrKey in attrGroup) {
459
+ if (!Object.prototype.hasOwnProperty.call(attrGroup, attrKey)) continue;
460
+ const cleanKey = attrKey.startsWith(this.options.attributeNamePrefix)
461
+ ? attrKey.substring(this.options.attributeNamePrefix.length)
462
+ : attrKey;
463
+ const val = attrGroup[attrKey];
464
+ if (val === true && this.options.suppressBooleanAttributes) {
465
+ attrStr += ' ' + cleanKey;
466
+ } else {
467
+ attrStr += ' ' + cleanKey + '="' + val + '"'; // No encoding for stopNode
468
+ }
469
+ }
470
+ } else {
471
+ // Look for individual attributes
472
+ for (let key in obj) {
473
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
474
+ const attr = this.isAttribute(key);
475
+ if (attr) {
476
+ const val = obj[key];
477
+ if (val === true && this.options.suppressBooleanAttributes) {
478
+ attrStr += ' ' + attr;
479
+ } else {
480
+ attrStr += ' ' + attr + '="' + val + '"'; // No encoding for stopNode
481
+ }
482
+ }
483
+ }
484
+ }
485
+
486
+ return attrStr;
487
+ };
488
+
489
+ Builder.prototype.buildObjectNode = function (val, key, attrStr, level) {
490
+ if (val === "") {
491
+ if (key[0] === "?") return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
492
+ else {
493
+ return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
494
+ }
495
+ } else if (key[0] === "?") {
496
+ // PI/XML-declaration tags never have body content — treat them like empty.
497
+ return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
498
+ } else {
499
+ let tagEndExp = '</' + key + this.tagEndChar;
500
+ let piClosingChar = "";
501
+
502
+ if (key[0] === "?") {
503
+ piClosingChar = "?";
504
+ tagEndExp = "";
505
+ }
506
+
507
+ // attrStr is an empty string in case the attribute came as undefined or null
508
+ if ((attrStr || attrStr === '') && val.indexOf('<') === -1) {
509
+ return (this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' + val + tagEndExp);
510
+ } else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
511
+ return this.indentate(level) + `<!--${val}-->` + this.newLine;
512
+ } else {
513
+ return (
514
+ this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
515
+ val +
516
+ this.indentate(level) + tagEndExp);
517
+ }
518
+ }
519
+ }
520
+
521
+ Builder.prototype.closeTag = function (key) {
522
+ let closeTag = "";
523
+ if (this.options.unpairedTags.indexOf(key) !== -1) { //unpaired
524
+ if (!this.options.suppressUnpairedNode) closeTag = "/"
525
+ } else if (this.options.suppressEmptyNode) { //empty
526
+ closeTag = "/";
527
+ } else {
528
+ closeTag = `></${key}`
529
+ }
530
+ return closeTag;
531
+ }
532
+
533
+ Builder.prototype.checkStopNode = function (matcher) {
534
+ if (!this.stopNodeExpressions || this.stopNodeExpressions.length === 0) return false;
535
+
536
+ for (let i = 0; i < this.stopNodeExpressions.length; i++) {
537
+ if (matcher.matches(this.stopNodeExpressions[i])) {
538
+ return true;
539
+ }
540
+ }
541
+ return false;
542
+ }
543
+
544
+ function buildEmptyObjNode(val, key, attrStr, level) {
545
+ if (val !== '') {
546
+ return this.buildObjectNode(val, key, attrStr, level);
547
+ } else {
548
+ if (key[0] === "?") return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
549
+ else {
550
+ return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
551
+ }
552
+ }
553
+ }
554
+
555
+ Builder.prototype.buildTextValNode = function (val, key, attrStr, level, matcher) {
556
+ if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
557
+ const safeVal = safeCdata(val);
558
+ return this.indentate(level) + `<![CDATA[${safeVal}]]>` + this.newLine;
559
+ } else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
560
+ const safeVal = safeComment(val);
561
+ return this.indentate(level) + `<!--${safeVal}-->` + this.newLine;
562
+ } else if (key[0] === "?") {//PI tag
563
+ return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
564
+ } else {
565
+ // Normal processing: apply tagValueProcessor and entity replacement
566
+ let textValue = this.options.tagValueProcessor(key, val);
567
+ textValue = this.replaceEntitiesValue(textValue);
568
+
569
+ if (textValue === '') {
570
+ return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
571
+ } else {
572
+ return this.indentate(level) + '<' + key + attrStr + '>' +
573
+ textValue +
574
+ '</' + key + this.tagEndChar;
575
+ }
576
+ }
577
+ }
578
+
579
+ Builder.prototype.replaceEntitiesValue = function (textValue) {
580
+ if (textValue && textValue.length > 0 && this.options.processEntities) {
581
+ for (let i = 0; i < this.options.entities.length; i++) {
582
+ const entity = this.options.entities[i];
583
+ textValue = textValue.replace(entity.regex, entity.val);
584
+ }
585
+ }
586
+ return textValue;
587
+ }
588
+
589
+ function indentate(level) {
590
+ return this.options.indentBy.repeat(level);
591
+ }
592
+
593
+ function isAttribute(name /*, options*/) {
594
+ if (name.startsWith(this.options.attributeNamePrefix) && name !== this.options.textNodeName) {
595
+ return name.substr(this.attrPrefixLen);
596
+ } else {
597
+ return false;
598
+ }
599
+ }
@@ -0,0 +1,18 @@
1
+ export default function getIgnoreAttributesFn(ignoreAttributes) {
2
+ if (typeof ignoreAttributes === 'function') {
3
+ return ignoreAttributes
4
+ }
5
+ if (Array.isArray(ignoreAttributes)) {
6
+ return (attrName) => {
7
+ for (const pattern of ignoreAttributes) {
8
+ if (typeof pattern === 'string' && attrName === pattern) {
9
+ return true
10
+ }
11
+ if (pattern instanceof RegExp && pattern.test(attrName)) {
12
+ return true
13
+ }
14
+ }
15
+ }
16
+ }
17
+ return () => false
18
+ }