@cdklabs/cdk-construct-connect-datalake 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +9 -9
- package/.jsii.tabl.json +1 -1
- package/lib/index.js +1 -1
- package/node_modules/@aws-sdk/client-cloudformation/dist-cjs/index.js +9 -9
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForChangeSetCreateComplete.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackCreateComplete.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackDeleteComplete.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackExists.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackImportComplete.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackRefactorCreateComplete.js +1 -1
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackRefactorExecuteComplete.js +1 -1
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackRollbackComplete.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-es/waiters/waitForStackUpdateComplete.js +2 -2
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/CloudFormation.d.ts +11 -10
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/CloudFormation.d.ts +13 -10
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForChangeSetCreateComplete.d.ts +9 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackCreateComplete.d.ts +9 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackDeleteComplete.d.ts +11 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackExists.d.ts +9 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackImportComplete.d.ts +9 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackRefactorCreateComplete.d.ts +11 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackRefactorExecuteComplete.d.ts +11 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackRollbackComplete.d.ts +9 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForStackUpdateComplete.d.ts +9 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/ts3.4/waiters/waitForTypeRegistrationComplete.d.ts +11 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForChangeSetCreateComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackCreateComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackDeleteComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackExists.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackImportComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackRefactorCreateComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackRefactorExecuteComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackRollbackComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForStackUpdateComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/dist-types/waiters/waitForTypeRegistrationComplete.d.ts +4 -3
- package/node_modules/@aws-sdk/client-cloudformation/package.json +8 -8
- package/node_modules/@aws-sdk/client-connect/package.json +7 -7
- package/node_modules/@aws-sdk/client-glue/dist-cjs/schemas/schemas_0.js +13 -12
- package/node_modules/@aws-sdk/client-glue/dist-es/schemas/schemas_0.js +13 -12
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/GetDataQualityRulesetEvaluationRunCommand.d.ts +1 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/GetPartitionCommand.d.ts +7 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/GetPartitionsCommand.d.ts +7 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/GetTableVersionCommand.d.ts +7 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/GetTableVersionsCommand.d.ts +7 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/ListDataQualityRulesetEvaluationRunsCommand.d.ts +1 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/commands/StartDataQualityRulesetEvaluationRunCommand.d.ts +1 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/models/models_1.d.ts +5 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/models/models_2.d.ts +25 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/ts3.4/models/models_1.d.ts +1 -0
- package/node_modules/@aws-sdk/client-glue/dist-types/ts3.4/models/models_2.d.ts +5 -0
- package/node_modules/@aws-sdk/client-glue/package.json +7 -7
- package/node_modules/@aws-sdk/client-lakeformation/package.json +7 -7
- package/node_modules/@aws-sdk/client-ram/package.json +7 -7
- package/node_modules/@aws-sdk/client-sts/package.json +8 -8
- package/node_modules/@aws-sdk/core/dist-cjs/index.js +15 -6
- package/node_modules/@aws-sdk/core/dist-cjs/submodules/client/index.js +15 -6
- package/node_modules/@aws-sdk/core/dist-es/submodules/client/emitWarningIfUnsupportedVersion.js +15 -6
- package/node_modules/@aws-sdk/core/dist-types/submodules/client/emitWarningIfUnsupportedVersion.d.ts +1 -2
- package/node_modules/@aws-sdk/core/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-env/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-http/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +9 -9
- package/node_modules/@aws-sdk/credential-provider-login/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-node/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-process/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +4 -4
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +3 -3
- package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +2 -2
- package/node_modules/@aws-sdk/middleware-user-agent/package.json +3 -3
- package/node_modules/@aws-sdk/nested-clients/package.json +7 -7
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +2 -2
- package/node_modules/@aws-sdk/token-providers/package.json +3 -3
- package/node_modules/@aws-sdk/util-user-agent-node/package.json +2 -2
- package/node_modules/@aws-sdk/xml-builder/dist-cjs/xml-external/nodable_entities.js +336 -0
- package/node_modules/@aws-sdk/xml-builder/dist-cjs/xml-parser.js +26 -0
- package/node_modules/@aws-sdk/xml-builder/dist-es/xml-external/nodable_entities.js +332 -0
- package/node_modules/@aws-sdk/xml-builder/dist-es/xml-parser.js +26 -0
- package/node_modules/@aws-sdk/xml-builder/dist-types/ts3.4/xml-external/nodable_entities.d.ts +66 -0
- package/node_modules/@aws-sdk/xml-builder/dist-types/xml-external/nodable_entities.d.ts +65 -0
- package/node_modules/@aws-sdk/xml-builder/package.json +3 -2
- package/node_modules/@smithy/middleware-retry/package.json +2 -2
- package/node_modules/@smithy/util-retry/dist-cjs/index.js +7 -3
- package/node_modules/@smithy/util-retry/dist-es/AdaptiveRetryStrategy.js +5 -2
- package/node_modules/@smithy/util-retry/dist-es/DefaultRateLimiter.js +2 -1
- package/node_modules/@smithy/util-retry/package.json +2 -2
- package/node_modules/fast-xml-builder/CHANGELOG.md +24 -0
- package/node_modules/fast-xml-builder/README.md +53 -2
- package/node_modules/fast-xml-builder/lib/fxb.cjs +1 -1
- package/node_modules/fast-xml-builder/lib/fxb.d.cts +91 -1
- package/node_modules/fast-xml-builder/lib/fxb.min.js +1 -1
- package/node_modules/fast-xml-builder/lib/fxb.min.js.map +1 -1
- package/node_modules/fast-xml-builder/package.json +3 -2
- package/node_modules/fast-xml-builder/src/fxb.d.ts +93 -3
- package/node_modules/fast-xml-builder/src/fxb.js +106 -40
- package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +95 -42
- package/node_modules/fast-xml-builder/src/util.js +16 -0
- package/node_modules/fast-xml-parser/CHANGELOG.md +8 -0
- package/node_modules/fast-xml-parser/lib/fxp.cjs +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js.map +1 -1
- package/node_modules/fast-xml-parser/package.json +1 -1
- package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +16 -11
- package/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js +2 -2
- package/node_modules/strnum/CHANGELOG.md +3 -0
- package/node_modules/strnum/package.json +3 -2
- package/node_modules/strnum/strnum.js +12 -10
- package/node_modules/xml-naming/README.md +189 -0
- package/node_modules/xml-naming/package.json +54 -0
- package/node_modules/xml-naming/src/index.d.ts +74 -0
- package/node_modules/xml-naming/src/index.js +270 -0
- package/package.json +10 -10
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import buildFromOrderedJs from './orderedJs2Xml.js';
|
|
4
4
|
import getIgnoreAttributesFn from "./ignoreAttributes.js";
|
|
5
5
|
import { Expression, Matcher } from 'path-expression-matcher';
|
|
6
|
+
import { safeComment, safeCdata, escapeAttribute } from './util.js';
|
|
7
|
+
import { qName } from 'xml-naming';
|
|
6
8
|
|
|
7
9
|
const defaultOptions = {
|
|
8
10
|
attributeNamePrefix: '@_',
|
|
@@ -37,7 +39,11 @@ const defaultOptions = {
|
|
|
37
39
|
// transformAttributeName: false,
|
|
38
40
|
oneListGroup: false,
|
|
39
41
|
maxNestedTags: 100,
|
|
40
|
-
jPath: true // When true, callbacks receive string jPath; when false, receive Matcher instance
|
|
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.
|
|
41
47
|
};
|
|
42
48
|
|
|
43
49
|
export default function Builder(options) {
|
|
@@ -94,6 +100,44 @@ export default function Builder(options) {
|
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
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
|
+
|
|
97
141
|
Builder.prototype.build = function (jObj) {
|
|
98
142
|
if (this.options.preserveOrder) {
|
|
99
143
|
return buildFromOrderedJs(jObj, this.options);
|
|
@@ -105,11 +149,12 @@ Builder.prototype.build = function (jObj) {
|
|
|
105
149
|
}
|
|
106
150
|
// Initialize matcher for path tracking
|
|
107
151
|
const matcher = new Matcher();
|
|
108
|
-
|
|
152
|
+
const xmlVersion = detectXmlVersionFromObj(jObj, this.options);
|
|
153
|
+
return this.j2x(jObj, 0, matcher, xmlVersion).val;
|
|
109
154
|
}
|
|
110
155
|
};
|
|
111
156
|
|
|
112
|
-
Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
157
|
+
Builder.prototype.j2x = function (jObj, level, matcher, xmlVersion) {
|
|
113
158
|
let attrStr = '';
|
|
114
159
|
let val = '';
|
|
115
160
|
if (this.options.maxNestedTags && matcher.getDepth() >= this.options.maxNestedTags) {
|
|
@@ -123,6 +168,22 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
123
168
|
|
|
124
169
|
for (let key in jObj) {
|
|
125
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
|
+
|
|
126
187
|
if (typeof jObj[key] === 'undefined') {
|
|
127
188
|
// supress undefined node only if it is not an attribute
|
|
128
189
|
if (this.isAttribute(key)) {
|
|
@@ -132,21 +193,22 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
132
193
|
// null attribute should be ignored by the attribute list, but should not cause the tag closing
|
|
133
194
|
if (this.isAttribute(key)) {
|
|
134
195
|
val += '';
|
|
135
|
-
} else if (
|
|
196
|
+
} else if (resolvedKey === this.options.cdataPropName || resolvedKey === this.options.commentPropName) {
|
|
136
197
|
val += '';
|
|
137
|
-
} else if (
|
|
138
|
-
val += this.indentate(level) + '<' +
|
|
198
|
+
} else if (resolvedKey[0] === '?') {
|
|
199
|
+
val += this.indentate(level) + '<' + resolvedKey + '?' + this.tagEndChar;
|
|
139
200
|
} else {
|
|
140
|
-
val += this.indentate(level) + '<' +
|
|
201
|
+
val += this.indentate(level) + '<' + resolvedKey + '/' + this.tagEndChar;
|
|
141
202
|
}
|
|
142
|
-
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
143
203
|
} else if (jObj[key] instanceof Date) {
|
|
144
|
-
val += this.buildTextValNode(jObj[key],
|
|
204
|
+
val += this.buildTextValNode(jObj[key], resolvedKey, '', level, matcher);
|
|
145
205
|
} else if (typeof jObj[key] !== 'object') {
|
|
146
206
|
//premitive type
|
|
147
207
|
const attr = this.isAttribute(key);
|
|
148
208
|
if (attr && !this.ignoreAttributesFn(attr, jPath)) {
|
|
149
|
-
|
|
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);
|
|
150
212
|
} else if (!attr) {
|
|
151
213
|
//tag value
|
|
152
214
|
if (key === this.options.textNodeName) {
|
|
@@ -154,7 +216,7 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
154
216
|
val += this.replaceEntitiesValue(newval);
|
|
155
217
|
} else {
|
|
156
218
|
// Check if this is a stopNode before building
|
|
157
|
-
matcher.push(
|
|
219
|
+
matcher.push(resolvedKey);
|
|
158
220
|
const isStopNode = this.checkStopNode(matcher);
|
|
159
221
|
matcher.pop();
|
|
160
222
|
|
|
@@ -162,12 +224,12 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
162
224
|
// Build as raw content without encoding
|
|
163
225
|
const textValue = '' + jObj[key];
|
|
164
226
|
if (textValue === '') {
|
|
165
|
-
val += this.indentate(level) + '<' +
|
|
227
|
+
val += this.indentate(level) + '<' + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
|
|
166
228
|
} else {
|
|
167
|
-
val += this.indentate(level) + '<' +
|
|
229
|
+
val += this.indentate(level) + '<' + resolvedKey + '>' + textValue + '</' + resolvedKey + this.tagEndChar;
|
|
168
230
|
}
|
|
169
231
|
} else {
|
|
170
|
-
val += this.buildTextValNode(jObj[key],
|
|
232
|
+
val += this.buildTextValNode(jObj[key], resolvedKey, '', level, matcher);
|
|
171
233
|
}
|
|
172
234
|
}
|
|
173
235
|
}
|
|
@@ -181,14 +243,13 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
181
243
|
if (typeof item === 'undefined') {
|
|
182
244
|
// supress undefined node
|
|
183
245
|
} else if (item === null) {
|
|
184
|
-
if (
|
|
185
|
-
else val += this.indentate(level) + '<' +
|
|
186
|
-
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
246
|
+
if (resolvedKey[0] === "?") val += this.indentate(level) + '<' + resolvedKey + '?' + this.tagEndChar;
|
|
247
|
+
else val += this.indentate(level) + '<' + resolvedKey + '/' + this.tagEndChar;
|
|
187
248
|
} else if (typeof item === 'object') {
|
|
188
249
|
if (this.options.oneListGroup) {
|
|
189
250
|
// Push tag to matcher before recursive call
|
|
190
|
-
matcher.push(
|
|
191
|
-
const result = this.j2x(item, level + 1, matcher);
|
|
251
|
+
matcher.push(resolvedKey);
|
|
252
|
+
const result = this.j2x(item, level + 1, matcher, xmlVersion);
|
|
192
253
|
// Pop tag from matcher after recursive call
|
|
193
254
|
matcher.pop();
|
|
194
255
|
|
|
@@ -197,16 +258,16 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
197
258
|
listTagAttr += result.attrStr
|
|
198
259
|
}
|
|
199
260
|
} else {
|
|
200
|
-
listTagVal += this.processTextOrObjNode(item,
|
|
261
|
+
listTagVal += this.processTextOrObjNode(item, resolvedKey, level, matcher, xmlVersion)
|
|
201
262
|
}
|
|
202
263
|
} else {
|
|
203
264
|
if (this.options.oneListGroup) {
|
|
204
|
-
let textValue = this.options.tagValueProcessor(
|
|
265
|
+
let textValue = this.options.tagValueProcessor(resolvedKey, item);
|
|
205
266
|
textValue = this.replaceEntitiesValue(textValue);
|
|
206
267
|
listTagVal += textValue;
|
|
207
268
|
} else {
|
|
208
269
|
// Check if this is a stopNode before building
|
|
209
|
-
matcher.push(
|
|
270
|
+
matcher.push(resolvedKey);
|
|
210
271
|
const isStopNode = this.checkStopNode(matcher);
|
|
211
272
|
matcher.pop();
|
|
212
273
|
|
|
@@ -214,18 +275,18 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
214
275
|
// Build as raw content without encoding
|
|
215
276
|
const textValue = '' + item;
|
|
216
277
|
if (textValue === '') {
|
|
217
|
-
listTagVal += this.indentate(level) + '<' +
|
|
278
|
+
listTagVal += this.indentate(level) + '<' + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
|
|
218
279
|
} else {
|
|
219
|
-
listTagVal += this.indentate(level) + '<' +
|
|
280
|
+
listTagVal += this.indentate(level) + '<' + resolvedKey + '>' + textValue + '</' + resolvedKey + this.tagEndChar;
|
|
220
281
|
}
|
|
221
282
|
} else {
|
|
222
|
-
listTagVal += this.buildTextValNode(item,
|
|
283
|
+
listTagVal += this.buildTextValNode(item, resolvedKey, '', level, matcher);
|
|
223
284
|
}
|
|
224
285
|
}
|
|
225
286
|
}
|
|
226
287
|
}
|
|
227
288
|
if (this.options.oneListGroup) {
|
|
228
|
-
listTagVal = this.buildObjectNode(listTagVal,
|
|
289
|
+
listTagVal = this.buildObjectNode(listTagVal, resolvedKey, listTagAttr, level);
|
|
229
290
|
}
|
|
230
291
|
val += listTagVal;
|
|
231
292
|
} else {
|
|
@@ -234,10 +295,12 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
234
295
|
const Ks = Object.keys(jObj[key]);
|
|
235
296
|
const L = Ks.length;
|
|
236
297
|
for (let j = 0; j < L; j++) {
|
|
237
|
-
|
|
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);
|
|
238
301
|
}
|
|
239
302
|
} else {
|
|
240
|
-
val += this.processTextOrObjNode(jObj[key],
|
|
303
|
+
val += this.processTextOrObjNode(jObj[key], resolvedKey, level, matcher, xmlVersion)
|
|
241
304
|
}
|
|
242
305
|
}
|
|
243
306
|
}
|
|
@@ -251,10 +314,10 @@ Builder.prototype.buildAttrPairStr = function (attrName, val, isStopNode) {
|
|
|
251
314
|
}
|
|
252
315
|
if (this.options.suppressBooleanAttributes && val === "true") {
|
|
253
316
|
return ' ' + attrName;
|
|
254
|
-
} else return ' ' + attrName + '="' + val + '"';
|
|
317
|
+
} else return ' ' + attrName + '="' + escapeAttribute(val) + '"';
|
|
255
318
|
}
|
|
256
319
|
|
|
257
|
-
function processTextOrObjNode(object, key, level, matcher) {
|
|
320
|
+
function processTextOrObjNode(object, key, level, matcher, xmlVersion) {
|
|
258
321
|
// Extract attributes to pass to matcher
|
|
259
322
|
const attrValues = this.extractAttributes(object);
|
|
260
323
|
|
|
@@ -272,11 +335,15 @@ function processTextOrObjNode(object, key, level, matcher) {
|
|
|
272
335
|
return this.buildObjectNode(rawContent, key, attrStr, level);
|
|
273
336
|
}
|
|
274
337
|
|
|
275
|
-
const result = this.j2x(object, level + 1, matcher);
|
|
338
|
+
const result = this.j2x(object, level + 1, matcher, xmlVersion);
|
|
276
339
|
// Pop tag from matcher after recursion
|
|
277
340
|
matcher.pop();
|
|
278
341
|
|
|
279
|
-
|
|
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) {
|
|
280
347
|
return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level, matcher);
|
|
281
348
|
} else {
|
|
282
349
|
return this.buildObjectNode(result.val, key, result.attrStr, level);
|
|
@@ -299,7 +366,7 @@ Builder.prototype.extractAttributes = function (obj) {
|
|
|
299
366
|
const cleanKey = attrKey.startsWith(this.options.attributeNamePrefix)
|
|
300
367
|
? attrKey.substring(this.options.attributeNamePrefix.length)
|
|
301
368
|
: attrKey;
|
|
302
|
-
attrValues[cleanKey] = attrGroup[attrKey];
|
|
369
|
+
attrValues[cleanKey] = escapeAttribute(attrGroup[attrKey]);
|
|
303
370
|
hasAttrs = true;
|
|
304
371
|
}
|
|
305
372
|
} else {
|
|
@@ -308,7 +375,7 @@ Builder.prototype.extractAttributes = function (obj) {
|
|
|
308
375
|
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
309
376
|
const attr = this.isAttribute(key);
|
|
310
377
|
if (attr) {
|
|
311
|
-
attrValues[attr] = obj[key];
|
|
378
|
+
attrValues[attr] = escapeAttribute(obj[key]);
|
|
312
379
|
hasAttrs = true;
|
|
313
380
|
}
|
|
314
381
|
}
|
|
@@ -425,8 +492,10 @@ Builder.prototype.buildObjectNode = function (val, key, attrStr, level) {
|
|
|
425
492
|
else {
|
|
426
493
|
return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
|
|
427
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;
|
|
428
498
|
} else {
|
|
429
|
-
|
|
430
499
|
let tagEndExp = '</' + key + this.tagEndChar;
|
|
431
500
|
let piClosingChar = "";
|
|
432
501
|
|
|
@@ -479,19 +548,16 @@ function buildEmptyObjNode(val, key, attrStr, level) {
|
|
|
479
548
|
if (key[0] === "?") return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
|
|
480
549
|
else {
|
|
481
550
|
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
|
|
482
|
-
// return this.buildTagStr(level,key, attrStr);
|
|
483
551
|
}
|
|
484
552
|
}
|
|
485
553
|
}
|
|
486
554
|
|
|
487
555
|
Builder.prototype.buildTextValNode = function (val, key, attrStr, level, matcher) {
|
|
488
556
|
if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
|
|
489
|
-
const safeVal =
|
|
557
|
+
const safeVal = safeCdata(val);
|
|
490
558
|
return this.indentate(level) + `<![CDATA[${safeVal}]]>` + this.newLine;
|
|
491
559
|
} else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
|
|
492
|
-
const safeVal =
|
|
493
|
-
.replace(/--/g, '- -') // -- is illegal anywhere in comment content
|
|
494
|
-
.replace(/-$/, '- '); // trailing - would form -- with the closing -->
|
|
560
|
+
const safeVal = safeComment(val);
|
|
495
561
|
return this.indentate(level) + `<!--${safeVal}-->` + this.newLine;
|
|
496
562
|
} else if (key[0] === "?") {//PI tag
|
|
497
563
|
return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
|
|
@@ -1,16 +1,57 @@
|
|
|
1
1
|
import { Expression, Matcher } from 'path-expression-matcher';
|
|
2
|
+
import { safeComment, safeCdata, escapeAttribute } from "./util.js";
|
|
3
|
+
import { qName } from 'xml-naming';
|
|
2
4
|
|
|
3
5
|
const EOL = "\n";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Detect XML version from the first element of the ordered array input.
|
|
9
|
+
* The first element must be a ?xml processing instruction with a version attribute.
|
|
10
|
+
* Returns '1.0' if not found.
|
|
11
|
+
*
|
|
12
|
+
* @param {array} jArray
|
|
13
|
+
* @param {object} options
|
|
14
|
+
*/
|
|
15
|
+
function detectXmlVersionFromArray(jArray, options) {
|
|
16
|
+
if (!Array.isArray(jArray) || jArray.length === 0) return '1.0';
|
|
17
|
+
const first = jArray[0];
|
|
18
|
+
const firstKey = propName(first);
|
|
19
|
+
if (firstKey === '?xml') {
|
|
20
|
+
const attrs = first[':@'];
|
|
21
|
+
if (attrs) {
|
|
22
|
+
const versionKey = options.attributeNamePrefix + 'version';
|
|
23
|
+
if (attrs[versionKey]) return attrs[versionKey];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return '1.0';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a tag or attribute name through sanitizeName if configured.
|
|
31
|
+
* Validation via xml-naming's qName is performed first; the sanitizeName
|
|
32
|
+
* callback is invoked only when the name is invalid. If sanitizeName is
|
|
33
|
+
* false (default), no validation occurs and the name is used as-is.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} name - raw name from the JS object
|
|
36
|
+
* @param {boolean} isAttribute - true when resolving an attribute name
|
|
37
|
+
* @param {object} options
|
|
38
|
+
* @param {Matcher} matcher - current matcher state (readonly from callback perspective)
|
|
39
|
+
* @param {string} xmlVersion - '1.0' or '1.1', forwarded to xml-naming
|
|
40
|
+
*/
|
|
41
|
+
function resolveTagName(name, isAttribute, options, matcher, xmlVersion) {
|
|
42
|
+
if (!options.sanitizeName) return name;
|
|
43
|
+
if (qName(name, { xmlVersion })) return name;
|
|
44
|
+
return options.sanitizeName(name, { isAttribute, matcher: matcher.readOnly() });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {array} jArray
|
|
49
|
+
* @param {any} options
|
|
50
|
+
* @returns
|
|
10
51
|
*/
|
|
11
52
|
export default function toXml(jArray, options) {
|
|
12
53
|
let indentation = "";
|
|
13
|
-
if (options.format
|
|
54
|
+
if (options.format) {
|
|
14
55
|
indentation = EOL;
|
|
15
56
|
}
|
|
16
57
|
|
|
@@ -27,13 +68,16 @@ export default function toXml(jArray, options) {
|
|
|
27
68
|
}
|
|
28
69
|
}
|
|
29
70
|
|
|
71
|
+
// Detect XML version for use in name validation
|
|
72
|
+
const xmlVersion = detectXmlVersionFromArray(jArray, options);
|
|
73
|
+
|
|
30
74
|
// Initialize matcher for path tracking
|
|
31
75
|
const matcher = new Matcher();
|
|
32
76
|
|
|
33
|
-
return arrToStr(jArray, options, indentation, matcher, stopNodeExpressions);
|
|
77
|
+
return arrToStr(jArray, options, indentation, matcher, stopNodeExpressions, xmlVersion);
|
|
34
78
|
}
|
|
35
79
|
|
|
36
|
-
function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
80
|
+
function arrToStr(arr, options, indentation, matcher, stopNodeExpressions, xmlVersion) {
|
|
37
81
|
let xmlStr = "";
|
|
38
82
|
let isPreviousElementTag = false;
|
|
39
83
|
|
|
@@ -53,20 +97,32 @@ function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
|
53
97
|
|
|
54
98
|
for (let i = 0; i < arr.length; i++) {
|
|
55
99
|
const tagObj = arr[i];
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
100
|
+
const rawTagName = propName(tagObj);
|
|
101
|
+
if (rawTagName === undefined) continue;
|
|
102
|
+
|
|
103
|
+
// Special names are exempt from sanitizeName: internal conventions and PI tags
|
|
104
|
+
// are not user-supplied XML element names.
|
|
105
|
+
const isSpecialName = rawTagName === options.textNodeName
|
|
106
|
+
|| rawTagName === options.cdataPropName
|
|
107
|
+
|| rawTagName === options.commentPropName
|
|
108
|
+
|| rawTagName[0] === '?';
|
|
109
|
+
|
|
110
|
+
// Resolve tag name (may transform it; may throw for invalid names)
|
|
111
|
+
const tagName = isSpecialName
|
|
112
|
+
? rawTagName
|
|
113
|
+
: resolveTagName(rawTagName, false, options, matcher, xmlVersion);
|
|
58
114
|
|
|
59
115
|
// Extract attributes from ":@" property
|
|
60
116
|
const attrValues = extractAttributeValues(tagObj[":@"], options);
|
|
61
117
|
|
|
62
|
-
// Push tag to matcher WITH attributes
|
|
118
|
+
// Push resolved tag to matcher WITH attributes
|
|
63
119
|
matcher.push(tagName, attrValues);
|
|
64
120
|
|
|
65
121
|
// Check if this is a stop node using Expression matching
|
|
66
122
|
const isStopNode = checkStopNode(matcher, stopNodeExpressions);
|
|
67
123
|
|
|
68
124
|
if (tagName === options.textNodeName) {
|
|
69
|
-
let tagText = tagObj[
|
|
125
|
+
let tagText = tagObj[rawTagName];
|
|
70
126
|
if (!isStopNode) {
|
|
71
127
|
tagText = options.tagValueProcessor(tagName, tagText);
|
|
72
128
|
tagText = replaceEntitiesValue(tagText, options);
|
|
@@ -82,27 +138,25 @@ function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
|
82
138
|
if (isPreviousElementTag) {
|
|
83
139
|
xmlStr += indentation;
|
|
84
140
|
}
|
|
85
|
-
const val = tagObj[
|
|
86
|
-
const safeVal =
|
|
141
|
+
const val = tagObj[rawTagName][0][options.textNodeName];
|
|
142
|
+
const safeVal = safeCdata(val);
|
|
87
143
|
xmlStr += `<![CDATA[${safeVal}]]>`;
|
|
88
144
|
isPreviousElementTag = false;
|
|
89
145
|
matcher.pop();
|
|
90
146
|
continue;
|
|
91
147
|
} else if (tagName === options.commentPropName) {
|
|
92
|
-
const val = tagObj[
|
|
93
|
-
const safeVal =
|
|
94
|
-
.replace(/--/g, '- -') // -- is illegal anywhere in comment content
|
|
95
|
-
.replace(/-$/, '- '); // trailing - would form -- with the closing -->
|
|
148
|
+
const val = tagObj[rawTagName][0][options.textNodeName];
|
|
149
|
+
const safeVal = safeComment(val);
|
|
96
150
|
xmlStr += indentation + `<!--${safeVal}-->`;
|
|
97
151
|
isPreviousElementTag = true;
|
|
98
152
|
matcher.pop();
|
|
99
153
|
continue;
|
|
100
154
|
} else if (tagName[0] === "?") {
|
|
101
|
-
const attStr = attr_to_str(tagObj[":@"], options, isStopNode);
|
|
155
|
+
const attStr = attr_to_str(tagObj[":@"], options, isStopNode, matcher, xmlVersion);
|
|
102
156
|
const tempInd = tagName === "?xml" ? "" : indentation;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
xmlStr += tempInd + `<${tagName}${
|
|
157
|
+
// Text node content on PI/XML declaration tags is intentionally ignored.
|
|
158
|
+
// Only attributes are valid on these tags per the XML spec.
|
|
159
|
+
xmlStr += tempInd + `<${tagName}${attStr}?>`;
|
|
106
160
|
isPreviousElementTag = true;
|
|
107
161
|
matcher.pop();
|
|
108
162
|
continue;
|
|
@@ -114,16 +168,15 @@ function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
|
114
168
|
}
|
|
115
169
|
|
|
116
170
|
// Pass isStopNode to attr_to_str so attributes are also not processed for stopNodes
|
|
117
|
-
const attStr = attr_to_str(tagObj[":@"], options, isStopNode);
|
|
171
|
+
const attStr = attr_to_str(tagObj[":@"], options, isStopNode, matcher, xmlVersion);
|
|
118
172
|
const tagStart = indentation + `<${tagName}${attStr}`;
|
|
119
173
|
|
|
120
174
|
// If this is a stopNode, get raw content without processing
|
|
121
175
|
let tagValue;
|
|
122
176
|
if (isStopNode) {
|
|
123
|
-
tagValue = getRawContent(tagObj[
|
|
177
|
+
tagValue = getRawContent(tagObj[rawTagName], options);
|
|
124
178
|
} else {
|
|
125
|
-
|
|
126
|
-
tagValue = arrToStr(tagObj[tagName], options, newIdentation, matcher, stopNodeExpressions);
|
|
179
|
+
tagValue = arrToStr(tagObj[rawTagName], options, newIdentation, matcher, stopNodeExpressions, xmlVersion);
|
|
127
180
|
}
|
|
128
181
|
|
|
129
182
|
if (options.unpairedTags.indexOf(tagName) !== -1) {
|
|
@@ -167,7 +220,7 @@ function extractAttributeValues(attrMap, options) {
|
|
|
167
220
|
const cleanAttrName = attr.startsWith(options.attributeNamePrefix)
|
|
168
221
|
? attr.substr(options.attributeNamePrefix.length)
|
|
169
222
|
: attr;
|
|
170
|
-
attrValues[cleanAttrName] = attrMap[attr];
|
|
223
|
+
attrValues[cleanAttrName] = escapeAttribute(attrMap[attr]);
|
|
171
224
|
hasAttrs = true;
|
|
172
225
|
}
|
|
173
226
|
|
|
@@ -205,9 +258,7 @@ function getRawContent(arr, options) {
|
|
|
205
258
|
// Processing instruction - skip for stopNodes
|
|
206
259
|
continue;
|
|
207
260
|
} else if (tagName) {
|
|
208
|
-
// Nested tags within stopNode
|
|
209
|
-
// Recursively get raw content and reconstruct the tag
|
|
210
|
-
// For stopNodes, we don't process attributes either
|
|
261
|
+
// Nested tags within stopNode — no sanitizeName, content is raw
|
|
211
262
|
const attStr = attr_to_str_raw(item[":@"], options);
|
|
212
263
|
const nestedContent = getRawContent(item[tagName], options);
|
|
213
264
|
|
|
@@ -234,7 +285,7 @@ function attr_to_str_raw(attrMap, options) {
|
|
|
234
285
|
if (attrVal === true && options.suppressBooleanAttributes) {
|
|
235
286
|
attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}`;
|
|
236
287
|
} else {
|
|
237
|
-
attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
|
|
288
|
+
attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}="${escapeAttribute(attrVal)}"`;
|
|
238
289
|
}
|
|
239
290
|
}
|
|
240
291
|
}
|
|
@@ -250,13 +301,23 @@ function propName(obj) {
|
|
|
250
301
|
}
|
|
251
302
|
}
|
|
252
303
|
|
|
253
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Build attribute string, resolving attribute names through sanitizeName when configured.
|
|
306
|
+
* Accepts matcher so the callback has path context.
|
|
307
|
+
*/
|
|
308
|
+
function attr_to_str(attrMap, options, isStopNode, matcher, xmlVersion) {
|
|
254
309
|
let attrStr = "";
|
|
255
310
|
if (attrMap && !options.ignoreAttributes) {
|
|
256
311
|
for (let attr in attrMap) {
|
|
257
312
|
if (!Object.prototype.hasOwnProperty.call(attrMap, attr)) continue;
|
|
258
|
-
let attrVal;
|
|
259
313
|
|
|
314
|
+
// Strip prefix to get the clean XML attribute name, then optionally sanitize it
|
|
315
|
+
const cleanAttrName = attr.substr(options.attributeNamePrefix.length);
|
|
316
|
+
const resolvedAttrName = isStopNode
|
|
317
|
+
? cleanAttrName // stopNodes are raw — skip sanitizeName for attr names too
|
|
318
|
+
: resolveTagName(cleanAttrName, true, options, matcher, xmlVersion);
|
|
319
|
+
|
|
320
|
+
let attrVal;
|
|
260
321
|
if (isStopNode) {
|
|
261
322
|
// For stopNodes, use raw value without any processing
|
|
262
323
|
attrVal = attrMap[attr];
|
|
@@ -267,9 +328,9 @@ function attr_to_str(attrMap, options, isStopNode) {
|
|
|
267
328
|
}
|
|
268
329
|
|
|
269
330
|
if (attrVal === true && options.suppressBooleanAttributes) {
|
|
270
|
-
attrStr += ` ${
|
|
331
|
+
attrStr += ` ${resolvedAttrName}`;
|
|
271
332
|
} else {
|
|
272
|
-
attrStr += ` ${
|
|
333
|
+
attrStr += ` ${resolvedAttrName}="${escapeAttribute(attrVal)}"`;
|
|
273
334
|
}
|
|
274
335
|
}
|
|
275
336
|
}
|
|
@@ -295,12 +356,4 @@ function replaceEntitiesValue(textValue, options) {
|
|
|
295
356
|
}
|
|
296
357
|
}
|
|
297
358
|
return textValue;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function cdataVal(val) {
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function commentVal(val) {
|
|
305
|
-
|
|
306
359
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export function safeComment(val) {
|
|
4
|
+
return String(val)
|
|
5
|
+
.replace(/--/g, '- -') // -- is illegal anywhere in comment content
|
|
6
|
+
.replace(/--/g, '- -') // handle the scenario when 2 consiucative dashes appears
|
|
7
|
+
.replace(/-$/, '- '); // trailing - would form -- with the closing -->
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function safeCdata(val) {
|
|
11
|
+
return String(val).replace(/\]\]>/g, ']]]]><![CDATA[>')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function escapeAttribute(val) {
|
|
15
|
+
return String(val).replace(/"/g, '"').replace(/'/g, ''')
|
|
16
|
+
}
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Note: Due to some last quick changes on v4, detail of v4.5.3 & v4.5.4 are not updated here. v4.5.4x is the last tag of v4 in github repository. I'm extremely sorry for the confusion
|
|
4
4
|
|
|
5
|
+
**5.7.2 / 2026-04-25**
|
|
6
|
+
- allow numerical external entity for backward compatibility
|
|
7
|
+
- fix #705: attributesGroupName working with preserveOrder
|
|
8
|
+
- fix #817: stackoverflow when tag expression is very long
|
|
9
|
+
|
|
10
|
+
**5.7.1 / 2026-04-20**
|
|
11
|
+
- fix typo in CJS typing file
|
|
12
|
+
|
|
5
13
|
**5.7.0 / 2026-04-17**
|
|
6
14
|
- Use `@nodable/entities` v2.1.0
|
|
7
15
|
- breaking changes
|