@rse/specbook 1.2.3 → 1.2.4
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 +112 -204
- package/dst/specbook-cmd-export.js +4 -1
- package/dst/specbook-config.js +6 -6
- package/dst/specbook-diagram.js +3 -3
- package/dst/specbook-export-ast.js +1 -1
- package/dst/specbook-export-common.js +1 -1
- package/dst/specbook-export-html.d.ts +1 -1
- package/dst/specbook-export-html.js +38 -38
- package/dst/specbook-export-md.js +9 -9
- package/dst/specbook-export-pdf.js +7 -7
- package/dst/specbook-format-schema.d.ts +1 -1
- package/dst/specbook-format-schema.js +2 -2
- package/dst/specbook-format-spec.d.ts +1 -1
- package/dst/specbook-format-spec.js +2 -2
- package/dst/specbook-format.md +4 -4
- package/dst/specbook-format.yaml +40 -40
- package/dst/specbook-link.js +2 -2
- package/dst/specbook-parse-semantic.js +22 -22
- package/dst/specbook-parse-syntax.js +5 -5
- package/dst/specbook-parse.js +1 -1
- package/package.json +2 -2
|
@@ -122,7 +122,7 @@ const templates = {
|
|
|
122
122
|
"TocPanelEntries": textframe `
|
|
123
123
|
<ul>
|
|
124
124
|
{% for entry in Entries %}
|
|
125
|
-
<li><a href="#{{ entry.id }}"><span class="entry"><span class="object-kind"{% if entry.info %} data-info="{{ entry.info }}" data-info-path="{{ entry.infopath }}"{% endif %}>{{ entry.kind }}:</span> <span class="object-name"{% if entry.spec %} data-info-spec="{{ entry.spec }}" data-info-path="{{ entry.specpath }}"{% endif %}>{{ entry.name }}</span></span></a>{{ entry.
|
|
125
|
+
<li><a href="#{{ entry.id }}"><span class="entry"><span class="object-kind"{% if entry.info %} data-info="{{ entry.info }}" data-info-path="{{ entry.infopath }}"{% endif %}>{{ entry.kind }}:</span> <span class="object-name"{% if entry.spec %} data-info-spec="{{ entry.spec }}" data-info-path="{{ entry.specpath }}"{% endif %}>{{ entry.name }}</span></span></a>{{ entry.children }}</li>
|
|
126
126
|
{% endfor %}
|
|
127
127
|
</ul>
|
|
128
128
|
`,
|
|
@@ -146,7 +146,7 @@ const templates = {
|
|
|
146
146
|
{{ Object.diagram }}
|
|
147
147
|
{{ Object.properties }}
|
|
148
148
|
{{ Object.description }}
|
|
149
|
-
{{ Object.
|
|
149
|
+
{{ Object.children }}
|
|
150
150
|
</section>
|
|
151
151
|
`,
|
|
152
152
|
/* <Properties/> */
|
|
@@ -800,7 +800,7 @@ const collectInfo = (nodes, prefix, keys, info) => {
|
|
|
800
800
|
}
|
|
801
801
|
if (entry.d !== undefined || entry.p !== undefined)
|
|
802
802
|
info[path] = entry;
|
|
803
|
-
collectInfo(schema.
|
|
803
|
+
collectInfo(schema.children ?? [], path, keys, info);
|
|
804
804
|
}
|
|
805
805
|
};
|
|
806
806
|
/* inject the description popup maps into their client-side script ("<"
|
|
@@ -888,7 +888,7 @@ const collectSpec = (objects, spec) => {
|
|
|
888
888
|
.trim();
|
|
889
889
|
if (text !== "")
|
|
890
890
|
spec[anchorOf(object)] = scoped(object, () => block(text, true).toString().trim());
|
|
891
|
-
collectSpec(object.
|
|
891
|
+
collectSpec(object.children, spec);
|
|
892
892
|
}
|
|
893
893
|
};
|
|
894
894
|
/* the literal members of an "enum(...)"/"tags(...)" expression, or
|
|
@@ -913,7 +913,7 @@ const collectMembers = (nodes, result) => {
|
|
|
913
913
|
/* an invalid expression is the concern of lint */
|
|
914
914
|
}
|
|
915
915
|
}
|
|
916
|
-
collectMembers(schema.
|
|
916
|
+
collectMembers(schema.children ?? [], result);
|
|
917
917
|
}
|
|
918
918
|
return result;
|
|
919
919
|
};
|
|
@@ -976,20 +976,20 @@ const effectiveProperties = (object) => {
|
|
|
976
976
|
order (unknown keys appended in occurrence order), with
|
|
977
977
|
"withUnusedProps" injecting the defined but still unused schema
|
|
978
978
|
properties as additional columns */
|
|
979
|
-
const tableShape = (
|
|
980
|
-
const used = [...new Set(
|
|
981
|
-
const schema = schemas?.get(
|
|
979
|
+
const tableShape = (children) => {
|
|
980
|
+
const used = [...new Set(children.flatMap((child) => child.properties.map((property) => property.key)))];
|
|
981
|
+
const schema = schemas?.get(children[0]);
|
|
982
982
|
const names = (schema?.props ?? []).map((prop) => prop.name);
|
|
983
983
|
const known = schema?.format?.withUnusedProps === true ?
|
|
984
984
|
names : names.filter((name) => used.includes(name));
|
|
985
985
|
const keys = [...known, ...used.filter((key) => !names.includes(key))];
|
|
986
|
-
return { keys, desc:
|
|
986
|
+
return { keys, desc: children.some((child) => child.description !== undefined || child.children.length > 0) };
|
|
987
987
|
};
|
|
988
|
-
/* provide the
|
|
988
|
+
/* provide the children of an object taking part in the regular document
|
|
989
989
|
flow: a nested title object leaves the flow, as it is rendered as the
|
|
990
990
|
title page instead (a top-level one instead removes its whole artifact) */
|
|
991
|
-
const
|
|
992
|
-
/* decide whether a single-kind group of
|
|
991
|
+
const flowChildren = (object) => object.children.filter((child) => !isTitleObject(child));
|
|
992
|
+
/* decide whether a single-kind group of children collapses into the
|
|
993
993
|
concise (tabular) rendering: an explicit "format" type of the kind
|
|
994
994
|
wins, "auto" collapses the deepest level only, and inside an already
|
|
995
995
|
concise rendering context "auto" groups implicitly stay concise, too */
|
|
@@ -997,12 +997,12 @@ const conciseGroup = (group, schemaMap, concise) => {
|
|
|
997
997
|
const type = schemaMap?.get(group[0])?.format?.type ?? "auto";
|
|
998
998
|
if (type !== "auto")
|
|
999
999
|
return type === "concise";
|
|
1000
|
-
return concise || group.every((child) => child.
|
|
1000
|
+
return concise || group.every((child) => child.children.length === 0);
|
|
1001
1001
|
};
|
|
1002
|
-
/* group the
|
|
1003
|
-
const
|
|
1002
|
+
/* group the children of an object by their kind, preserving order */
|
|
1003
|
+
const groupChildren = (children) => {
|
|
1004
1004
|
const groups = new Map();
|
|
1005
|
-
for (const child of
|
|
1005
|
+
for (const child of children) {
|
|
1006
1006
|
const group = groups.get(child.kind);
|
|
1007
1007
|
if (group === undefined)
|
|
1008
1008
|
groups.set(child.kind, [child]);
|
|
@@ -1011,21 +1011,21 @@ const groupChilds = (childs) => {
|
|
|
1011
1011
|
}
|
|
1012
1012
|
return [...groups.values()];
|
|
1013
1013
|
};
|
|
1014
|
-
/* render the
|
|
1014
|
+
/* render the children of an object group-wise by kind: a concise group
|
|
1015
1015
|
as one compact table, a complex group as regular nested object
|
|
1016
1016
|
renderings */
|
|
1017
|
-
const
|
|
1017
|
+
const renderChildren = (object, level, concise) => groupChildren(flowChildren(object)).map((group) => conciseGroup(group, schemas, concise) ?
|
|
1018
1018
|
renderTable(group, maxColumnsOf(group[0])) :
|
|
1019
1019
|
group.map((child) => renderObject(child, level, concise)).join("")).join("");
|
|
1020
1020
|
/* render the description cell of a table row: the description of the
|
|
1021
|
-
object followed by its recursively rendered
|
|
1021
|
+
object followed by its recursively rendered children (implicitly or
|
|
1022
1022
|
explicitly concise groups as nested sub-tables, explicitly complex
|
|
1023
1023
|
ones as regular nested object renderings pressed into the cell);
|
|
1024
1024
|
a cell left without any content renders as the absent marker,
|
|
1025
1025
|
exactly like a property cell of a not given property */
|
|
1026
1026
|
const renderCell = (child) => {
|
|
1027
1027
|
let html = child.description !== undefined ? renderDescription(child.description) : "";
|
|
1028
|
-
html +=
|
|
1028
|
+
html += renderChildren(child, 6, true);
|
|
1029
1029
|
return html.trim() !== "" ? html : "<span class=\"value-absent\"></span>";
|
|
1030
1030
|
};
|
|
1031
1031
|
/* the pre-rendered diagram of an object as an embeddable block
|
|
@@ -1034,27 +1034,27 @@ const diagramOf = (object) => {
|
|
|
1034
1034
|
const diagram = diagrams?.get(object);
|
|
1035
1035
|
return diagram !== undefined ? safe(`<div class="diagram">${diagram}</div>`) : "";
|
|
1036
1036
|
};
|
|
1037
|
-
/* render a single-kind group of
|
|
1037
|
+
/* render a single-kind group of children into one compact table:
|
|
1038
1038
|
the name first, then the property columns, then the description;
|
|
1039
1039
|
a group wider than maxColumns, or carrying diagrams, instead chunks
|
|
1040
1040
|
the property and description cells (and the leading diagram) of
|
|
1041
1041
|
every object into an embedded per-object table, as a diagram is
|
|
1042
1042
|
identifiable only under a header of its own, not under the column
|
|
1043
1043
|
headers of a plain table */
|
|
1044
|
-
const renderTable = (
|
|
1045
|
-
const { keys, desc } = tableShape(
|
|
1046
|
-
const diagrammed =
|
|
1044
|
+
const renderTable = (children, maxColumns) => {
|
|
1045
|
+
const { keys, desc } = tableShape(children);
|
|
1046
|
+
const diagrammed = children.some((child) => diagramOf(child) !== "");
|
|
1047
1047
|
if (!diagrammed && 1 + keys.length + (desc ? 1 : 0) <= maxColumns)
|
|
1048
1048
|
return render("Table", { Table: {
|
|
1049
|
-
head:
|
|
1050
|
-
info: infoKeyOf(
|
|
1051
|
-
infopath: infoPathOf(
|
|
1049
|
+
head: children[0].kind !== "" ? children[0].kind : "Name",
|
|
1050
|
+
info: infoKeyOf(children[0]),
|
|
1051
|
+
infopath: infoPathOf(children[0], false),
|
|
1052
1052
|
keys,
|
|
1053
1053
|
desc,
|
|
1054
1054
|
/* under the fixed table layout the description column claims
|
|
1055
1055
|
twice the share of a regular column, compressing the others */
|
|
1056
1056
|
width: Math.round(200 / (keys.length + 3)),
|
|
1057
|
-
rows:
|
|
1057
|
+
rows: children.map((child, i) => scoped(child, () => ({
|
|
1058
1058
|
id: anchorOf(child),
|
|
1059
1059
|
paren: child.paren,
|
|
1060
1060
|
primary: child.primary,
|
|
@@ -1071,11 +1071,11 @@ const renderTable = (childs, maxColumns) => {
|
|
|
1071
1071
|
with the last cell spanning the leftover columns of the final row */
|
|
1072
1072
|
const size = Math.max(1, maxColumns - 1);
|
|
1073
1073
|
return render("TableChunked", { Table: {
|
|
1074
|
-
head:
|
|
1075
|
-
info: infoKeyOf(
|
|
1076
|
-
infopath: infoPathOf(
|
|
1074
|
+
head: children[0].kind !== "" ? children[0].kind : "Name",
|
|
1075
|
+
info: infoKeyOf(children[0]),
|
|
1076
|
+
infopath: infoPathOf(children[0], false),
|
|
1077
1077
|
width: Math.round(100 / maxColumns),
|
|
1078
|
-
rows:
|
|
1078
|
+
rows: children.map((child, i) => scoped(child, () => {
|
|
1079
1079
|
const cells = keys.map((key) => ({ key, desc: false, span: 1,
|
|
1080
1080
|
value: inlineValue(child.kind, child.properties.find((property) => property.key === key)) }));
|
|
1081
1081
|
if (desc)
|
|
@@ -1125,7 +1125,7 @@ const renderObject = (object, level, concise) => {
|
|
|
1125
1125
|
safe(render("Properties", { Properties: inlineProperties(object, properties) })) : "",
|
|
1126
1126
|
description: object.description !== undefined ?
|
|
1127
1127
|
safe(renderDescription(object.description)) : "",
|
|
1128
|
-
|
|
1128
|
+
children: safe(renderChildren(object, level + 1, concise))
|
|
1129
1129
|
} }));
|
|
1130
1130
|
};
|
|
1131
1131
|
/* format a timestamp as its calendar date in local time (the
|
|
@@ -1167,10 +1167,10 @@ const renderTitlePage = (object, created, modified) => {
|
|
|
1167
1167
|
const renderArtifact = (artifact) => render("Artifact", { Artifact: {
|
|
1168
1168
|
objects: safe(artifact.objects.map((object) => renderObject(object, 1, false)).join(""))
|
|
1169
1169
|
} });
|
|
1170
|
-
/* provide the
|
|
1170
|
+
/* provide the children of an object rendered with headings of their own
|
|
1171
1171
|
(skipping the child groups collapsing into compact tables), which
|
|
1172
1172
|
are the ones taking part in the table of contents and its side panel */
|
|
1173
|
-
const
|
|
1173
|
+
const headingChildren = (object) => groupChildren(flowChildren(object))
|
|
1174
1174
|
.filter((group) => !conciseGroup(group, schemas, false))
|
|
1175
1175
|
.flat();
|
|
1176
1176
|
/* flatten the hierarchy of the rendered object headings (exactly like
|
|
@@ -1185,7 +1185,7 @@ const tocEntries = (objects, pages) => {
|
|
|
1185
1185
|
info: infoKeyOf(object), infopath: infoPathOf(object, false),
|
|
1186
1186
|
spec: infoParents !== null ? id : undefined, specpath: infoPathOf(object),
|
|
1187
1187
|
name: inline(object.name), level: Math.min(level, 6), page: pages?.get(id) });
|
|
1188
|
-
collect(
|
|
1188
|
+
collect(headingChildren(object), level + 1);
|
|
1189
1189
|
}
|
|
1190
1190
|
};
|
|
1191
1191
|
collect(objects, 1);
|
|
@@ -1205,7 +1205,7 @@ const renderTocPanel = (objects, title, doc) => {
|
|
|
1205
1205
|
spec: infoParents !== null ? anchorOf(object) : undefined,
|
|
1206
1206
|
specpath: infoPathOf(object),
|
|
1207
1207
|
name: inline(object.name),
|
|
1208
|
-
|
|
1208
|
+
children: safe(entries(headingChildren(object)))
|
|
1209
1209
|
})) });
|
|
1210
1210
|
return render("TocPanel", { TocPanel: { title, doc, entries: safe(entries(objects)) } });
|
|
1211
1211
|
};
|
|
@@ -1217,7 +1217,7 @@ export const htmlOutline = (specification, config) => {
|
|
|
1217
1217
|
const entry = (object) => ({
|
|
1218
1218
|
title: (object.kind !== "" ? `${object.kind}: ` : "") + plainText(object.name),
|
|
1219
1219
|
anchor: paths.get(object) ?? object.id,
|
|
1220
|
-
|
|
1220
|
+
children: groupChildren(flowChildren(object))
|
|
1221
1221
|
.filter((group) => !conciseGroup(group, schemaMap, false))
|
|
1222
1222
|
.flatMap((group) => group.map(entry))
|
|
1223
1223
|
});
|
|
@@ -39,11 +39,11 @@ const nameSuffixMd = (object) => (object.primary === true ? " (*)" : "") +
|
|
|
39
39
|
code block, an ordered list, a blockquote, or multiple paragraphs) or
|
|
40
40
|
one carrying a ";", as only a ";"-free description stays a single
|
|
41
41
|
segment on re-parsing instead of splitting off a spurious property */
|
|
42
|
-
const complexBelowMd = (object) => object.
|
|
42
|
+
const complexBelowMd = (object) => object.children.some((child) => child.name.includes(";")
|
|
43
43
|
|| (child.description !== undefined
|
|
44
44
|
&& /[\n;]/.test(renderDescriptionMd(child.description))) || complexBelowMd(child));
|
|
45
45
|
/* render an object in the Concise Format as a list item of the
|
|
46
|
-
given nesting depth, with its
|
|
46
|
+
given nesting depth, with its children as nested list items */
|
|
47
47
|
const renderConciseMd = (object, depth = 0) => {
|
|
48
48
|
const indent = " ".repeat(depth * 4);
|
|
49
49
|
const segments = [`${object.kind}: ${object.name}${nameSuffixMd(object)}`];
|
|
@@ -62,14 +62,14 @@ const renderConciseMd = (object, depth = 0) => {
|
|
|
62
62
|
`${indent}- ${segments.join("; ").replace(/\.?$/, ".")}` :
|
|
63
63
|
`${indent}- ${segments.join("; ")};`;
|
|
64
64
|
return [item, renderKeyValuesMd(wrapped, `${indent} `),
|
|
65
|
-
...object.
|
|
65
|
+
...object.children.map((child) => renderConciseMd(child, depth + 1))]
|
|
66
66
|
.filter((part) => part !== "").join("\n");
|
|
67
67
|
};
|
|
68
|
-
/* render an object in the Complex Format (levels 1-3), with
|
|
68
|
+
/* render an object in the Complex Format (levels 1-3), with children from
|
|
69
69
|
level 4 upwards in the Concise Format -- unless a descendant carries
|
|
70
|
-
a description no concise item can carry, so all
|
|
70
|
+
a description no concise item can carry, so all children (as siblings
|
|
71
71
|
have to share the format) stay in the Complex Format down to heading
|
|
72
|
-
level 6, the deepest one Markdown knows, below which the
|
|
72
|
+
level 6, the deepest one Markdown knows, below which the children are
|
|
73
73
|
concise regardless -- and the optionally derived Gradia diagram spec
|
|
74
74
|
embedded as a "gradia" fenced code block below the heading */
|
|
75
75
|
const renderObjectMd = (object, level, diagrams) => {
|
|
@@ -83,12 +83,12 @@ const renderObjectMd = (object, level, diagrams) => {
|
|
|
83
83
|
parts.push(renderKeyValuesMd(object.properties));
|
|
84
84
|
if (object.description !== undefined)
|
|
85
85
|
parts.push(renderDescriptionMd(object.description));
|
|
86
|
-
/* the
|
|
86
|
+
/* the children share one format, as a concise item following
|
|
87
87
|
a heading would attach to the heading's object instead */
|
|
88
88
|
if (level >= 6 || (level >= 3 && !complexBelowMd(object)))
|
|
89
|
-
parts.push(object.
|
|
89
|
+
parts.push(object.children.map((child) => renderConciseMd(child)).join("\n"));
|
|
90
90
|
else
|
|
91
|
-
parts.push(...object.
|
|
91
|
+
parts.push(...object.children.map((child) => renderObjectMd(child, level + 1, diagrams)));
|
|
92
92
|
return parts.filter((part) => part !== "").join("\n\n");
|
|
93
93
|
};
|
|
94
94
|
/* render the entire specification into normalized Markdown */
|
|
@@ -28,7 +28,7 @@ const anchorPages = async (pdf) => {
|
|
|
28
28
|
};
|
|
29
29
|
/* attach a hierarchical PDF outline (the "bookmarks" of the viewer
|
|
30
30
|
side-bar) to a document, resolving the entry anchors against the
|
|
31
|
-
named destinations and hoisting the
|
|
31
|
+
named destinations and hoisting the children of an unresolvable entry */
|
|
32
32
|
const addOutline = async (doc, entries) => {
|
|
33
33
|
const { PDFName, PDFDict, PDFArray, PDFRef, PDFHexString } = await import("pdf-lib");
|
|
34
34
|
const context = doc.context;
|
|
@@ -48,8 +48,8 @@ const addOutline = async (doc, entries) => {
|
|
|
48
48
|
}
|
|
49
49
|
const resolve = (entries) => entries.flatMap((entry) => {
|
|
50
50
|
const dest = targets.get(entry.anchor);
|
|
51
|
-
const
|
|
52
|
-
return dest !== undefined ? [{ title: entry.title, dest,
|
|
51
|
+
const children = resolve(entry.children);
|
|
52
|
+
return dest !== undefined ? [{ title: entry.title, dest, children }] : children;
|
|
53
53
|
});
|
|
54
54
|
const items = resolve(entries);
|
|
55
55
|
if (items.length === 0)
|
|
@@ -59,16 +59,16 @@ const addOutline = async (doc, entries) => {
|
|
|
59
59
|
const materialize = (items, parent) => {
|
|
60
60
|
const refs = items.map(() => context.nextRef());
|
|
61
61
|
items.forEach((item, i) => {
|
|
62
|
-
const
|
|
62
|
+
const children = item.children.length > 0 ? materialize(item.children, refs[i]) : undefined;
|
|
63
63
|
context.assign(refs[i], context.obj({
|
|
64
64
|
Title: PDFHexString.fromText(item.title),
|
|
65
65
|
Parent: parent,
|
|
66
66
|
Prev: i > 0 ? refs[i - 1] : undefined,
|
|
67
67
|
Next: i < refs.length - 1 ? refs[i + 1] : undefined,
|
|
68
|
-
First:
|
|
69
|
-
Last:
|
|
68
|
+
First: children?.first,
|
|
69
|
+
Last: children?.last,
|
|
70
70
|
/* a negative count keeps the sub-tree initially collapsed */
|
|
71
|
-
Count:
|
|
71
|
+
Count: children !== undefined ? -item.children.length : undefined,
|
|
72
72
|
Dest: item.dest
|
|
73
73
|
}));
|
|
74
74
|
});
|
|
@@ -17,7 +17,7 @@ const GradiaConfigSchemas = Object.fromEntries(Object.entries(Gradia.config).map
|
|
|
17
17
|
const SchemaDiagramConfig = v.partial(v.strictObject(GradiaConfigSchemas, (issue) => issue.expected === "never" ? "unknown Gradia rendering option" :
|
|
18
18
|
"expected a map of Gradia rendering options"));
|
|
19
19
|
/* the Valibot schemas mirroring the types above, with the object
|
|
20
|
-
kind schema referencing itself lazily for the nested
|
|
20
|
+
kind schema referencing itself lazily for the nested children */
|
|
21
21
|
const SchemaProperty = v.strictObject({
|
|
22
22
|
name: v.string(),
|
|
23
23
|
desc: v.optional(v.string()),
|
|
@@ -87,6 +87,6 @@ const SchemaObject = v.strictObject({
|
|
|
87
87
|
diagram: v.optional(SchemaDiagram),
|
|
88
88
|
format: v.optional(SchemaFormat),
|
|
89
89
|
props: v.optional(v.array(SchemaProperty)),
|
|
90
|
-
|
|
90
|
+
children: v.optional(v.array(v.lazy(() => SchemaObject)))
|
|
91
91
|
});
|
|
92
92
|
export const Schema = v.pipe(v.array(SchemaObject), v.minLength(1));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import * as v from "valibot";
|
|
7
7
|
/* ==== Schema ==== */
|
|
8
8
|
/* the Valibot schemas mirroring the types above, with the object
|
|
9
|
-
schema referencing itself lazily for the nested
|
|
9
|
+
schema referencing itself lazily for the nested children */
|
|
10
10
|
const SpecProperty = v.object({
|
|
11
11
|
key: v.string(),
|
|
12
12
|
value: v.string(),
|
|
@@ -26,7 +26,7 @@ const SpecObject = v.object({
|
|
|
26
26
|
primary: v.optional(v.boolean()),
|
|
27
27
|
description: v.optional(SpecDescription),
|
|
28
28
|
properties: v.array(SpecProperty),
|
|
29
|
-
|
|
29
|
+
children: v.array(v.lazy(() => SpecObject))
|
|
30
30
|
});
|
|
31
31
|
const SpecArtifact = v.object({
|
|
32
32
|
created: v.date(),
|
package/dst/specbook-format.md
CHANGED
|
@@ -78,7 +78,7 @@ type SchemaObject = {
|
|
|
78
78
|
diagram?: SchemaDiagram
|
|
79
79
|
format?: SchemaFormat
|
|
80
80
|
props?: SchemaProperty[]
|
|
81
|
-
|
|
81
|
+
children?: SchemaObject[]
|
|
82
82
|
}
|
|
83
83
|
type SchemaAutomaton = {
|
|
84
84
|
nodes: string
|
|
@@ -251,7 +251,7 @@ type SchemaGradiaConfig = Partial<{
|
|
|
251
251
|
properties allowed on objects of this kind,
|
|
252
252
|
BECAUSE they carry the structured, machine-checkable content
|
|
253
253
|
|
|
254
|
-
- `SchemaObject.
|
|
254
|
+
- `SchemaObject.children?: SchemaObject[]`:
|
|
255
255
|
object kinds allowed one level below this object (RECURSION),
|
|
256
256
|
BECAUSE the nesting defines and bounds the document structure
|
|
257
257
|
|
|
@@ -579,7 +579,7 @@ type SpecObject = {
|
|
|
579
579
|
primary?: boolean
|
|
580
580
|
description?: SpecDescription
|
|
581
581
|
properties: SpecProperty[]
|
|
582
|
-
|
|
582
|
+
children: SpecObject[]
|
|
583
583
|
}
|
|
584
584
|
type SpecDescription = {
|
|
585
585
|
description: string
|
|
@@ -653,7 +653,7 @@ type SpecProperty = {
|
|
|
653
653
|
key/value properties of the object, in canonical order,
|
|
654
654
|
BECAUSE properties carry the structured, machine-checkable content
|
|
655
655
|
|
|
656
|
-
- `SpecObject.
|
|
656
|
+
- `SpecObject.children: SpecObject[]`:
|
|
657
657
|
child objects nested one level below this object (RECURSION),
|
|
658
658
|
BECAUSE a specification is a tree, not a flat list of statements
|
|
659
659
|
|