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
|
*/
|
|
@@ -69,6 +69,8 @@ var ISSUE_CODES = {
|
|
|
69
69
|
XML_KEY_NULL_VIOLATION: "XML_KEY_NULL_VIOLATION",
|
|
70
70
|
XML_KEYREF_VIOLATION: "XML_KEYREF_VIOLATION",
|
|
71
71
|
XML_UNIQUE_VIOLATION: "XML_UNIQUE_VIOLATION",
|
|
72
|
+
XML_ANY_STRICT_VALIDATION_FAILED: "XML_ANY_STRICT_VALIDATION_FAILED",
|
|
73
|
+
XML_ANYATTRIBUTE_STRICT_VALIDATION_FAILED: "XML_ANYATTRIBUTE_STRICT_VALIDATION_FAILED",
|
|
72
74
|
XML_PATTERN_MISMATCH: "XML_PATTERN_MISMATCH",
|
|
73
75
|
XML_LENGTH_MISMATCH: "XML_LENGTH_MISMATCH",
|
|
74
76
|
XML_MIN_LENGTH_VIOLATION: "XML_MIN_LENGTH_VIOLATION",
|
|
@@ -95,6 +97,9 @@ var ISSUE_CODES = {
|
|
|
95
97
|
XSD_RESTRICTION_NOT_SUBSET: "XSD_RESTRICTION_NOT_SUBSET",
|
|
96
98
|
XSD_RESTRICTION_OCCURS_WIDENED: "XSD_RESTRICTION_OCCURS_WIDENED",
|
|
97
99
|
XSD_RESTRICTION_ATTRIBUTE_WIDENED: "XSD_RESTRICTION_ATTRIBUTE_WIDENED",
|
|
100
|
+
XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE: "XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE",
|
|
101
|
+
XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE: "XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE",
|
|
102
|
+
XSD_RESTRICTION_WILDCARD_INCOMPATIBLE: "XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
98
103
|
XSD_INCLUDE_NOT_PROVIDED: "XSD_INCLUDE_NOT_PROVIDED",
|
|
99
104
|
XSD_IMPORT_NOT_PROVIDED: "XSD_IMPORT_NOT_PROVIDED",
|
|
100
105
|
XSD_INCLUDE_NAMESPACE_MISMATCH: "XSD_INCLUDE_NAMESPACE_MISMATCH",
|
|
@@ -480,9 +485,11 @@ function createGroupRefNode({
|
|
|
480
485
|
}
|
|
481
486
|
function createAnyNode({
|
|
482
487
|
namespace = null,
|
|
483
|
-
processContents =
|
|
488
|
+
processContents = "strict",
|
|
484
489
|
minOccurs = 1,
|
|
485
490
|
maxOccurs = 1,
|
|
491
|
+
notNamespace = [],
|
|
492
|
+
notQName = [],
|
|
486
493
|
line = null,
|
|
487
494
|
column = null,
|
|
488
495
|
path = null
|
|
@@ -490,9 +497,31 @@ function createAnyNode({
|
|
|
490
497
|
return {
|
|
491
498
|
kind: "any",
|
|
492
499
|
namespace,
|
|
493
|
-
processContents,
|
|
500
|
+
processContents: processContents || "strict",
|
|
494
501
|
minOccurs,
|
|
495
502
|
maxOccurs,
|
|
503
|
+
notNamespace,
|
|
504
|
+
notQName,
|
|
505
|
+
line,
|
|
506
|
+
column,
|
|
507
|
+
path
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function createAnyAttributeNode({
|
|
511
|
+
namespace = null,
|
|
512
|
+
processContents = "strict",
|
|
513
|
+
notNamespace = [],
|
|
514
|
+
notQName = [],
|
|
515
|
+
line = null,
|
|
516
|
+
column = null,
|
|
517
|
+
path = null
|
|
518
|
+
} = {}) {
|
|
519
|
+
return {
|
|
520
|
+
kind: "anyAttribute",
|
|
521
|
+
namespace,
|
|
522
|
+
processContents: processContents || "strict",
|
|
523
|
+
notNamespace,
|
|
524
|
+
notQName,
|
|
496
525
|
line,
|
|
497
526
|
column,
|
|
498
527
|
path
|
|
@@ -817,8 +846,6 @@ function getEffectiveContent(schema, complexTypeDecl) {
|
|
|
817
846
|
|
|
818
847
|
// src/parser/buildSchemaModel.js
|
|
819
848
|
var UNSUPPORTED_NODE_FEATURES = /* @__PURE__ */ new Set([
|
|
820
|
-
"any",
|
|
821
|
-
"anyAttribute",
|
|
822
849
|
"redefine",
|
|
823
850
|
"notation"
|
|
824
851
|
]);
|
|
@@ -1253,6 +1280,10 @@ function parseAttributesContainer(nodes, xsdText, lineStarts, parentPath, schema
|
|
|
1253
1280
|
issues
|
|
1254
1281
|
)
|
|
1255
1282
|
);
|
|
1283
|
+
} else if (child.localName === "anyAttribute") {
|
|
1284
|
+
attributes.push(
|
|
1285
|
+
parseAnyAttribute(child, xsdText, lineStarts, parentPath, schema, issues)
|
|
1286
|
+
);
|
|
1256
1287
|
}
|
|
1257
1288
|
}
|
|
1258
1289
|
return attributes;
|
|
@@ -1336,15 +1367,58 @@ function parseGroupRef(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
|
1336
1367
|
path
|
|
1337
1368
|
});
|
|
1338
1369
|
}
|
|
1370
|
+
function parseWildcardNamespace(namespaceStr) {
|
|
1371
|
+
if (!namespaceStr) return null;
|
|
1372
|
+
const trimmed = namespaceStr.trim();
|
|
1373
|
+
if (!trimmed) return null;
|
|
1374
|
+
if (trimmed.includes(" ")) {
|
|
1375
|
+
return trimmed.split(/\s+/).filter((ns) => ns);
|
|
1376
|
+
}
|
|
1377
|
+
return trimmed;
|
|
1378
|
+
}
|
|
1379
|
+
function parseNotNamespace(notNamespaceStr) {
|
|
1380
|
+
if (!notNamespaceStr) return [];
|
|
1381
|
+
const trimmed = notNamespaceStr.trim();
|
|
1382
|
+
if (!trimmed) return [];
|
|
1383
|
+
if (trimmed.includes(" ")) {
|
|
1384
|
+
return trimmed.split(/\s+/).filter((ns) => ns);
|
|
1385
|
+
}
|
|
1386
|
+
return [trimmed];
|
|
1387
|
+
}
|
|
1388
|
+
function parseNotQName(notQNameStr) {
|
|
1389
|
+
if (!notQNameStr) return [];
|
|
1390
|
+
const trimmed = notQNameStr.trim();
|
|
1391
|
+
if (!trimmed) return [];
|
|
1392
|
+
if (trimmed.includes(" ")) {
|
|
1393
|
+
return trimmed.split(/\s+/).filter((qn) => qn);
|
|
1394
|
+
}
|
|
1395
|
+
return [trimmed];
|
|
1396
|
+
}
|
|
1339
1397
|
function parseAny(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1340
1398
|
const path = buildPath(parentPath, node);
|
|
1341
1399
|
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1342
1400
|
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1343
1401
|
return createAnyNode({
|
|
1344
|
-
namespace: node.getAttribute("namespace"),
|
|
1402
|
+
namespace: parseWildcardNamespace(node.getAttribute("namespace")),
|
|
1345
1403
|
processContents: node.getAttribute("processContents"),
|
|
1346
1404
|
minOccurs: normalizeOccurs(node.getAttribute("minOccurs"), 1),
|
|
1347
1405
|
maxOccurs: normalizeOccurs(node.getAttribute("maxOccurs"), 1),
|
|
1406
|
+
notNamespace: parseNotNamespace(node.getAttribute("notNamespace")),
|
|
1407
|
+
notQName: parseNotQName(node.getAttribute("notQName")),
|
|
1408
|
+
line: loc.line,
|
|
1409
|
+
column: loc.column,
|
|
1410
|
+
path
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
function parseAnyAttribute(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1414
|
+
const path = buildPath(parentPath, node);
|
|
1415
|
+
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1416
|
+
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1417
|
+
return createAnyAttributeNode({
|
|
1418
|
+
namespace: parseWildcardNamespace(node.getAttribute("namespace")),
|
|
1419
|
+
processContents: node.getAttribute("processContents"),
|
|
1420
|
+
notNamespace: parseNotNamespace(node.getAttribute("notNamespace")),
|
|
1421
|
+
notQName: parseNotQName(node.getAttribute("notQName")),
|
|
1348
1422
|
line: loc.line,
|
|
1349
1423
|
column: loc.column,
|
|
1350
1424
|
path
|
|
@@ -1767,24 +1841,24 @@ function resolveExternalDocument(ref, externalDocuments) {
|
|
|
1767
1841
|
}
|
|
1768
1842
|
}
|
|
1769
1843
|
if (ref?.kind === "import" && ref.namespace) {
|
|
1770
|
-
const
|
|
1844
|
+
const namespaceMatches2 = entries.filter((entry) => {
|
|
1771
1845
|
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText(
|
|
1772
1846
|
entry.text
|
|
1773
1847
|
);
|
|
1774
1848
|
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
1775
1849
|
});
|
|
1776
|
-
if (
|
|
1850
|
+
if (namespaceMatches2.length === 1) {
|
|
1777
1851
|
return {
|
|
1778
1852
|
kind: "namespace",
|
|
1779
|
-
entry:
|
|
1780
|
-
matches:
|
|
1853
|
+
entry: namespaceMatches2[0],
|
|
1854
|
+
matches: namespaceMatches2
|
|
1781
1855
|
};
|
|
1782
1856
|
}
|
|
1783
|
-
if (
|
|
1857
|
+
if (namespaceMatches2.length > 1) {
|
|
1784
1858
|
return {
|
|
1785
1859
|
kind: "ambiguous-namespace",
|
|
1786
1860
|
entry: null,
|
|
1787
|
-
matches:
|
|
1861
|
+
matches: namespaceMatches2
|
|
1788
1862
|
};
|
|
1789
1863
|
}
|
|
1790
1864
|
}
|
|
@@ -2401,6 +2475,115 @@ function checkRestrictedAttributes(schema, derivedType, baseType, issues) {
|
|
|
2401
2475
|
}
|
|
2402
2476
|
}
|
|
2403
2477
|
}
|
|
2478
|
+
function checkWildcardRestriction(schema, derivedType, baseType, issues) {
|
|
2479
|
+
const derivedContent = getEffectiveContent(schema, derivedType);
|
|
2480
|
+
const baseContent = getEffectiveContent(schema, baseType);
|
|
2481
|
+
const derivedWildcards = [];
|
|
2482
|
+
const baseWildcards = [];
|
|
2483
|
+
function collectWildcards(node, arr) {
|
|
2484
|
+
if (!node) return;
|
|
2485
|
+
if (node.kind === "any") {
|
|
2486
|
+
arr.push(node);
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
if (node.children) {
|
|
2490
|
+
for (const child of asArray(node.children)) {
|
|
2491
|
+
collectWildcards(child, arr);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
collectWildcards(derivedContent, derivedWildcards);
|
|
2496
|
+
collectWildcards(baseContent, baseWildcards);
|
|
2497
|
+
if (derivedWildcards.length > 0 && baseWildcards.length === 0) {
|
|
2498
|
+
issues.push(
|
|
2499
|
+
buildRestrictionIssue(
|
|
2500
|
+
"XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
2501
|
+
`Restricted type introduces wildcard elements that base type does not have.`,
|
|
2502
|
+
derivedType
|
|
2503
|
+
)
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
const derivedAttrs = asArray(getEffectiveAttributes(schema, derivedType));
|
|
2507
|
+
const baseAttrs = asArray(getEffectiveAttributes(schema, baseType));
|
|
2508
|
+
const derivedHasAnyAttribute = derivedAttrs.some((a) => a?.kind === "anyAttribute");
|
|
2509
|
+
const baseHasAnyAttribute = baseAttrs.some((a) => a?.kind === "anyAttribute");
|
|
2510
|
+
if (derivedHasAnyAttribute && !baseHasAnyAttribute) {
|
|
2511
|
+
issues.push(
|
|
2512
|
+
buildRestrictionIssue(
|
|
2513
|
+
"XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
2514
|
+
`Restricted type introduces anyAttribute that base type does not have.`,
|
|
2515
|
+
derivedType
|
|
2516
|
+
)
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
function checkOccurrenceCompatibility(derived, base, name) {
|
|
2521
|
+
const dMin = typeof derived.minOccurs === "number" ? derived.minOccurs : 1;
|
|
2522
|
+
const bMin = typeof base.minOccurs === "number" ? base.minOccurs : 1;
|
|
2523
|
+
const dMax = maxToNumber(derived.maxOccurs ?? 1);
|
|
2524
|
+
const bMax = maxToNumber(base.maxOccurs ?? 1);
|
|
2525
|
+
return dMin >= bMin && dMax <= bMax;
|
|
2526
|
+
}
|
|
2527
|
+
function checkComplexContentRestriction(schema, derivedType, baseType, issues) {
|
|
2528
|
+
const derivedContent = getEffectiveContent(schema, derivedType);
|
|
2529
|
+
const baseContent = getEffectiveContent(schema, baseType);
|
|
2530
|
+
if (!derivedContent || !baseContent) return;
|
|
2531
|
+
const derivedFlat = flattenContent(derivedContent, []);
|
|
2532
|
+
const baseFlat = flattenContent(baseContent, []);
|
|
2533
|
+
const baseMap = new Map(baseFlat.map((item) => [item.name, item]));
|
|
2534
|
+
for (const item of derivedFlat) {
|
|
2535
|
+
const baseItem = baseMap.get(item.name);
|
|
2536
|
+
if (!baseItem) {
|
|
2537
|
+
issues.push(
|
|
2538
|
+
buildRestrictionIssue(
|
|
2539
|
+
"XSD_RESTRICTION_NOT_SUBSET",
|
|
2540
|
+
`Restricted type contains element '${item.name}' not in base type.`,
|
|
2541
|
+
item
|
|
2542
|
+
)
|
|
2543
|
+
);
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
if (!checkOccurrenceCompatibility(item, baseItem, item.name)) {
|
|
2547
|
+
issues.push(
|
|
2548
|
+
buildRestrictionIssue(
|
|
2549
|
+
"XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE",
|
|
2550
|
+
`Restricted type has incompatible occurrence constraints for '${item.name}'.`,
|
|
2551
|
+
item
|
|
2552
|
+
)
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
function checkSimpleContentRestriction(schema, derivedType, baseType, issues) {
|
|
2558
|
+
if (baseType.contentModel !== "simple") {
|
|
2559
|
+
issues.push(
|
|
2560
|
+
buildRestrictionIssue(
|
|
2561
|
+
"XSD_RESTRICTION_NOT_SUBSET",
|
|
2562
|
+
`SimpleContent restriction requires base type to have simple content.`,
|
|
2563
|
+
derivedType
|
|
2564
|
+
)
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2567
|
+
const derivedAttrs = asArray(getEffectiveAttributes(schema, derivedType));
|
|
2568
|
+
const baseAttrs = asArray(getEffectiveAttributes(schema, baseType));
|
|
2569
|
+
const baseAttrMap = new Map(
|
|
2570
|
+
baseAttrs.filter((attr) => attr?.kind === "attribute").map((attr) => [localDeclName(attr), attr])
|
|
2571
|
+
);
|
|
2572
|
+
for (const attr of derivedAttrs) {
|
|
2573
|
+
if (attr?.kind !== "attribute") continue;
|
|
2574
|
+
const name = localDeclName(attr);
|
|
2575
|
+
const baseAttr = baseAttrMap.get(name);
|
|
2576
|
+
if (!baseAttr && !baseAttrs.some((a) => a?.kind === "anyAttribute")) {
|
|
2577
|
+
issues.push(
|
|
2578
|
+
buildRestrictionIssue(
|
|
2579
|
+
"XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE",
|
|
2580
|
+
`SimpleContent restricted type adds attribute '${name}' not in base type.`,
|
|
2581
|
+
attr
|
|
2582
|
+
)
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2404
2587
|
function runRestrictionDiagnostics(schema) {
|
|
2405
2588
|
const issues = [];
|
|
2406
2589
|
for (const complexType of Object.values(schema.globals.complexTypes || {})) {
|
|
@@ -2410,6 +2593,12 @@ function runRestrictionDiagnostics(schema) {
|
|
|
2410
2593
|
if (!baseType) continue;
|
|
2411
2594
|
checkRestrictedContentSubset(schema, complexType, baseType, issues);
|
|
2412
2595
|
checkRestrictedAttributes(schema, complexType, baseType, issues);
|
|
2596
|
+
checkWildcardRestriction(schema, complexType, baseType, issues);
|
|
2597
|
+
if (complexType.contentModel === "complex" && baseType.contentModel === "complex") {
|
|
2598
|
+
checkComplexContentRestriction(schema, complexType, baseType, issues);
|
|
2599
|
+
} else if (complexType.contentModel === "simple" && baseType.contentModel === "simple") {
|
|
2600
|
+
checkSimpleContentRestriction(schema, complexType, baseType, issues);
|
|
2601
|
+
}
|
|
2413
2602
|
}
|
|
2414
2603
|
return issues;
|
|
2415
2604
|
}
|
|
@@ -2736,6 +2925,130 @@ function runImportDiagnostics(schema, options = {}) {
|
|
|
2736
2925
|
return issues;
|
|
2737
2926
|
}
|
|
2738
2927
|
|
|
2928
|
+
// src/diagnostics/schemaWildcardDiagnostics.js
|
|
2929
|
+
function validateWildcardNamespace(namespace, path) {
|
|
2930
|
+
if (!namespace) return null;
|
|
2931
|
+
const trimmed = namespace.trim();
|
|
2932
|
+
if (!trimmed) return null;
|
|
2933
|
+
const parts = trimmed.split(/\s+/);
|
|
2934
|
+
for (const part of parts) {
|
|
2935
|
+
if (part === "##any" || part === "##other" || part === "##targetNamespace") {
|
|
2936
|
+
continue;
|
|
2937
|
+
}
|
|
2938
|
+
if (!part.includes(":") && part !== "") {
|
|
2939
|
+
return createIssue({
|
|
2940
|
+
code: "INVALID_WILDCARD_NAMESPACE",
|
|
2941
|
+
severity: "warning",
|
|
2942
|
+
message: `Invalid namespace in wildcard constraint: '${part}'. Expected ##any, ##other, ##targetNamespace, or a valid namespace URI.`,
|
|
2943
|
+
path
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
return null;
|
|
2948
|
+
}
|
|
2949
|
+
function validateProcessContents(processContents, path) {
|
|
2950
|
+
if (!processContents) return null;
|
|
2951
|
+
const trimmed = processContents.trim();
|
|
2952
|
+
if (trimmed === "strict" || trimmed === "lax" || trimmed === "skip") {
|
|
2953
|
+
return null;
|
|
2954
|
+
}
|
|
2955
|
+
return createIssue({
|
|
2956
|
+
code: "INVALID_PROCESS_CONTENTS",
|
|
2957
|
+
severity: "error",
|
|
2958
|
+
message: `Invalid processContents value: '${processContents}'. Must be 'strict', 'lax', or 'skip'.`,
|
|
2959
|
+
path
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
function validateSingleWildcard(wildcardNode, nodeName) {
|
|
2963
|
+
const issues = [];
|
|
2964
|
+
const nsIssue = validateWildcardNamespace(wildcardNode.namespace, wildcardNode.path);
|
|
2965
|
+
if (nsIssue) {
|
|
2966
|
+
issues.push(nsIssue);
|
|
2967
|
+
}
|
|
2968
|
+
const pcIssue = validateProcessContents(wildcardNode.processContents, wildcardNode.path);
|
|
2969
|
+
if (pcIssue) {
|
|
2970
|
+
issues.push(pcIssue);
|
|
2971
|
+
}
|
|
2972
|
+
if (wildcardNode.notNamespace && wildcardNode.notNamespace.length > 0) {
|
|
2973
|
+
if (wildcardNode.namespace !== "##other" && wildcardNode.namespace !== "##targetNamespace") {
|
|
2974
|
+
issues.push(
|
|
2975
|
+
createIssue({
|
|
2976
|
+
code: "INVALID_NOT_NAMESPACE_USAGE",
|
|
2977
|
+
severity: "warning",
|
|
2978
|
+
message: `notNamespace is only meaningful with ##other or ##targetNamespace namespace constraint.`,
|
|
2979
|
+
path: wildcardNode.path
|
|
2980
|
+
})
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
if (wildcardNode.notQName && wildcardNode.notQName.length > 0) {
|
|
2985
|
+
for (const qname of wildcardNode.notQName) {
|
|
2986
|
+
if (!qname || qname.trim() === "") {
|
|
2987
|
+
issues.push(
|
|
2988
|
+
createIssue({
|
|
2989
|
+
code: "INVALID_NOT_QNAME",
|
|
2990
|
+
severity: "warning",
|
|
2991
|
+
message: `Empty QName in notQName constraint.`,
|
|
2992
|
+
path: wildcardNode.path
|
|
2993
|
+
})
|
|
2994
|
+
);
|
|
2995
|
+
break;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
return issues;
|
|
3000
|
+
}
|
|
3001
|
+
function findWildcardsInContent(node, wildcards) {
|
|
3002
|
+
if (!node) return;
|
|
3003
|
+
if (node.kind === "any" || node.kind === "anyAttribute") {
|
|
3004
|
+
wildcards.push(node);
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
if (node.children && Array.isArray(node.children)) {
|
|
3008
|
+
for (const child of node.children) {
|
|
3009
|
+
findWildcardsInContent(child, wildcards);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
function findWildcardsInAttributes(attributes, wildcards) {
|
|
3014
|
+
if (!attributes || !Array.isArray(attributes)) return;
|
|
3015
|
+
for (const attr of attributes) {
|
|
3016
|
+
if (attr.kind === "anyAttribute") {
|
|
3017
|
+
wildcards.push(attr);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
function runWildcardDiagnostics(schema) {
|
|
3022
|
+
const issues = [];
|
|
3023
|
+
const wildcards = [];
|
|
3024
|
+
for (const elem of Object.values(schema.globals.elements || {})) {
|
|
3025
|
+
if (elem.inlineType?.content) {
|
|
3026
|
+
findWildcardsInContent(elem.inlineType.content, wildcards);
|
|
3027
|
+
}
|
|
3028
|
+
if (elem.inlineType?.attributes) {
|
|
3029
|
+
findWildcardsInAttributes(elem.inlineType.attributes, wildcards);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
for (const complexType of Object.values(schema.globals.complexTypes || {})) {
|
|
3033
|
+
if (complexType.content) {
|
|
3034
|
+
findWildcardsInContent(complexType.content, wildcards);
|
|
3035
|
+
}
|
|
3036
|
+
if (complexType.attributes) {
|
|
3037
|
+
findWildcardsInAttributes(complexType.attributes, wildcards);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
for (const group of Object.values(schema.globals.groups || {})) {
|
|
3041
|
+
if (group.content) {
|
|
3042
|
+
findWildcardsInContent(group.content, wildcards);
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
for (const wildcard of wildcards) {
|
|
3046
|
+
const wildcardIssues = validateSingleWildcard(wildcard, wildcard.kind);
|
|
3047
|
+
issues.push(...wildcardIssues);
|
|
3048
|
+
}
|
|
3049
|
+
return issues;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
2739
3052
|
// src/diagnostics/schemaDiagnostics.js
|
|
2740
3053
|
function buildStats(schema) {
|
|
2741
3054
|
return {
|
|
@@ -2910,6 +3223,8 @@ function runSchemaDiagnostics(schema, options = {}) {
|
|
|
2910
3223
|
const identityIssues = runIdentityConstraintDiagnostics(schema);
|
|
2911
3224
|
issues.push(...identityIssues);
|
|
2912
3225
|
emitUnsupportedFeatureWarnings(schema, issues, options);
|
|
3226
|
+
const wildcardIssues = runWildcardDiagnostics(schema);
|
|
3227
|
+
issues.push(...wildcardIssues);
|
|
2913
3228
|
const facetIssues = runFacetDiagnostics(schema, options);
|
|
2914
3229
|
issues.push(...facetIssues);
|
|
2915
3230
|
const restrictionIssues = runRestrictionDiagnostics(schema);
|
|
@@ -2930,7 +3245,7 @@ function runSchemaDiagnostics(schema, options = {}) {
|
|
|
2930
3245
|
}
|
|
2931
3246
|
|
|
2932
3247
|
// src/version.js
|
|
2933
|
-
var ENGINE_VERSION = "v0.2.
|
|
3248
|
+
var ENGINE_VERSION = "v0.2.1";
|
|
2934
3249
|
|
|
2935
3250
|
// src/utils/result.js
|
|
2936
3251
|
function summarizeIssues(issues = []) {
|
|
@@ -3818,6 +4133,24 @@ function buildNodesFromContent(schema, contentNode, options, state) {
|
|
|
3818
4133
|
function buildComplexTypeContent(schema, complexTypeDecl, options, state) {
|
|
3819
4134
|
const content = getEffectiveContent(schema, complexTypeDecl);
|
|
3820
4135
|
const attributes = getEffectiveAttributes(schema, complexTypeDecl);
|
|
4136
|
+
if (state.currentDepth >= options.maxDepth) {
|
|
4137
|
+
return {
|
|
4138
|
+
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
4139
|
+
children: []
|
|
4140
|
+
};
|
|
4141
|
+
}
|
|
4142
|
+
const typeKey = complexTypeDecl.name || complexTypeDecl.qName;
|
|
4143
|
+
if (typeKey && state.visitedTypes.has(typeKey)) {
|
|
4144
|
+
return {
|
|
4145
|
+
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
4146
|
+
children: []
|
|
4147
|
+
};
|
|
4148
|
+
}
|
|
4149
|
+
if (typeKey) {
|
|
4150
|
+
state.visitedTypes.add(typeKey);
|
|
4151
|
+
}
|
|
4152
|
+
const previousDepth = state.currentDepth;
|
|
4153
|
+
state.currentDepth += 1;
|
|
3821
4154
|
let children = content ? buildNodesFromContent(schema, content, options, state) : [];
|
|
3822
4155
|
if (options.mode === "minimal" && children.length === 0 && content) {
|
|
3823
4156
|
children = buildRepresentativeNodesFromContent(
|
|
@@ -3827,6 +4160,10 @@ function buildComplexTypeContent(schema, complexTypeDecl, options, state) {
|
|
|
3827
4160
|
state
|
|
3828
4161
|
);
|
|
3829
4162
|
}
|
|
4163
|
+
state.currentDepth = previousDepth;
|
|
4164
|
+
if (typeKey) {
|
|
4165
|
+
state.visitedTypes.delete(typeKey);
|
|
4166
|
+
}
|
|
3830
4167
|
return {
|
|
3831
4168
|
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
3832
4169
|
children
|
|
@@ -3905,7 +4242,10 @@ function selectRoot(schema, options = {}) {
|
|
|
3905
4242
|
function generateXmlFromSchema(schema, options = {}, helpers = {}) {
|
|
3906
4243
|
const normalizedOptions = {
|
|
3907
4244
|
mode: options.mode === "full" ? "full" : "minimal",
|
|
3908
|
-
includeOptionalAttributes: options.includeOptionalAttributes === true
|
|
4245
|
+
includeOptionalAttributes: options.includeOptionalAttributes === true,
|
|
4246
|
+
maxDepth: options.maxDepth ?? 3,
|
|
4247
|
+
maxChoiceBranches: options.maxChoiceBranches ?? 1,
|
|
4248
|
+
expandRepeatingElements: options.expandRepeatingElements ?? 2
|
|
3909
4249
|
};
|
|
3910
4250
|
const root = selectRoot(schema, options);
|
|
3911
4251
|
if (!root) {
|
|
@@ -3918,7 +4258,9 @@ function generateXmlFromSchema(schema, options = {}, helpers = {}) {
|
|
|
3918
4258
|
const state = {
|
|
3919
4259
|
resolveAttributeGroup: helpers.resolveAttributeGroup,
|
|
3920
4260
|
targetPrefix: options.targetPrefix || "tns",
|
|
3921
|
-
nsContext
|
|
4261
|
+
nsContext,
|
|
4262
|
+
visitedTypes: /* @__PURE__ */ new Set(),
|
|
4263
|
+
currentDepth: 0
|
|
3922
4264
|
};
|
|
3923
4265
|
const [rootNode] = buildElementInstances(
|
|
3924
4266
|
schema,
|
|
@@ -4021,6 +4363,80 @@ function generateSampleXml({ xsdText, options = {} } = {}) {
|
|
|
4021
4363
|
});
|
|
4022
4364
|
}
|
|
4023
4365
|
|
|
4366
|
+
// src/validation/wildcardValidator.js
|
|
4367
|
+
function namespaceMatches(elementNamespace, wildcardNamespace, targetNamespace) {
|
|
4368
|
+
if (!wildcardNamespace) return true;
|
|
4369
|
+
if (wildcardNamespace === "##any") return true;
|
|
4370
|
+
if (wildcardNamespace === "##targetNamespace") {
|
|
4371
|
+
return elementNamespace === targetNamespace;
|
|
4372
|
+
}
|
|
4373
|
+
if (wildcardNamespace === "##other") {
|
|
4374
|
+
return elementNamespace !== targetNamespace;
|
|
4375
|
+
}
|
|
4376
|
+
if (Array.isArray(wildcardNamespace)) {
|
|
4377
|
+
return wildcardNamespace.includes(elementNamespace);
|
|
4378
|
+
}
|
|
4379
|
+
return elementNamespace === wildcardNamespace;
|
|
4380
|
+
}
|
|
4381
|
+
function isExcludedByNotNamespace(elementNamespace, notNamespace) {
|
|
4382
|
+
if (!notNamespace || notNamespace.length === 0) return false;
|
|
4383
|
+
return notNamespace.includes(elementNamespace);
|
|
4384
|
+
}
|
|
4385
|
+
function isExcludedByNotQName(qName, notQName) {
|
|
4386
|
+
if (!notQName || notQName.length === 0) return false;
|
|
4387
|
+
return notQName.includes(qName);
|
|
4388
|
+
}
|
|
4389
|
+
function buildQName(localName4, namespaceUri4) {
|
|
4390
|
+
if (!namespaceUri4 || namespaceUri4 === "") {
|
|
4391
|
+
return localName4;
|
|
4392
|
+
}
|
|
4393
|
+
return `{${namespaceUri4}}${localName4}`;
|
|
4394
|
+
}
|
|
4395
|
+
function elementMatchesWildcard(elementLocalName, elementNamespace, wildcardNode, targetNamespace) {
|
|
4396
|
+
if (!wildcardNode || wildcardNode.kind !== "any") {
|
|
4397
|
+
return false;
|
|
4398
|
+
}
|
|
4399
|
+
if (!namespaceMatches(elementNamespace, wildcardNode.namespace, targetNamespace)) {
|
|
4400
|
+
return false;
|
|
4401
|
+
}
|
|
4402
|
+
if (isExcludedByNotNamespace(elementNamespace, wildcardNode.notNamespace)) {
|
|
4403
|
+
return false;
|
|
4404
|
+
}
|
|
4405
|
+
const qName = buildQName(elementLocalName, elementNamespace);
|
|
4406
|
+
if (isExcludedByNotQName(qName, wildcardNode.notQName)) {
|
|
4407
|
+
return false;
|
|
4408
|
+
}
|
|
4409
|
+
return true;
|
|
4410
|
+
}
|
|
4411
|
+
function attributeMatchesWildcard(attrLocalName, attrNamespace, wildcardNode, targetNamespace) {
|
|
4412
|
+
if (!wildcardNode || wildcardNode.kind !== "anyAttribute") {
|
|
4413
|
+
return false;
|
|
4414
|
+
}
|
|
4415
|
+
if (!namespaceMatches(attrNamespace, wildcardNode.namespace, targetNamespace)) {
|
|
4416
|
+
return false;
|
|
4417
|
+
}
|
|
4418
|
+
if (isExcludedByNotNamespace(attrNamespace, wildcardNode.notNamespace)) {
|
|
4419
|
+
return false;
|
|
4420
|
+
}
|
|
4421
|
+
const qName = buildQName(attrLocalName, attrNamespace);
|
|
4422
|
+
if (isExcludedByNotQName(qName, wildcardNode.notQName)) {
|
|
4423
|
+
return false;
|
|
4424
|
+
}
|
|
4425
|
+
return true;
|
|
4426
|
+
}
|
|
4427
|
+
function normalizeProcessContents(processContents) {
|
|
4428
|
+
if (processContents === "lax" || processContents === "skip") {
|
|
4429
|
+
return processContents;
|
|
4430
|
+
}
|
|
4431
|
+
return "strict";
|
|
4432
|
+
}
|
|
4433
|
+
function isStrictWildcardValidation(processContents) {
|
|
4434
|
+
return normalizeProcessContents(processContents) === "strict";
|
|
4435
|
+
}
|
|
4436
|
+
function shouldSkipWildcardValidation(processContents) {
|
|
4437
|
+
return normalizeProcessContents(processContents) === "skip";
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4024
4440
|
// src/validation/structureValidator.js
|
|
4025
4441
|
function elementChildren2(xmlNode) {
|
|
4026
4442
|
return Array.from(xmlNode?.children || []).filter((child) => child.nodeType === 1);
|
|
@@ -4090,6 +4506,7 @@ function isSimpleContentComplexType(complexTypeDecl) {
|
|
|
4090
4506
|
function validateAttributes(xmlNode, attributes, context) {
|
|
4091
4507
|
const { schema, createIssue: createIssue2, ISSUE_CODES: ISSUE_CODES2, issues, pathParts, validateAttributeValue: validateAttributeValue2 } = context;
|
|
4092
4508
|
const allowed = /* @__PURE__ */ new Map();
|
|
4509
|
+
let anyAttributeWildcard = null;
|
|
4093
4510
|
for (const attr of attributes || []) {
|
|
4094
4511
|
if (!attr) continue;
|
|
4095
4512
|
if (attr.kind === "attribute") {
|
|
@@ -4101,6 +4518,8 @@ function validateAttributes(xmlNode, attributes, context) {
|
|
|
4101
4518
|
const group = context.resolveAttributeGroup?.(attr.refName);
|
|
4102
4519
|
if (!group) continue;
|
|
4103
4520
|
validateAttributes(xmlNode, group.attributes || [], context);
|
|
4521
|
+
} else if (attr.kind === "anyAttribute") {
|
|
4522
|
+
anyAttributeWildcard = attr;
|
|
4104
4523
|
}
|
|
4105
4524
|
}
|
|
4106
4525
|
for (const attrDecl of allowed.values()) {
|
|
@@ -4146,6 +4565,15 @@ function validateAttributes(xmlNode, attributes, context) {
|
|
|
4146
4565
|
continue;
|
|
4147
4566
|
}
|
|
4148
4567
|
if (!allowed.has(attr.name)) {
|
|
4568
|
+
if (anyAttributeWildcard) {
|
|
4569
|
+
const attrLocalName = attr.localName || attr.name.split(":")[1] || attr.name;
|
|
4570
|
+
const attrNamespace = attr.namespaceURI || null;
|
|
4571
|
+
if (attributeMatchesWildcard(attrLocalName, attrNamespace, anyAttributeWildcard, schema.targetNamespace)) {
|
|
4572
|
+
if (!shouldSkipWildcardValidation(anyAttributeWildcard.processContents)) {
|
|
4573
|
+
}
|
|
4574
|
+
continue;
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4149
4577
|
issues.push(
|
|
4150
4578
|
createIssue2({
|
|
4151
4579
|
code: ISSUE_CODES2.XML_UNEXPECTED_ATTRIBUTE,
|
|
@@ -4563,7 +4991,16 @@ function validateContentModel(children, modelNode, context, pathParts, startInde
|
|
|
4563
4991
|
return validateAll(children, startIndex, modelNode, context, pathParts, silent);
|
|
4564
4992
|
case "any":
|
|
4565
4993
|
if (startIndex < children.length) {
|
|
4566
|
-
|
|
4994
|
+
const childNode = children[startIndex];
|
|
4995
|
+
const childLocalName = localName(childNode);
|
|
4996
|
+
const childNamespace = namespaceUri(childNode);
|
|
4997
|
+
if (elementMatchesWildcard(childLocalName, childNamespace, modelNode, context.schema.targetNamespace)) {
|
|
4998
|
+
if (isStrictWildcardValidation(modelNode.processContents)) {
|
|
4999
|
+
validateElementDecl(childNode, { name: childLocalName, typeName: null }, context, [...pathParts, childLocalName]);
|
|
5000
|
+
} else if (!shouldSkipWildcardValidation(modelNode.processContents)) {
|
|
5001
|
+
}
|
|
5002
|
+
return { nextIndex: startIndex + 1, matched: true, matchedAny: true };
|
|
5003
|
+
}
|
|
4567
5004
|
}
|
|
4568
5005
|
return { nextIndex: startIndex, matched: true, matchedAny: false };
|
|
4569
5006
|
default:
|