@cdklabs/cdk-construct-connect-datalake 0.0.8 → 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/package.json +1 -1
- package/node_modules/@aws-sdk/client-connect/package.json +1 -1
- package/node_modules/@aws-sdk/client-glue/dist-cjs/schemas/schemas_0.js +5 -4
- package/node_modules/@aws-sdk/client-glue/dist-es/schemas/schemas_0.js +5 -4
- 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/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 +5 -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 +1 -0
- package/node_modules/@aws-sdk/client-glue/package.json +1 -1
- package/node_modules/@aws-sdk/client-lakeformation/package.json +1 -1
- package/node_modules/@aws-sdk/client-ram/package.json +1 -1
- package/node_modules/@aws-sdk/client-sts/package.json +1 -1
- package/node_modules/fast-xml-builder/CHANGELOG.md +11 -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 +100 -33
- package/node_modules/fast-xml-builder/src/orderedJs2Xml.js +91 -37
- 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
|
@@ -4,6 +4,7 @@ import buildFromOrderedJs from './orderedJs2Xml.js';
|
|
|
4
4
|
import getIgnoreAttributesFn from "./ignoreAttributes.js";
|
|
5
5
|
import { Expression, Matcher } from 'path-expression-matcher';
|
|
6
6
|
import { safeComment, safeCdata, escapeAttribute } from './util.js';
|
|
7
|
+
import { qName } from 'xml-naming';
|
|
7
8
|
|
|
8
9
|
const defaultOptions = {
|
|
9
10
|
attributeNamePrefix: '@_',
|
|
@@ -38,7 +39,11 @@ const defaultOptions = {
|
|
|
38
39
|
// transformAttributeName: false,
|
|
39
40
|
oneListGroup: false,
|
|
40
41
|
maxNestedTags: 100,
|
|
41
|
-
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.
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
export default function Builder(options) {
|
|
@@ -95,6 +100,44 @@ export default function Builder(options) {
|
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
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
|
+
|
|
98
141
|
Builder.prototype.build = function (jObj) {
|
|
99
142
|
if (this.options.preserveOrder) {
|
|
100
143
|
return buildFromOrderedJs(jObj, this.options);
|
|
@@ -106,11 +149,12 @@ Builder.prototype.build = function (jObj) {
|
|
|
106
149
|
}
|
|
107
150
|
// Initialize matcher for path tracking
|
|
108
151
|
const matcher = new Matcher();
|
|
109
|
-
|
|
152
|
+
const xmlVersion = detectXmlVersionFromObj(jObj, this.options);
|
|
153
|
+
return this.j2x(jObj, 0, matcher, xmlVersion).val;
|
|
110
154
|
}
|
|
111
155
|
};
|
|
112
156
|
|
|
113
|
-
Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
157
|
+
Builder.prototype.j2x = function (jObj, level, matcher, xmlVersion) {
|
|
114
158
|
let attrStr = '';
|
|
115
159
|
let val = '';
|
|
116
160
|
if (this.options.maxNestedTags && matcher.getDepth() >= this.options.maxNestedTags) {
|
|
@@ -124,6 +168,22 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
124
168
|
|
|
125
169
|
for (let key in jObj) {
|
|
126
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
|
+
|
|
127
187
|
if (typeof jObj[key] === 'undefined') {
|
|
128
188
|
// supress undefined node only if it is not an attribute
|
|
129
189
|
if (this.isAttribute(key)) {
|
|
@@ -133,21 +193,22 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
133
193
|
// null attribute should be ignored by the attribute list, but should not cause the tag closing
|
|
134
194
|
if (this.isAttribute(key)) {
|
|
135
195
|
val += '';
|
|
136
|
-
} else if (
|
|
196
|
+
} else if (resolvedKey === this.options.cdataPropName || resolvedKey === this.options.commentPropName) {
|
|
137
197
|
val += '';
|
|
138
|
-
} else if (
|
|
139
|
-
val += this.indentate(level) + '<' +
|
|
198
|
+
} else if (resolvedKey[0] === '?') {
|
|
199
|
+
val += this.indentate(level) + '<' + resolvedKey + '?' + this.tagEndChar;
|
|
140
200
|
} else {
|
|
141
|
-
val += this.indentate(level) + '<' +
|
|
201
|
+
val += this.indentate(level) + '<' + resolvedKey + '/' + this.tagEndChar;
|
|
142
202
|
}
|
|
143
|
-
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
144
203
|
} else if (jObj[key] instanceof Date) {
|
|
145
|
-
val += this.buildTextValNode(jObj[key],
|
|
204
|
+
val += this.buildTextValNode(jObj[key], resolvedKey, '', level, matcher);
|
|
146
205
|
} else if (typeof jObj[key] !== 'object') {
|
|
147
206
|
//premitive type
|
|
148
207
|
const attr = this.isAttribute(key);
|
|
149
208
|
if (attr && !this.ignoreAttributesFn(attr, jPath)) {
|
|
150
|
-
|
|
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);
|
|
151
212
|
} else if (!attr) {
|
|
152
213
|
//tag value
|
|
153
214
|
if (key === this.options.textNodeName) {
|
|
@@ -155,7 +216,7 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
155
216
|
val += this.replaceEntitiesValue(newval);
|
|
156
217
|
} else {
|
|
157
218
|
// Check if this is a stopNode before building
|
|
158
|
-
matcher.push(
|
|
219
|
+
matcher.push(resolvedKey);
|
|
159
220
|
const isStopNode = this.checkStopNode(matcher);
|
|
160
221
|
matcher.pop();
|
|
161
222
|
|
|
@@ -163,12 +224,12 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
163
224
|
// Build as raw content without encoding
|
|
164
225
|
const textValue = '' + jObj[key];
|
|
165
226
|
if (textValue === '') {
|
|
166
|
-
val += this.indentate(level) + '<' +
|
|
227
|
+
val += this.indentate(level) + '<' + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
|
|
167
228
|
} else {
|
|
168
|
-
val += this.indentate(level) + '<' +
|
|
229
|
+
val += this.indentate(level) + '<' + resolvedKey + '>' + textValue + '</' + resolvedKey + this.tagEndChar;
|
|
169
230
|
}
|
|
170
231
|
} else {
|
|
171
|
-
val += this.buildTextValNode(jObj[key],
|
|
232
|
+
val += this.buildTextValNode(jObj[key], resolvedKey, '', level, matcher);
|
|
172
233
|
}
|
|
173
234
|
}
|
|
174
235
|
}
|
|
@@ -182,14 +243,13 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
182
243
|
if (typeof item === 'undefined') {
|
|
183
244
|
// supress undefined node
|
|
184
245
|
} else if (item === null) {
|
|
185
|
-
if (
|
|
186
|
-
else val += this.indentate(level) + '<' +
|
|
187
|
-
// 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;
|
|
188
248
|
} else if (typeof item === 'object') {
|
|
189
249
|
if (this.options.oneListGroup) {
|
|
190
250
|
// Push tag to matcher before recursive call
|
|
191
|
-
matcher.push(
|
|
192
|
-
const result = this.j2x(item, level + 1, matcher);
|
|
251
|
+
matcher.push(resolvedKey);
|
|
252
|
+
const result = this.j2x(item, level + 1, matcher, xmlVersion);
|
|
193
253
|
// Pop tag from matcher after recursive call
|
|
194
254
|
matcher.pop();
|
|
195
255
|
|
|
@@ -198,16 +258,16 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
198
258
|
listTagAttr += result.attrStr
|
|
199
259
|
}
|
|
200
260
|
} else {
|
|
201
|
-
listTagVal += this.processTextOrObjNode(item,
|
|
261
|
+
listTagVal += this.processTextOrObjNode(item, resolvedKey, level, matcher, xmlVersion)
|
|
202
262
|
}
|
|
203
263
|
} else {
|
|
204
264
|
if (this.options.oneListGroup) {
|
|
205
|
-
let textValue = this.options.tagValueProcessor(
|
|
265
|
+
let textValue = this.options.tagValueProcessor(resolvedKey, item);
|
|
206
266
|
textValue = this.replaceEntitiesValue(textValue);
|
|
207
267
|
listTagVal += textValue;
|
|
208
268
|
} else {
|
|
209
269
|
// Check if this is a stopNode before building
|
|
210
|
-
matcher.push(
|
|
270
|
+
matcher.push(resolvedKey);
|
|
211
271
|
const isStopNode = this.checkStopNode(matcher);
|
|
212
272
|
matcher.pop();
|
|
213
273
|
|
|
@@ -215,18 +275,18 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
215
275
|
// Build as raw content without encoding
|
|
216
276
|
const textValue = '' + item;
|
|
217
277
|
if (textValue === '') {
|
|
218
|
-
listTagVal += this.indentate(level) + '<' +
|
|
278
|
+
listTagVal += this.indentate(level) + '<' + resolvedKey + this.closeTag(resolvedKey) + this.tagEndChar;
|
|
219
279
|
} else {
|
|
220
|
-
listTagVal += this.indentate(level) + '<' +
|
|
280
|
+
listTagVal += this.indentate(level) + '<' + resolvedKey + '>' + textValue + '</' + resolvedKey + this.tagEndChar;
|
|
221
281
|
}
|
|
222
282
|
} else {
|
|
223
|
-
listTagVal += this.buildTextValNode(item,
|
|
283
|
+
listTagVal += this.buildTextValNode(item, resolvedKey, '', level, matcher);
|
|
224
284
|
}
|
|
225
285
|
}
|
|
226
286
|
}
|
|
227
287
|
}
|
|
228
288
|
if (this.options.oneListGroup) {
|
|
229
|
-
listTagVal = this.buildObjectNode(listTagVal,
|
|
289
|
+
listTagVal = this.buildObjectNode(listTagVal, resolvedKey, listTagAttr, level);
|
|
230
290
|
}
|
|
231
291
|
val += listTagVal;
|
|
232
292
|
} else {
|
|
@@ -235,10 +295,12 @@ Builder.prototype.j2x = function (jObj, level, matcher) {
|
|
|
235
295
|
const Ks = Object.keys(jObj[key]);
|
|
236
296
|
const L = Ks.length;
|
|
237
297
|
for (let j = 0; j < L; j++) {
|
|
238
|
-
|
|
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);
|
|
239
301
|
}
|
|
240
302
|
} else {
|
|
241
|
-
val += this.processTextOrObjNode(jObj[key],
|
|
303
|
+
val += this.processTextOrObjNode(jObj[key], resolvedKey, level, matcher, xmlVersion)
|
|
242
304
|
}
|
|
243
305
|
}
|
|
244
306
|
}
|
|
@@ -255,7 +317,7 @@ Builder.prototype.buildAttrPairStr = function (attrName, val, isStopNode) {
|
|
|
255
317
|
} else return ' ' + attrName + '="' + escapeAttribute(val) + '"';
|
|
256
318
|
}
|
|
257
319
|
|
|
258
|
-
function processTextOrObjNode(object, key, level, matcher) {
|
|
320
|
+
function processTextOrObjNode(object, key, level, matcher, xmlVersion) {
|
|
259
321
|
// Extract attributes to pass to matcher
|
|
260
322
|
const attrValues = this.extractAttributes(object);
|
|
261
323
|
|
|
@@ -273,11 +335,15 @@ function processTextOrObjNode(object, key, level, matcher) {
|
|
|
273
335
|
return this.buildObjectNode(rawContent, key, attrStr, level);
|
|
274
336
|
}
|
|
275
337
|
|
|
276
|
-
const result = this.j2x(object, level + 1, matcher);
|
|
338
|
+
const result = this.j2x(object, level + 1, matcher, xmlVersion);
|
|
277
339
|
// Pop tag from matcher after recursion
|
|
278
340
|
matcher.pop();
|
|
279
341
|
|
|
280
|
-
|
|
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) {
|
|
281
347
|
return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level, matcher);
|
|
282
348
|
} else {
|
|
283
349
|
return this.buildObjectNode(result.val, key, result.attrStr, level);
|
|
@@ -426,8 +492,10 @@ Builder.prototype.buildObjectNode = function (val, key, attrStr, level) {
|
|
|
426
492
|
else {
|
|
427
493
|
return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
|
|
428
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;
|
|
429
498
|
} else {
|
|
430
|
-
|
|
431
499
|
let tagEndExp = '</' + key + this.tagEndChar;
|
|
432
500
|
let piClosingChar = "";
|
|
433
501
|
|
|
@@ -480,7 +548,6 @@ function buildEmptyObjNode(val, key, attrStr, level) {
|
|
|
480
548
|
if (key[0] === "?") return this.indentate(level) + '<' + key + attrStr + '?' + this.tagEndChar;
|
|
481
549
|
else {
|
|
482
550
|
return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
|
|
483
|
-
// return this.buildTagStr(level,key, attrStr);
|
|
484
551
|
}
|
|
485
552
|
}
|
|
486
553
|
}
|
|
@@ -1,17 +1,57 @@
|
|
|
1
1
|
import { Expression, Matcher } from 'path-expression-matcher';
|
|
2
2
|
import { safeComment, safeCdata, escapeAttribute } from "./util.js";
|
|
3
|
+
import { qName } from 'xml-naming';
|
|
3
4
|
|
|
4
5
|
const EOL = "\n";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
|
11
51
|
*/
|
|
12
52
|
export default function toXml(jArray, options) {
|
|
13
53
|
let indentation = "";
|
|
14
|
-
if (options.format
|
|
54
|
+
if (options.format) {
|
|
15
55
|
indentation = EOL;
|
|
16
56
|
}
|
|
17
57
|
|
|
@@ -28,13 +68,16 @@ export default function toXml(jArray, options) {
|
|
|
28
68
|
}
|
|
29
69
|
}
|
|
30
70
|
|
|
71
|
+
// Detect XML version for use in name validation
|
|
72
|
+
const xmlVersion = detectXmlVersionFromArray(jArray, options);
|
|
73
|
+
|
|
31
74
|
// Initialize matcher for path tracking
|
|
32
75
|
const matcher = new Matcher();
|
|
33
76
|
|
|
34
|
-
return arrToStr(jArray, options, indentation, matcher, stopNodeExpressions);
|
|
77
|
+
return arrToStr(jArray, options, indentation, matcher, stopNodeExpressions, xmlVersion);
|
|
35
78
|
}
|
|
36
79
|
|
|
37
|
-
function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
80
|
+
function arrToStr(arr, options, indentation, matcher, stopNodeExpressions, xmlVersion) {
|
|
38
81
|
let xmlStr = "";
|
|
39
82
|
let isPreviousElementTag = false;
|
|
40
83
|
|
|
@@ -54,20 +97,32 @@ function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
|
54
97
|
|
|
55
98
|
for (let i = 0; i < arr.length; i++) {
|
|
56
99
|
const tagObj = arr[i];
|
|
57
|
-
const
|
|
58
|
-
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);
|
|
59
114
|
|
|
60
115
|
// Extract attributes from ":@" property
|
|
61
116
|
const attrValues = extractAttributeValues(tagObj[":@"], options);
|
|
62
117
|
|
|
63
|
-
// Push tag to matcher WITH attributes
|
|
118
|
+
// Push resolved tag to matcher WITH attributes
|
|
64
119
|
matcher.push(tagName, attrValues);
|
|
65
120
|
|
|
66
121
|
// Check if this is a stop node using Expression matching
|
|
67
122
|
const isStopNode = checkStopNode(matcher, stopNodeExpressions);
|
|
68
123
|
|
|
69
124
|
if (tagName === options.textNodeName) {
|
|
70
|
-
let tagText = tagObj[
|
|
125
|
+
let tagText = tagObj[rawTagName];
|
|
71
126
|
if (!isStopNode) {
|
|
72
127
|
tagText = options.tagValueProcessor(tagName, tagText);
|
|
73
128
|
tagText = replaceEntitiesValue(tagText, options);
|
|
@@ -83,25 +138,25 @@ function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
|
83
138
|
if (isPreviousElementTag) {
|
|
84
139
|
xmlStr += indentation;
|
|
85
140
|
}
|
|
86
|
-
const val = tagObj[
|
|
141
|
+
const val = tagObj[rawTagName][0][options.textNodeName];
|
|
87
142
|
const safeVal = safeCdata(val);
|
|
88
143
|
xmlStr += `<![CDATA[${safeVal}]]>`;
|
|
89
144
|
isPreviousElementTag = false;
|
|
90
145
|
matcher.pop();
|
|
91
146
|
continue;
|
|
92
147
|
} else if (tagName === options.commentPropName) {
|
|
93
|
-
const val = tagObj[
|
|
94
|
-
const safeVal = safeComment(val)
|
|
148
|
+
const val = tagObj[rawTagName][0][options.textNodeName];
|
|
149
|
+
const safeVal = safeComment(val);
|
|
95
150
|
xmlStr += indentation + `<!--${safeVal}-->`;
|
|
96
151
|
isPreviousElementTag = true;
|
|
97
152
|
matcher.pop();
|
|
98
153
|
continue;
|
|
99
154
|
} else if (tagName[0] === "?") {
|
|
100
|
-
const attStr = attr_to_str(tagObj[":@"], options, isStopNode);
|
|
155
|
+
const attStr = attr_to_str(tagObj[":@"], options, isStopNode, matcher, xmlVersion);
|
|
101
156
|
const tempInd = tagName === "?xml" ? "" : indentation;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
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}?>`;
|
|
105
160
|
isPreviousElementTag = true;
|
|
106
161
|
matcher.pop();
|
|
107
162
|
continue;
|
|
@@ -113,16 +168,15 @@ function arrToStr(arr, options, indentation, matcher, stopNodeExpressions) {
|
|
|
113
168
|
}
|
|
114
169
|
|
|
115
170
|
// Pass isStopNode to attr_to_str so attributes are also not processed for stopNodes
|
|
116
|
-
const attStr = attr_to_str(tagObj[":@"], options, isStopNode);
|
|
171
|
+
const attStr = attr_to_str(tagObj[":@"], options, isStopNode, matcher, xmlVersion);
|
|
117
172
|
const tagStart = indentation + `<${tagName}${attStr}`;
|
|
118
173
|
|
|
119
174
|
// If this is a stopNode, get raw content without processing
|
|
120
175
|
let tagValue;
|
|
121
176
|
if (isStopNode) {
|
|
122
|
-
tagValue = getRawContent(tagObj[
|
|
177
|
+
tagValue = getRawContent(tagObj[rawTagName], options);
|
|
123
178
|
} else {
|
|
124
|
-
|
|
125
|
-
tagValue = arrToStr(tagObj[tagName], options, newIdentation, matcher, stopNodeExpressions);
|
|
179
|
+
tagValue = arrToStr(tagObj[rawTagName], options, newIdentation, matcher, stopNodeExpressions, xmlVersion);
|
|
126
180
|
}
|
|
127
181
|
|
|
128
182
|
if (options.unpairedTags.indexOf(tagName) !== -1) {
|
|
@@ -204,9 +258,7 @@ function getRawContent(arr, options) {
|
|
|
204
258
|
// Processing instruction - skip for stopNodes
|
|
205
259
|
continue;
|
|
206
260
|
} else if (tagName) {
|
|
207
|
-
// Nested tags within stopNode
|
|
208
|
-
// Recursively get raw content and reconstruct the tag
|
|
209
|
-
// For stopNodes, we don't process attributes either
|
|
261
|
+
// Nested tags within stopNode — no sanitizeName, content is raw
|
|
210
262
|
const attStr = attr_to_str_raw(item[":@"], options);
|
|
211
263
|
const nestedContent = getRawContent(item[tagName], options);
|
|
212
264
|
|
|
@@ -249,13 +301,23 @@ function propName(obj) {
|
|
|
249
301
|
}
|
|
250
302
|
}
|
|
251
303
|
|
|
252
|
-
|
|
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) {
|
|
253
309
|
let attrStr = "";
|
|
254
310
|
if (attrMap && !options.ignoreAttributes) {
|
|
255
311
|
for (let attr in attrMap) {
|
|
256
312
|
if (!Object.prototype.hasOwnProperty.call(attrMap, attr)) continue;
|
|
257
|
-
let attrVal;
|
|
258
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;
|
|
259
321
|
if (isStopNode) {
|
|
260
322
|
// For stopNodes, use raw value without any processing
|
|
261
323
|
attrVal = attrMap[attr];
|
|
@@ -266,9 +328,9 @@ function attr_to_str(attrMap, options, isStopNode) {
|
|
|
266
328
|
}
|
|
267
329
|
|
|
268
330
|
if (attrVal === true && options.suppressBooleanAttributes) {
|
|
269
|
-
attrStr += ` ${
|
|
331
|
+
attrStr += ` ${resolvedAttrName}`;
|
|
270
332
|
} else {
|
|
271
|
-
attrStr += ` ${
|
|
333
|
+
attrStr += ` ${resolvedAttrName}="${escapeAttribute(attrVal)}"`;
|
|
272
334
|
}
|
|
273
335
|
}
|
|
274
336
|
}
|
|
@@ -294,12 +356,4 @@ function replaceEntitiesValue(textValue, options) {
|
|
|
294
356
|
}
|
|
295
357
|
}
|
|
296
358
|
return textValue;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function cdataVal(val) {
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function commentVal(val) {
|
|
304
|
-
|
|
305
359
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strnum",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Parse String to Number based on configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "strnum.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "https://github.com/NaturalIntelligence/strnum"
|
|
19
19
|
},
|
|
20
|
-
"author": "Amit Gupta (https://
|
|
20
|
+
"author": "Amit Gupta (https://solothought.work/)",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"funding": [
|
|
23
23
|
{
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"@byspec/numbers": "^0.1.1",
|
|
29
30
|
"jasmine": "^5.6.0"
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;
|
|
2
|
+
const binRegex = /^0b[01]+$/;
|
|
3
|
+
const octRegex = /^0o[0-7]+$/;
|
|
2
4
|
const numRegex = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/;
|
|
3
|
-
// const octRegex = /^0x[a-z0-9]+/;
|
|
4
|
-
// const binRegex = /0x[a-z0-9]+/;
|
|
5
|
-
|
|
6
5
|
|
|
7
6
|
const consider = {
|
|
8
7
|
hex: true,
|
|
9
|
-
|
|
8
|
+
binary: false,
|
|
9
|
+
octal: false,
|
|
10
10
|
leadingZeros: true,
|
|
11
11
|
decimalPoint: "\.",
|
|
12
12
|
eNotation: true,
|
|
@@ -25,14 +25,14 @@ export default function toNumber(str, options = {}) {
|
|
|
25
25
|
else if (trimmedStr === "0") return 0;
|
|
26
26
|
else if (options.hex && hexRegex.test(trimmedStr)) {
|
|
27
27
|
return parse_int(trimmedStr, 16);
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
} else if (options.binary && binRegex.test(trimmedStr)) {
|
|
29
|
+
return parse_int(trimmedStr, 2);
|
|
30
|
+
} else if (options.octal && octRegex.test(trimmedStr)) {
|
|
31
|
+
return parse_int(trimmedStr, 8);
|
|
30
32
|
} else if (!isFinite(trimmedStr)) { //Infinity
|
|
31
33
|
return handleInfinity(str, Number(trimmedStr), options);
|
|
32
34
|
} else if (trimmedStr.includes('e') || trimmedStr.includes('E')) { //eNotation
|
|
33
35
|
return resolveEnotation(str, trimmedStr, options);
|
|
34
|
-
// }else if (options.parseBin && binRegex.test(str)) {
|
|
35
|
-
// return Number.parseInt(val, 2);
|
|
36
36
|
} else {
|
|
37
37
|
//separate negative sign, leading zeros, and rest number
|
|
38
38
|
const match = numRegex.exec(trimmedStr);
|
|
@@ -130,11 +130,13 @@ function trimZeros(numStr) {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function parse_int(numStr, base) {
|
|
133
|
-
|
|
133
|
+
const str = numStr.trim();
|
|
134
|
+
if (base === 2 || base === 8) numStr = str.substring(2);
|
|
135
|
+
|
|
134
136
|
if (parseInt) return parseInt(numStr, base);
|
|
135
137
|
else if (Number.parseInt) return Number.parseInt(numStr, base);
|
|
136
138
|
else if (window && window.parseInt) return window.parseInt(numStr, base);
|
|
137
|
-
else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported")
|
|
139
|
+
else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported");
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
/**
|