uss-xsd-engine 0.1.0-rc.2 → 0.1.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/dist/uss-xsd-engine.esm.js +239 -9
- package/dist/uss-xsd-engine.standalone.js +239 -9
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* uss-xsd-engine v0.1.
|
|
2
|
+
* uss-xsd-engine v0.1.1
|
|
3
3
|
* (c) 2026 Bernard Mumble
|
|
4
4
|
* MIT License
|
|
5
5
|
*/
|
|
@@ -583,6 +583,33 @@ function lookupInImportedSchemas(bucketName, schema, namespaceUri3, localName3,
|
|
|
583
583
|
}
|
|
584
584
|
return null;
|
|
585
585
|
}
|
|
586
|
+
function lookupInImportedSchemasForUnprefixed(bucketName, schema, localName3, visited = /* @__PURE__ */ new Set()) {
|
|
587
|
+
if (!schema || !localName3) return null;
|
|
588
|
+
if (visited.has(schema)) return null;
|
|
589
|
+
visited.add(schema);
|
|
590
|
+
const targetNs = schema.targetNamespace || null;
|
|
591
|
+
for (const importedSchema of schema.importedSchemas || []) {
|
|
592
|
+
if (!importedSchema) continue;
|
|
593
|
+
const importedNs = importedSchema.targetNamespace || null;
|
|
594
|
+
if (importedNs === targetNs) {
|
|
595
|
+
const direct = lookupInSchemaBucket(
|
|
596
|
+
bucketName,
|
|
597
|
+
importedSchema,
|
|
598
|
+
importedNs,
|
|
599
|
+
localName3
|
|
600
|
+
);
|
|
601
|
+
if (direct) return direct;
|
|
602
|
+
}
|
|
603
|
+
const nested = lookupInImportedSchemasForUnprefixed(
|
|
604
|
+
bucketName,
|
|
605
|
+
importedSchema,
|
|
606
|
+
localName3,
|
|
607
|
+
visited
|
|
608
|
+
);
|
|
609
|
+
if (nested) return nested;
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
586
613
|
function lookupByQName(bucketName, schema, name) {
|
|
587
614
|
if (!schema || !name) return null;
|
|
588
615
|
const parsed = parseQName(name);
|
|
@@ -615,6 +642,12 @@ function lookupByQName(bucketName, schema, name) {
|
|
|
615
642
|
localName3
|
|
616
643
|
);
|
|
617
644
|
if (hostNamespaceDecl) return hostNamespaceDecl;
|
|
645
|
+
const importedSameNamespaceDecl = lookupInImportedSchemasForUnprefixed(
|
|
646
|
+
bucketName,
|
|
647
|
+
schema,
|
|
648
|
+
localName3
|
|
649
|
+
);
|
|
650
|
+
if (importedSameNamespaceDecl) return importedSameNamespaceDecl;
|
|
618
651
|
}
|
|
619
652
|
return null;
|
|
620
653
|
}
|
|
@@ -1542,6 +1575,111 @@ function isImportNamespaceCompatible(ref, importedSchema) {
|
|
|
1542
1575
|
}
|
|
1543
1576
|
return (declaredNs || null) === (importedNs || null);
|
|
1544
1577
|
}
|
|
1578
|
+
function normalizeSchemaPath(value) {
|
|
1579
|
+
if (!value || typeof value !== "string") return null;
|
|
1580
|
+
const trimmed = value.trim();
|
|
1581
|
+
if (!trimmed) return null;
|
|
1582
|
+
return trimmed.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
1583
|
+
}
|
|
1584
|
+
function getSchemaPathBasename(value) {
|
|
1585
|
+
const normalized = normalizeSchemaPath(value);
|
|
1586
|
+
if (!normalized) return null;
|
|
1587
|
+
const parts = normalized.split("/");
|
|
1588
|
+
return parts[parts.length - 1] || null;
|
|
1589
|
+
}
|
|
1590
|
+
function getExternalDocumentEntries(externalDocuments) {
|
|
1591
|
+
return Object.entries(externalDocuments || {}).map(([key, text]) => ({
|
|
1592
|
+
key,
|
|
1593
|
+
normalizedKey: normalizeSchemaPath(key),
|
|
1594
|
+
basename: getSchemaPathBasename(key),
|
|
1595
|
+
text
|
|
1596
|
+
}));
|
|
1597
|
+
}
|
|
1598
|
+
function getDeclaredTargetNamespaceFromText(xsdText) {
|
|
1599
|
+
if (typeof xsdText !== "string" || !xsdText.trim()) {
|
|
1600
|
+
return null;
|
|
1601
|
+
}
|
|
1602
|
+
try {
|
|
1603
|
+
const parser = new DOMParser();
|
|
1604
|
+
const doc = parser.parseFromString(xsdText, "application/xml");
|
|
1605
|
+
const parserError = doc.querySelector("parsererror");
|
|
1606
|
+
if (parserError) return null;
|
|
1607
|
+
const root = getSchemaRoot(doc);
|
|
1608
|
+
if (!root) return null;
|
|
1609
|
+
return root.hasAttribute("targetNamespace") ? root.getAttribute("targetNamespace") : null;
|
|
1610
|
+
} catch {
|
|
1611
|
+
return null;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
function resolveExternalDocument(ref, externalDocuments) {
|
|
1615
|
+
const entries = getExternalDocumentEntries(externalDocuments);
|
|
1616
|
+
const requestedLocation = ref?.schemaLocation || null;
|
|
1617
|
+
const normalizedRequestedLocation = normalizeSchemaPath(requestedLocation);
|
|
1618
|
+
const requestedBasename = getSchemaPathBasename(requestedLocation);
|
|
1619
|
+
if (requestedLocation && Object.prototype.hasOwnProperty.call(externalDocuments, requestedLocation)) {
|
|
1620
|
+
return {
|
|
1621
|
+
kind: "exact",
|
|
1622
|
+
entry: {
|
|
1623
|
+
key: requestedLocation,
|
|
1624
|
+
normalizedKey: normalizedRequestedLocation,
|
|
1625
|
+
basename: requestedBasename,
|
|
1626
|
+
text: externalDocuments[requestedLocation]
|
|
1627
|
+
},
|
|
1628
|
+
matches: []
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
if (normalizedRequestedLocation) {
|
|
1632
|
+
const normalizedMatches = entries.filter(
|
|
1633
|
+
(entry) => entry.normalizedKey === normalizedRequestedLocation
|
|
1634
|
+
);
|
|
1635
|
+
if (normalizedMatches.length === 1) {
|
|
1636
|
+
return {
|
|
1637
|
+
kind: "normalized",
|
|
1638
|
+
entry: normalizedMatches[0],
|
|
1639
|
+
matches: normalizedMatches
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (requestedBasename) {
|
|
1644
|
+
const basenameMatches = entries.filter(
|
|
1645
|
+
(entry) => entry.basename === requestedBasename
|
|
1646
|
+
);
|
|
1647
|
+
if (basenameMatches.length === 1) {
|
|
1648
|
+
return {
|
|
1649
|
+
kind: "basename",
|
|
1650
|
+
entry: basenameMatches[0],
|
|
1651
|
+
matches: basenameMatches
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
if (ref?.kind === "import" && ref.namespace) {
|
|
1656
|
+
const namespaceMatches = entries.filter((entry) => {
|
|
1657
|
+
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText(
|
|
1658
|
+
entry.text
|
|
1659
|
+
);
|
|
1660
|
+
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
1661
|
+
});
|
|
1662
|
+
if (namespaceMatches.length === 1) {
|
|
1663
|
+
return {
|
|
1664
|
+
kind: "namespace",
|
|
1665
|
+
entry: namespaceMatches[0],
|
|
1666
|
+
matches: namespaceMatches
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
if (namespaceMatches.length > 1) {
|
|
1670
|
+
return {
|
|
1671
|
+
kind: "ambiguous-namespace",
|
|
1672
|
+
entry: null,
|
|
1673
|
+
matches: namespaceMatches
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
return {
|
|
1678
|
+
kind: "not-found",
|
|
1679
|
+
entry: null,
|
|
1680
|
+
matches: []
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1545
1683
|
function buildSchemaModel(doc, options = {}) {
|
|
1546
1684
|
const issues = [];
|
|
1547
1685
|
const schema = createEmptySchemaModel();
|
|
@@ -1740,11 +1878,34 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1740
1878
|
const imports = schema.externalRefs.imports || [];
|
|
1741
1879
|
for (const ref of [...includes, ...imports]) {
|
|
1742
1880
|
if (!ref.schemaLocation) continue;
|
|
1743
|
-
const
|
|
1881
|
+
const resolution = resolveExternalDocument(ref, externalDocuments);
|
|
1882
|
+
if (resolution.kind === "ambiguous-namespace") {
|
|
1883
|
+
issues.push(
|
|
1884
|
+
createIssue({
|
|
1885
|
+
code: ISSUE_CODES.XSD_IMPORT_NOT_PROVIDED,
|
|
1886
|
+
severity: "error",
|
|
1887
|
+
message: `Multiple provided schemas match xs:import namespace '${ref.namespace || ""}'. Unable to determine which schema to use.`,
|
|
1888
|
+
line: ref.line,
|
|
1889
|
+
column: ref.column,
|
|
1890
|
+
path: ref.path,
|
|
1891
|
+
source: "xsd",
|
|
1892
|
+
nodeKind: ref.kind,
|
|
1893
|
+
details: {
|
|
1894
|
+
schemaLocation: ref.schemaLocation,
|
|
1895
|
+
namespace: ref.namespace,
|
|
1896
|
+
matchingKeys: resolution.matches.map((match) => match.key)
|
|
1897
|
+
}
|
|
1898
|
+
})
|
|
1899
|
+
);
|
|
1900
|
+
continue;
|
|
1901
|
+
}
|
|
1902
|
+
const resolvedEntry = resolution.entry;
|
|
1903
|
+
const resolvedSchemaKey = resolvedEntry?.key || ref.schemaLocation;
|
|
1904
|
+
const visitKey = ref.kind === "include" && schema.targetNamespace ? `${resolvedSchemaKey}::include::${schema.targetNamespace}` : resolvedSchemaKey;
|
|
1744
1905
|
if (visited.has(visitKey)) {
|
|
1745
1906
|
continue;
|
|
1746
1907
|
}
|
|
1747
|
-
const externalXsdText =
|
|
1908
|
+
const externalXsdText = resolvedEntry?.text || null;
|
|
1748
1909
|
if (!externalXsdText) {
|
|
1749
1910
|
issues.push(
|
|
1750
1911
|
createIssue({
|
|
@@ -1787,9 +1948,6 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1787
1948
|
...shouldApplyChameleonInclude ? { _overrideTargetNamespace: schema.targetNamespace } : {}
|
|
1788
1949
|
});
|
|
1789
1950
|
if (externalBuild.schema) {
|
|
1790
|
-
if (ref.kind === "import") {
|
|
1791
|
-
schema.importedSchemas.push(externalBuild.schema);
|
|
1792
|
-
}
|
|
1793
1951
|
if (ref.kind === "include") {
|
|
1794
1952
|
if (!isIncludeNamespaceCompatible(schema, externalBuild.schema)) {
|
|
1795
1953
|
issues.push(
|
|
@@ -1804,6 +1962,8 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1804
1962
|
nodeKind: "include",
|
|
1805
1963
|
details: {
|
|
1806
1964
|
schemaLocation: ref.schemaLocation,
|
|
1965
|
+
resolvedSchemaKey,
|
|
1966
|
+
resolutionKind: resolution.kind,
|
|
1807
1967
|
hostTargetNamespace: schema.targetNamespace || null,
|
|
1808
1968
|
includedTargetNamespace: externalBuild.schema.targetNamespace || null,
|
|
1809
1969
|
includedDeclaredTargetNamespace: externalDeclaredTargetNamespace,
|
|
@@ -1833,6 +1993,8 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1833
1993
|
nodeKind: "import",
|
|
1834
1994
|
details: {
|
|
1835
1995
|
schemaLocation: ref.schemaLocation,
|
|
1996
|
+
resolvedSchemaKey,
|
|
1997
|
+
resolutionKind: resolution.kind,
|
|
1836
1998
|
declaredNamespace: ref.namespace || null,
|
|
1837
1999
|
importedTargetNamespace: externalBuild.schema.targetNamespace || null
|
|
1838
2000
|
}
|
|
@@ -2171,12 +2333,72 @@ function runDefaultFixedDiagnostics(schema) {
|
|
|
2171
2333
|
}
|
|
2172
2334
|
|
|
2173
2335
|
// src/diagnostics/schemaImportDiagnostics.js
|
|
2336
|
+
function normalizeSchemaPath2(value) {
|
|
2337
|
+
if (!value || typeof value !== "string") return null;
|
|
2338
|
+
const trimmed = value.trim();
|
|
2339
|
+
if (!trimmed) return null;
|
|
2340
|
+
return trimmed.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
2341
|
+
}
|
|
2342
|
+
function getSchemaPathBasename2(value) {
|
|
2343
|
+
const normalized = normalizeSchemaPath2(value);
|
|
2344
|
+
if (!normalized) return null;
|
|
2345
|
+
const parts = normalized.split("/");
|
|
2346
|
+
return parts[parts.length - 1] || null;
|
|
2347
|
+
}
|
|
2348
|
+
function getExternalDocumentEntries2(externalDocuments) {
|
|
2349
|
+
return Object.entries(externalDocuments || {}).map(([key, text]) => ({
|
|
2350
|
+
key,
|
|
2351
|
+
normalizedKey: normalizeSchemaPath2(key),
|
|
2352
|
+
basename: getSchemaPathBasename2(key),
|
|
2353
|
+
text
|
|
2354
|
+
}));
|
|
2355
|
+
}
|
|
2356
|
+
function getDeclaredTargetNamespaceFromText2(xsdText) {
|
|
2357
|
+
if (typeof xsdText !== "string" || !xsdText.trim()) {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
try {
|
|
2361
|
+
const parser = new DOMParser();
|
|
2362
|
+
const doc = parser.parseFromString(xsdText, "application/xml");
|
|
2363
|
+
const parserError = doc.querySelector("parsererror");
|
|
2364
|
+
if (parserError) return null;
|
|
2365
|
+
const root = doc?.documentElement || null;
|
|
2366
|
+
if (!root || root.localName !== "schema") return null;
|
|
2367
|
+
return root.hasAttribute("targetNamespace") ? root.getAttribute("targetNamespace") : null;
|
|
2368
|
+
} catch {
|
|
2369
|
+
return null;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
function hasMatchingExternalDocument(ref, externalDocuments) {
|
|
2373
|
+
const entries = getExternalDocumentEntries2(externalDocuments);
|
|
2374
|
+
const requestedLocation = ref?.schemaLocation || null;
|
|
2375
|
+
const normalizedRequestedLocation = normalizeSchemaPath2(requestedLocation);
|
|
2376
|
+
const requestedBasename = getSchemaPathBasename2(requestedLocation);
|
|
2377
|
+
if (requestedLocation && Object.prototype.hasOwnProperty.call(externalDocuments, requestedLocation)) {
|
|
2378
|
+
return true;
|
|
2379
|
+
}
|
|
2380
|
+
if (normalizedRequestedLocation && entries.some((entry) => entry.normalizedKey === normalizedRequestedLocation)) {
|
|
2381
|
+
return true;
|
|
2382
|
+
}
|
|
2383
|
+
if (requestedBasename && entries.some((entry) => entry.basename === requestedBasename)) {
|
|
2384
|
+
return true;
|
|
2385
|
+
}
|
|
2386
|
+
if (ref?.kind === "import" && ref.namespace) {
|
|
2387
|
+
return entries.some((entry) => {
|
|
2388
|
+
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText2(
|
|
2389
|
+
entry.text
|
|
2390
|
+
);
|
|
2391
|
+
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
return false;
|
|
2395
|
+
}
|
|
2174
2396
|
function runImportDiagnostics(schema, options = {}) {
|
|
2175
2397
|
const issues = [];
|
|
2176
2398
|
const externalDocuments = options.externalDocuments || {};
|
|
2177
2399
|
for (const ref of schema.externalRefs.includes || []) {
|
|
2178
2400
|
if (!ref.schemaLocation) continue;
|
|
2179
|
-
if (externalDocuments
|
|
2401
|
+
if (hasMatchingExternalDocument(ref, externalDocuments)) continue;
|
|
2180
2402
|
issues.push(
|
|
2181
2403
|
createIssue({
|
|
2182
2404
|
code: ISSUE_CODES.XSD_INCLUDE_NOT_PROVIDED,
|
|
@@ -2196,7 +2418,7 @@ function runImportDiagnostics(schema, options = {}) {
|
|
|
2196
2418
|
}
|
|
2197
2419
|
for (const ref of schema.externalRefs.imports || []) {
|
|
2198
2420
|
if (!ref.schemaLocation) continue;
|
|
2199
|
-
if (externalDocuments
|
|
2421
|
+
if (hasMatchingExternalDocument(ref, externalDocuments)) continue;
|
|
2200
2422
|
issues.push(
|
|
2201
2423
|
createIssue({
|
|
2202
2424
|
code: ISSUE_CODES.XSD_IMPORT_NOT_PROVIDED,
|
|
@@ -2407,7 +2629,7 @@ function runSchemaDiagnostics(schema, options = {}) {
|
|
|
2407
2629
|
}
|
|
2408
2630
|
|
|
2409
2631
|
// src/version.js
|
|
2410
|
-
var ENGINE_VERSION = "v0.1.
|
|
2632
|
+
var ENGINE_VERSION = "v0.1.1";
|
|
2411
2633
|
|
|
2412
2634
|
// src/utils/result.js
|
|
2413
2635
|
function summarizeIssues(issues = []) {
|
|
@@ -3404,6 +3626,14 @@ function generateXmlFromSchema(schema, options = {}, helpers = {}) {
|
|
|
3404
3626
|
state,
|
|
3405
3627
|
true
|
|
3406
3628
|
);
|
|
3629
|
+
if (rootNode) {
|
|
3630
|
+
const finalNsAttrs = buildRootNamespaceAttributes(schema, root, state);
|
|
3631
|
+
rootNode.attributes = {
|
|
3632
|
+
...finalNsAttrs,
|
|
3633
|
+
...rootNode.attributes
|
|
3634
|
+
// preserve any existing attrs
|
|
3635
|
+
};
|
|
3636
|
+
}
|
|
3407
3637
|
return {
|
|
3408
3638
|
rootElementName: root.name,
|
|
3409
3639
|
rootNode
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* uss-xsd-engine v0.1.
|
|
2
|
+
* uss-xsd-engine v0.1.1
|
|
3
3
|
* (c) 2026 Bernard Mumble
|
|
4
4
|
* MIT License
|
|
5
5
|
*/
|
|
@@ -610,6 +610,33 @@ var UssXsdEngine = (() => {
|
|
|
610
610
|
}
|
|
611
611
|
return null;
|
|
612
612
|
}
|
|
613
|
+
function lookupInImportedSchemasForUnprefixed(bucketName, schema, localName3, visited = /* @__PURE__ */ new Set()) {
|
|
614
|
+
if (!schema || !localName3) return null;
|
|
615
|
+
if (visited.has(schema)) return null;
|
|
616
|
+
visited.add(schema);
|
|
617
|
+
const targetNs = schema.targetNamespace || null;
|
|
618
|
+
for (const importedSchema of schema.importedSchemas || []) {
|
|
619
|
+
if (!importedSchema) continue;
|
|
620
|
+
const importedNs = importedSchema.targetNamespace || null;
|
|
621
|
+
if (importedNs === targetNs) {
|
|
622
|
+
const direct = lookupInSchemaBucket(
|
|
623
|
+
bucketName,
|
|
624
|
+
importedSchema,
|
|
625
|
+
importedNs,
|
|
626
|
+
localName3
|
|
627
|
+
);
|
|
628
|
+
if (direct) return direct;
|
|
629
|
+
}
|
|
630
|
+
const nested = lookupInImportedSchemasForUnprefixed(
|
|
631
|
+
bucketName,
|
|
632
|
+
importedSchema,
|
|
633
|
+
localName3,
|
|
634
|
+
visited
|
|
635
|
+
);
|
|
636
|
+
if (nested) return nested;
|
|
637
|
+
}
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
613
640
|
function lookupByQName(bucketName, schema, name) {
|
|
614
641
|
if (!schema || !name) return null;
|
|
615
642
|
const parsed = parseQName(name);
|
|
@@ -642,6 +669,12 @@ var UssXsdEngine = (() => {
|
|
|
642
669
|
localName3
|
|
643
670
|
);
|
|
644
671
|
if (hostNamespaceDecl) return hostNamespaceDecl;
|
|
672
|
+
const importedSameNamespaceDecl = lookupInImportedSchemasForUnprefixed(
|
|
673
|
+
bucketName,
|
|
674
|
+
schema,
|
|
675
|
+
localName3
|
|
676
|
+
);
|
|
677
|
+
if (importedSameNamespaceDecl) return importedSameNamespaceDecl;
|
|
645
678
|
}
|
|
646
679
|
return null;
|
|
647
680
|
}
|
|
@@ -1569,6 +1602,111 @@ var UssXsdEngine = (() => {
|
|
|
1569
1602
|
}
|
|
1570
1603
|
return (declaredNs || null) === (importedNs || null);
|
|
1571
1604
|
}
|
|
1605
|
+
function normalizeSchemaPath(value) {
|
|
1606
|
+
if (!value || typeof value !== "string") return null;
|
|
1607
|
+
const trimmed = value.trim();
|
|
1608
|
+
if (!trimmed) return null;
|
|
1609
|
+
return trimmed.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
1610
|
+
}
|
|
1611
|
+
function getSchemaPathBasename(value) {
|
|
1612
|
+
const normalized = normalizeSchemaPath(value);
|
|
1613
|
+
if (!normalized) return null;
|
|
1614
|
+
const parts = normalized.split("/");
|
|
1615
|
+
return parts[parts.length - 1] || null;
|
|
1616
|
+
}
|
|
1617
|
+
function getExternalDocumentEntries(externalDocuments) {
|
|
1618
|
+
return Object.entries(externalDocuments || {}).map(([key, text]) => ({
|
|
1619
|
+
key,
|
|
1620
|
+
normalizedKey: normalizeSchemaPath(key),
|
|
1621
|
+
basename: getSchemaPathBasename(key),
|
|
1622
|
+
text
|
|
1623
|
+
}));
|
|
1624
|
+
}
|
|
1625
|
+
function getDeclaredTargetNamespaceFromText(xsdText) {
|
|
1626
|
+
if (typeof xsdText !== "string" || !xsdText.trim()) {
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
try {
|
|
1630
|
+
const parser = new DOMParser();
|
|
1631
|
+
const doc = parser.parseFromString(xsdText, "application/xml");
|
|
1632
|
+
const parserError = doc.querySelector("parsererror");
|
|
1633
|
+
if (parserError) return null;
|
|
1634
|
+
const root = getSchemaRoot(doc);
|
|
1635
|
+
if (!root) return null;
|
|
1636
|
+
return root.hasAttribute("targetNamespace") ? root.getAttribute("targetNamespace") : null;
|
|
1637
|
+
} catch {
|
|
1638
|
+
return null;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
function resolveExternalDocument(ref, externalDocuments) {
|
|
1642
|
+
const entries = getExternalDocumentEntries(externalDocuments);
|
|
1643
|
+
const requestedLocation = ref?.schemaLocation || null;
|
|
1644
|
+
const normalizedRequestedLocation = normalizeSchemaPath(requestedLocation);
|
|
1645
|
+
const requestedBasename = getSchemaPathBasename(requestedLocation);
|
|
1646
|
+
if (requestedLocation && Object.prototype.hasOwnProperty.call(externalDocuments, requestedLocation)) {
|
|
1647
|
+
return {
|
|
1648
|
+
kind: "exact",
|
|
1649
|
+
entry: {
|
|
1650
|
+
key: requestedLocation,
|
|
1651
|
+
normalizedKey: normalizedRequestedLocation,
|
|
1652
|
+
basename: requestedBasename,
|
|
1653
|
+
text: externalDocuments[requestedLocation]
|
|
1654
|
+
},
|
|
1655
|
+
matches: []
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
if (normalizedRequestedLocation) {
|
|
1659
|
+
const normalizedMatches = entries.filter(
|
|
1660
|
+
(entry) => entry.normalizedKey === normalizedRequestedLocation
|
|
1661
|
+
);
|
|
1662
|
+
if (normalizedMatches.length === 1) {
|
|
1663
|
+
return {
|
|
1664
|
+
kind: "normalized",
|
|
1665
|
+
entry: normalizedMatches[0],
|
|
1666
|
+
matches: normalizedMatches
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (requestedBasename) {
|
|
1671
|
+
const basenameMatches = entries.filter(
|
|
1672
|
+
(entry) => entry.basename === requestedBasename
|
|
1673
|
+
);
|
|
1674
|
+
if (basenameMatches.length === 1) {
|
|
1675
|
+
return {
|
|
1676
|
+
kind: "basename",
|
|
1677
|
+
entry: basenameMatches[0],
|
|
1678
|
+
matches: basenameMatches
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
if (ref?.kind === "import" && ref.namespace) {
|
|
1683
|
+
const namespaceMatches = entries.filter((entry) => {
|
|
1684
|
+
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText(
|
|
1685
|
+
entry.text
|
|
1686
|
+
);
|
|
1687
|
+
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
1688
|
+
});
|
|
1689
|
+
if (namespaceMatches.length === 1) {
|
|
1690
|
+
return {
|
|
1691
|
+
kind: "namespace",
|
|
1692
|
+
entry: namespaceMatches[0],
|
|
1693
|
+
matches: namespaceMatches
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
if (namespaceMatches.length > 1) {
|
|
1697
|
+
return {
|
|
1698
|
+
kind: "ambiguous-namespace",
|
|
1699
|
+
entry: null,
|
|
1700
|
+
matches: namespaceMatches
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
return {
|
|
1705
|
+
kind: "not-found",
|
|
1706
|
+
entry: null,
|
|
1707
|
+
matches: []
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1572
1710
|
function buildSchemaModel(doc, options = {}) {
|
|
1573
1711
|
const issues = [];
|
|
1574
1712
|
const schema = createEmptySchemaModel();
|
|
@@ -1767,11 +1905,34 @@ var UssXsdEngine = (() => {
|
|
|
1767
1905
|
const imports = schema.externalRefs.imports || [];
|
|
1768
1906
|
for (const ref of [...includes, ...imports]) {
|
|
1769
1907
|
if (!ref.schemaLocation) continue;
|
|
1770
|
-
const
|
|
1908
|
+
const resolution = resolveExternalDocument(ref, externalDocuments);
|
|
1909
|
+
if (resolution.kind === "ambiguous-namespace") {
|
|
1910
|
+
issues.push(
|
|
1911
|
+
createIssue({
|
|
1912
|
+
code: ISSUE_CODES.XSD_IMPORT_NOT_PROVIDED,
|
|
1913
|
+
severity: "error",
|
|
1914
|
+
message: `Multiple provided schemas match xs:import namespace '${ref.namespace || ""}'. Unable to determine which schema to use.`,
|
|
1915
|
+
line: ref.line,
|
|
1916
|
+
column: ref.column,
|
|
1917
|
+
path: ref.path,
|
|
1918
|
+
source: "xsd",
|
|
1919
|
+
nodeKind: ref.kind,
|
|
1920
|
+
details: {
|
|
1921
|
+
schemaLocation: ref.schemaLocation,
|
|
1922
|
+
namespace: ref.namespace,
|
|
1923
|
+
matchingKeys: resolution.matches.map((match) => match.key)
|
|
1924
|
+
}
|
|
1925
|
+
})
|
|
1926
|
+
);
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
const resolvedEntry = resolution.entry;
|
|
1930
|
+
const resolvedSchemaKey = resolvedEntry?.key || ref.schemaLocation;
|
|
1931
|
+
const visitKey = ref.kind === "include" && schema.targetNamespace ? `${resolvedSchemaKey}::include::${schema.targetNamespace}` : resolvedSchemaKey;
|
|
1771
1932
|
if (visited.has(visitKey)) {
|
|
1772
1933
|
continue;
|
|
1773
1934
|
}
|
|
1774
|
-
const externalXsdText =
|
|
1935
|
+
const externalXsdText = resolvedEntry?.text || null;
|
|
1775
1936
|
if (!externalXsdText) {
|
|
1776
1937
|
issues.push(
|
|
1777
1938
|
createIssue({
|
|
@@ -1814,9 +1975,6 @@ var UssXsdEngine = (() => {
|
|
|
1814
1975
|
...shouldApplyChameleonInclude ? { _overrideTargetNamespace: schema.targetNamespace } : {}
|
|
1815
1976
|
});
|
|
1816
1977
|
if (externalBuild.schema) {
|
|
1817
|
-
if (ref.kind === "import") {
|
|
1818
|
-
schema.importedSchemas.push(externalBuild.schema);
|
|
1819
|
-
}
|
|
1820
1978
|
if (ref.kind === "include") {
|
|
1821
1979
|
if (!isIncludeNamespaceCompatible(schema, externalBuild.schema)) {
|
|
1822
1980
|
issues.push(
|
|
@@ -1831,6 +1989,8 @@ var UssXsdEngine = (() => {
|
|
|
1831
1989
|
nodeKind: "include",
|
|
1832
1990
|
details: {
|
|
1833
1991
|
schemaLocation: ref.schemaLocation,
|
|
1992
|
+
resolvedSchemaKey,
|
|
1993
|
+
resolutionKind: resolution.kind,
|
|
1834
1994
|
hostTargetNamespace: schema.targetNamespace || null,
|
|
1835
1995
|
includedTargetNamespace: externalBuild.schema.targetNamespace || null,
|
|
1836
1996
|
includedDeclaredTargetNamespace: externalDeclaredTargetNamespace,
|
|
@@ -1860,6 +2020,8 @@ var UssXsdEngine = (() => {
|
|
|
1860
2020
|
nodeKind: "import",
|
|
1861
2021
|
details: {
|
|
1862
2022
|
schemaLocation: ref.schemaLocation,
|
|
2023
|
+
resolvedSchemaKey,
|
|
2024
|
+
resolutionKind: resolution.kind,
|
|
1863
2025
|
declaredNamespace: ref.namespace || null,
|
|
1864
2026
|
importedTargetNamespace: externalBuild.schema.targetNamespace || null
|
|
1865
2027
|
}
|
|
@@ -2198,12 +2360,72 @@ var UssXsdEngine = (() => {
|
|
|
2198
2360
|
}
|
|
2199
2361
|
|
|
2200
2362
|
// src/diagnostics/schemaImportDiagnostics.js
|
|
2363
|
+
function normalizeSchemaPath2(value) {
|
|
2364
|
+
if (!value || typeof value !== "string") return null;
|
|
2365
|
+
const trimmed = value.trim();
|
|
2366
|
+
if (!trimmed) return null;
|
|
2367
|
+
return trimmed.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
2368
|
+
}
|
|
2369
|
+
function getSchemaPathBasename2(value) {
|
|
2370
|
+
const normalized = normalizeSchemaPath2(value);
|
|
2371
|
+
if (!normalized) return null;
|
|
2372
|
+
const parts = normalized.split("/");
|
|
2373
|
+
return parts[parts.length - 1] || null;
|
|
2374
|
+
}
|
|
2375
|
+
function getExternalDocumentEntries2(externalDocuments) {
|
|
2376
|
+
return Object.entries(externalDocuments || {}).map(([key, text]) => ({
|
|
2377
|
+
key,
|
|
2378
|
+
normalizedKey: normalizeSchemaPath2(key),
|
|
2379
|
+
basename: getSchemaPathBasename2(key),
|
|
2380
|
+
text
|
|
2381
|
+
}));
|
|
2382
|
+
}
|
|
2383
|
+
function getDeclaredTargetNamespaceFromText2(xsdText) {
|
|
2384
|
+
if (typeof xsdText !== "string" || !xsdText.trim()) {
|
|
2385
|
+
return null;
|
|
2386
|
+
}
|
|
2387
|
+
try {
|
|
2388
|
+
const parser = new DOMParser();
|
|
2389
|
+
const doc = parser.parseFromString(xsdText, "application/xml");
|
|
2390
|
+
const parserError = doc.querySelector("parsererror");
|
|
2391
|
+
if (parserError) return null;
|
|
2392
|
+
const root = doc?.documentElement || null;
|
|
2393
|
+
if (!root || root.localName !== "schema") return null;
|
|
2394
|
+
return root.hasAttribute("targetNamespace") ? root.getAttribute("targetNamespace") : null;
|
|
2395
|
+
} catch {
|
|
2396
|
+
return null;
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
function hasMatchingExternalDocument(ref, externalDocuments) {
|
|
2400
|
+
const entries = getExternalDocumentEntries2(externalDocuments);
|
|
2401
|
+
const requestedLocation = ref?.schemaLocation || null;
|
|
2402
|
+
const normalizedRequestedLocation = normalizeSchemaPath2(requestedLocation);
|
|
2403
|
+
const requestedBasename = getSchemaPathBasename2(requestedLocation);
|
|
2404
|
+
if (requestedLocation && Object.prototype.hasOwnProperty.call(externalDocuments, requestedLocation)) {
|
|
2405
|
+
return true;
|
|
2406
|
+
}
|
|
2407
|
+
if (normalizedRequestedLocation && entries.some((entry) => entry.normalizedKey === normalizedRequestedLocation)) {
|
|
2408
|
+
return true;
|
|
2409
|
+
}
|
|
2410
|
+
if (requestedBasename && entries.some((entry) => entry.basename === requestedBasename)) {
|
|
2411
|
+
return true;
|
|
2412
|
+
}
|
|
2413
|
+
if (ref?.kind === "import" && ref.namespace) {
|
|
2414
|
+
return entries.some((entry) => {
|
|
2415
|
+
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText2(
|
|
2416
|
+
entry.text
|
|
2417
|
+
);
|
|
2418
|
+
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
return false;
|
|
2422
|
+
}
|
|
2201
2423
|
function runImportDiagnostics(schema, options = {}) {
|
|
2202
2424
|
const issues = [];
|
|
2203
2425
|
const externalDocuments = options.externalDocuments || {};
|
|
2204
2426
|
for (const ref of schema.externalRefs.includes || []) {
|
|
2205
2427
|
if (!ref.schemaLocation) continue;
|
|
2206
|
-
if (externalDocuments
|
|
2428
|
+
if (hasMatchingExternalDocument(ref, externalDocuments)) continue;
|
|
2207
2429
|
issues.push(
|
|
2208
2430
|
createIssue({
|
|
2209
2431
|
code: ISSUE_CODES.XSD_INCLUDE_NOT_PROVIDED,
|
|
@@ -2223,7 +2445,7 @@ var UssXsdEngine = (() => {
|
|
|
2223
2445
|
}
|
|
2224
2446
|
for (const ref of schema.externalRefs.imports || []) {
|
|
2225
2447
|
if (!ref.schemaLocation) continue;
|
|
2226
|
-
if (externalDocuments
|
|
2448
|
+
if (hasMatchingExternalDocument(ref, externalDocuments)) continue;
|
|
2227
2449
|
issues.push(
|
|
2228
2450
|
createIssue({
|
|
2229
2451
|
code: ISSUE_CODES.XSD_IMPORT_NOT_PROVIDED,
|
|
@@ -2434,7 +2656,7 @@ var UssXsdEngine = (() => {
|
|
|
2434
2656
|
}
|
|
2435
2657
|
|
|
2436
2658
|
// src/version.js
|
|
2437
|
-
var ENGINE_VERSION = "v0.1.
|
|
2659
|
+
var ENGINE_VERSION = "v0.1.1";
|
|
2438
2660
|
|
|
2439
2661
|
// src/utils/result.js
|
|
2440
2662
|
function summarizeIssues(issues = []) {
|
|
@@ -3431,6 +3653,14 @@ var UssXsdEngine = (() => {
|
|
|
3431
3653
|
state,
|
|
3432
3654
|
true
|
|
3433
3655
|
);
|
|
3656
|
+
if (rootNode) {
|
|
3657
|
+
const finalNsAttrs = buildRootNamespaceAttributes(schema, root, state);
|
|
3658
|
+
rootNode.attributes = {
|
|
3659
|
+
...finalNsAttrs,
|
|
3660
|
+
...rootNode.attributes
|
|
3661
|
+
// preserve any existing attrs
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3434
3664
|
return {
|
|
3435
3665
|
rootElementName: root.name,
|
|
3436
3666
|
rootNode
|
package/package.json
CHANGED