@llm4ts/shell 2.13.0 → 2.14.0
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.
|
@@ -406,7 +406,9 @@ export const resolveQName = (element, value) => {
|
|
|
406
406
|
};
|
|
407
407
|
// ---------------------------------------------------------------------------
|
|
408
408
|
// Writing
|
|
409
|
-
|
|
409
|
+
// A carriage return is written as a reference: a literal one would be
|
|
410
|
+
// turned into a line feed by the reader's end-of-line handling.
|
|
411
|
+
export const escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r/g, " ");
|
|
410
412
|
export const escapeAttribute = (value) => escapeText(value).replace(/"/g, """);
|
|
411
413
|
const qualifiedName = (prefix, local) => prefix === "" ? local : `${prefix}:${local}`;
|
|
412
414
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AttributeField, ComplexTypeDef, ElementDecl, ElementField, Facets, isBuiltin, OpenQuestion, qname, SimpleTypeDef, splitClark, XsdNamespace } from "./Catalog.js";
|
|
1
|
+
import { AttributeField, ComplexTypeDef, ElementDecl, ElementField, Facets, isBuiltin, ModelGroup, OpenQuestion, qname, SimpleTypeDef, splitClark, XsdNamespace } from "./Catalog.js";
|
|
2
2
|
import { attribute, elements, firstChild, is, resolveQName, textOf } from "./Xml.js";
|
|
3
3
|
// XSD reading in two passes. `collectSchema` reads one `xs:schema` element
|
|
4
4
|
// into a collector without resolving anything across documents (imports and
|
|
@@ -13,8 +13,11 @@ export const makeSchemaCollector = () => ({
|
|
|
13
13
|
complex: new Map(),
|
|
14
14
|
simple: new Map(),
|
|
15
15
|
elements: new Map(),
|
|
16
|
+
attributes: new Map(),
|
|
16
17
|
questions: [],
|
|
17
|
-
namespaces: new Set()
|
|
18
|
+
namespaces: new Set(),
|
|
19
|
+
anonymous: new Set(),
|
|
20
|
+
locations: new Map()
|
|
18
21
|
});
|
|
19
22
|
const xs = (element, local) => is(element, XsdNamespace, local);
|
|
20
23
|
const xsChildren = (element) => elements(element).filter((child) => child.name.namespace === XsdNamespace);
|
|
@@ -42,10 +45,30 @@ const parseMinOccurs = (value) => {
|
|
|
42
45
|
const parsed = parseOccurs(value, 1);
|
|
43
46
|
return parsed === "unbounded" ? 1 : parsed;
|
|
44
47
|
};
|
|
48
|
+
/** An XSD boolean attribute: `true` or `1`. */
|
|
49
|
+
const flag = (element, name) => {
|
|
50
|
+
const value = attribute(element, name)?.trim();
|
|
51
|
+
return value === "true" || value === "1";
|
|
52
|
+
};
|
|
53
|
+
const facetNames = new Set([
|
|
54
|
+
"enumeration",
|
|
55
|
+
"pattern",
|
|
56
|
+
"length",
|
|
57
|
+
"minLength",
|
|
58
|
+
"maxLength",
|
|
59
|
+
"totalDigits",
|
|
60
|
+
"fractionDigits",
|
|
61
|
+
"minInclusive",
|
|
62
|
+
"maxInclusive",
|
|
63
|
+
"minExclusive",
|
|
64
|
+
"maxExclusive",
|
|
65
|
+
"whiteSpace"
|
|
66
|
+
]);
|
|
45
67
|
const multiply = (left, right) => left === "unbounded" || right === "unbounded" ? "unbounded" : left * right;
|
|
46
68
|
class SchemaReader {
|
|
47
69
|
targetNamespace;
|
|
48
70
|
qualifiedElements;
|
|
71
|
+
qualifiedAttributes;
|
|
49
72
|
/** A schema without targetNamespace included into one: its no-namespace refs mean the includer's. */
|
|
50
73
|
chameleon;
|
|
51
74
|
schema;
|
|
@@ -61,6 +84,20 @@ class SchemaReader {
|
|
|
61
84
|
this.chameleon =
|
|
62
85
|
attribute(schema, "targetNamespace") === undefined && namespaceOverride !== undefined;
|
|
63
86
|
this.qualifiedElements = attribute(schema, "elementFormDefault") === "qualified";
|
|
87
|
+
this.qualifiedAttributes = attribute(schema, "attributeFormDefault") === "qualified";
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A name for an anonymous type at `path` (`~op/x`). A global element and
|
|
91
|
+
* a named type may share a local name, so a taken path gets a `#n` suffix.
|
|
92
|
+
*/
|
|
93
|
+
anonymousName(path) {
|
|
94
|
+
const base = `~${path.replace(/^~/, "")}`;
|
|
95
|
+
let name = qname(this.targetNamespace, base);
|
|
96
|
+
for (let n = 2; this.into.anonymous.has(name); n++) {
|
|
97
|
+
name = qname(this.targetNamespace, `${base}#${n}`);
|
|
98
|
+
}
|
|
99
|
+
this.into.anonymous.add(name);
|
|
100
|
+
return name;
|
|
64
101
|
}
|
|
65
102
|
where(element) {
|
|
66
103
|
return `${this.document}:${element.line}`;
|
|
@@ -89,7 +126,7 @@ class SchemaReader {
|
|
|
89
126
|
const references = [];
|
|
90
127
|
for (const child of xsChildren(this.schema)) {
|
|
91
128
|
const name = attribute(child, "name");
|
|
92
|
-
if (xs(child, "import") || xs(child, "include")) {
|
|
129
|
+
if (xs(child, "import") || xs(child, "include") || xs(child, "redefine")) {
|
|
93
130
|
const location = attribute(child, "schemaLocation");
|
|
94
131
|
if (location !== undefined) {
|
|
95
132
|
references.push({
|
|
@@ -98,6 +135,15 @@ class SchemaReader {
|
|
|
98
135
|
location
|
|
99
136
|
});
|
|
100
137
|
}
|
|
138
|
+
const redefined = xsChildren(child)
|
|
139
|
+
.filter((definition) => !xs(definition, "annotation"))
|
|
140
|
+
.map((definition) => attribute(definition, "name") ?? "?");
|
|
141
|
+
if (xs(child, "redefine") && redefined.length > 0) {
|
|
142
|
+
this.question("redefine", child, location ?? "?", `schema ${location ?? "?"} is included, but its redefinitions of ${redefined.join(", ")} are not applied`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (xs(child, "attribute") && name !== undefined) {
|
|
146
|
+
this.globalAttribute(child, name);
|
|
101
147
|
}
|
|
102
148
|
else if (xs(child, "element") && name !== undefined) {
|
|
103
149
|
this.globalElement(child, name);
|
|
@@ -119,31 +165,47 @@ class SchemaReader {
|
|
|
119
165
|
if (attribute(element, "substitutionGroup") !== undefined) {
|
|
120
166
|
this.question("substitution-group", element, name, `element ${name} joins substitution group ${attribute(element, "substitutionGroup") ?? ""}; substitutes are not modelled`);
|
|
121
167
|
}
|
|
122
|
-
if (
|
|
168
|
+
if (flag(element, "abstract")) {
|
|
123
169
|
this.question("abstract", element, name, `element ${name} is abstract`);
|
|
124
170
|
}
|
|
125
|
-
const type = this.elementType(element,
|
|
171
|
+
const type = this.elementType(element, name);
|
|
172
|
+
const fixed = attribute(element, "fixed");
|
|
173
|
+
this.into.locations.set(elementName, this.where(element));
|
|
126
174
|
this.into.elements.set(elementName, new ElementDecl({
|
|
127
175
|
name: elementName,
|
|
128
176
|
type,
|
|
129
|
-
nillable:
|
|
177
|
+
nillable: flag(element, "nillable"),
|
|
178
|
+
...(fixed === undefined ? {} : { fixed }),
|
|
130
179
|
...optionalDoc(documentationOf(element))
|
|
131
180
|
}));
|
|
132
181
|
}
|
|
182
|
+
globalAttribute(element, name) {
|
|
183
|
+
const inline = firstChild(element, XsdNamespace, "simpleType");
|
|
184
|
+
let type = this.typeRef(element, attribute(element, "type"));
|
|
185
|
+
if (type === undefined && inline !== undefined) {
|
|
186
|
+
type = this.anonymousName(`@${name}`);
|
|
187
|
+
this.simpleType(inline, type, true);
|
|
188
|
+
}
|
|
189
|
+
this.into.attributes.set(qname(this.targetNamespace, name), {
|
|
190
|
+
type: type ?? stringType,
|
|
191
|
+
fixed: attribute(element, "fixed"),
|
|
192
|
+
documentation: documentationOf(element)
|
|
193
|
+
});
|
|
194
|
+
}
|
|
133
195
|
/** The type of an element: its `type=` or its inline (anonymous) type. */
|
|
134
|
-
elementType(element,
|
|
196
|
+
elementType(element, anonymousPath) {
|
|
135
197
|
const declared = this.typeRef(element, attribute(element, "type"));
|
|
136
198
|
if (declared !== undefined)
|
|
137
199
|
return declared;
|
|
138
200
|
const inlineComplex = firstChild(element, XsdNamespace, "complexType");
|
|
139
201
|
if (inlineComplex !== undefined) {
|
|
140
|
-
const name =
|
|
202
|
+
const name = this.anonymousName(anonymousPath);
|
|
141
203
|
this.complexType(inlineComplex, name, true);
|
|
142
204
|
return name;
|
|
143
205
|
}
|
|
144
206
|
const inlineSimple = firstChild(element, XsdNamespace, "simpleType");
|
|
145
207
|
if (inlineSimple !== undefined) {
|
|
146
|
-
const name =
|
|
208
|
+
const name = this.anonymousName(anonymousPath);
|
|
147
209
|
this.simpleType(inlineSimple, name, true);
|
|
148
210
|
return name;
|
|
149
211
|
}
|
|
@@ -151,48 +213,69 @@ class SchemaReader {
|
|
|
151
213
|
}
|
|
152
214
|
complexType(element, name, anonymous) {
|
|
153
215
|
const local = splitClark(name).local;
|
|
154
|
-
|
|
216
|
+
const complexContent = firstChild(element, XsdNamespace, "complexContent");
|
|
217
|
+
const simpleContent = firstChild(element, XsdNamespace, "simpleContent");
|
|
218
|
+
if (flag(element, "mixed") || (complexContent !== undefined && flag(complexContent, "mixed"))) {
|
|
155
219
|
this.question("mixed-content", element, local, `type ${local} allows mixed text content`);
|
|
156
220
|
}
|
|
157
|
-
if (
|
|
221
|
+
if (flag(element, "abstract")) {
|
|
158
222
|
this.question("abstract", element, local, `type ${local} is abstract`);
|
|
159
223
|
}
|
|
160
224
|
let base;
|
|
225
|
+
let derivation;
|
|
161
226
|
let textType;
|
|
162
227
|
let content = element;
|
|
163
|
-
const complexContent = firstChild(element, XsdNamespace, "complexContent");
|
|
164
|
-
const simpleContent = firstChild(element, XsdNamespace, "simpleContent");
|
|
165
228
|
if (complexContent !== undefined) {
|
|
166
229
|
const extension = firstChild(complexContent, XsdNamespace, "extension");
|
|
167
230
|
const restriction = firstChild(complexContent, XsdNamespace, "restriction");
|
|
168
231
|
if (extension !== undefined) {
|
|
169
232
|
base = this.typeRef(extension, attribute(extension, "base"));
|
|
233
|
+
derivation = "extension";
|
|
170
234
|
content = extension;
|
|
171
235
|
}
|
|
172
236
|
else if (restriction !== undefined) {
|
|
173
237
|
this.question("complex-restriction", restriction, local, `type ${local} restricts ${attribute(restriction, "base") ?? "?"}; only the restated content is modelled`);
|
|
238
|
+
base = this.typeRef(restriction, attribute(restriction, "base"));
|
|
239
|
+
derivation = "restriction";
|
|
174
240
|
content = restriction;
|
|
175
241
|
}
|
|
176
242
|
}
|
|
177
243
|
else if (simpleContent !== undefined) {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
244
|
+
const extension = firstChild(simpleContent, XsdNamespace, "extension");
|
|
245
|
+
const restriction = firstChild(simpleContent, XsdNamespace, "restriction");
|
|
246
|
+
const step = extension ?? restriction;
|
|
247
|
+
if (step !== undefined) {
|
|
181
248
|
// The base may be a simple type (the text's type) or a complex type
|
|
182
249
|
// with simple content (inherit its text type and attributes); which
|
|
183
250
|
// one is only known once every schema is read.
|
|
184
|
-
base = this.typeRef(
|
|
251
|
+
base = this.typeRef(step, attribute(step, "base")) ?? stringType;
|
|
252
|
+
derivation = extension === undefined ? "restriction" : "extension";
|
|
185
253
|
textType = base;
|
|
186
|
-
content =
|
|
254
|
+
content = step;
|
|
255
|
+
const inline = firstChild(step, XsdNamespace, "simpleType");
|
|
256
|
+
const facets = xsChildren(step).some((child) => facetNames.has(child.name.local));
|
|
257
|
+
if (extension === undefined && (facets || inline !== undefined)) {
|
|
258
|
+
// A restriction narrows the text: an anonymous simple type over
|
|
259
|
+
// the base's text type (resolved in the finish pass) keeps its facets.
|
|
260
|
+
textType = this.anonymousName(`${local}/#text`);
|
|
261
|
+
this.simpleType(step, textType, true, base);
|
|
262
|
+
}
|
|
187
263
|
}
|
|
188
264
|
}
|
|
189
265
|
const fields = [];
|
|
266
|
+
const groups = [];
|
|
190
267
|
const attributes = [];
|
|
191
268
|
let counter = 0;
|
|
192
269
|
const next = () => counter++;
|
|
193
270
|
for (const child of xsChildren(content)) {
|
|
194
271
|
if (xs(child, "sequence") || xs(child, "all") || xs(child, "choice")) {
|
|
195
|
-
this.particle(child, local, fields,
|
|
272
|
+
this.particle(child, local, fields, groups, {
|
|
273
|
+
choice: undefined,
|
|
274
|
+
branch: undefined,
|
|
275
|
+
group: undefined,
|
|
276
|
+
optional: false,
|
|
277
|
+
repeats: 1
|
|
278
|
+
}, next);
|
|
196
279
|
}
|
|
197
280
|
else if (xs(child, "group")) {
|
|
198
281
|
this.question("group-ref", child, local, `type ${local} uses model group ${attribute(child, "ref") ?? "?"}; its fields are not modelled`);
|
|
@@ -203,7 +286,9 @@ class SchemaReader {
|
|
|
203
286
|
name,
|
|
204
287
|
anonymous,
|
|
205
288
|
base,
|
|
289
|
+
derivation,
|
|
206
290
|
fields,
|
|
291
|
+
groups,
|
|
207
292
|
attributes,
|
|
208
293
|
textType,
|
|
209
294
|
simpleContent: simpleContent !== undefined,
|
|
@@ -211,21 +296,33 @@ class SchemaReader {
|
|
|
211
296
|
location: this.where(element)
|
|
212
297
|
});
|
|
213
298
|
}
|
|
214
|
-
particle(group, owner, into, context, next) {
|
|
215
|
-
const
|
|
216
|
-
const
|
|
299
|
+
particle(group, owner, into, groups, context, next) {
|
|
300
|
+
const minOccurs = parseMinOccurs(attribute(group, "minOccurs"));
|
|
301
|
+
const maxOccurs = parseOccurs(attribute(group, "maxOccurs"), 1);
|
|
302
|
+
const optional = context.optional || minOccurs === 0;
|
|
303
|
+
const repeats = multiply(context.repeats, maxOccurs);
|
|
217
304
|
const isChoice = xs(group, "choice");
|
|
218
305
|
const choice = isChoice ? next() : context.choice;
|
|
306
|
+
// The exact content model: every group, with its own bounds and parent.
|
|
307
|
+
const id = groups.length;
|
|
308
|
+
groups.push(new ModelGroup({
|
|
309
|
+
id,
|
|
310
|
+
kind: isChoice ? "choice" : xs(group, "all") ? "all" : "sequence",
|
|
311
|
+
minOccurs,
|
|
312
|
+
maxOccurs,
|
|
313
|
+
...(context.group === undefined ? {} : { parent: context.group }),
|
|
314
|
+
start: into.length
|
|
315
|
+
}));
|
|
219
316
|
for (const child of xsChildren(group)) {
|
|
220
317
|
// Every alternative of a choice is its own branch; inside a branch,
|
|
221
318
|
// a nested sequence keeps the branch it belongs to.
|
|
222
319
|
const branch = isChoice ? next() : context.branch;
|
|
223
|
-
const inner = { choice, branch, optional, repeats };
|
|
320
|
+
const inner = { choice, branch, group: id, optional, repeats };
|
|
224
321
|
if (xs(child, "element")) {
|
|
225
322
|
into.push(this.localElement(child, owner, inner));
|
|
226
323
|
}
|
|
227
324
|
else if (xs(child, "sequence") || xs(child, "choice") || xs(child, "all")) {
|
|
228
|
-
this.particle(child, owner, into, inner, next);
|
|
325
|
+
this.particle(child, owner, into, groups, inner, next);
|
|
229
326
|
}
|
|
230
327
|
else if (xs(child, "any")) {
|
|
231
328
|
this.question("xsd-any", child, owner, `type ${owner} accepts any element (namespace ${attribute(child, "namespace") ?? "##any"}); its content is open`);
|
|
@@ -237,14 +334,19 @@ class SchemaReader {
|
|
|
237
334
|
}
|
|
238
335
|
localElement(element, owner, context) {
|
|
239
336
|
const ref = this.typeRef(element, attribute(element, "ref"));
|
|
240
|
-
const
|
|
241
|
-
|
|
337
|
+
const own = {
|
|
338
|
+
min: parseMinOccurs(attribute(element, "minOccurs")),
|
|
339
|
+
max: parseOccurs(attribute(element, "maxOccurs"), 1)
|
|
340
|
+
};
|
|
242
341
|
const base = {
|
|
243
|
-
minOccurs,
|
|
244
|
-
maxOccurs,
|
|
245
|
-
|
|
342
|
+
minOccurs: context.optional ? 0 : own.min,
|
|
343
|
+
maxOccurs: multiply(own.max, context.repeats),
|
|
344
|
+
own,
|
|
345
|
+
nillable: flag(element, "nillable"),
|
|
346
|
+
fixed: attribute(element, "fixed"),
|
|
246
347
|
choice: context.choice,
|
|
247
348
|
branch: context.branch,
|
|
349
|
+
group: context.group ?? 0,
|
|
248
350
|
documentation: documentationOf(element),
|
|
249
351
|
location: this.where(element)
|
|
250
352
|
};
|
|
@@ -259,26 +361,49 @@ class SchemaReader {
|
|
|
259
361
|
...base,
|
|
260
362
|
name,
|
|
261
363
|
namespace: qualified ? this.targetNamespace : "",
|
|
262
|
-
type: this.elementType(element,
|
|
364
|
+
type: this.elementType(element, `${owner}/${name}`),
|
|
263
365
|
ref: undefined
|
|
264
366
|
};
|
|
265
367
|
}
|
|
266
368
|
attributes(content, owner, into) {
|
|
267
369
|
for (const child of xsChildren(content)) {
|
|
268
370
|
if (xs(child, "attribute")) {
|
|
269
|
-
const
|
|
371
|
+
const common = {
|
|
372
|
+
use: attribute(child, "use"),
|
|
373
|
+
fixed: attribute(child, "fixed"),
|
|
374
|
+
documentation: documentationOf(child),
|
|
375
|
+
location: this.where(child)
|
|
376
|
+
};
|
|
377
|
+
const ref = this.typeRef(child, attribute(child, "ref"));
|
|
378
|
+
if (ref !== undefined) {
|
|
379
|
+
// A global attribute: its name, type and namespace are the
|
|
380
|
+
// declaration's, looked up once every schema is read.
|
|
381
|
+
const target = splitClark(ref);
|
|
382
|
+
into.push({
|
|
383
|
+
...common,
|
|
384
|
+
name: target.local,
|
|
385
|
+
namespace: target.namespace === "" ? undefined : target.namespace,
|
|
386
|
+
type: stringType,
|
|
387
|
+
ref
|
|
388
|
+
});
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
const name = attribute(child, "name") ?? "?";
|
|
270
392
|
const inline = firstChild(child, XsdNamespace, "simpleType");
|
|
271
393
|
let type = this.typeRef(child, attribute(child, "type"));
|
|
272
394
|
if (type === undefined && inline !== undefined) {
|
|
273
|
-
type =
|
|
395
|
+
type = this.anonymousName(`${owner}/@${name}`);
|
|
274
396
|
this.simpleType(inline, type, true);
|
|
275
397
|
}
|
|
276
|
-
|
|
398
|
+
const form = attribute(child, "form");
|
|
399
|
+
const qualified = form === undefined ? this.qualifiedAttributes : form === "qualified";
|
|
400
|
+
into.push({
|
|
401
|
+
...common,
|
|
277
402
|
name,
|
|
403
|
+
namespace: qualified && this.targetNamespace !== "" ? this.targetNamespace : undefined,
|
|
278
404
|
type: type ?? stringType,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}));
|
|
405
|
+
ref: undefined
|
|
406
|
+
});
|
|
282
407
|
}
|
|
283
408
|
else if (xs(child, "attributeGroup")) {
|
|
284
409
|
this.question("attribute-group", child, owner, `type ${owner} uses attribute group ${attribute(child, "ref") ?? "?"}; its attributes are not modelled`);
|
|
@@ -288,24 +413,59 @@ class SchemaReader {
|
|
|
288
413
|
}
|
|
289
414
|
}
|
|
290
415
|
}
|
|
291
|
-
|
|
416
|
+
/**
|
|
417
|
+
* A simple type from its `xs:simpleType` element, or (with `restrictionBase`)
|
|
418
|
+
* from a simpleContent restriction whose base is only known later.
|
|
419
|
+
*/
|
|
420
|
+
simpleType(element, name, anonymous, restrictionBase) {
|
|
292
421
|
const local = splitClark(name).local;
|
|
293
|
-
|
|
422
|
+
this.into.locations.set(name, this.where(element));
|
|
423
|
+
const restriction = restrictionBase === undefined ? firstChild(element, XsdNamespace, "restriction") : element;
|
|
424
|
+
const list = firstChild(element, XsdNamespace, "list");
|
|
425
|
+
if (restriction === undefined && list !== undefined) {
|
|
426
|
+
// A list: whitespace-separated items of the item type; length facets
|
|
427
|
+
// of a restriction over it count items.
|
|
428
|
+
const inline = firstChild(list, XsdNamespace, "simpleType");
|
|
429
|
+
let itemType = this.typeRef(list, attribute(list, "itemType"));
|
|
430
|
+
if (itemType === undefined && inline !== undefined) {
|
|
431
|
+
itemType = this.anonymousName(`${local}/#item`);
|
|
432
|
+
this.simpleType(inline, itemType, true);
|
|
433
|
+
}
|
|
434
|
+
this.into.simple.set(name, new SimpleTypeDef({
|
|
435
|
+
name,
|
|
436
|
+
anonymous,
|
|
437
|
+
base: stringType,
|
|
438
|
+
facets: new Facets({}),
|
|
439
|
+
variety: "list",
|
|
440
|
+
itemType: itemType ?? stringType,
|
|
441
|
+
...optionalDoc(documentationOf(element))
|
|
442
|
+
}));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
294
445
|
if (restriction === undefined) {
|
|
295
|
-
this.question("list-or-union", element, local, `simple type ${local} is a
|
|
446
|
+
this.question("list-or-union", element, local, `simple type ${local} is a union; values are treated as strings`);
|
|
296
447
|
this.into.simple.set(name, new SimpleTypeDef({
|
|
297
448
|
name,
|
|
298
449
|
anonymous,
|
|
299
450
|
base: stringType,
|
|
300
451
|
facets: new Facets({}),
|
|
452
|
+
variety: "union",
|
|
301
453
|
...optionalDoc(documentationOf(element))
|
|
302
454
|
}));
|
|
303
455
|
return;
|
|
304
456
|
}
|
|
457
|
+
// The base is `base=`, or an anonymous simple type written inside.
|
|
458
|
+
let base = this.typeRef(restriction, attribute(restriction, "base")) ?? restrictionBase;
|
|
459
|
+
const inline = firstChild(restriction, XsdNamespace, "simpleType");
|
|
460
|
+
if (inline !== undefined && (base === undefined || restrictionBase !== undefined)) {
|
|
461
|
+
const inlineName = this.anonymousName(`${local}/#base`);
|
|
462
|
+
this.simpleType(inline, inlineName, true);
|
|
463
|
+
base = inlineName;
|
|
464
|
+
}
|
|
305
465
|
this.into.simple.set(name, new SimpleTypeDef({
|
|
306
466
|
name,
|
|
307
467
|
anonymous,
|
|
308
|
-
base:
|
|
468
|
+
base: base ?? stringType,
|
|
309
469
|
facets: readFacets(restriction),
|
|
310
470
|
...optionalDoc(documentationOf(element))
|
|
311
471
|
}));
|
|
@@ -337,6 +497,7 @@ const readFacets = (restriction) => {
|
|
|
337
497
|
const maxLength = intFacet(restriction, "maxLength");
|
|
338
498
|
const totalDigits = intFacet(restriction, "totalDigits");
|
|
339
499
|
const fractionDigits = intFacet(restriction, "fractionDigits");
|
|
500
|
+
const whiteSpace = stringFacet(restriction, "whiteSpace")?.trim();
|
|
340
501
|
return new Facets({
|
|
341
502
|
...(enumeration.length > 0 ? { enumeration } : {}),
|
|
342
503
|
...(pattern.length > 0 ? { pattern } : {}),
|
|
@@ -348,7 +509,10 @@ const readFacets = (restriction) => {
|
|
|
348
509
|
...(minInclusive === undefined ? {} : { minInclusive }),
|
|
349
510
|
...(maxInclusive === undefined ? {} : { maxInclusive }),
|
|
350
511
|
...(minExclusive === undefined ? {} : { minExclusive }),
|
|
351
|
-
...(maxExclusive === undefined ? {} : { maxExclusive })
|
|
512
|
+
...(maxExclusive === undefined ? {} : { maxExclusive }),
|
|
513
|
+
...(whiteSpace === "preserve" || whiteSpace === "replace" || whiteSpace === "collapse"
|
|
514
|
+
? { whiteSpace }
|
|
515
|
+
: {})
|
|
352
516
|
});
|
|
353
517
|
};
|
|
354
518
|
/** Read one `xs:schema` into the collector; returns the imports/includes to follow. */
|
|
@@ -368,9 +532,10 @@ export const finishSchemas = (collector) => {
|
|
|
368
532
|
detail: `type ${name} is not defined in any schema that was read`
|
|
369
533
|
}));
|
|
370
534
|
};
|
|
371
|
-
const resolveField = (field, owner, offset) => {
|
|
535
|
+
const resolveField = (field, owner, offset, groupOffset) => {
|
|
372
536
|
let type = field.type ?? anyType;
|
|
373
537
|
let nillable = field.nillable;
|
|
538
|
+
let fixed = field.fixed;
|
|
374
539
|
if (field.ref !== undefined) {
|
|
375
540
|
const target = collector.elements.get(field.ref);
|
|
376
541
|
if (target === undefined) {
|
|
@@ -384,6 +549,7 @@ export const finishSchemas = (collector) => {
|
|
|
384
549
|
else {
|
|
385
550
|
type = target.type;
|
|
386
551
|
nillable = nillable || target.nillable;
|
|
552
|
+
fixed = fixed ?? target.fixed;
|
|
387
553
|
}
|
|
388
554
|
}
|
|
389
555
|
else if (!typeExists(collector, type)) {
|
|
@@ -398,9 +564,46 @@ export const finishSchemas = (collector) => {
|
|
|
398
564
|
nillable,
|
|
399
565
|
...(field.choice === undefined ? {} : { choice: field.choice + offset }),
|
|
400
566
|
...(field.branch === undefined ? {} : { branch: field.branch + offset }),
|
|
567
|
+
group: field.group + groupOffset,
|
|
568
|
+
occurs: field.own,
|
|
569
|
+
...(fixed === undefined ? {} : { fixed }),
|
|
401
570
|
...optionalDoc(field.documentation)
|
|
402
571
|
});
|
|
403
572
|
};
|
|
573
|
+
const resolveAttribute = (raw, owner) => {
|
|
574
|
+
if (raw.use === "prohibited")
|
|
575
|
+
return undefined;
|
|
576
|
+
let type = raw.type;
|
|
577
|
+
let fixed = raw.fixed;
|
|
578
|
+
let documentation = raw.documentation;
|
|
579
|
+
if (raw.ref !== undefined) {
|
|
580
|
+
const target = collector.attributes.get(raw.ref);
|
|
581
|
+
if (target === undefined) {
|
|
582
|
+
questions.push(new OpenQuestion({
|
|
583
|
+
code: "unresolved-attribute",
|
|
584
|
+
location: raw.location,
|
|
585
|
+
subject: owner,
|
|
586
|
+
detail: `attribute ref ${raw.ref} is not declared in any schema that was read`
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
type = target.type;
|
|
591
|
+
fixed = fixed ?? target.fixed;
|
|
592
|
+
documentation = documentation ?? target.documentation;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (!typeExists(collector, type))
|
|
596
|
+
unresolvedType(type, raw.location, owner);
|
|
597
|
+
return new AttributeField({
|
|
598
|
+
name: raw.name,
|
|
599
|
+
...(raw.namespace === undefined ? {} : { namespace: raw.namespace }),
|
|
600
|
+
type,
|
|
601
|
+
required: raw.use === "required",
|
|
602
|
+
...(fixed === undefined ? {} : { fixed }),
|
|
603
|
+
...optionalDoc(documentation)
|
|
604
|
+
});
|
|
605
|
+
};
|
|
606
|
+
const sameAttribute = (left, right) => left.name === right.name && (left.namespace ?? "") === (right.namespace ?? "");
|
|
404
607
|
const flattened = new Map();
|
|
405
608
|
const flatten = (raw, visiting) => {
|
|
406
609
|
const done = flattened.get(raw.name);
|
|
@@ -408,6 +611,7 @@ export const finishSchemas = (collector) => {
|
|
|
408
611
|
return done;
|
|
409
612
|
const owner = splitClark(raw.name).local;
|
|
410
613
|
let inheritedFields = [];
|
|
614
|
+
let inheritedGroups = [];
|
|
411
615
|
let inheritedAttributes = [];
|
|
412
616
|
let textType = raw.textType;
|
|
413
617
|
if (raw.base !== undefined && !isBuiltin(raw.base)) {
|
|
@@ -418,33 +622,65 @@ export const finishSchemas = (collector) => {
|
|
|
418
622
|
}
|
|
419
623
|
else if (!visiting.has(raw.base)) {
|
|
420
624
|
const base = flatten(baseRaw, new Set([...visiting, raw.name]));
|
|
421
|
-
|
|
625
|
+
// A restriction restates the content; only attributes carry over.
|
|
626
|
+
if (raw.derivation !== "restriction" || raw.simpleContent) {
|
|
627
|
+
inheritedFields = base.fields;
|
|
628
|
+
inheritedGroups = base.groups ?? [];
|
|
629
|
+
}
|
|
422
630
|
inheritedAttributes = base.attributes;
|
|
423
631
|
// Simple content over a complex base: the text type is the base's.
|
|
424
|
-
if (raw.simpleContent)
|
|
632
|
+
if (raw.simpleContent && raw.textType === raw.base)
|
|
425
633
|
textType = base.textType ?? stringType;
|
|
426
634
|
}
|
|
427
635
|
}
|
|
428
636
|
// Choice and branch ids of the derived part continue after the base's,
|
|
429
637
|
// so a base choice and a derived choice stay separate groups.
|
|
430
638
|
const offset = inheritedFields.reduce((max, field) => Math.max(max, (field.choice ?? -1) + 1, (field.branch ?? -1) + 1), 0);
|
|
639
|
+
const groupOffset = inheritedGroups.length;
|
|
640
|
+
const fieldOffset = inheritedFields.length;
|
|
431
641
|
if (textType !== undefined && !typeExists(collector, textType)) {
|
|
432
642
|
unresolvedType(textType, raw.location, owner);
|
|
433
643
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
644
|
+
// Derived attributes restate (or prohibit) inherited ones of the same name.
|
|
645
|
+
const attributes = [];
|
|
646
|
+
for (const inherited of inheritedAttributes) {
|
|
647
|
+
const restated = raw.attributes.find((candidate) => sameAttribute(inherited, candidate));
|
|
648
|
+
if (restated === undefined)
|
|
649
|
+
attributes.push(inherited);
|
|
650
|
+
else {
|
|
651
|
+
const resolved = resolveAttribute(restated, owner);
|
|
652
|
+
if (resolved !== undefined)
|
|
653
|
+
attributes.push(resolved);
|
|
437
654
|
}
|
|
438
655
|
}
|
|
656
|
+
for (const declared of raw.attributes) {
|
|
657
|
+
if (inheritedAttributes.some((inherited) => sameAttribute(inherited, declared)))
|
|
658
|
+
continue;
|
|
659
|
+
const resolved = resolveAttribute(declared, owner);
|
|
660
|
+
if (resolved !== undefined)
|
|
661
|
+
attributes.push(resolved);
|
|
662
|
+
}
|
|
663
|
+
const groups = [
|
|
664
|
+
...inheritedGroups,
|
|
665
|
+
...raw.groups.map((group) => new ModelGroup({
|
|
666
|
+
...group,
|
|
667
|
+
id: group.id + groupOffset,
|
|
668
|
+
...(group.parent === undefined ? {} : { parent: group.parent + groupOffset }),
|
|
669
|
+
start: group.start + fieldOffset
|
|
670
|
+
}))
|
|
671
|
+
];
|
|
439
672
|
const result = new ComplexTypeDef({
|
|
440
673
|
name: raw.name,
|
|
441
674
|
anonymous: raw.anonymous,
|
|
442
|
-
...(raw.base === undefined
|
|
675
|
+
...(raw.base === undefined || (raw.derivation === "restriction" && !raw.simpleContent)
|
|
676
|
+
? {}
|
|
677
|
+
: { base: raw.base }),
|
|
443
678
|
fields: [
|
|
444
679
|
...inheritedFields,
|
|
445
|
-
...raw.fields.map((field) => resolveField(field, owner, offset))
|
|
680
|
+
...raw.fields.map((field) => resolveField(field, owner, offset, groupOffset))
|
|
446
681
|
],
|
|
447
|
-
|
|
682
|
+
...(groups.length === 0 ? {} : { groups }),
|
|
683
|
+
attributes,
|
|
448
684
|
...(textType === undefined ? {} : { textType }),
|
|
449
685
|
...optionalDoc(raw.documentation)
|
|
450
686
|
});
|
|
@@ -452,15 +688,28 @@ export const finishSchemas = (collector) => {
|
|
|
452
688
|
return result;
|
|
453
689
|
};
|
|
454
690
|
const complex = [...collector.complex.values()].map((raw) => flatten(raw, new Set()));
|
|
455
|
-
|
|
691
|
+
// A simpleContent restriction's text type restricts its complex base's
|
|
692
|
+
// text type, known only now.
|
|
693
|
+
const simple = [...collector.simple.values()].map((type) => {
|
|
694
|
+
const complexBase = collector.complex.get(type.base);
|
|
695
|
+
if (complexBase === undefined)
|
|
696
|
+
return type;
|
|
697
|
+
return new SimpleTypeDef({
|
|
698
|
+
...type,
|
|
699
|
+
base: flatten(complexBase, new Set()).textType ?? stringType
|
|
700
|
+
});
|
|
701
|
+
});
|
|
456
702
|
for (const type of simple) {
|
|
457
|
-
|
|
458
|
-
|
|
703
|
+
const types = [type.base, ...(type.itemType === undefined ? [] : [type.itemType])];
|
|
704
|
+
for (const name of types) {
|
|
705
|
+
if (!typeExists(collector, name)) {
|
|
706
|
+
unresolvedType(name, collector.locations.get(type.name) ?? "(simple type)", splitClark(type.name).local);
|
|
707
|
+
}
|
|
459
708
|
}
|
|
460
709
|
}
|
|
461
710
|
for (const element of collector.elements.values()) {
|
|
462
711
|
if (!typeExists(collector, element.type)) {
|
|
463
|
-
unresolvedType(element.type, "(global element)", splitClark(element.name).local);
|
|
712
|
+
unresolvedType(element.type, collector.locations.get(element.name) ?? "(global element)", splitClark(element.name).local);
|
|
464
713
|
}
|
|
465
714
|
}
|
|
466
715
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm4ts/shell",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Interactive shell and CLI for llm4ts: flow discovery, run-a-flow, and view",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@effect/platform-node": "4.0.0-rc.115",
|
|
54
54
|
"@effect/platform-node-shared": "4.0.0-rc.115",
|
|
55
|
-
"@llm4ts/
|
|
56
|
-
"@llm4ts/
|
|
57
|
-
"@llm4ts/
|
|
55
|
+
"@llm4ts/core": "2.14.0",
|
|
56
|
+
"@llm4ts/flow": "2.14.0",
|
|
57
|
+
"@llm4ts/runner": "2.14.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"effect": "4.0.0-rc.115"
|