uss-xsd-engine 0.2.0 → 0.2.1
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/README.md +15 -71
- package/dist/uss-xsd-engine.esm.js +453 -16
- package/dist/uss-xsd-engine.standalone.js +453 -16
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* uss-xsd-engine v0.2.
|
|
2
|
+
* uss-xsd-engine v0.2.1
|
|
3
3
|
* (c) 2026 Bernard Mumble
|
|
4
4
|
* MIT License
|
|
5
5
|
*/
|
|
@@ -96,6 +96,8 @@ var UssXsdEngine = (() => {
|
|
|
96
96
|
XML_KEY_NULL_VIOLATION: "XML_KEY_NULL_VIOLATION",
|
|
97
97
|
XML_KEYREF_VIOLATION: "XML_KEYREF_VIOLATION",
|
|
98
98
|
XML_UNIQUE_VIOLATION: "XML_UNIQUE_VIOLATION",
|
|
99
|
+
XML_ANY_STRICT_VALIDATION_FAILED: "XML_ANY_STRICT_VALIDATION_FAILED",
|
|
100
|
+
XML_ANYATTRIBUTE_STRICT_VALIDATION_FAILED: "XML_ANYATTRIBUTE_STRICT_VALIDATION_FAILED",
|
|
99
101
|
XML_PATTERN_MISMATCH: "XML_PATTERN_MISMATCH",
|
|
100
102
|
XML_LENGTH_MISMATCH: "XML_LENGTH_MISMATCH",
|
|
101
103
|
XML_MIN_LENGTH_VIOLATION: "XML_MIN_LENGTH_VIOLATION",
|
|
@@ -122,6 +124,9 @@ var UssXsdEngine = (() => {
|
|
|
122
124
|
XSD_RESTRICTION_NOT_SUBSET: "XSD_RESTRICTION_NOT_SUBSET",
|
|
123
125
|
XSD_RESTRICTION_OCCURS_WIDENED: "XSD_RESTRICTION_OCCURS_WIDENED",
|
|
124
126
|
XSD_RESTRICTION_ATTRIBUTE_WIDENED: "XSD_RESTRICTION_ATTRIBUTE_WIDENED",
|
|
127
|
+
XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE: "XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE",
|
|
128
|
+
XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE: "XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE",
|
|
129
|
+
XSD_RESTRICTION_WILDCARD_INCOMPATIBLE: "XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
125
130
|
XSD_INCLUDE_NOT_PROVIDED: "XSD_INCLUDE_NOT_PROVIDED",
|
|
126
131
|
XSD_IMPORT_NOT_PROVIDED: "XSD_IMPORT_NOT_PROVIDED",
|
|
127
132
|
XSD_INCLUDE_NAMESPACE_MISMATCH: "XSD_INCLUDE_NAMESPACE_MISMATCH",
|
|
@@ -507,9 +512,11 @@ var UssXsdEngine = (() => {
|
|
|
507
512
|
}
|
|
508
513
|
function createAnyNode({
|
|
509
514
|
namespace = null,
|
|
510
|
-
processContents =
|
|
515
|
+
processContents = "strict",
|
|
511
516
|
minOccurs = 1,
|
|
512
517
|
maxOccurs = 1,
|
|
518
|
+
notNamespace = [],
|
|
519
|
+
notQName = [],
|
|
513
520
|
line = null,
|
|
514
521
|
column = null,
|
|
515
522
|
path = null
|
|
@@ -517,9 +524,31 @@ var UssXsdEngine = (() => {
|
|
|
517
524
|
return {
|
|
518
525
|
kind: "any",
|
|
519
526
|
namespace,
|
|
520
|
-
processContents,
|
|
527
|
+
processContents: processContents || "strict",
|
|
521
528
|
minOccurs,
|
|
522
529
|
maxOccurs,
|
|
530
|
+
notNamespace,
|
|
531
|
+
notQName,
|
|
532
|
+
line,
|
|
533
|
+
column,
|
|
534
|
+
path
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function createAnyAttributeNode({
|
|
538
|
+
namespace = null,
|
|
539
|
+
processContents = "strict",
|
|
540
|
+
notNamespace = [],
|
|
541
|
+
notQName = [],
|
|
542
|
+
line = null,
|
|
543
|
+
column = null,
|
|
544
|
+
path = null
|
|
545
|
+
} = {}) {
|
|
546
|
+
return {
|
|
547
|
+
kind: "anyAttribute",
|
|
548
|
+
namespace,
|
|
549
|
+
processContents: processContents || "strict",
|
|
550
|
+
notNamespace,
|
|
551
|
+
notQName,
|
|
523
552
|
line,
|
|
524
553
|
column,
|
|
525
554
|
path
|
|
@@ -844,8 +873,6 @@ var UssXsdEngine = (() => {
|
|
|
844
873
|
|
|
845
874
|
// src/parser/buildSchemaModel.js
|
|
846
875
|
var UNSUPPORTED_NODE_FEATURES = /* @__PURE__ */ new Set([
|
|
847
|
-
"any",
|
|
848
|
-
"anyAttribute",
|
|
849
876
|
"redefine",
|
|
850
877
|
"notation"
|
|
851
878
|
]);
|
|
@@ -1280,6 +1307,10 @@ var UssXsdEngine = (() => {
|
|
|
1280
1307
|
issues
|
|
1281
1308
|
)
|
|
1282
1309
|
);
|
|
1310
|
+
} else if (child.localName === "anyAttribute") {
|
|
1311
|
+
attributes.push(
|
|
1312
|
+
parseAnyAttribute(child, xsdText, lineStarts, parentPath, schema, issues)
|
|
1313
|
+
);
|
|
1283
1314
|
}
|
|
1284
1315
|
}
|
|
1285
1316
|
return attributes;
|
|
@@ -1363,15 +1394,58 @@ var UssXsdEngine = (() => {
|
|
|
1363
1394
|
path
|
|
1364
1395
|
});
|
|
1365
1396
|
}
|
|
1397
|
+
function parseWildcardNamespace(namespaceStr) {
|
|
1398
|
+
if (!namespaceStr) return null;
|
|
1399
|
+
const trimmed = namespaceStr.trim();
|
|
1400
|
+
if (!trimmed) return null;
|
|
1401
|
+
if (trimmed.includes(" ")) {
|
|
1402
|
+
return trimmed.split(/\s+/).filter((ns) => ns);
|
|
1403
|
+
}
|
|
1404
|
+
return trimmed;
|
|
1405
|
+
}
|
|
1406
|
+
function parseNotNamespace(notNamespaceStr) {
|
|
1407
|
+
if (!notNamespaceStr) return [];
|
|
1408
|
+
const trimmed = notNamespaceStr.trim();
|
|
1409
|
+
if (!trimmed) return [];
|
|
1410
|
+
if (trimmed.includes(" ")) {
|
|
1411
|
+
return trimmed.split(/\s+/).filter((ns) => ns);
|
|
1412
|
+
}
|
|
1413
|
+
return [trimmed];
|
|
1414
|
+
}
|
|
1415
|
+
function parseNotQName(notQNameStr) {
|
|
1416
|
+
if (!notQNameStr) return [];
|
|
1417
|
+
const trimmed = notQNameStr.trim();
|
|
1418
|
+
if (!trimmed) return [];
|
|
1419
|
+
if (trimmed.includes(" ")) {
|
|
1420
|
+
return trimmed.split(/\s+/).filter((qn) => qn);
|
|
1421
|
+
}
|
|
1422
|
+
return [trimmed];
|
|
1423
|
+
}
|
|
1366
1424
|
function parseAny(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1367
1425
|
const path = buildPath(parentPath, node);
|
|
1368
1426
|
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1369
1427
|
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1370
1428
|
return createAnyNode({
|
|
1371
|
-
namespace: node.getAttribute("namespace"),
|
|
1429
|
+
namespace: parseWildcardNamespace(node.getAttribute("namespace")),
|
|
1372
1430
|
processContents: node.getAttribute("processContents"),
|
|
1373
1431
|
minOccurs: normalizeOccurs(node.getAttribute("minOccurs"), 1),
|
|
1374
1432
|
maxOccurs: normalizeOccurs(node.getAttribute("maxOccurs"), 1),
|
|
1433
|
+
notNamespace: parseNotNamespace(node.getAttribute("notNamespace")),
|
|
1434
|
+
notQName: parseNotQName(node.getAttribute("notQName")),
|
|
1435
|
+
line: loc.line,
|
|
1436
|
+
column: loc.column,
|
|
1437
|
+
path
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
function parseAnyAttribute(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1441
|
+
const path = buildPath(parentPath, node);
|
|
1442
|
+
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1443
|
+
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1444
|
+
return createAnyAttributeNode({
|
|
1445
|
+
namespace: parseWildcardNamespace(node.getAttribute("namespace")),
|
|
1446
|
+
processContents: node.getAttribute("processContents"),
|
|
1447
|
+
notNamespace: parseNotNamespace(node.getAttribute("notNamespace")),
|
|
1448
|
+
notQName: parseNotQName(node.getAttribute("notQName")),
|
|
1375
1449
|
line: loc.line,
|
|
1376
1450
|
column: loc.column,
|
|
1377
1451
|
path
|
|
@@ -1794,24 +1868,24 @@ var UssXsdEngine = (() => {
|
|
|
1794
1868
|
}
|
|
1795
1869
|
}
|
|
1796
1870
|
if (ref?.kind === "import" && ref.namespace) {
|
|
1797
|
-
const
|
|
1871
|
+
const namespaceMatches2 = entries.filter((entry) => {
|
|
1798
1872
|
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText(
|
|
1799
1873
|
entry.text
|
|
1800
1874
|
);
|
|
1801
1875
|
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
1802
1876
|
});
|
|
1803
|
-
if (
|
|
1877
|
+
if (namespaceMatches2.length === 1) {
|
|
1804
1878
|
return {
|
|
1805
1879
|
kind: "namespace",
|
|
1806
|
-
entry:
|
|
1807
|
-
matches:
|
|
1880
|
+
entry: namespaceMatches2[0],
|
|
1881
|
+
matches: namespaceMatches2
|
|
1808
1882
|
};
|
|
1809
1883
|
}
|
|
1810
|
-
if (
|
|
1884
|
+
if (namespaceMatches2.length > 1) {
|
|
1811
1885
|
return {
|
|
1812
1886
|
kind: "ambiguous-namespace",
|
|
1813
1887
|
entry: null,
|
|
1814
|
-
matches:
|
|
1888
|
+
matches: namespaceMatches2
|
|
1815
1889
|
};
|
|
1816
1890
|
}
|
|
1817
1891
|
}
|
|
@@ -2428,6 +2502,115 @@ var UssXsdEngine = (() => {
|
|
|
2428
2502
|
}
|
|
2429
2503
|
}
|
|
2430
2504
|
}
|
|
2505
|
+
function checkWildcardRestriction(schema, derivedType, baseType, issues) {
|
|
2506
|
+
const derivedContent = getEffectiveContent(schema, derivedType);
|
|
2507
|
+
const baseContent = getEffectiveContent(schema, baseType);
|
|
2508
|
+
const derivedWildcards = [];
|
|
2509
|
+
const baseWildcards = [];
|
|
2510
|
+
function collectWildcards(node, arr) {
|
|
2511
|
+
if (!node) return;
|
|
2512
|
+
if (node.kind === "any") {
|
|
2513
|
+
arr.push(node);
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
if (node.children) {
|
|
2517
|
+
for (const child of asArray(node.children)) {
|
|
2518
|
+
collectWildcards(child, arr);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
collectWildcards(derivedContent, derivedWildcards);
|
|
2523
|
+
collectWildcards(baseContent, baseWildcards);
|
|
2524
|
+
if (derivedWildcards.length > 0 && baseWildcards.length === 0) {
|
|
2525
|
+
issues.push(
|
|
2526
|
+
buildRestrictionIssue(
|
|
2527
|
+
"XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
2528
|
+
`Restricted type introduces wildcard elements that base type does not have.`,
|
|
2529
|
+
derivedType
|
|
2530
|
+
)
|
|
2531
|
+
);
|
|
2532
|
+
}
|
|
2533
|
+
const derivedAttrs = asArray(getEffectiveAttributes(schema, derivedType));
|
|
2534
|
+
const baseAttrs = asArray(getEffectiveAttributes(schema, baseType));
|
|
2535
|
+
const derivedHasAnyAttribute = derivedAttrs.some((a) => a?.kind === "anyAttribute");
|
|
2536
|
+
const baseHasAnyAttribute = baseAttrs.some((a) => a?.kind === "anyAttribute");
|
|
2537
|
+
if (derivedHasAnyAttribute && !baseHasAnyAttribute) {
|
|
2538
|
+
issues.push(
|
|
2539
|
+
buildRestrictionIssue(
|
|
2540
|
+
"XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
2541
|
+
`Restricted type introduces anyAttribute that base type does not have.`,
|
|
2542
|
+
derivedType
|
|
2543
|
+
)
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
function checkOccurrenceCompatibility(derived, base, name) {
|
|
2548
|
+
const dMin = typeof derived.minOccurs === "number" ? derived.minOccurs : 1;
|
|
2549
|
+
const bMin = typeof base.minOccurs === "number" ? base.minOccurs : 1;
|
|
2550
|
+
const dMax = maxToNumber(derived.maxOccurs ?? 1);
|
|
2551
|
+
const bMax = maxToNumber(base.maxOccurs ?? 1);
|
|
2552
|
+
return dMin >= bMin && dMax <= bMax;
|
|
2553
|
+
}
|
|
2554
|
+
function checkComplexContentRestriction(schema, derivedType, baseType, issues) {
|
|
2555
|
+
const derivedContent = getEffectiveContent(schema, derivedType);
|
|
2556
|
+
const baseContent = getEffectiveContent(schema, baseType);
|
|
2557
|
+
if (!derivedContent || !baseContent) return;
|
|
2558
|
+
const derivedFlat = flattenContent(derivedContent, []);
|
|
2559
|
+
const baseFlat = flattenContent(baseContent, []);
|
|
2560
|
+
const baseMap = new Map(baseFlat.map((item) => [item.name, item]));
|
|
2561
|
+
for (const item of derivedFlat) {
|
|
2562
|
+
const baseItem = baseMap.get(item.name);
|
|
2563
|
+
if (!baseItem) {
|
|
2564
|
+
issues.push(
|
|
2565
|
+
buildRestrictionIssue(
|
|
2566
|
+
"XSD_RESTRICTION_NOT_SUBSET",
|
|
2567
|
+
`Restricted type contains element '${item.name}' not in base type.`,
|
|
2568
|
+
item
|
|
2569
|
+
)
|
|
2570
|
+
);
|
|
2571
|
+
continue;
|
|
2572
|
+
}
|
|
2573
|
+
if (!checkOccurrenceCompatibility(item, baseItem, item.name)) {
|
|
2574
|
+
issues.push(
|
|
2575
|
+
buildRestrictionIssue(
|
|
2576
|
+
"XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE",
|
|
2577
|
+
`Restricted type has incompatible occurrence constraints for '${item.name}'.`,
|
|
2578
|
+
item
|
|
2579
|
+
)
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
function checkSimpleContentRestriction(schema, derivedType, baseType, issues) {
|
|
2585
|
+
if (baseType.contentModel !== "simple") {
|
|
2586
|
+
issues.push(
|
|
2587
|
+
buildRestrictionIssue(
|
|
2588
|
+
"XSD_RESTRICTION_NOT_SUBSET",
|
|
2589
|
+
`SimpleContent restriction requires base type to have simple content.`,
|
|
2590
|
+
derivedType
|
|
2591
|
+
)
|
|
2592
|
+
);
|
|
2593
|
+
}
|
|
2594
|
+
const derivedAttrs = asArray(getEffectiveAttributes(schema, derivedType));
|
|
2595
|
+
const baseAttrs = asArray(getEffectiveAttributes(schema, baseType));
|
|
2596
|
+
const baseAttrMap = new Map(
|
|
2597
|
+
baseAttrs.filter((attr) => attr?.kind === "attribute").map((attr) => [localDeclName(attr), attr])
|
|
2598
|
+
);
|
|
2599
|
+
for (const attr of derivedAttrs) {
|
|
2600
|
+
if (attr?.kind !== "attribute") continue;
|
|
2601
|
+
const name = localDeclName(attr);
|
|
2602
|
+
const baseAttr = baseAttrMap.get(name);
|
|
2603
|
+
if (!baseAttr && !baseAttrs.some((a) => a?.kind === "anyAttribute")) {
|
|
2604
|
+
issues.push(
|
|
2605
|
+
buildRestrictionIssue(
|
|
2606
|
+
"XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE",
|
|
2607
|
+
`SimpleContent restricted type adds attribute '${name}' not in base type.`,
|
|
2608
|
+
attr
|
|
2609
|
+
)
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2431
2614
|
function runRestrictionDiagnostics(schema) {
|
|
2432
2615
|
const issues = [];
|
|
2433
2616
|
for (const complexType of Object.values(schema.globals.complexTypes || {})) {
|
|
@@ -2437,6 +2620,12 @@ var UssXsdEngine = (() => {
|
|
|
2437
2620
|
if (!baseType) continue;
|
|
2438
2621
|
checkRestrictedContentSubset(schema, complexType, baseType, issues);
|
|
2439
2622
|
checkRestrictedAttributes(schema, complexType, baseType, issues);
|
|
2623
|
+
checkWildcardRestriction(schema, complexType, baseType, issues);
|
|
2624
|
+
if (complexType.contentModel === "complex" && baseType.contentModel === "complex") {
|
|
2625
|
+
checkComplexContentRestriction(schema, complexType, baseType, issues);
|
|
2626
|
+
} else if (complexType.contentModel === "simple" && baseType.contentModel === "simple") {
|
|
2627
|
+
checkSimpleContentRestriction(schema, complexType, baseType, issues);
|
|
2628
|
+
}
|
|
2440
2629
|
}
|
|
2441
2630
|
return issues;
|
|
2442
2631
|
}
|
|
@@ -2763,6 +2952,130 @@ var UssXsdEngine = (() => {
|
|
|
2763
2952
|
return issues;
|
|
2764
2953
|
}
|
|
2765
2954
|
|
|
2955
|
+
// src/diagnostics/schemaWildcardDiagnostics.js
|
|
2956
|
+
function validateWildcardNamespace(namespace, path) {
|
|
2957
|
+
if (!namespace) return null;
|
|
2958
|
+
const trimmed = namespace.trim();
|
|
2959
|
+
if (!trimmed) return null;
|
|
2960
|
+
const parts = trimmed.split(/\s+/);
|
|
2961
|
+
for (const part of parts) {
|
|
2962
|
+
if (part === "##any" || part === "##other" || part === "##targetNamespace") {
|
|
2963
|
+
continue;
|
|
2964
|
+
}
|
|
2965
|
+
if (!part.includes(":") && part !== "") {
|
|
2966
|
+
return createIssue({
|
|
2967
|
+
code: "INVALID_WILDCARD_NAMESPACE",
|
|
2968
|
+
severity: "warning",
|
|
2969
|
+
message: `Invalid namespace in wildcard constraint: '${part}'. Expected ##any, ##other, ##targetNamespace, or a valid namespace URI.`,
|
|
2970
|
+
path
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
return null;
|
|
2975
|
+
}
|
|
2976
|
+
function validateProcessContents(processContents, path) {
|
|
2977
|
+
if (!processContents) return null;
|
|
2978
|
+
const trimmed = processContents.trim();
|
|
2979
|
+
if (trimmed === "strict" || trimmed === "lax" || trimmed === "skip") {
|
|
2980
|
+
return null;
|
|
2981
|
+
}
|
|
2982
|
+
return createIssue({
|
|
2983
|
+
code: "INVALID_PROCESS_CONTENTS",
|
|
2984
|
+
severity: "error",
|
|
2985
|
+
message: `Invalid processContents value: '${processContents}'. Must be 'strict', 'lax', or 'skip'.`,
|
|
2986
|
+
path
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
function validateSingleWildcard(wildcardNode, nodeName) {
|
|
2990
|
+
const issues = [];
|
|
2991
|
+
const nsIssue = validateWildcardNamespace(wildcardNode.namespace, wildcardNode.path);
|
|
2992
|
+
if (nsIssue) {
|
|
2993
|
+
issues.push(nsIssue);
|
|
2994
|
+
}
|
|
2995
|
+
const pcIssue = validateProcessContents(wildcardNode.processContents, wildcardNode.path);
|
|
2996
|
+
if (pcIssue) {
|
|
2997
|
+
issues.push(pcIssue);
|
|
2998
|
+
}
|
|
2999
|
+
if (wildcardNode.notNamespace && wildcardNode.notNamespace.length > 0) {
|
|
3000
|
+
if (wildcardNode.namespace !== "##other" && wildcardNode.namespace !== "##targetNamespace") {
|
|
3001
|
+
issues.push(
|
|
3002
|
+
createIssue({
|
|
3003
|
+
code: "INVALID_NOT_NAMESPACE_USAGE",
|
|
3004
|
+
severity: "warning",
|
|
3005
|
+
message: `notNamespace is only meaningful with ##other or ##targetNamespace namespace constraint.`,
|
|
3006
|
+
path: wildcardNode.path
|
|
3007
|
+
})
|
|
3008
|
+
);
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
if (wildcardNode.notQName && wildcardNode.notQName.length > 0) {
|
|
3012
|
+
for (const qname of wildcardNode.notQName) {
|
|
3013
|
+
if (!qname || qname.trim() === "") {
|
|
3014
|
+
issues.push(
|
|
3015
|
+
createIssue({
|
|
3016
|
+
code: "INVALID_NOT_QNAME",
|
|
3017
|
+
severity: "warning",
|
|
3018
|
+
message: `Empty QName in notQName constraint.`,
|
|
3019
|
+
path: wildcardNode.path
|
|
3020
|
+
})
|
|
3021
|
+
);
|
|
3022
|
+
break;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
return issues;
|
|
3027
|
+
}
|
|
3028
|
+
function findWildcardsInContent(node, wildcards) {
|
|
3029
|
+
if (!node) return;
|
|
3030
|
+
if (node.kind === "any" || node.kind === "anyAttribute") {
|
|
3031
|
+
wildcards.push(node);
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
if (node.children && Array.isArray(node.children)) {
|
|
3035
|
+
for (const child of node.children) {
|
|
3036
|
+
findWildcardsInContent(child, wildcards);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
function findWildcardsInAttributes(attributes, wildcards) {
|
|
3041
|
+
if (!attributes || !Array.isArray(attributes)) return;
|
|
3042
|
+
for (const attr of attributes) {
|
|
3043
|
+
if (attr.kind === "anyAttribute") {
|
|
3044
|
+
wildcards.push(attr);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
function runWildcardDiagnostics(schema) {
|
|
3049
|
+
const issues = [];
|
|
3050
|
+
const wildcards = [];
|
|
3051
|
+
for (const elem of Object.values(schema.globals.elements || {})) {
|
|
3052
|
+
if (elem.inlineType?.content) {
|
|
3053
|
+
findWildcardsInContent(elem.inlineType.content, wildcards);
|
|
3054
|
+
}
|
|
3055
|
+
if (elem.inlineType?.attributes) {
|
|
3056
|
+
findWildcardsInAttributes(elem.inlineType.attributes, wildcards);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
for (const complexType of Object.values(schema.globals.complexTypes || {})) {
|
|
3060
|
+
if (complexType.content) {
|
|
3061
|
+
findWildcardsInContent(complexType.content, wildcards);
|
|
3062
|
+
}
|
|
3063
|
+
if (complexType.attributes) {
|
|
3064
|
+
findWildcardsInAttributes(complexType.attributes, wildcards);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
for (const group of Object.values(schema.globals.groups || {})) {
|
|
3068
|
+
if (group.content) {
|
|
3069
|
+
findWildcardsInContent(group.content, wildcards);
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
for (const wildcard of wildcards) {
|
|
3073
|
+
const wildcardIssues = validateSingleWildcard(wildcard, wildcard.kind);
|
|
3074
|
+
issues.push(...wildcardIssues);
|
|
3075
|
+
}
|
|
3076
|
+
return issues;
|
|
3077
|
+
}
|
|
3078
|
+
|
|
2766
3079
|
// src/diagnostics/schemaDiagnostics.js
|
|
2767
3080
|
function buildStats(schema) {
|
|
2768
3081
|
return {
|
|
@@ -2937,6 +3250,8 @@ var UssXsdEngine = (() => {
|
|
|
2937
3250
|
const identityIssues = runIdentityConstraintDiagnostics(schema);
|
|
2938
3251
|
issues.push(...identityIssues);
|
|
2939
3252
|
emitUnsupportedFeatureWarnings(schema, issues, options);
|
|
3253
|
+
const wildcardIssues = runWildcardDiagnostics(schema);
|
|
3254
|
+
issues.push(...wildcardIssues);
|
|
2940
3255
|
const facetIssues = runFacetDiagnostics(schema, options);
|
|
2941
3256
|
issues.push(...facetIssues);
|
|
2942
3257
|
const restrictionIssues = runRestrictionDiagnostics(schema);
|
|
@@ -2957,7 +3272,7 @@ var UssXsdEngine = (() => {
|
|
|
2957
3272
|
}
|
|
2958
3273
|
|
|
2959
3274
|
// src/version.js
|
|
2960
|
-
var ENGINE_VERSION = "v0.2.
|
|
3275
|
+
var ENGINE_VERSION = "v0.2.1";
|
|
2961
3276
|
|
|
2962
3277
|
// src/utils/result.js
|
|
2963
3278
|
function summarizeIssues(issues = []) {
|
|
@@ -3845,6 +4160,24 @@ var UssXsdEngine = (() => {
|
|
|
3845
4160
|
function buildComplexTypeContent(schema, complexTypeDecl, options, state) {
|
|
3846
4161
|
const content = getEffectiveContent(schema, complexTypeDecl);
|
|
3847
4162
|
const attributes = getEffectiveAttributes(schema, complexTypeDecl);
|
|
4163
|
+
if (state.currentDepth >= options.maxDepth) {
|
|
4164
|
+
return {
|
|
4165
|
+
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
4166
|
+
children: []
|
|
4167
|
+
};
|
|
4168
|
+
}
|
|
4169
|
+
const typeKey = complexTypeDecl.name || complexTypeDecl.qName;
|
|
4170
|
+
if (typeKey && state.visitedTypes.has(typeKey)) {
|
|
4171
|
+
return {
|
|
4172
|
+
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
4173
|
+
children: []
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
if (typeKey) {
|
|
4177
|
+
state.visitedTypes.add(typeKey);
|
|
4178
|
+
}
|
|
4179
|
+
const previousDepth = state.currentDepth;
|
|
4180
|
+
state.currentDepth += 1;
|
|
3848
4181
|
let children = content ? buildNodesFromContent(schema, content, options, state) : [];
|
|
3849
4182
|
if (options.mode === "minimal" && children.length === 0 && content) {
|
|
3850
4183
|
children = buildRepresentativeNodesFromContent(
|
|
@@ -3854,6 +4187,10 @@ var UssXsdEngine = (() => {
|
|
|
3854
4187
|
state
|
|
3855
4188
|
);
|
|
3856
4189
|
}
|
|
4190
|
+
state.currentDepth = previousDepth;
|
|
4191
|
+
if (typeKey) {
|
|
4192
|
+
state.visitedTypes.delete(typeKey);
|
|
4193
|
+
}
|
|
3857
4194
|
return {
|
|
3858
4195
|
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
3859
4196
|
children
|
|
@@ -3932,7 +4269,10 @@ var UssXsdEngine = (() => {
|
|
|
3932
4269
|
function generateXmlFromSchema(schema, options = {}, helpers = {}) {
|
|
3933
4270
|
const normalizedOptions = {
|
|
3934
4271
|
mode: options.mode === "full" ? "full" : "minimal",
|
|
3935
|
-
includeOptionalAttributes: options.includeOptionalAttributes === true
|
|
4272
|
+
includeOptionalAttributes: options.includeOptionalAttributes === true,
|
|
4273
|
+
maxDepth: options.maxDepth ?? 3,
|
|
4274
|
+
maxChoiceBranches: options.maxChoiceBranches ?? 1,
|
|
4275
|
+
expandRepeatingElements: options.expandRepeatingElements ?? 2
|
|
3936
4276
|
};
|
|
3937
4277
|
const root = selectRoot(schema, options);
|
|
3938
4278
|
if (!root) {
|
|
@@ -3945,7 +4285,9 @@ var UssXsdEngine = (() => {
|
|
|
3945
4285
|
const state = {
|
|
3946
4286
|
resolveAttributeGroup: helpers.resolveAttributeGroup,
|
|
3947
4287
|
targetPrefix: options.targetPrefix || "tns",
|
|
3948
|
-
nsContext
|
|
4288
|
+
nsContext,
|
|
4289
|
+
visitedTypes: /* @__PURE__ */ new Set(),
|
|
4290
|
+
currentDepth: 0
|
|
3949
4291
|
};
|
|
3950
4292
|
const [rootNode] = buildElementInstances(
|
|
3951
4293
|
schema,
|
|
@@ -4048,6 +4390,80 @@ ${writeNode(rootNode, 0)}`;
|
|
|
4048
4390
|
});
|
|
4049
4391
|
}
|
|
4050
4392
|
|
|
4393
|
+
// src/validation/wildcardValidator.js
|
|
4394
|
+
function namespaceMatches(elementNamespace, wildcardNamespace, targetNamespace) {
|
|
4395
|
+
if (!wildcardNamespace) return true;
|
|
4396
|
+
if (wildcardNamespace === "##any") return true;
|
|
4397
|
+
if (wildcardNamespace === "##targetNamespace") {
|
|
4398
|
+
return elementNamespace === targetNamespace;
|
|
4399
|
+
}
|
|
4400
|
+
if (wildcardNamespace === "##other") {
|
|
4401
|
+
return elementNamespace !== targetNamespace;
|
|
4402
|
+
}
|
|
4403
|
+
if (Array.isArray(wildcardNamespace)) {
|
|
4404
|
+
return wildcardNamespace.includes(elementNamespace);
|
|
4405
|
+
}
|
|
4406
|
+
return elementNamespace === wildcardNamespace;
|
|
4407
|
+
}
|
|
4408
|
+
function isExcludedByNotNamespace(elementNamespace, notNamespace) {
|
|
4409
|
+
if (!notNamespace || notNamespace.length === 0) return false;
|
|
4410
|
+
return notNamespace.includes(elementNamespace);
|
|
4411
|
+
}
|
|
4412
|
+
function isExcludedByNotQName(qName, notQName) {
|
|
4413
|
+
if (!notQName || notQName.length === 0) return false;
|
|
4414
|
+
return notQName.includes(qName);
|
|
4415
|
+
}
|
|
4416
|
+
function buildQName(localName4, namespaceUri4) {
|
|
4417
|
+
if (!namespaceUri4 || namespaceUri4 === "") {
|
|
4418
|
+
return localName4;
|
|
4419
|
+
}
|
|
4420
|
+
return `{${namespaceUri4}}${localName4}`;
|
|
4421
|
+
}
|
|
4422
|
+
function elementMatchesWildcard(elementLocalName, elementNamespace, wildcardNode, targetNamespace) {
|
|
4423
|
+
if (!wildcardNode || wildcardNode.kind !== "any") {
|
|
4424
|
+
return false;
|
|
4425
|
+
}
|
|
4426
|
+
if (!namespaceMatches(elementNamespace, wildcardNode.namespace, targetNamespace)) {
|
|
4427
|
+
return false;
|
|
4428
|
+
}
|
|
4429
|
+
if (isExcludedByNotNamespace(elementNamespace, wildcardNode.notNamespace)) {
|
|
4430
|
+
return false;
|
|
4431
|
+
}
|
|
4432
|
+
const qName = buildQName(elementLocalName, elementNamespace);
|
|
4433
|
+
if (isExcludedByNotQName(qName, wildcardNode.notQName)) {
|
|
4434
|
+
return false;
|
|
4435
|
+
}
|
|
4436
|
+
return true;
|
|
4437
|
+
}
|
|
4438
|
+
function attributeMatchesWildcard(attrLocalName, attrNamespace, wildcardNode, targetNamespace) {
|
|
4439
|
+
if (!wildcardNode || wildcardNode.kind !== "anyAttribute") {
|
|
4440
|
+
return false;
|
|
4441
|
+
}
|
|
4442
|
+
if (!namespaceMatches(attrNamespace, wildcardNode.namespace, targetNamespace)) {
|
|
4443
|
+
return false;
|
|
4444
|
+
}
|
|
4445
|
+
if (isExcludedByNotNamespace(attrNamespace, wildcardNode.notNamespace)) {
|
|
4446
|
+
return false;
|
|
4447
|
+
}
|
|
4448
|
+
const qName = buildQName(attrLocalName, attrNamespace);
|
|
4449
|
+
if (isExcludedByNotQName(qName, wildcardNode.notQName)) {
|
|
4450
|
+
return false;
|
|
4451
|
+
}
|
|
4452
|
+
return true;
|
|
4453
|
+
}
|
|
4454
|
+
function normalizeProcessContents(processContents) {
|
|
4455
|
+
if (processContents === "lax" || processContents === "skip") {
|
|
4456
|
+
return processContents;
|
|
4457
|
+
}
|
|
4458
|
+
return "strict";
|
|
4459
|
+
}
|
|
4460
|
+
function isStrictWildcardValidation(processContents) {
|
|
4461
|
+
return normalizeProcessContents(processContents) === "strict";
|
|
4462
|
+
}
|
|
4463
|
+
function shouldSkipWildcardValidation(processContents) {
|
|
4464
|
+
return normalizeProcessContents(processContents) === "skip";
|
|
4465
|
+
}
|
|
4466
|
+
|
|
4051
4467
|
// src/validation/structureValidator.js
|
|
4052
4468
|
function elementChildren2(xmlNode) {
|
|
4053
4469
|
return Array.from(xmlNode?.children || []).filter((child) => child.nodeType === 1);
|
|
@@ -4117,6 +4533,7 @@ ${writeNode(rootNode, 0)}`;
|
|
|
4117
4533
|
function validateAttributes(xmlNode, attributes, context) {
|
|
4118
4534
|
const { schema, createIssue: createIssue2, ISSUE_CODES: ISSUE_CODES2, issues, pathParts, validateAttributeValue: validateAttributeValue2 } = context;
|
|
4119
4535
|
const allowed = /* @__PURE__ */ new Map();
|
|
4536
|
+
let anyAttributeWildcard = null;
|
|
4120
4537
|
for (const attr of attributes || []) {
|
|
4121
4538
|
if (!attr) continue;
|
|
4122
4539
|
if (attr.kind === "attribute") {
|
|
@@ -4128,6 +4545,8 @@ ${writeNode(rootNode, 0)}`;
|
|
|
4128
4545
|
const group = context.resolveAttributeGroup?.(attr.refName);
|
|
4129
4546
|
if (!group) continue;
|
|
4130
4547
|
validateAttributes(xmlNode, group.attributes || [], context);
|
|
4548
|
+
} else if (attr.kind === "anyAttribute") {
|
|
4549
|
+
anyAttributeWildcard = attr;
|
|
4131
4550
|
}
|
|
4132
4551
|
}
|
|
4133
4552
|
for (const attrDecl of allowed.values()) {
|
|
@@ -4173,6 +4592,15 @@ ${writeNode(rootNode, 0)}`;
|
|
|
4173
4592
|
continue;
|
|
4174
4593
|
}
|
|
4175
4594
|
if (!allowed.has(attr.name)) {
|
|
4595
|
+
if (anyAttributeWildcard) {
|
|
4596
|
+
const attrLocalName = attr.localName || attr.name.split(":")[1] || attr.name;
|
|
4597
|
+
const attrNamespace = attr.namespaceURI || null;
|
|
4598
|
+
if (attributeMatchesWildcard(attrLocalName, attrNamespace, anyAttributeWildcard, schema.targetNamespace)) {
|
|
4599
|
+
if (!shouldSkipWildcardValidation(anyAttributeWildcard.processContents)) {
|
|
4600
|
+
}
|
|
4601
|
+
continue;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4176
4604
|
issues.push(
|
|
4177
4605
|
createIssue2({
|
|
4178
4606
|
code: ISSUE_CODES2.XML_UNEXPECTED_ATTRIBUTE,
|
|
@@ -4590,7 +5018,16 @@ ${writeNode(rootNode, 0)}`;
|
|
|
4590
5018
|
return validateAll(children, startIndex, modelNode, context, pathParts, silent);
|
|
4591
5019
|
case "any":
|
|
4592
5020
|
if (startIndex < children.length) {
|
|
4593
|
-
|
|
5021
|
+
const childNode = children[startIndex];
|
|
5022
|
+
const childLocalName = localName(childNode);
|
|
5023
|
+
const childNamespace = namespaceUri(childNode);
|
|
5024
|
+
if (elementMatchesWildcard(childLocalName, childNamespace, modelNode, context.schema.targetNamespace)) {
|
|
5025
|
+
if (isStrictWildcardValidation(modelNode.processContents)) {
|
|
5026
|
+
validateElementDecl(childNode, { name: childLocalName, typeName: null }, context, [...pathParts, childLocalName]);
|
|
5027
|
+
} else if (!shouldSkipWildcardValidation(modelNode.processContents)) {
|
|
5028
|
+
}
|
|
5029
|
+
return { nextIndex: startIndex + 1, matched: true, matchedAny: true };
|
|
5030
|
+
}
|
|
4594
5031
|
}
|
|
4595
5032
|
return { nextIndex: startIndex, matched: true, matchedAny: false };
|
|
4596
5033
|
default:
|
package/package.json
CHANGED