uss-xsd-engine 0.2.0 → 0.2.2
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 +640 -22
- package/dist/uss-xsd-engine.standalone.js +640 -22
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* uss-xsd-engine v0.2.
|
|
2
|
+
* uss-xsd-engine v0.2.2
|
|
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,10 +97,15 @@ 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",
|
|
101
|
-
XSD_IMPORT_NAMESPACE_MISMATCH: "XSD_IMPORT_NAMESPACE_MISMATCH"
|
|
106
|
+
XSD_IMPORT_NAMESPACE_MISMATCH: "XSD_IMPORT_NAMESPACE_MISMATCH",
|
|
107
|
+
XSD_REDEFINE_NAMESPACE_MISMATCH: "XSD_REDEFINE_NAMESPACE_MISMATCH",
|
|
108
|
+
REDEFINE_INVALID_OVERRIDE: "REDEFINE_INVALID_OVERRIDE"
|
|
102
109
|
};
|
|
103
110
|
|
|
104
111
|
// src/parser/parseXsd.js
|
|
@@ -151,6 +158,7 @@ function createEmptySchemaModel() {
|
|
|
151
158
|
targetNamespace: null,
|
|
152
159
|
elementFormDefault: null,
|
|
153
160
|
attributeFormDefault: null,
|
|
161
|
+
schemaVersion: null,
|
|
154
162
|
namespaces: {
|
|
155
163
|
default: null,
|
|
156
164
|
prefixes: /* @__PURE__ */ Object.create(null)
|
|
@@ -166,7 +174,8 @@ function createEmptySchemaModel() {
|
|
|
166
174
|
documents: [],
|
|
167
175
|
externalRefs: {
|
|
168
176
|
includes: [],
|
|
169
|
-
imports: []
|
|
177
|
+
imports: [],
|
|
178
|
+
redefines: []
|
|
170
179
|
},
|
|
171
180
|
importedSchemas: [],
|
|
172
181
|
roots: [],
|
|
@@ -199,6 +208,16 @@ function normalizeUse(value) {
|
|
|
199
208
|
}
|
|
200
209
|
return null;
|
|
201
210
|
}
|
|
211
|
+
function createAnnotation({
|
|
212
|
+
documentation = null,
|
|
213
|
+
appinfo = null
|
|
214
|
+
} = {}) {
|
|
215
|
+
return {
|
|
216
|
+
kind: "annotation",
|
|
217
|
+
documentation,
|
|
218
|
+
appinfo
|
|
219
|
+
};
|
|
220
|
+
}
|
|
202
221
|
function createElementDecl({
|
|
203
222
|
name = null,
|
|
204
223
|
qName = null,
|
|
@@ -212,6 +231,7 @@ function createElementDecl({
|
|
|
212
231
|
fixedValue = null,
|
|
213
232
|
nillable = false,
|
|
214
233
|
identityConstraints = [],
|
|
234
|
+
annotation = null,
|
|
215
235
|
line = null,
|
|
216
236
|
column = null,
|
|
217
237
|
path = null
|
|
@@ -230,6 +250,7 @@ function createElementDecl({
|
|
|
230
250
|
fixedValue,
|
|
231
251
|
nillable,
|
|
232
252
|
identityConstraints,
|
|
253
|
+
annotation,
|
|
233
254
|
line,
|
|
234
255
|
column,
|
|
235
256
|
path
|
|
@@ -245,6 +266,7 @@ function createAttributeDecl({
|
|
|
245
266
|
use = null,
|
|
246
267
|
defaultValue = null,
|
|
247
268
|
fixedValue = null,
|
|
269
|
+
annotation = null,
|
|
248
270
|
line = null,
|
|
249
271
|
column = null,
|
|
250
272
|
path = null
|
|
@@ -260,6 +282,7 @@ function createAttributeDecl({
|
|
|
260
282
|
use,
|
|
261
283
|
defaultValue,
|
|
262
284
|
fixedValue,
|
|
285
|
+
annotation,
|
|
263
286
|
line,
|
|
264
287
|
column,
|
|
265
288
|
path
|
|
@@ -276,6 +299,7 @@ function createComplexTypeDecl({
|
|
|
276
299
|
mixed = false,
|
|
277
300
|
abstract = false,
|
|
278
301
|
identityConstraints = [],
|
|
302
|
+
annotation = null,
|
|
279
303
|
line = null,
|
|
280
304
|
column = null,
|
|
281
305
|
path = null
|
|
@@ -292,6 +316,7 @@ function createComplexTypeDecl({
|
|
|
292
316
|
mixed,
|
|
293
317
|
abstract,
|
|
294
318
|
identityConstraints,
|
|
319
|
+
annotation,
|
|
295
320
|
line,
|
|
296
321
|
column,
|
|
297
322
|
path
|
|
@@ -304,6 +329,7 @@ function createSimpleTypeDecl({
|
|
|
304
329
|
baseTypeName = null,
|
|
305
330
|
facets = {},
|
|
306
331
|
enumerations = [],
|
|
332
|
+
annotation = null,
|
|
307
333
|
line = null,
|
|
308
334
|
column = null,
|
|
309
335
|
path = null
|
|
@@ -316,6 +342,7 @@ function createSimpleTypeDecl({
|
|
|
316
342
|
baseTypeName,
|
|
317
343
|
facets,
|
|
318
344
|
enumerations,
|
|
345
|
+
annotation,
|
|
319
346
|
line,
|
|
320
347
|
column,
|
|
321
348
|
path
|
|
@@ -363,6 +390,7 @@ function createAttributeGroupDecl({
|
|
|
363
390
|
}
|
|
364
391
|
function createAttributeGroupRef({
|
|
365
392
|
refName,
|
|
393
|
+
use = null,
|
|
366
394
|
line = null,
|
|
367
395
|
column = null,
|
|
368
396
|
path = null
|
|
@@ -370,6 +398,7 @@ function createAttributeGroupRef({
|
|
|
370
398
|
return {
|
|
371
399
|
kind: "attributeGroupRef",
|
|
372
400
|
refName,
|
|
401
|
+
use,
|
|
373
402
|
line,
|
|
374
403
|
column,
|
|
375
404
|
path
|
|
@@ -480,9 +509,11 @@ function createGroupRefNode({
|
|
|
480
509
|
}
|
|
481
510
|
function createAnyNode({
|
|
482
511
|
namespace = null,
|
|
483
|
-
processContents =
|
|
512
|
+
processContents = "strict",
|
|
484
513
|
minOccurs = 1,
|
|
485
514
|
maxOccurs = 1,
|
|
515
|
+
notNamespace = [],
|
|
516
|
+
notQName = [],
|
|
486
517
|
line = null,
|
|
487
518
|
column = null,
|
|
488
519
|
path = null
|
|
@@ -490,9 +521,31 @@ function createAnyNode({
|
|
|
490
521
|
return {
|
|
491
522
|
kind: "any",
|
|
492
523
|
namespace,
|
|
493
|
-
processContents,
|
|
524
|
+
processContents: processContents || "strict",
|
|
494
525
|
minOccurs,
|
|
495
526
|
maxOccurs,
|
|
527
|
+
notNamespace,
|
|
528
|
+
notQName,
|
|
529
|
+
line,
|
|
530
|
+
column,
|
|
531
|
+
path
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function createAnyAttributeNode({
|
|
535
|
+
namespace = null,
|
|
536
|
+
processContents = "strict",
|
|
537
|
+
notNamespace = [],
|
|
538
|
+
notQName = [],
|
|
539
|
+
line = null,
|
|
540
|
+
column = null,
|
|
541
|
+
path = null
|
|
542
|
+
} = {}) {
|
|
543
|
+
return {
|
|
544
|
+
kind: "anyAttribute",
|
|
545
|
+
namespace,
|
|
546
|
+
processContents: processContents || "strict",
|
|
547
|
+
notNamespace,
|
|
548
|
+
notQName,
|
|
496
549
|
line,
|
|
497
550
|
column,
|
|
498
551
|
path
|
|
@@ -817,9 +870,6 @@ function getEffectiveContent(schema, complexTypeDecl) {
|
|
|
817
870
|
|
|
818
871
|
// src/parser/buildSchemaModel.js
|
|
819
872
|
var UNSUPPORTED_NODE_FEATURES = /* @__PURE__ */ new Set([
|
|
820
|
-
"any",
|
|
821
|
-
"anyAttribute",
|
|
822
|
-
"redefine",
|
|
823
873
|
"notation"
|
|
824
874
|
]);
|
|
825
875
|
function elementChildren(node) {
|
|
@@ -840,6 +890,8 @@ function recordExternalRef(schema, kind, node, path, loc) {
|
|
|
840
890
|
schema.externalRefs.includes.push(entry);
|
|
841
891
|
} else if (kind === "import") {
|
|
842
892
|
schema.externalRefs.imports.push(entry);
|
|
893
|
+
} else if (kind === "redefine") {
|
|
894
|
+
schema.externalRefs.redefines.push(entry);
|
|
843
895
|
}
|
|
844
896
|
}
|
|
845
897
|
function mergeGlobalsIntoSchema(targetSchema, sourceSchema, issues, createDuplicateIssue) {
|
|
@@ -880,6 +932,9 @@ function mergeGlobalsIntoSchema(targetSchema, sourceSchema, issues, createDuplic
|
|
|
880
932
|
targetSchema.externalRefs.imports.push(
|
|
881
933
|
...sourceSchema.externalRefs.imports || []
|
|
882
934
|
);
|
|
935
|
+
targetSchema.externalRefs.redefines.push(
|
|
936
|
+
...sourceSchema.externalRefs.redefines || []
|
|
937
|
+
);
|
|
883
938
|
targetSchema.importedSchemas.push(...sourceSchema.importedSchemas || []);
|
|
884
939
|
targetSchema.identityConstraints.push(
|
|
885
940
|
...sourceSchema.identityConstraints || []
|
|
@@ -1153,6 +1208,42 @@ function parseFacets(node) {
|
|
|
1153
1208
|
}
|
|
1154
1209
|
return { facets, enumerations };
|
|
1155
1210
|
}
|
|
1211
|
+
function parseAnnotation(node) {
|
|
1212
|
+
if (!node || node.localName !== "annotation") {
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
const children = Array.from(node.children || []).filter(
|
|
1216
|
+
(child) => child.nodeType === 1
|
|
1217
|
+
);
|
|
1218
|
+
let documentation = null;
|
|
1219
|
+
let appinfo = null;
|
|
1220
|
+
for (const child of children) {
|
|
1221
|
+
if (child.localName === "documentation") {
|
|
1222
|
+
const lang = child.getAttribute("xml:lang") || null;
|
|
1223
|
+
const source = child.getAttribute("source") || null;
|
|
1224
|
+
const text = (child.textContent || "").trim();
|
|
1225
|
+
documentation = {
|
|
1226
|
+
text,
|
|
1227
|
+
lang,
|
|
1228
|
+
source
|
|
1229
|
+
};
|
|
1230
|
+
} else if (child.localName === "appinfo") {
|
|
1231
|
+
const source = child.getAttribute("source") || null;
|
|
1232
|
+
const text = (child.textContent || "").trim();
|
|
1233
|
+
appinfo = {
|
|
1234
|
+
text,
|
|
1235
|
+
source
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (!documentation && !appinfo) {
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
return createAnnotation({
|
|
1243
|
+
documentation,
|
|
1244
|
+
appinfo
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1156
1247
|
function parseSimpleType(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1157
1248
|
const path = buildPath(parentPath, node);
|
|
1158
1249
|
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
@@ -1160,7 +1251,12 @@ function parseSimpleType(node, xsdText, lineStarts, parentPath, schema, issues)
|
|
|
1160
1251
|
let baseTypeName = null;
|
|
1161
1252
|
let facets = {};
|
|
1162
1253
|
let enumerations = [];
|
|
1163
|
-
const
|
|
1254
|
+
const children = elementChildren(node);
|
|
1255
|
+
const annotationNode = children.find(
|
|
1256
|
+
(child) => child.localName === "annotation"
|
|
1257
|
+
);
|
|
1258
|
+
const annotation = parseAnnotation(annotationNode);
|
|
1259
|
+
const restriction = children.find(
|
|
1164
1260
|
(child) => child.localName === "restriction"
|
|
1165
1261
|
);
|
|
1166
1262
|
if (restriction) {
|
|
@@ -1187,6 +1283,7 @@ function parseSimpleType(node, xsdText, lineStarts, parentPath, schema, issues)
|
|
|
1187
1283
|
baseTypeName,
|
|
1188
1284
|
facets,
|
|
1189
1285
|
enumerations,
|
|
1286
|
+
annotation,
|
|
1190
1287
|
line: loc.line,
|
|
1191
1288
|
column: loc.column,
|
|
1192
1289
|
path
|
|
@@ -1196,7 +1293,12 @@ function parseAttribute(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
|
1196
1293
|
const path = buildPath(parentPath, node);
|
|
1197
1294
|
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1198
1295
|
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1199
|
-
const
|
|
1296
|
+
const children = elementChildren(node);
|
|
1297
|
+
const annotationNode = children.find(
|
|
1298
|
+
(child) => child.localName === "annotation"
|
|
1299
|
+
);
|
|
1300
|
+
const annotation = parseAnnotation(annotationNode);
|
|
1301
|
+
const inlineSimpleTypeNode = children.find(
|
|
1200
1302
|
(child) => child.localName === "simpleType"
|
|
1201
1303
|
);
|
|
1202
1304
|
const inlineType = inlineSimpleTypeNode ? parseSimpleType(
|
|
@@ -1219,6 +1321,7 @@ function parseAttribute(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
|
1219
1321
|
use: normalizeUse(node.getAttribute("use")),
|
|
1220
1322
|
defaultValue: node.getAttribute("default"),
|
|
1221
1323
|
fixedValue: node.getAttribute("fixed"),
|
|
1324
|
+
annotation,
|
|
1222
1325
|
line: loc.line,
|
|
1223
1326
|
column: loc.column,
|
|
1224
1327
|
path
|
|
@@ -1230,6 +1333,7 @@ function parseAttributeGroupRef(node, xsdText, lineStarts, parentPath, schema, i
|
|
|
1230
1333
|
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1231
1334
|
return createAttributeGroupRef({
|
|
1232
1335
|
refName: node.getAttribute("ref"),
|
|
1336
|
+
use: normalizeUse(node.getAttribute("use")),
|
|
1233
1337
|
line: loc.line,
|
|
1234
1338
|
column: loc.column,
|
|
1235
1339
|
path
|
|
@@ -1253,6 +1357,10 @@ function parseAttributesContainer(nodes, xsdText, lineStarts, parentPath, schema
|
|
|
1253
1357
|
issues
|
|
1254
1358
|
)
|
|
1255
1359
|
);
|
|
1360
|
+
} else if (child.localName === "anyAttribute") {
|
|
1361
|
+
attributes.push(
|
|
1362
|
+
parseAnyAttribute(child, xsdText, lineStarts, parentPath, schema, issues)
|
|
1363
|
+
);
|
|
1256
1364
|
}
|
|
1257
1365
|
}
|
|
1258
1366
|
return attributes;
|
|
@@ -1263,6 +1371,10 @@ function parseElement(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
|
1263
1371
|
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1264
1372
|
let inlineType = null;
|
|
1265
1373
|
const children = elementChildren(node);
|
|
1374
|
+
const annotationNode = children.find(
|
|
1375
|
+
(child) => child.localName === "annotation"
|
|
1376
|
+
);
|
|
1377
|
+
const annotation = parseAnnotation(annotationNode);
|
|
1266
1378
|
const inlineComplexTypeNode = children.find(
|
|
1267
1379
|
(child) => child.localName === "complexType"
|
|
1268
1380
|
);
|
|
@@ -1318,6 +1430,7 @@ function parseElement(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
|
1318
1430
|
fixedValue: node.getAttribute("fixed"),
|
|
1319
1431
|
nillable: node.getAttribute("nillable") === "true",
|
|
1320
1432
|
identityConstraints,
|
|
1433
|
+
annotation,
|
|
1321
1434
|
line: loc.line,
|
|
1322
1435
|
column: loc.column,
|
|
1323
1436
|
path
|
|
@@ -1336,15 +1449,58 @@ function parseGroupRef(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
|
1336
1449
|
path
|
|
1337
1450
|
});
|
|
1338
1451
|
}
|
|
1452
|
+
function parseWildcardNamespace(namespaceStr) {
|
|
1453
|
+
if (!namespaceStr) return null;
|
|
1454
|
+
const trimmed = namespaceStr.trim();
|
|
1455
|
+
if (!trimmed) return null;
|
|
1456
|
+
if (trimmed.includes(" ")) {
|
|
1457
|
+
return trimmed.split(/\s+/).filter((ns) => ns);
|
|
1458
|
+
}
|
|
1459
|
+
return trimmed;
|
|
1460
|
+
}
|
|
1461
|
+
function parseNotNamespace(notNamespaceStr) {
|
|
1462
|
+
if (!notNamespaceStr) return [];
|
|
1463
|
+
const trimmed = notNamespaceStr.trim();
|
|
1464
|
+
if (!trimmed) return [];
|
|
1465
|
+
if (trimmed.includes(" ")) {
|
|
1466
|
+
return trimmed.split(/\s+/).filter((ns) => ns);
|
|
1467
|
+
}
|
|
1468
|
+
return [trimmed];
|
|
1469
|
+
}
|
|
1470
|
+
function parseNotQName(notQNameStr) {
|
|
1471
|
+
if (!notQNameStr) return [];
|
|
1472
|
+
const trimmed = notQNameStr.trim();
|
|
1473
|
+
if (!trimmed) return [];
|
|
1474
|
+
if (trimmed.includes(" ")) {
|
|
1475
|
+
return trimmed.split(/\s+/).filter((qn) => qn);
|
|
1476
|
+
}
|
|
1477
|
+
return [trimmed];
|
|
1478
|
+
}
|
|
1339
1479
|
function parseAny(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1340
1480
|
const path = buildPath(parentPath, node);
|
|
1341
1481
|
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1342
1482
|
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1343
1483
|
return createAnyNode({
|
|
1344
|
-
namespace: node.getAttribute("namespace"),
|
|
1484
|
+
namespace: parseWildcardNamespace(node.getAttribute("namespace")),
|
|
1345
1485
|
processContents: node.getAttribute("processContents"),
|
|
1346
1486
|
minOccurs: normalizeOccurs(node.getAttribute("minOccurs"), 1),
|
|
1347
1487
|
maxOccurs: normalizeOccurs(node.getAttribute("maxOccurs"), 1),
|
|
1488
|
+
notNamespace: parseNotNamespace(node.getAttribute("notNamespace")),
|
|
1489
|
+
notQName: parseNotQName(node.getAttribute("notQName")),
|
|
1490
|
+
line: loc.line,
|
|
1491
|
+
column: loc.column,
|
|
1492
|
+
path
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
function parseAnyAttribute(node, xsdText, lineStarts, parentPath, schema, issues) {
|
|
1496
|
+
const path = buildPath(parentPath, node);
|
|
1497
|
+
const loc = locateNodeInSource(xsdText, lineStarts, node);
|
|
1498
|
+
collectNodeDiagnostics(schema, issues, node, path, loc);
|
|
1499
|
+
return createAnyAttributeNode({
|
|
1500
|
+
namespace: parseWildcardNamespace(node.getAttribute("namespace")),
|
|
1501
|
+
processContents: node.getAttribute("processContents"),
|
|
1502
|
+
notNamespace: parseNotNamespace(node.getAttribute("notNamespace")),
|
|
1503
|
+
notQName: parseNotQName(node.getAttribute("notQName")),
|
|
1348
1504
|
line: loc.line,
|
|
1349
1505
|
column: loc.column,
|
|
1350
1506
|
path
|
|
@@ -1535,6 +1691,10 @@ function parseComplexType(node, xsdText, lineStarts, parentPath, schema, issues)
|
|
|
1535
1691
|
let contentModel = "complex";
|
|
1536
1692
|
const children = elementChildren(node);
|
|
1537
1693
|
const namespaceUri4 = schema.targetNamespace || null;
|
|
1694
|
+
const annotationNode = children.find(
|
|
1695
|
+
(child) => child.localName === "annotation"
|
|
1696
|
+
);
|
|
1697
|
+
const annotation = parseAnnotation(annotationNode);
|
|
1538
1698
|
const identityConstraints = children.filter(
|
|
1539
1699
|
(child) => ["key", "keyref", "unique"].includes(child.localName)
|
|
1540
1700
|
).map(
|
|
@@ -1620,6 +1780,7 @@ function parseComplexType(node, xsdText, lineStarts, parentPath, schema, issues)
|
|
|
1620
1780
|
mixed: node.getAttribute("mixed") === "true",
|
|
1621
1781
|
abstract: node.getAttribute("abstract") === "true",
|
|
1622
1782
|
identityConstraints,
|
|
1783
|
+
annotation,
|
|
1623
1784
|
line: loc.line,
|
|
1624
1785
|
column: loc.column,
|
|
1625
1786
|
path
|
|
@@ -1689,6 +1850,63 @@ function isImportNamespaceCompatible(ref, importedSchema) {
|
|
|
1689
1850
|
}
|
|
1690
1851
|
return (declaredNs || null) === (importedNs || null);
|
|
1691
1852
|
}
|
|
1853
|
+
function isRedefineNamespaceCompatible(hostSchema, redefinedSchema) {
|
|
1854
|
+
const hostNs = hostSchema?.targetNamespace || null;
|
|
1855
|
+
const redefinedNs = redefinedSchema?.targetNamespace || null;
|
|
1856
|
+
return hostNs === redefinedNs;
|
|
1857
|
+
}
|
|
1858
|
+
function mergeRedefinesIntoSchema(targetSchema, sourceSchema, issues, createDuplicateIssue) {
|
|
1859
|
+
const buckets = [
|
|
1860
|
+
["complexTypes", "DUPLICATE_GLOBAL_COMPLEX_TYPE"],
|
|
1861
|
+
["simpleTypes", "DUPLICATE_GLOBAL_SIMPLE_TYPE"],
|
|
1862
|
+
["groups", "DUPLICATE_GLOBAL_GROUP"],
|
|
1863
|
+
["attributeGroups", "DUPLICATE_GLOBAL_ATTRIBUTE_GROUP"]
|
|
1864
|
+
];
|
|
1865
|
+
for (const [bucketName, _duplicateCode] of buckets) {
|
|
1866
|
+
for (const [key, decl] of Object.entries(
|
|
1867
|
+
sourceSchema.globals[bucketName] || {}
|
|
1868
|
+
)) {
|
|
1869
|
+
if (targetSchema.globals[bucketName][key]) {
|
|
1870
|
+
targetSchema.globals[bucketName][key] = {
|
|
1871
|
+
...decl,
|
|
1872
|
+
redefined: true,
|
|
1873
|
+
originalDefinition: targetSchema.globals[bucketName][key]
|
|
1874
|
+
};
|
|
1875
|
+
} else {
|
|
1876
|
+
issues.push(
|
|
1877
|
+
createIssue({
|
|
1878
|
+
code: ISSUE_CODES.REDEFINE_INVALID_OVERRIDE,
|
|
1879
|
+
severity: "error",
|
|
1880
|
+
message: `xs:redefine attempts to override ${bucketName.slice(0, -1)} '${decl.name}' which does not exist in the base schema.`,
|
|
1881
|
+
line: decl.line,
|
|
1882
|
+
column: decl.column,
|
|
1883
|
+
path: decl.path,
|
|
1884
|
+
source: "xsd",
|
|
1885
|
+
nodeKind: decl.kind,
|
|
1886
|
+
name: decl.name,
|
|
1887
|
+
details: { declarationName: decl.name }
|
|
1888
|
+
})
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
targetSchema.references.types.push(...sourceSchema.references.types || []);
|
|
1894
|
+
targetSchema.references.baseTypes.push(
|
|
1895
|
+
...sourceSchema.references.baseTypes || []
|
|
1896
|
+
);
|
|
1897
|
+
targetSchema.references.groupRefs.push(
|
|
1898
|
+
...sourceSchema.references.groupRefs || []
|
|
1899
|
+
);
|
|
1900
|
+
targetSchema.references.attributeGroupRefs.push(
|
|
1901
|
+
...sourceSchema.references.attributeGroupRefs || []
|
|
1902
|
+
);
|
|
1903
|
+
for (const feature of sourceSchema.usedFeatures || []) {
|
|
1904
|
+
targetSchema.usedFeatures.add(feature);
|
|
1905
|
+
}
|
|
1906
|
+
targetSchema.unsupportedFeatures.push(
|
|
1907
|
+
...sourceSchema.unsupportedFeatures || []
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1692
1910
|
function normalizeSchemaPath(value) {
|
|
1693
1911
|
if (!value || typeof value !== "string") return null;
|
|
1694
1912
|
const trimmed = value.trim();
|
|
@@ -1767,24 +1985,24 @@ function resolveExternalDocument(ref, externalDocuments) {
|
|
|
1767
1985
|
}
|
|
1768
1986
|
}
|
|
1769
1987
|
if (ref?.kind === "import" && ref.namespace) {
|
|
1770
|
-
const
|
|
1988
|
+
const namespaceMatches2 = entries.filter((entry) => {
|
|
1771
1989
|
const declaredTargetNamespace = getDeclaredTargetNamespaceFromText(
|
|
1772
1990
|
entry.text
|
|
1773
1991
|
);
|
|
1774
1992
|
return (declaredTargetNamespace || null) === (ref.namespace || null);
|
|
1775
1993
|
});
|
|
1776
|
-
if (
|
|
1994
|
+
if (namespaceMatches2.length === 1) {
|
|
1777
1995
|
return {
|
|
1778
1996
|
kind: "namespace",
|
|
1779
|
-
entry:
|
|
1780
|
-
matches:
|
|
1997
|
+
entry: namespaceMatches2[0],
|
|
1998
|
+
matches: namespaceMatches2
|
|
1781
1999
|
};
|
|
1782
2000
|
}
|
|
1783
|
-
if (
|
|
2001
|
+
if (namespaceMatches2.length > 1) {
|
|
1784
2002
|
return {
|
|
1785
2003
|
kind: "ambiguous-namespace",
|
|
1786
2004
|
entry: null,
|
|
1787
|
-
matches:
|
|
2005
|
+
matches: namespaceMatches2
|
|
1788
2006
|
};
|
|
1789
2007
|
}
|
|
1790
2008
|
}
|
|
@@ -1814,6 +2032,7 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1814
2032
|
schema.targetNamespace = getEffectiveTargetNamespace(schemaRoot, options);
|
|
1815
2033
|
schema.elementFormDefault = schemaRoot.getAttribute("elementFormDefault");
|
|
1816
2034
|
schema.attributeFormDefault = schemaRoot.getAttribute("attributeFormDefault");
|
|
2035
|
+
schema.schemaVersion = schemaRoot.getAttribute("version");
|
|
1817
2036
|
extractNamespaces(schemaRoot, schema);
|
|
1818
2037
|
const rootPath = "/schema";
|
|
1819
2038
|
const rootLoc = locateNodeInSource(xsdText, lineStarts, schemaRoot);
|
|
@@ -1966,6 +2185,13 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1966
2185
|
recordExternalRef(schema, "import", child, path, loc);
|
|
1967
2186
|
break;
|
|
1968
2187
|
}
|
|
2188
|
+
case "redefine": {
|
|
2189
|
+
const path = buildPath(rootPath, child);
|
|
2190
|
+
const loc = locateNodeInSource(xsdText, lineStarts, child);
|
|
2191
|
+
collectNodeDiagnostics(schema, issues, child, path, loc);
|
|
2192
|
+
recordExternalRef(schema, "redefine", child, path, loc);
|
|
2193
|
+
break;
|
|
2194
|
+
}
|
|
1969
2195
|
default: {
|
|
1970
2196
|
const path = buildPath(rootPath, child);
|
|
1971
2197
|
const loc = locateNodeInSource(xsdText, lineStarts, child);
|
|
@@ -1990,7 +2216,8 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
1990
2216
|
const visited = options._visitedExternalSchemas || /* @__PURE__ */ new Set();
|
|
1991
2217
|
const includes = schema.externalRefs.includes || [];
|
|
1992
2218
|
const imports = schema.externalRefs.imports || [];
|
|
1993
|
-
|
|
2219
|
+
const redefines = schema.externalRefs.redefines || [];
|
|
2220
|
+
for (const ref of [...includes, ...imports, ...redefines]) {
|
|
1994
2221
|
if (!ref.schemaLocation) continue;
|
|
1995
2222
|
const resolution = resolveExternalDocument(ref, externalDocuments);
|
|
1996
2223
|
if (resolution.kind === "ambiguous-namespace") {
|
|
@@ -2117,6 +2344,34 @@ function buildSchemaModel(doc, options = {}) {
|
|
|
2117
2344
|
} else {
|
|
2118
2345
|
schema.importedSchemas.push(externalBuild.schema);
|
|
2119
2346
|
}
|
|
2347
|
+
} else if (ref.kind === "redefine") {
|
|
2348
|
+
if (!isRedefineNamespaceCompatible(schema, externalBuild.schema)) {
|
|
2349
|
+
issues.push(
|
|
2350
|
+
createIssue({
|
|
2351
|
+
code: ISSUE_CODES.XSD_REDEFINE_NAMESPACE_MISMATCH,
|
|
2352
|
+
severity: "error",
|
|
2353
|
+
message: `xs:redefine namespace mismatch. Host targetNamespace is '${schema.targetNamespace || ""}', redefined schema targetNamespace is '${externalBuild.schema.targetNamespace || ""}'.`,
|
|
2354
|
+
line: ref.line,
|
|
2355
|
+
column: ref.column,
|
|
2356
|
+
path: ref.path,
|
|
2357
|
+
source: "xsd",
|
|
2358
|
+
nodeKind: "redefine",
|
|
2359
|
+
details: {
|
|
2360
|
+
schemaLocation: ref.schemaLocation,
|
|
2361
|
+
resolvedSchemaKey,
|
|
2362
|
+
hostTargetNamespace: schema.targetNamespace || null,
|
|
2363
|
+
redefinedTargetNamespace: externalBuild.schema.targetNamespace || null
|
|
2364
|
+
}
|
|
2365
|
+
})
|
|
2366
|
+
);
|
|
2367
|
+
} else {
|
|
2368
|
+
mergeRedefinesIntoSchema(
|
|
2369
|
+
schema,
|
|
2370
|
+
externalBuild.schema,
|
|
2371
|
+
issues,
|
|
2372
|
+
createDuplicateIssue
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2120
2375
|
}
|
|
2121
2376
|
}
|
|
2122
2377
|
issues.push(...externalBuild.issues || []);
|
|
@@ -2401,6 +2656,115 @@ function checkRestrictedAttributes(schema, derivedType, baseType, issues) {
|
|
|
2401
2656
|
}
|
|
2402
2657
|
}
|
|
2403
2658
|
}
|
|
2659
|
+
function checkWildcardRestriction(schema, derivedType, baseType, issues) {
|
|
2660
|
+
const derivedContent = getEffectiveContent(schema, derivedType);
|
|
2661
|
+
const baseContent = getEffectiveContent(schema, baseType);
|
|
2662
|
+
const derivedWildcards = [];
|
|
2663
|
+
const baseWildcards = [];
|
|
2664
|
+
function collectWildcards(node, arr) {
|
|
2665
|
+
if (!node) return;
|
|
2666
|
+
if (node.kind === "any") {
|
|
2667
|
+
arr.push(node);
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
if (node.children) {
|
|
2671
|
+
for (const child of asArray(node.children)) {
|
|
2672
|
+
collectWildcards(child, arr);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
collectWildcards(derivedContent, derivedWildcards);
|
|
2677
|
+
collectWildcards(baseContent, baseWildcards);
|
|
2678
|
+
if (derivedWildcards.length > 0 && baseWildcards.length === 0) {
|
|
2679
|
+
issues.push(
|
|
2680
|
+
buildRestrictionIssue(
|
|
2681
|
+
"XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
2682
|
+
`Restricted type introduces wildcard elements that base type does not have.`,
|
|
2683
|
+
derivedType
|
|
2684
|
+
)
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2687
|
+
const derivedAttrs = asArray(getEffectiveAttributes(schema, derivedType));
|
|
2688
|
+
const baseAttrs = asArray(getEffectiveAttributes(schema, baseType));
|
|
2689
|
+
const derivedHasAnyAttribute = derivedAttrs.some((a) => a?.kind === "anyAttribute");
|
|
2690
|
+
const baseHasAnyAttribute = baseAttrs.some((a) => a?.kind === "anyAttribute");
|
|
2691
|
+
if (derivedHasAnyAttribute && !baseHasAnyAttribute) {
|
|
2692
|
+
issues.push(
|
|
2693
|
+
buildRestrictionIssue(
|
|
2694
|
+
"XSD_RESTRICTION_WILDCARD_INCOMPATIBLE",
|
|
2695
|
+
`Restricted type introduces anyAttribute that base type does not have.`,
|
|
2696
|
+
derivedType
|
|
2697
|
+
)
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
function checkOccurrenceCompatibility(derived, base, name) {
|
|
2702
|
+
const dMin = typeof derived.minOccurs === "number" ? derived.minOccurs : 1;
|
|
2703
|
+
const bMin = typeof base.minOccurs === "number" ? base.minOccurs : 1;
|
|
2704
|
+
const dMax = maxToNumber(derived.maxOccurs ?? 1);
|
|
2705
|
+
const bMax = maxToNumber(base.maxOccurs ?? 1);
|
|
2706
|
+
return dMin >= bMin && dMax <= bMax;
|
|
2707
|
+
}
|
|
2708
|
+
function checkComplexContentRestriction(schema, derivedType, baseType, issues) {
|
|
2709
|
+
const derivedContent = getEffectiveContent(schema, derivedType);
|
|
2710
|
+
const baseContent = getEffectiveContent(schema, baseType);
|
|
2711
|
+
if (!derivedContent || !baseContent) return;
|
|
2712
|
+
const derivedFlat = flattenContent(derivedContent, []);
|
|
2713
|
+
const baseFlat = flattenContent(baseContent, []);
|
|
2714
|
+
const baseMap = new Map(baseFlat.map((item) => [item.name, item]));
|
|
2715
|
+
for (const item of derivedFlat) {
|
|
2716
|
+
const baseItem = baseMap.get(item.name);
|
|
2717
|
+
if (!baseItem) {
|
|
2718
|
+
issues.push(
|
|
2719
|
+
buildRestrictionIssue(
|
|
2720
|
+
"XSD_RESTRICTION_NOT_SUBSET",
|
|
2721
|
+
`Restricted type contains element '${item.name}' not in base type.`,
|
|
2722
|
+
item
|
|
2723
|
+
)
|
|
2724
|
+
);
|
|
2725
|
+
continue;
|
|
2726
|
+
}
|
|
2727
|
+
if (!checkOccurrenceCompatibility(item, baseItem, item.name)) {
|
|
2728
|
+
issues.push(
|
|
2729
|
+
buildRestrictionIssue(
|
|
2730
|
+
"XSD_RESTRICTION_OCCURRENCE_INCOMPATIBLE",
|
|
2731
|
+
`Restricted type has incompatible occurrence constraints for '${item.name}'.`,
|
|
2732
|
+
item
|
|
2733
|
+
)
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
function checkSimpleContentRestriction(schema, derivedType, baseType, issues) {
|
|
2739
|
+
if (baseType.contentModel !== "simple") {
|
|
2740
|
+
issues.push(
|
|
2741
|
+
buildRestrictionIssue(
|
|
2742
|
+
"XSD_RESTRICTION_NOT_SUBSET",
|
|
2743
|
+
`SimpleContent restriction requires base type to have simple content.`,
|
|
2744
|
+
derivedType
|
|
2745
|
+
)
|
|
2746
|
+
);
|
|
2747
|
+
}
|
|
2748
|
+
const derivedAttrs = asArray(getEffectiveAttributes(schema, derivedType));
|
|
2749
|
+
const baseAttrs = asArray(getEffectiveAttributes(schema, baseType));
|
|
2750
|
+
const baseAttrMap = new Map(
|
|
2751
|
+
baseAttrs.filter((attr) => attr?.kind === "attribute").map((attr) => [localDeclName(attr), attr])
|
|
2752
|
+
);
|
|
2753
|
+
for (const attr of derivedAttrs) {
|
|
2754
|
+
if (attr?.kind !== "attribute") continue;
|
|
2755
|
+
const name = localDeclName(attr);
|
|
2756
|
+
const baseAttr = baseAttrMap.get(name);
|
|
2757
|
+
if (!baseAttr && !baseAttrs.some((a) => a?.kind === "anyAttribute")) {
|
|
2758
|
+
issues.push(
|
|
2759
|
+
buildRestrictionIssue(
|
|
2760
|
+
"XSD_RESTRICTION_ATTRIBUTE_INCOMPATIBLE",
|
|
2761
|
+
`SimpleContent restricted type adds attribute '${name}' not in base type.`,
|
|
2762
|
+
attr
|
|
2763
|
+
)
|
|
2764
|
+
);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2404
2768
|
function runRestrictionDiagnostics(schema) {
|
|
2405
2769
|
const issues = [];
|
|
2406
2770
|
for (const complexType of Object.values(schema.globals.complexTypes || {})) {
|
|
@@ -2410,6 +2774,12 @@ function runRestrictionDiagnostics(schema) {
|
|
|
2410
2774
|
if (!baseType) continue;
|
|
2411
2775
|
checkRestrictedContentSubset(schema, complexType, baseType, issues);
|
|
2412
2776
|
checkRestrictedAttributes(schema, complexType, baseType, issues);
|
|
2777
|
+
checkWildcardRestriction(schema, complexType, baseType, issues);
|
|
2778
|
+
if (complexType.contentModel === "complex" && baseType.contentModel === "complex") {
|
|
2779
|
+
checkComplexContentRestriction(schema, complexType, baseType, issues);
|
|
2780
|
+
} else if (complexType.contentModel === "simple" && baseType.contentModel === "simple") {
|
|
2781
|
+
checkSimpleContentRestriction(schema, complexType, baseType, issues);
|
|
2782
|
+
}
|
|
2413
2783
|
}
|
|
2414
2784
|
return issues;
|
|
2415
2785
|
}
|
|
@@ -2736,6 +3106,130 @@ function runImportDiagnostics(schema, options = {}) {
|
|
|
2736
3106
|
return issues;
|
|
2737
3107
|
}
|
|
2738
3108
|
|
|
3109
|
+
// src/diagnostics/schemaWildcardDiagnostics.js
|
|
3110
|
+
function validateWildcardNamespace(namespace, path) {
|
|
3111
|
+
if (!namespace) return null;
|
|
3112
|
+
const trimmed = namespace.trim();
|
|
3113
|
+
if (!trimmed) return null;
|
|
3114
|
+
const parts = trimmed.split(/\s+/);
|
|
3115
|
+
for (const part of parts) {
|
|
3116
|
+
if (part === "##any" || part === "##other" || part === "##targetNamespace") {
|
|
3117
|
+
continue;
|
|
3118
|
+
}
|
|
3119
|
+
if (!part.includes(":") && part !== "") {
|
|
3120
|
+
return createIssue({
|
|
3121
|
+
code: "INVALID_WILDCARD_NAMESPACE",
|
|
3122
|
+
severity: "warning",
|
|
3123
|
+
message: `Invalid namespace in wildcard constraint: '${part}'. Expected ##any, ##other, ##targetNamespace, or a valid namespace URI.`,
|
|
3124
|
+
path
|
|
3125
|
+
});
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
return null;
|
|
3129
|
+
}
|
|
3130
|
+
function validateProcessContents(processContents, path) {
|
|
3131
|
+
if (!processContents) return null;
|
|
3132
|
+
const trimmed = processContents.trim();
|
|
3133
|
+
if (trimmed === "strict" || trimmed === "lax" || trimmed === "skip") {
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
return createIssue({
|
|
3137
|
+
code: "INVALID_PROCESS_CONTENTS",
|
|
3138
|
+
severity: "error",
|
|
3139
|
+
message: `Invalid processContents value: '${processContents}'. Must be 'strict', 'lax', or 'skip'.`,
|
|
3140
|
+
path
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
function validateSingleWildcard(wildcardNode, nodeName) {
|
|
3144
|
+
const issues = [];
|
|
3145
|
+
const nsIssue = validateWildcardNamespace(wildcardNode.namespace, wildcardNode.path);
|
|
3146
|
+
if (nsIssue) {
|
|
3147
|
+
issues.push(nsIssue);
|
|
3148
|
+
}
|
|
3149
|
+
const pcIssue = validateProcessContents(wildcardNode.processContents, wildcardNode.path);
|
|
3150
|
+
if (pcIssue) {
|
|
3151
|
+
issues.push(pcIssue);
|
|
3152
|
+
}
|
|
3153
|
+
if (wildcardNode.notNamespace && wildcardNode.notNamespace.length > 0) {
|
|
3154
|
+
if (wildcardNode.namespace !== "##other" && wildcardNode.namespace !== "##targetNamespace") {
|
|
3155
|
+
issues.push(
|
|
3156
|
+
createIssue({
|
|
3157
|
+
code: "INVALID_NOT_NAMESPACE_USAGE",
|
|
3158
|
+
severity: "warning",
|
|
3159
|
+
message: `notNamespace is only meaningful with ##other or ##targetNamespace namespace constraint.`,
|
|
3160
|
+
path: wildcardNode.path
|
|
3161
|
+
})
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
if (wildcardNode.notQName && wildcardNode.notQName.length > 0) {
|
|
3166
|
+
for (const qname of wildcardNode.notQName) {
|
|
3167
|
+
if (!qname || qname.trim() === "") {
|
|
3168
|
+
issues.push(
|
|
3169
|
+
createIssue({
|
|
3170
|
+
code: "INVALID_NOT_QNAME",
|
|
3171
|
+
severity: "warning",
|
|
3172
|
+
message: `Empty QName in notQName constraint.`,
|
|
3173
|
+
path: wildcardNode.path
|
|
3174
|
+
})
|
|
3175
|
+
);
|
|
3176
|
+
break;
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
return issues;
|
|
3181
|
+
}
|
|
3182
|
+
function findWildcardsInContent(node, wildcards) {
|
|
3183
|
+
if (!node) return;
|
|
3184
|
+
if (node.kind === "any" || node.kind === "anyAttribute") {
|
|
3185
|
+
wildcards.push(node);
|
|
3186
|
+
return;
|
|
3187
|
+
}
|
|
3188
|
+
if (node.children && Array.isArray(node.children)) {
|
|
3189
|
+
for (const child of node.children) {
|
|
3190
|
+
findWildcardsInContent(child, wildcards);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
function findWildcardsInAttributes(attributes, wildcards) {
|
|
3195
|
+
if (!attributes || !Array.isArray(attributes)) return;
|
|
3196
|
+
for (const attr of attributes) {
|
|
3197
|
+
if (attr.kind === "anyAttribute") {
|
|
3198
|
+
wildcards.push(attr);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
function runWildcardDiagnostics(schema) {
|
|
3203
|
+
const issues = [];
|
|
3204
|
+
const wildcards = [];
|
|
3205
|
+
for (const elem of Object.values(schema.globals.elements || {})) {
|
|
3206
|
+
if (elem.inlineType?.content) {
|
|
3207
|
+
findWildcardsInContent(elem.inlineType.content, wildcards);
|
|
3208
|
+
}
|
|
3209
|
+
if (elem.inlineType?.attributes) {
|
|
3210
|
+
findWildcardsInAttributes(elem.inlineType.attributes, wildcards);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
for (const complexType of Object.values(schema.globals.complexTypes || {})) {
|
|
3214
|
+
if (complexType.content) {
|
|
3215
|
+
findWildcardsInContent(complexType.content, wildcards);
|
|
3216
|
+
}
|
|
3217
|
+
if (complexType.attributes) {
|
|
3218
|
+
findWildcardsInAttributes(complexType.attributes, wildcards);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
for (const group of Object.values(schema.globals.groups || {})) {
|
|
3222
|
+
if (group.content) {
|
|
3223
|
+
findWildcardsInContent(group.content, wildcards);
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
for (const wildcard of wildcards) {
|
|
3227
|
+
const wildcardIssues = validateSingleWildcard(wildcard, wildcard.kind);
|
|
3228
|
+
issues.push(...wildcardIssues);
|
|
3229
|
+
}
|
|
3230
|
+
return issues;
|
|
3231
|
+
}
|
|
3232
|
+
|
|
2739
3233
|
// src/diagnostics/schemaDiagnostics.js
|
|
2740
3234
|
function buildStats(schema) {
|
|
2741
3235
|
return {
|
|
@@ -2910,6 +3404,8 @@ function runSchemaDiagnostics(schema, options = {}) {
|
|
|
2910
3404
|
const identityIssues = runIdentityConstraintDiagnostics(schema);
|
|
2911
3405
|
issues.push(...identityIssues);
|
|
2912
3406
|
emitUnsupportedFeatureWarnings(schema, issues, options);
|
|
3407
|
+
const wildcardIssues = runWildcardDiagnostics(schema);
|
|
3408
|
+
issues.push(...wildcardIssues);
|
|
2913
3409
|
const facetIssues = runFacetDiagnostics(schema, options);
|
|
2914
3410
|
issues.push(...facetIssues);
|
|
2915
3411
|
const restrictionIssues = runRestrictionDiagnostics(schema);
|
|
@@ -2930,7 +3426,7 @@ function runSchemaDiagnostics(schema, options = {}) {
|
|
|
2930
3426
|
}
|
|
2931
3427
|
|
|
2932
3428
|
// src/version.js
|
|
2933
|
-
var ENGINE_VERSION = "v0.2.
|
|
3429
|
+
var ENGINE_VERSION = "v0.2.2";
|
|
2934
3430
|
|
|
2935
3431
|
// src/utils/result.js
|
|
2936
3432
|
function summarizeIssues(issues = []) {
|
|
@@ -3818,6 +4314,24 @@ function buildNodesFromContent(schema, contentNode, options, state) {
|
|
|
3818
4314
|
function buildComplexTypeContent(schema, complexTypeDecl, options, state) {
|
|
3819
4315
|
const content = getEffectiveContent(schema, complexTypeDecl);
|
|
3820
4316
|
const attributes = getEffectiveAttributes(schema, complexTypeDecl);
|
|
4317
|
+
if (state.currentDepth >= options.maxDepth) {
|
|
4318
|
+
return {
|
|
4319
|
+
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
4320
|
+
children: []
|
|
4321
|
+
};
|
|
4322
|
+
}
|
|
4323
|
+
const typeKey = complexTypeDecl.name || complexTypeDecl.qName;
|
|
4324
|
+
if (typeKey && state.visitedTypes.has(typeKey)) {
|
|
4325
|
+
return {
|
|
4326
|
+
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
4327
|
+
children: []
|
|
4328
|
+
};
|
|
4329
|
+
}
|
|
4330
|
+
if (typeKey) {
|
|
4331
|
+
state.visitedTypes.add(typeKey);
|
|
4332
|
+
}
|
|
4333
|
+
const previousDepth = state.currentDepth;
|
|
4334
|
+
state.currentDepth += 1;
|
|
3821
4335
|
let children = content ? buildNodesFromContent(schema, content, options, state) : [];
|
|
3822
4336
|
if (options.mode === "minimal" && children.length === 0 && content) {
|
|
3823
4337
|
children = buildRepresentativeNodesFromContent(
|
|
@@ -3827,6 +4341,10 @@ function buildComplexTypeContent(schema, complexTypeDecl, options, state) {
|
|
|
3827
4341
|
state
|
|
3828
4342
|
);
|
|
3829
4343
|
}
|
|
4344
|
+
state.currentDepth = previousDepth;
|
|
4345
|
+
if (typeKey) {
|
|
4346
|
+
state.visitedTypes.delete(typeKey);
|
|
4347
|
+
}
|
|
3830
4348
|
return {
|
|
3831
4349
|
attributes: buildAttributesObject(schema, attributes, options, state),
|
|
3832
4350
|
children
|
|
@@ -3905,7 +4423,10 @@ function selectRoot(schema, options = {}) {
|
|
|
3905
4423
|
function generateXmlFromSchema(schema, options = {}, helpers = {}) {
|
|
3906
4424
|
const normalizedOptions = {
|
|
3907
4425
|
mode: options.mode === "full" ? "full" : "minimal",
|
|
3908
|
-
includeOptionalAttributes: options.includeOptionalAttributes === true
|
|
4426
|
+
includeOptionalAttributes: options.includeOptionalAttributes === true,
|
|
4427
|
+
maxDepth: options.maxDepth ?? 3,
|
|
4428
|
+
maxChoiceBranches: options.maxChoiceBranches ?? 1,
|
|
4429
|
+
expandRepeatingElements: options.expandRepeatingElements ?? 2
|
|
3909
4430
|
};
|
|
3910
4431
|
const root = selectRoot(schema, options);
|
|
3911
4432
|
if (!root) {
|
|
@@ -3918,7 +4439,9 @@ function generateXmlFromSchema(schema, options = {}, helpers = {}) {
|
|
|
3918
4439
|
const state = {
|
|
3919
4440
|
resolveAttributeGroup: helpers.resolveAttributeGroup,
|
|
3920
4441
|
targetPrefix: options.targetPrefix || "tns",
|
|
3921
|
-
nsContext
|
|
4442
|
+
nsContext,
|
|
4443
|
+
visitedTypes: /* @__PURE__ */ new Set(),
|
|
4444
|
+
currentDepth: 0
|
|
3922
4445
|
};
|
|
3923
4446
|
const [rootNode] = buildElementInstances(
|
|
3924
4447
|
schema,
|
|
@@ -4021,6 +4544,80 @@ function generateSampleXml({ xsdText, options = {} } = {}) {
|
|
|
4021
4544
|
});
|
|
4022
4545
|
}
|
|
4023
4546
|
|
|
4547
|
+
// src/validation/wildcardValidator.js
|
|
4548
|
+
function namespaceMatches(elementNamespace, wildcardNamespace, targetNamespace) {
|
|
4549
|
+
if (!wildcardNamespace) return true;
|
|
4550
|
+
if (wildcardNamespace === "##any") return true;
|
|
4551
|
+
if (wildcardNamespace === "##targetNamespace") {
|
|
4552
|
+
return elementNamespace === targetNamespace;
|
|
4553
|
+
}
|
|
4554
|
+
if (wildcardNamespace === "##other") {
|
|
4555
|
+
return elementNamespace !== targetNamespace;
|
|
4556
|
+
}
|
|
4557
|
+
if (Array.isArray(wildcardNamespace)) {
|
|
4558
|
+
return wildcardNamespace.includes(elementNamespace);
|
|
4559
|
+
}
|
|
4560
|
+
return elementNamespace === wildcardNamespace;
|
|
4561
|
+
}
|
|
4562
|
+
function isExcludedByNotNamespace(elementNamespace, notNamespace) {
|
|
4563
|
+
if (!notNamespace || notNamespace.length === 0) return false;
|
|
4564
|
+
return notNamespace.includes(elementNamespace);
|
|
4565
|
+
}
|
|
4566
|
+
function isExcludedByNotQName(qName, notQName) {
|
|
4567
|
+
if (!notQName || notQName.length === 0) return false;
|
|
4568
|
+
return notQName.includes(qName);
|
|
4569
|
+
}
|
|
4570
|
+
function buildQName(localName4, namespaceUri4) {
|
|
4571
|
+
if (!namespaceUri4 || namespaceUri4 === "") {
|
|
4572
|
+
return localName4;
|
|
4573
|
+
}
|
|
4574
|
+
return `{${namespaceUri4}}${localName4}`;
|
|
4575
|
+
}
|
|
4576
|
+
function elementMatchesWildcard(elementLocalName, elementNamespace, wildcardNode, targetNamespace) {
|
|
4577
|
+
if (!wildcardNode || wildcardNode.kind !== "any") {
|
|
4578
|
+
return false;
|
|
4579
|
+
}
|
|
4580
|
+
if (!namespaceMatches(elementNamespace, wildcardNode.namespace, targetNamespace)) {
|
|
4581
|
+
return false;
|
|
4582
|
+
}
|
|
4583
|
+
if (isExcludedByNotNamespace(elementNamespace, wildcardNode.notNamespace)) {
|
|
4584
|
+
return false;
|
|
4585
|
+
}
|
|
4586
|
+
const qName = buildQName(elementLocalName, elementNamespace);
|
|
4587
|
+
if (isExcludedByNotQName(qName, wildcardNode.notQName)) {
|
|
4588
|
+
return false;
|
|
4589
|
+
}
|
|
4590
|
+
return true;
|
|
4591
|
+
}
|
|
4592
|
+
function attributeMatchesWildcard(attrLocalName, attrNamespace, wildcardNode, targetNamespace) {
|
|
4593
|
+
if (!wildcardNode || wildcardNode.kind !== "anyAttribute") {
|
|
4594
|
+
return false;
|
|
4595
|
+
}
|
|
4596
|
+
if (!namespaceMatches(attrNamespace, wildcardNode.namespace, targetNamespace)) {
|
|
4597
|
+
return false;
|
|
4598
|
+
}
|
|
4599
|
+
if (isExcludedByNotNamespace(attrNamespace, wildcardNode.notNamespace)) {
|
|
4600
|
+
return false;
|
|
4601
|
+
}
|
|
4602
|
+
const qName = buildQName(attrLocalName, attrNamespace);
|
|
4603
|
+
if (isExcludedByNotQName(qName, wildcardNode.notQName)) {
|
|
4604
|
+
return false;
|
|
4605
|
+
}
|
|
4606
|
+
return true;
|
|
4607
|
+
}
|
|
4608
|
+
function normalizeProcessContents(processContents) {
|
|
4609
|
+
if (processContents === "lax" || processContents === "skip") {
|
|
4610
|
+
return processContents;
|
|
4611
|
+
}
|
|
4612
|
+
return "strict";
|
|
4613
|
+
}
|
|
4614
|
+
function isStrictWildcardValidation(processContents) {
|
|
4615
|
+
return normalizeProcessContents(processContents) === "strict";
|
|
4616
|
+
}
|
|
4617
|
+
function shouldSkipWildcardValidation(processContents) {
|
|
4618
|
+
return normalizeProcessContents(processContents) === "skip";
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4024
4621
|
// src/validation/structureValidator.js
|
|
4025
4622
|
function elementChildren2(xmlNode) {
|
|
4026
4623
|
return Array.from(xmlNode?.children || []).filter((child) => child.nodeType === 1);
|
|
@@ -4090,6 +4687,7 @@ function isSimpleContentComplexType(complexTypeDecl) {
|
|
|
4090
4687
|
function validateAttributes(xmlNode, attributes, context) {
|
|
4091
4688
|
const { schema, createIssue: createIssue2, ISSUE_CODES: ISSUE_CODES2, issues, pathParts, validateAttributeValue: validateAttributeValue2 } = context;
|
|
4092
4689
|
const allowed = /* @__PURE__ */ new Map();
|
|
4690
|
+
let anyAttributeWildcard = null;
|
|
4093
4691
|
for (const attr of attributes || []) {
|
|
4094
4692
|
if (!attr) continue;
|
|
4095
4693
|
if (attr.kind === "attribute") {
|
|
@@ -4101,6 +4699,8 @@ function validateAttributes(xmlNode, attributes, context) {
|
|
|
4101
4699
|
const group = context.resolveAttributeGroup?.(attr.refName);
|
|
4102
4700
|
if (!group) continue;
|
|
4103
4701
|
validateAttributes(xmlNode, group.attributes || [], context);
|
|
4702
|
+
} else if (attr.kind === "anyAttribute") {
|
|
4703
|
+
anyAttributeWildcard = attr;
|
|
4104
4704
|
}
|
|
4105
4705
|
}
|
|
4106
4706
|
for (const attrDecl of allowed.values()) {
|
|
@@ -4146,6 +4746,15 @@ function validateAttributes(xmlNode, attributes, context) {
|
|
|
4146
4746
|
continue;
|
|
4147
4747
|
}
|
|
4148
4748
|
if (!allowed.has(attr.name)) {
|
|
4749
|
+
if (anyAttributeWildcard) {
|
|
4750
|
+
const attrLocalName = attr.localName || attr.name.split(":")[1] || attr.name;
|
|
4751
|
+
const attrNamespace = attr.namespaceURI || null;
|
|
4752
|
+
if (attributeMatchesWildcard(attrLocalName, attrNamespace, anyAttributeWildcard, schema.targetNamespace)) {
|
|
4753
|
+
if (!shouldSkipWildcardValidation(anyAttributeWildcard.processContents)) {
|
|
4754
|
+
}
|
|
4755
|
+
continue;
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4149
4758
|
issues.push(
|
|
4150
4759
|
createIssue2({
|
|
4151
4760
|
code: ISSUE_CODES2.XML_UNEXPECTED_ATTRIBUTE,
|
|
@@ -4563,7 +5172,16 @@ function validateContentModel(children, modelNode, context, pathParts, startInde
|
|
|
4563
5172
|
return validateAll(children, startIndex, modelNode, context, pathParts, silent);
|
|
4564
5173
|
case "any":
|
|
4565
5174
|
if (startIndex < children.length) {
|
|
4566
|
-
|
|
5175
|
+
const childNode = children[startIndex];
|
|
5176
|
+
const childLocalName = localName(childNode);
|
|
5177
|
+
const childNamespace = namespaceUri(childNode);
|
|
5178
|
+
if (elementMatchesWildcard(childLocalName, childNamespace, modelNode, context.schema.targetNamespace)) {
|
|
5179
|
+
if (isStrictWildcardValidation(modelNode.processContents)) {
|
|
5180
|
+
validateElementDecl(childNode, { name: childLocalName, typeName: null }, context, [...pathParts, childLocalName]);
|
|
5181
|
+
} else if (!shouldSkipWildcardValidation(modelNode.processContents)) {
|
|
5182
|
+
}
|
|
5183
|
+
return { nextIndex: startIndex + 1, matched: true, matchedAny: true };
|
|
5184
|
+
}
|
|
4567
5185
|
}
|
|
4568
5186
|
return { nextIndex: startIndex, matched: true, matchedAny: false };
|
|
4569
5187
|
default:
|