@rse/specbook 1.0.3 → 1.1.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.
- package/README.md +106 -19
- package/dst/specbook-api.d.ts +29 -9
- package/dst/specbook-api.js +108 -33
- package/dst/specbook-cli.js +84 -25
- package/dst/specbook-cmd-describe.d.ts +7 -1
- package/dst/specbook-cmd-describe.js +56 -11
- package/dst/specbook-cmd-export.d.ts +2 -1
- package/dst/specbook-cmd-export.js +46 -3
- package/dst/specbook-cmd-lint.d.ts +2 -1
- package/dst/specbook-cmd-lint.js +12 -8
- package/dst/specbook-cmd-preview.d.ts +12 -0
- package/dst/specbook-cmd-preview.js +57 -0
- package/dst/specbook-config.d.ts +1 -1
- package/dst/specbook-config.js +187 -37
- package/dst/specbook-diagnostic.d.ts +2 -0
- package/dst/specbook-diagnostic.js +4 -2
- package/dst/specbook-diagram.d.ts +1 -0
- package/dst/specbook-diagram.js +0 -0
- package/dst/specbook-export-ast.js +4 -2
- package/dst/specbook-export-common.js +5 -3
- package/dst/specbook-export-html-search.js +118 -30
- package/dst/specbook-export-html.css +1140 -233
- package/dst/specbook-export-html.d.ts +1 -1
- package/dst/specbook-export-html.js +764 -120
- package/dst/specbook-export-md.js +4 -2
- package/dst/specbook-format-schema.d.ts +33 -1
- package/dst/specbook-format-schema.js +34 -2
- package/dst/specbook-format.md +193 -25
- package/dst/specbook-format.yaml +2291 -299
- package/dst/specbook-link.d.ts +2 -1
- package/dst/specbook-link.js +43 -3
- package/dst/specbook-mcp.js +19 -10
- package/dst/specbook-parse-common.d.ts +4 -2
- package/dst/specbook-parse-common.js +6 -3
- package/dst/specbook-parse-semantic.js +290 -30
- package/dst/specbook-parse-syntax.js +3 -1
- package/dst/specbook-parse-value.d.ts +1 -0
- package/dst/specbook-parse-value.js +17 -3
- package/dst/specbook-parse.js +1 -1
- package/package.json +14 -6
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { specDiagrams } from "./specbook-diagram.js";
|
|
7
7
|
import { becauseRegex } from "./specbook-parse-common.js";
|
|
8
|
+
import { isTitleObject } from "./specbook-export-common.js";
|
|
8
9
|
/* format a timestamp in the frontmatter format */
|
|
9
10
|
const formatTimestamp = (date) => {
|
|
10
11
|
const pad = (n) => String(n).padStart(2, "0");
|
|
@@ -91,11 +92,12 @@ const renderObjectMd = (object, level, diagrams) => {
|
|
|
91
92
|
export const renderMarkdown = (specification, config) => {
|
|
92
93
|
/* derive the Gradia specs of the diagram-configured objects
|
|
93
94
|
(an invalid diagram situation omits the diagram, as it is
|
|
94
|
-
already reported as a lint diagnostic
|
|
95
|
+
already reported as a lint diagnostic, and the diagram of
|
|
96
|
+
the title object is reserved for the HTML/PDF export) */
|
|
95
97
|
const diagrams = new Map();
|
|
96
98
|
if (config !== undefined)
|
|
97
99
|
for (const [object, result] of specDiagrams(specification, config))
|
|
98
|
-
if (result.spec !== undefined)
|
|
100
|
+
if (result.spec !== undefined && !isTitleObject(object))
|
|
99
101
|
diagrams.set(object, result.spec);
|
|
100
102
|
/* the single frontmatter block (only recognized at the start of a
|
|
101
103
|
file on re-parse) carries the earliest creation and the latest
|
|
@@ -7,22 +7,49 @@ export type SchemaObject = {
|
|
|
7
7
|
id?: string;
|
|
8
8
|
file?: string;
|
|
9
9
|
desc?: string;
|
|
10
|
+
refs?: string;
|
|
10
11
|
optional?: boolean;
|
|
12
|
+
referenced?: string[];
|
|
13
|
+
automaton?: SchemaAutomaton;
|
|
11
14
|
diagram?: SchemaDiagram;
|
|
12
15
|
format?: SchemaFormat;
|
|
13
16
|
props?: SchemaProperty[];
|
|
14
17
|
childs?: SchemaObject[];
|
|
15
18
|
};
|
|
19
|
+
export type SchemaAutomaton = {
|
|
20
|
+
nodes: string;
|
|
21
|
+
edges: string;
|
|
22
|
+
source: string;
|
|
23
|
+
target: string;
|
|
24
|
+
initial: string;
|
|
25
|
+
final: string;
|
|
26
|
+
};
|
|
27
|
+
export type SchemaDiagramCenter = {
|
|
28
|
+
source?: string;
|
|
29
|
+
property?: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
kind?: string;
|
|
32
|
+
};
|
|
33
|
+
export type SchemaDiagramCenterEdges = {
|
|
34
|
+
property: string;
|
|
35
|
+
inbound?: string;
|
|
36
|
+
outbound?: string;
|
|
37
|
+
both?: string;
|
|
38
|
+
labeled?: string;
|
|
39
|
+
};
|
|
16
40
|
export type SchemaDiagram = {
|
|
17
41
|
type?: "graph" | "hub" | "grid";
|
|
18
42
|
nodes?: string;
|
|
19
43
|
edges?: string;
|
|
20
|
-
center?: string;
|
|
44
|
+
center?: string | SchemaDiagramCenter;
|
|
45
|
+
centerEdges?: SchemaDiagramCenterEdges;
|
|
21
46
|
links?: "props" | "all";
|
|
47
|
+
labeled?: boolean;
|
|
22
48
|
edgeSource?: string;
|
|
23
49
|
edgeTarget?: string;
|
|
24
50
|
edgeArity?: string;
|
|
25
51
|
hierarchy?: boolean;
|
|
52
|
+
deep?: boolean;
|
|
26
53
|
onlyConnected?: boolean;
|
|
27
54
|
collapse?: boolean;
|
|
28
55
|
qualified?: boolean;
|
|
@@ -39,5 +66,10 @@ export type SchemaProperty = {
|
|
|
39
66
|
desc?: string;
|
|
40
67
|
value?: string;
|
|
41
68
|
optional?: boolean;
|
|
69
|
+
unique?: boolean | string;
|
|
70
|
+
present?: boolean | string;
|
|
71
|
+
local?: boolean;
|
|
72
|
+
symmetric?: boolean;
|
|
73
|
+
acyclic?: boolean;
|
|
42
74
|
};
|
|
43
75
|
export declare const Schema: v.GenericSchema<Schema>;
|
|
@@ -20,18 +20,47 @@ const SchemaProperty = v.strictObject({
|
|
|
20
20
|
name: v.string(),
|
|
21
21
|
desc: v.optional(v.string()),
|
|
22
22
|
value: v.optional(v.string()),
|
|
23
|
-
optional: v.optional(v.boolean())
|
|
23
|
+
optional: v.optional(v.boolean()),
|
|
24
|
+
unique: v.optional(v.union([v.boolean(), v.string()])),
|
|
25
|
+
present: v.optional(v.union([v.boolean(), v.string()])),
|
|
26
|
+
local: v.optional(v.boolean()),
|
|
27
|
+
symmetric: v.optional(v.boolean()),
|
|
28
|
+
acyclic: v.optional(v.boolean())
|
|
29
|
+
});
|
|
30
|
+
const SchemaAutomaton = v.strictObject({
|
|
31
|
+
nodes: v.string(),
|
|
32
|
+
edges: v.string(),
|
|
33
|
+
source: v.string(),
|
|
34
|
+
target: v.string(),
|
|
35
|
+
initial: v.string(),
|
|
36
|
+
final: v.string()
|
|
37
|
+
});
|
|
38
|
+
const SchemaDiagramCenter = v.strictObject({
|
|
39
|
+
source: v.optional(v.string()),
|
|
40
|
+
property: v.optional(v.string()),
|
|
41
|
+
label: v.optional(v.string()),
|
|
42
|
+
kind: v.optional(v.string())
|
|
43
|
+
});
|
|
44
|
+
const SchemaDiagramCenterEdges = v.strictObject({
|
|
45
|
+
property: v.string(),
|
|
46
|
+
inbound: v.optional(v.string()),
|
|
47
|
+
outbound: v.optional(v.string()),
|
|
48
|
+
both: v.optional(v.string()),
|
|
49
|
+
labeled: v.optional(v.string())
|
|
24
50
|
});
|
|
25
51
|
const SchemaDiagram = v.strictObject({
|
|
26
52
|
type: v.optional(v.picklist(["graph", "hub", "grid"])),
|
|
27
53
|
nodes: v.optional(v.string()),
|
|
28
54
|
edges: v.optional(v.string()),
|
|
29
|
-
center: v.optional(v.string()),
|
|
55
|
+
center: v.optional(v.union([v.string(), SchemaDiagramCenter])),
|
|
56
|
+
centerEdges: v.optional(SchemaDiagramCenterEdges),
|
|
30
57
|
links: v.optional(v.picklist(["props", "all"])),
|
|
58
|
+
labeled: v.optional(v.boolean()),
|
|
31
59
|
edgeSource: v.optional(v.string()),
|
|
32
60
|
edgeTarget: v.optional(v.string()),
|
|
33
61
|
edgeArity: v.optional(v.string()),
|
|
34
62
|
hierarchy: v.optional(v.boolean()),
|
|
63
|
+
deep: v.optional(v.boolean()),
|
|
35
64
|
onlyConnected: v.optional(v.boolean()),
|
|
36
65
|
collapse: v.optional(v.boolean()),
|
|
37
66
|
qualified: v.optional(v.boolean()),
|
|
@@ -49,7 +78,10 @@ const SchemaObject = v.strictObject({
|
|
|
49
78
|
id: v.optional(v.string()),
|
|
50
79
|
file: v.optional(v.string()),
|
|
51
80
|
desc: v.optional(v.string()),
|
|
81
|
+
refs: v.optional(v.string()),
|
|
52
82
|
optional: v.optional(v.boolean()),
|
|
83
|
+
referenced: v.optional(v.pipe(v.array(v.string()), v.minLength(1))),
|
|
84
|
+
automaton: v.optional(SchemaAutomaton),
|
|
53
85
|
diagram: v.optional(SchemaDiagram),
|
|
54
86
|
format: v.optional(SchemaFormat),
|
|
55
87
|
props: v.optional(v.array(SchemaProperty)),
|
package/dst/specbook-format.md
CHANGED
|
@@ -59,7 +59,9 @@ level, the object kinds of the deeper levels, and their properties
|
|
|
59
59
|
with optional value constraints: regular expressions `/xxx/` (implicitly
|
|
60
60
|
anchored, so they have to match the value as a whole),
|
|
61
61
|
object references `[[xxx]]`, enumerations `enum(xxx,yyy)`, tag sets
|
|
62
|
-
`tags(xxx,yyy)`, and lists `list(xxx[, ...])` of such alternatives
|
|
62
|
+
`tags(xxx,yyy)`, and lists `list(xxx[, ...])` of such alternatives,
|
|
63
|
+
whose comma-separated items are split at top-level commas only, i.e.
|
|
64
|
+
outside double quotes, `[[xxx]]` references, and parentheses.
|
|
63
65
|
|
|
64
66
|
```ts
|
|
65
67
|
type Schema = SchemaObject[]
|
|
@@ -69,22 +71,49 @@ type SchemaObject = {
|
|
|
69
71
|
id?: string
|
|
70
72
|
file?: string
|
|
71
73
|
desc?: string
|
|
74
|
+
refs?: string
|
|
72
75
|
optional?: boolean
|
|
76
|
+
referenced?: string[]
|
|
77
|
+
automaton?: SchemaAutomaton
|
|
73
78
|
diagram?: SchemaDiagram
|
|
74
79
|
format?: SchemaFormat
|
|
75
80
|
props?: SchemaProperty[]
|
|
76
81
|
childs?: SchemaObject[]
|
|
77
82
|
}
|
|
83
|
+
type SchemaAutomaton = {
|
|
84
|
+
nodes: string
|
|
85
|
+
edges: string
|
|
86
|
+
source: string
|
|
87
|
+
target: string
|
|
88
|
+
initial: string
|
|
89
|
+
final: string
|
|
90
|
+
}
|
|
91
|
+
type SchemaDiagramCenter = {
|
|
92
|
+
source?: string
|
|
93
|
+
property?: string
|
|
94
|
+
label?: string
|
|
95
|
+
kind?: string
|
|
96
|
+
}
|
|
97
|
+
type SchemaDiagramCenterEdges = {
|
|
98
|
+
property: string
|
|
99
|
+
inbound?: string
|
|
100
|
+
outbound?: string
|
|
101
|
+
both?: string
|
|
102
|
+
labeled?: string
|
|
103
|
+
}
|
|
78
104
|
type SchemaDiagram = {
|
|
79
105
|
type?: "graph" | "hub" | "grid"
|
|
80
106
|
nodes?: string
|
|
81
107
|
edges?: string
|
|
82
|
-
center?: string
|
|
108
|
+
center?: string | SchemaDiagramCenter
|
|
109
|
+
centerEdges?: SchemaDiagramCenterEdges
|
|
83
110
|
links?: "props" | "all"
|
|
111
|
+
labeled?: boolean
|
|
84
112
|
edgeSource?: string
|
|
85
113
|
edgeTarget?: string
|
|
86
114
|
edgeArity?: string
|
|
87
115
|
hierarchy?: boolean
|
|
116
|
+
deep?: boolean
|
|
88
117
|
onlyConnected?: boolean
|
|
89
118
|
collapse?: boolean
|
|
90
119
|
qualified?: boolean
|
|
@@ -101,6 +130,11 @@ type SchemaProperty = {
|
|
|
101
130
|
desc?: string
|
|
102
131
|
value?: string
|
|
103
132
|
optional?: boolean
|
|
133
|
+
unique?: boolean | string
|
|
134
|
+
present?: boolean | string
|
|
135
|
+
local?: boolean
|
|
136
|
+
symmetric?: boolean
|
|
137
|
+
acyclic?: boolean
|
|
104
138
|
}
|
|
105
139
|
type SchemaGradiaConfig = Partial<{
|
|
106
140
|
"font-family": string
|
|
@@ -160,7 +194,7 @@ type SchemaGradiaConfig = Partial<{
|
|
|
160
194
|
BECAUSE artifacts and their content follow one recursive structure
|
|
161
195
|
|
|
162
196
|
- `SchemaObject.kind: string`:
|
|
163
|
-
kind name of the object (e.g. `
|
|
197
|
+
kind name of the object (e.g. `REQS`, `REQUIREMENT`), unique among
|
|
164
198
|
its siblings, on level 1 together with its `id`,
|
|
165
199
|
BECAUSE it discriminates objects and anchors `[[xxx]]` references
|
|
166
200
|
|
|
@@ -181,17 +215,35 @@ type SchemaGradiaConfig = Partial<{
|
|
|
181
215
|
prose description of the object kind,
|
|
182
216
|
BECAUSE it guides the authors and instructs the AI
|
|
183
217
|
|
|
218
|
+
- `SchemaObject.refs?: string`:
|
|
219
|
+
Markdown list of the standards, books, articles, or websites
|
|
220
|
+
(`- <id>: <title> (<year>),` followed by an indented `<url>` line)
|
|
221
|
+
primarily describing the methodology the object kind picks up (left
|
|
222
|
+
out when the configuration is emitted compressed by level 2 or higher),
|
|
223
|
+
BECAUSE the authors and the AI can consult the original method
|
|
224
|
+
|
|
184
225
|
- `SchemaObject.optional?: boolean`:
|
|
185
226
|
whether the kind may be absent below its parent or, for a level 1
|
|
186
227
|
artifact, from its file (default: `false`),
|
|
187
228
|
BECAUSE not every kind is mandatory below every parent object
|
|
188
229
|
|
|
230
|
+
- `SchemaObject.referenced?: string[]`:
|
|
231
|
+
wildcard references (e.g. `[[*]]` for any object, `[[UC.USE-CASE:*]]`
|
|
232
|
+
for the use cases) matching the objects -- themselves or through their
|
|
233
|
+
descendants -- from which every object of this kind has to be referenced
|
|
234
|
+
at least once, where a lapse is reported as a warning only,
|
|
235
|
+
BECAUSE a term nobody uses or a requirement no use case exercises is dead weight
|
|
236
|
+
|
|
237
|
+
- `SchemaObject.automaton?: SchemaAutomaton`:
|
|
238
|
+
finite state machine the child objects of every object of this kind form,
|
|
239
|
+
BECAUSE a lifecycle is only trustworthy when its dead-ends and livelocks are ruled out
|
|
240
|
+
|
|
189
241
|
- `SchemaObject.diagram?: SchemaDiagram`:
|
|
190
242
|
diagram generated for every object of this kind,
|
|
191
243
|
BECAUSE relations buried in references are graspable only when drawn
|
|
192
244
|
|
|
193
245
|
- `SchemaObject.format?: SchemaFormat`:
|
|
194
|
-
HTML/PDF rendering format of the
|
|
246
|
+
HTML/PDF rendering format of the objects of this kind,
|
|
195
247
|
BECAUSE rich objects need sections, uniform ones read as tables
|
|
196
248
|
|
|
197
249
|
- `SchemaObject.props?: SchemaProperty[]`:
|
|
@@ -202,6 +254,37 @@ type SchemaGradiaConfig = Partial<{
|
|
|
202
254
|
object kinds allowed one level below this object (RECURSION),
|
|
203
255
|
BECAUSE the nesting defines and bounds the document structure
|
|
204
256
|
|
|
257
|
+
- `SchemaAutomaton`:
|
|
258
|
+
finite state machine formed by the child objects of an object: the
|
|
259
|
+
nodes have to be reachable from an initial node, a node without
|
|
260
|
+
outgoing edge has to be final (else it is a dead-end), and a final
|
|
261
|
+
node has to be reachable from every node (else it is a livelock),
|
|
262
|
+
BECAUSE the structural sanity of a state machine is checkable, not just describable
|
|
263
|
+
|
|
264
|
+
- `SchemaAutomaton.nodes: string`:
|
|
265
|
+
child object kind whose objects are the nodes (e.g. `STATE`),
|
|
266
|
+
BECAUSE the states are objects of their own, carrying descriptions and flags
|
|
267
|
+
|
|
268
|
+
- `SchemaAutomaton.edges: string`:
|
|
269
|
+
child object kind whose objects are the edges (e.g. `TRANSITION`),
|
|
270
|
+
BECAUSE the transitions are objects of their own, carrying guards and effects
|
|
271
|
+
|
|
272
|
+
- `SchemaAutomaton.source: string`:
|
|
273
|
+
reference-valued property of an edge object referencing its source node,
|
|
274
|
+
BECAUSE an edge is directed
|
|
275
|
+
|
|
276
|
+
- `SchemaAutomaton.target: string`:
|
|
277
|
+
reference-valued property of an edge object referencing its target node,
|
|
278
|
+
BECAUSE an edge is directed
|
|
279
|
+
|
|
280
|
+
- `SchemaAutomaton.initial: string`:
|
|
281
|
+
property of a node object flagging (with the value `true`) an initial node,
|
|
282
|
+
BECAUSE reachability needs a start (the check is skipped without any initial node)
|
|
283
|
+
|
|
284
|
+
- `SchemaAutomaton.final: string`:
|
|
285
|
+
property of a node object flagging (with the value `true`) a final node,
|
|
286
|
+
BECAUSE resting is legal in a final node only (the livelock check is skipped without any final node)
|
|
287
|
+
|
|
205
288
|
- `SchemaDiagram`:
|
|
206
289
|
diagram derived for every object of an object kind,
|
|
207
290
|
BECAUSE the relations of a specification are graspable only when drawn
|
|
@@ -218,14 +301,33 @@ type SchemaGradiaConfig = Partial<{
|
|
|
218
301
|
comma-separated `[[xxx]]` patterns of the objects acting as edges, not nodes,
|
|
219
302
|
BECAUSE some relations are objects, yet still connect two nodes
|
|
220
303
|
|
|
221
|
-
- `SchemaDiagram.center?: string`:
|
|
222
|
-
object a `hub` diagram is projected onto (default: `self`),
|
|
223
|
-
|
|
304
|
+
- `SchemaDiagram.center?: string | SchemaDiagramCenter`:
|
|
305
|
+
object a `hub` diagram is projected onto (default: `self`), or a
|
|
306
|
+
synthetic center node, labeled from a referenced `source` object (or
|
|
307
|
+
one of its properties, via `property`) or a literal `label`, typed
|
|
308
|
+
(under `qualified`) by `kind`, and linked to the `source` object,
|
|
309
|
+
BECAUSE the focus of a hub is not always a specification object --
|
|
310
|
+
the solution itself, e.g., sits in the middle of a context diagram
|
|
311
|
+
without being modeled anywhere
|
|
312
|
+
|
|
313
|
+
- `SchemaDiagram.centerEdges?: SchemaDiagramCenterEdges`:
|
|
314
|
+
synthesized center edges of a `hub`: the `property` of the node
|
|
315
|
+
objects whose `inbound` value maps onto a node-to-center edge, whose
|
|
316
|
+
`outbound` value onto a center-to-node edge, and whose `both` value
|
|
317
|
+
onto both edges (the second placement rendered as a "ghost" node),
|
|
318
|
+
each optionally named by the value of the `labeled` property,
|
|
319
|
+
BECAUSE the nodes carry no `[[xxx]]` reference to a synthetic center,
|
|
320
|
+
so their relation to it lives in a direction property instead
|
|
224
321
|
|
|
225
322
|
- `SchemaDiagram.links?: "props" | "all"`:
|
|
226
323
|
edge source: property values (`props`) or also texts (`all`),
|
|
227
324
|
BECAUSE prose references are incidental and need an opt-in
|
|
228
325
|
|
|
326
|
+
- `SchemaDiagram.labeled?: boolean`:
|
|
327
|
+
whether the edges derived from property values carry the lower-cased
|
|
328
|
+
property key as their label (default: `false`),
|
|
329
|
+
BECAUSE edges of several reference properties need telling apart
|
|
330
|
+
|
|
229
331
|
- `SchemaDiagram.edgeSource?: string`:
|
|
230
332
|
edge property naming the source node (default: the parent object),
|
|
231
333
|
BECAUSE an edge object is not always nested below its own source
|
|
@@ -243,6 +345,11 @@ type SchemaGradiaConfig = Partial<{
|
|
|
243
345
|
whether containment edges derive from the nesting (default: `false`),
|
|
244
346
|
BECAUSE the nesting carries no reference and would stay invisible
|
|
245
347
|
|
|
348
|
+
- `SchemaDiagram.deep?: boolean`:
|
|
349
|
+
whether the references of the descendants of a node count as its own,
|
|
350
|
+
lifted to their nearest node and counted as the arity (default: `false`),
|
|
351
|
+
BECAUSE the relations between coarse objects live in their fine parts
|
|
352
|
+
|
|
246
353
|
- `SchemaDiagram.onlyConnected?: boolean`:
|
|
247
354
|
whether a `graph` drops its edge-less nodes (default: `false`),
|
|
248
355
|
BECAUSE isolated nodes dilute a diagram about relationships
|
|
@@ -264,7 +371,7 @@ type SchemaGradiaConfig = Partial<{
|
|
|
264
371
|
BECAUSE layout tuning is cosmetics, not a schema concern
|
|
265
372
|
|
|
266
373
|
- `SchemaFormat`:
|
|
267
|
-
HTML/PDF rendering of the
|
|
374
|
+
HTML/PDF rendering of the objects of an object kind among their siblings,
|
|
268
375
|
BECAUSE one uniform rendering fits neither rich nor numerous objects
|
|
269
376
|
|
|
270
377
|
- `SchemaFormat.type?: "auto" | "complex" | "concise"`:
|
|
@@ -272,7 +379,7 @@ type SchemaGradiaConfig = Partial<{
|
|
|
272
379
|
BECAUSE sections bury small objects, tables truncate rich ones
|
|
273
380
|
|
|
274
381
|
- `SchemaFormat.maxTableColumns?: number`:
|
|
275
|
-
maximum column count of the
|
|
382
|
+
maximum column count of the table of the kind (default: `4`),
|
|
276
383
|
BECAUSE the fixed print/PDF page width bounds the table width
|
|
277
384
|
|
|
278
385
|
- `SchemaFormat.withUnusedProps?: boolean`:
|
|
@@ -299,6 +406,33 @@ type SchemaGradiaConfig = Partial<{
|
|
|
299
406
|
whether the property may be missing (default: `false`),
|
|
300
407
|
BECAUSE not every property applies to every object
|
|
301
408
|
|
|
409
|
+
- `SchemaProperty.unique?: boolean | string`:
|
|
410
|
+
whether every value (`true`) or only the values matching a regexp or
|
|
411
|
+
enum expression occur at most once among the sibling objects of the kind,
|
|
412
|
+
BECAUSE some markers, like a `Main` flow, designate a single sibling
|
|
413
|
+
|
|
414
|
+
- `SchemaProperty.present?: boolean | string`:
|
|
415
|
+
whether some value (`true`) or a value matching a regexp or enum
|
|
416
|
+
expression has to occur on at least one of the (existing) sibling
|
|
417
|
+
objects of the kind, so that `unique` plus `present` demand exactly one,
|
|
418
|
+
BECAUSE some markers, like an initial state, are not just single but indispensable
|
|
419
|
+
|
|
420
|
+
- `SchemaProperty.local?: boolean`:
|
|
421
|
+
whether the reference-valued property has to reference objects below
|
|
422
|
+
the parent object of the referencing object only (default: `false`),
|
|
423
|
+
BECAUSE a transition between the states of two different lifecycles is nonsense
|
|
424
|
+
|
|
425
|
+
- `SchemaProperty.symmetric?: boolean`:
|
|
426
|
+
whether every object the reference-valued property references has to
|
|
427
|
+
reference the referencing object back through the same property
|
|
428
|
+
(default: `false`),
|
|
429
|
+
BECAUSE an undirected relation authored on one side only is half a lie
|
|
430
|
+
|
|
431
|
+
- `SchemaProperty.acyclic?: boolean`:
|
|
432
|
+
whether following the reference-valued property from object to object
|
|
433
|
+
must never return to an object already passed (default: `false`),
|
|
434
|
+
BECAUSE a hierarchy with a loop has no root and an inclusion with a loop never ends
|
|
435
|
+
|
|
302
436
|
- `SchemaGradiaConfig`:
|
|
303
437
|
the Gradia rendering options, mirrored from `@rse/gradia`,
|
|
304
438
|
BECAUSE the diagram cosmetics remain Gradia's concern, not SpecBook's
|
|
@@ -335,7 +469,15 @@ type SchemaGradiaConfig = Partial<{
|
|
|
335
469
|
|
|
336
470
|
The edges are derived from the `[[xxx]]` references of the node objects
|
|
337
471
|
-- from their property values only, or, with `links: all`, from their
|
|
338
|
-
descriptions as well.
|
|
472
|
+
descriptions as well. With `deep: true` the references of all
|
|
473
|
+
descendants of a node object count as its own, every referenced object
|
|
474
|
+
is lifted to its nearest ancestor-or-self within the node set, and the
|
|
475
|
+
number of references behind an edge becomes its arity, so a diagram of
|
|
476
|
+
coarse objects (like the artifacts of a whole specification) shows the
|
|
477
|
+
relations living in their fine-grained parts. With `labeled: true` an
|
|
478
|
+
edge derived from a property value carries the lower-cased property key
|
|
479
|
+
as its label, telling the edges of different properties apart, while
|
|
480
|
+
the edges derived from descriptions stay unlabeled.
|
|
339
481
|
|
|
340
482
|
An object acting as an edge (`edges`) connects its parent object to the
|
|
341
483
|
object its target property references. Where an edge object is a sibling
|
|
@@ -360,6 +502,18 @@ comma-separated `[[xxx]]` reference patterns (`nodes` defaults to the
|
|
|
360
502
|
object itself plus all objects below it), and `center` names the object
|
|
361
503
|
a `hub` is projected onto (default: `self`).
|
|
362
504
|
|
|
505
|
+
Instead of naming an existing object, `center` can also declare a
|
|
506
|
+
*synthetic* center node, which represents something no specification
|
|
507
|
+
object models -- like the solution itself in the middle of a context
|
|
508
|
+
diagram. Such a center is labeled from a referenced `source` object (or
|
|
509
|
+
one of its properties, via `property`) or a literal `label`, and links
|
|
510
|
+
to the `source` object. As the nodes carry no `[[xxx]]` reference to a
|
|
511
|
+
synthetic center, `centerEdges` synthesizes the edges from a direction
|
|
512
|
+
`property` of the node objects: its `inbound` value maps onto a
|
|
513
|
+
node-to-center edge, its `outbound` value onto a center-to-node edge,
|
|
514
|
+
and its `both` value onto both edges, each optionally named by the value
|
|
515
|
+
of the `labeled` property.
|
|
516
|
+
|
|
363
517
|
`onlyConnected` drops the edge-less nodes of a `graph`, `collapse`
|
|
364
518
|
(default: `true`) omits a degenerated diagram consisting of a single
|
|
365
519
|
node only, `qualified` labels every node with its object kind,
|
|
@@ -371,19 +525,26 @@ rendering options (e.g. `grid-columns-max: 5`) through to the diagram.
|
|
|
371
525
|
### Format
|
|
372
526
|
|
|
373
527
|
An object kind can furthermore carry a `format` field, which controls
|
|
374
|
-
how the HTML/PDF export renders the
|
|
375
|
-
that
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
528
|
+
how the HTML/PDF export renders the objects of that kind among their
|
|
529
|
+
siblings, so that the sibling kinds below one parent can render
|
|
530
|
+
differently (e.g. one kind as a table, another as sections). Its `type`
|
|
531
|
+
selects the rendering: `complex` (nested sections), `concise` (one
|
|
532
|
+
compact table of all sibling objects of the kind), or the default
|
|
533
|
+
`auto`, which collapses only the deepest level into tables.
|
|
534
|
+
|
|
535
|
+
Inside a `concise` table, unconfigured child kinds implicitly stay
|
|
380
536
|
`concise` and render as sub-tables inside the description cells, while
|
|
381
537
|
an explicitly configured `type` is always honored, even a `complex`
|
|
382
538
|
rendering pressed into a cell. `maxTableColumns` (default: `4`) bounds
|
|
383
|
-
the columns of the compact
|
|
384
|
-
|
|
539
|
+
the columns of the compact table of the kind (a wider group chunks
|
|
540
|
+
its properties into embedded per-object tables), and `withUnusedProps`
|
|
385
541
|
(default: `false`) unconditionally renders the defined but unused
|
|
386
|
-
properties of that kind, as
|
|
542
|
+
properties of that kind, as property table lines or table columns. In
|
|
543
|
+
the HTML/PDF export, a property absent from an object shows an "empty
|
|
544
|
+
set" marker in place of its value, which tells it apart from a property
|
|
545
|
+
given with an empty value.
|
|
546
|
+
Property lines and table columns follow the property order of the
|
|
547
|
+
schema, with properties unknown to the schema appended in document order.
|
|
387
548
|
Objects collapsed into tables carry no headings and hence leave the
|
|
388
549
|
table of contents and the PDF outline.
|
|
389
550
|
|
|
@@ -531,9 +692,12 @@ SpecBook SCHEMA Model
|
|
|
531
692
|
### Artifacts
|
|
532
693
|
|
|
533
694
|
A schema consists of a YAML file, which is exactly a serialization of
|
|
534
|
-
the object model defined by the **SpecBook SCHEMA Meta Model
|
|
535
|
-
|
|
536
|
-
|
|
695
|
+
the object model defined by the **SpecBook SCHEMA Meta Model**, or of
|
|
696
|
+
several such files, merged in order into one schema (the later files
|
|
697
|
+
into the earlier ones, where the objects merge deeply and the elements
|
|
698
|
+
of the lists are matched by their identity). If no particular schema is
|
|
699
|
+
given, the standard schema configuration bundled with **SpecBook**
|
|
700
|
+
applies.
|
|
537
701
|
|
|
538
702
|
SpecBook SPEC Model
|
|
539
703
|
-------------------
|
|
@@ -671,7 +835,7 @@ order:
|
|
|
671
835
|
(e.g. `## ENTITY: Attendee Browser {{attendee}}`).
|
|
672
836
|
|
|
673
837
|
- `(<token/>)`: a parenthesized token with three possible roles: on
|
|
674
|
-
level 1 it becomes the artifact id (e.g. `#
|
|
838
|
+
level 1 it becomes the artifact id (e.g. `# DATA: Data Model (DM)`);
|
|
675
839
|
on deeper levels it acts as the implicit anchor id.
|
|
676
840
|
|
|
677
841
|
- `(*)`: the *primary* marker, flagging the object as primary (e.g.
|
|
@@ -701,7 +865,7 @@ whole.
|
|
|
701
865
|
|
|
702
866
|
The description of a Complex Format object is all block content below
|
|
703
867
|
its heading (up to the next heading) except the property list:
|
|
704
|
-
paragraphs, blockquotes, ordered lists (e.g. scenario steps), and
|
|
868
|
+
paragraphs, blockquotes, ordered lists (e.g. scenario steps or test case procedures), and
|
|
705
869
|
fenced code blocks. A fenced code block
|
|
706
870
|
of language `gradia` is skipped, as it is the derived diagram which
|
|
707
871
|
**SpecBook** itself emits into exported Markdown and which must not
|
|
@@ -741,7 +905,11 @@ below the artifact with id `FV`).
|
|
|
741
905
|
A reference has to resolve to exactly one object: a single-segment
|
|
742
906
|
reference tries the variants (1) id/anchor, (2) name, and (3)
|
|
743
907
|
`<kind/>:<name-or-id/>` in order, where the first variant yielding any
|
|
744
|
-
matches decides; several matches are
|
|
908
|
+
matches decides; several matches are narrowed down to the ones nearest
|
|
909
|
+
to the referencing object, i.e. sharing the longest ancestor chain with
|
|
910
|
+
it (so `[[STATE:Draft]]` inside a lifecycle picks the `Draft` state of
|
|
911
|
+
this very lifecycle, even if other lifecycles carry a `Draft` state,
|
|
912
|
+
too); still several matches are an ambiguity and zero matches an
|
|
745
913
|
unresolvable reference, both reported by the linter. Wildcard references
|
|
746
914
|
resolve into match *sets* and are used in the schema configuration to
|
|
747
915
|
constrain reference-valued properties (e.g. `[[PERSONA:*]]` or
|