@rse/gradia 0.9.9 → 1.0.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 +73 -12
- package/dst/gradia-api-config.d.ts +1 -0
- package/dst/gradia-api-config.js +10 -4
- package/dst/gradia-api-parser.js +8 -5
- package/dst/gradia-api-render-base.js +5 -3
- package/dst/gradia-api-render-edge.js +3 -2
- package/dst/gradia-api-render-group.js +1 -1
- package/dst/gradia-api-render-node.js +5 -4
- package/dst/gradia-api-render-svg.d.ts +1 -1
- package/dst/gradia-api-render-svg.js +70 -54
- package/dst/gradia-api-type-graph.js +20 -10
- package/dst/gradia-api-type-grid.js +7 -4
- package/dst/gradia-api-type-hub.js +7 -9
- package/dst/gradia-api.js +20 -4
- package/dst/gradia-api.umd.js +2 -2
- package/dst/gradia-cli.js +5 -3
- package/dst/gradia-mcp.js +82 -13
- package/package.json +2 -2
package/dst/gradia-cli.js
CHANGED
|
@@ -13,7 +13,6 @@ import { Command, Option } from "commander";
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import { configDefaults } from "./gradia-api-config.js";
|
|
15
15
|
import { Gradia, diagramTypes, diagramTypeDefault, diagramFormats, diagramFormatDefault } from "./gradia-api.js";
|
|
16
|
-
import { serve as serveMCP } from "./gradia-mcp.js";
|
|
17
16
|
/* internal package meta-information */
|
|
18
17
|
const pkg = JSON.parse(fs.readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf8"));
|
|
19
18
|
/* establish the command-line interface */
|
|
@@ -37,9 +36,9 @@ program.name("gradia")
|
|
|
37
36
|
const m = /^([a-z][a-z0-9-]*)=(.*)$/.exec(nv);
|
|
38
37
|
if (m === null)
|
|
39
38
|
throw new Error(`invalid configuration option "${nv}" (expected "<name>=<value>")`);
|
|
40
|
-
|
|
41
|
-
if (!Object.hasOwn(configDefaults, key))
|
|
39
|
+
if (!Object.hasOwn(configDefaults, m[1]))
|
|
42
40
|
throw new Error(`unknown configuration option "${m[1]}"`);
|
|
41
|
+
const key = m[1];
|
|
43
42
|
if (typeof configDefaults[key] === "boolean") {
|
|
44
43
|
if (m[2] !== "true" && m[2] !== "false")
|
|
45
44
|
throw new Error(`invalid value "${m[2]}" for boolean configuration option "${key}" (expected "true" or "false")`);
|
|
@@ -51,6 +50,8 @@ program.name("gradia")
|
|
|
51
50
|
throw new Error(`invalid value "${m[2]}" for numeric configuration option "${key}" (expected a non-negative number)`);
|
|
52
51
|
store[key] = num;
|
|
53
52
|
}
|
|
53
|
+
else if (m[2] === "")
|
|
54
|
+
throw new Error(`invalid empty value for string configuration option "${key}"`);
|
|
54
55
|
else
|
|
55
56
|
store[key] = m[2];
|
|
56
57
|
}
|
|
@@ -63,6 +64,7 @@ program.name("gradia")
|
|
|
63
64
|
throw new Error("option \"--mcp\" cannot be combined with an input graph description file");
|
|
64
65
|
if (options.output !== undefined)
|
|
65
66
|
throw new Error("option \"--mcp\" cannot be combined with option \"--output\"");
|
|
67
|
+
const { serve: serveMCP } = await import("./gradia-mcp.js");
|
|
66
68
|
await serveMCP(pkg, { type: options.type, format: options.format, config });
|
|
67
69
|
return;
|
|
68
70
|
}
|
package/dst/gradia-mcp.js
CHANGED
|
@@ -28,11 +28,37 @@ component relationships, data flows, dependency graphs, and similar structures.
|
|
|
28
28
|
## Diagram Types
|
|
29
29
|
|
|
30
30
|
- "graph": a loosely grid-snapped layered layout of the whole graph. This is
|
|
31
|
-
the general-purpose type and the built-in default.
|
|
31
|
+
the general-purpose type and the built-in default. It accepts any topology,
|
|
32
|
+
including self-loops and free-standing nodes.
|
|
32
33
|
- "hub": a hub graph, placing the single primary node (the node carrying the
|
|
33
34
|
attribute "primary: true") in the center, its input nodes to the left and
|
|
34
|
-
its output nodes to the right.
|
|
35
|
-
|
|
35
|
+
its output nodes to the right. It accepts only the constrained topology
|
|
36
|
+
described below.
|
|
37
|
+
- "grid": a compact grid of tiles for an edge-less graph. The nodes are placed
|
|
38
|
+
in declaration order onto a roughly square, row-major grid.
|
|
39
|
+
|
|
40
|
+
### Diagram Type Constraints
|
|
41
|
+
|
|
42
|
+
The "hub" type rejects the input with an error unless all of the following
|
|
43
|
+
conditions hold:
|
|
44
|
+
|
|
45
|
+
- Exactly one node carries the attribute "primary: true".
|
|
46
|
+
- Every edge either points to or originates from that primary node. An edge
|
|
47
|
+
between two non-primary nodes is rejected.
|
|
48
|
+
- The primary node carries no self-loop.
|
|
49
|
+
- Every non-primary node is an input or an output of the primary node, i.e.,
|
|
50
|
+
no node is free-standing.
|
|
51
|
+
|
|
52
|
+
A node which is both an input and an output of the primary node is placed
|
|
53
|
+
twice, once in the input column and once in the output column. The second
|
|
54
|
+
placement is rendered as a dashed "ghost" box, colored by the
|
|
55
|
+
"color-node-ghost-*" options.
|
|
56
|
+
|
|
57
|
+
The "grid" type rejects the input with an error as soon as the graph contains
|
|
58
|
+
at least one edge.
|
|
59
|
+
|
|
60
|
+
The "graph" type imposes no such constraints and hence is the safe choice
|
|
61
|
+
whenever the topology is not known to fit "hub" or "grid".
|
|
36
62
|
|
|
37
63
|
## Input Language
|
|
38
64
|
|
|
@@ -85,6 +111,10 @@ as <name>, terminals as "...", optional parts as [ ... ], repeatable parts as
|
|
|
85
111
|
- A <string> cannot span lines. Inside it, "\\" escapes the following character.
|
|
86
112
|
- A <directive> is a <comment> which occupies its entire line and starts with
|
|
87
113
|
the keyword "#config" or "#type". All other comments are ignored.
|
|
114
|
+
- Of multiple "#type" directives the last one wins, and a directive naming an
|
|
115
|
+
invalid type is silently ignored. A "#config" directive naming an unknown
|
|
116
|
+
option or carrying an invalid value is silently ignored, too, whereas the
|
|
117
|
+
same mistake in the "config" tool argument is rejected with an error.
|
|
88
118
|
|
|
89
119
|
### Semantic Rules
|
|
90
120
|
|
|
@@ -100,14 +130,20 @@ as <name>, terminals as "...", optional parts as [ ... ], repeatable parts as
|
|
|
100
130
|
its cardinality label.
|
|
101
131
|
- Four attribute keys are reserved and consumed by the renderer instead of being
|
|
102
132
|
displayed as a regular "<key>: <val>" line:
|
|
103
|
-
- "url: <url>" renders the node box as a hyperlink
|
|
133
|
+
- "url: <url>" renders the node box as a hyperlink. Only relative URLs and
|
|
134
|
+
the schemes "http", "https", and "mailto" are honored; any other URL is
|
|
135
|
+
silently dropped and the node box then simply stays unlinked,
|
|
104
136
|
- "type: <name>" renders <name> as a smaller text above the node label,
|
|
105
137
|
- "primary: true" renders the node in the primary node colors (and, for the
|
|
106
|
-
diagram type "hub", marks the single central hub node)
|
|
138
|
+
diagram type "hub", marks the single central hub node). The value has to
|
|
139
|
+
be the literal string "true",
|
|
107
140
|
- "group: <name>" places the node into the surrounding group box <name>.
|
|
108
141
|
- A node cannot be a member of more than one group. As soon as at least one node
|
|
109
142
|
carries a "group" attribute, all nodes without one implicitly belong to the
|
|
110
143
|
group "default", and every edge has to stay within a single group.
|
|
144
|
+
- The groups are laid out individually with the selected diagram type and then
|
|
145
|
+
stacked vertically as decorated group boxes. For the type "hub" this means
|
|
146
|
+
that every single group needs its own primary node.
|
|
111
147
|
- All remaining attributes are displayed as "<key>: <val>" lines inside the node
|
|
112
148
|
box.
|
|
113
149
|
|
|
@@ -138,12 +174,35 @@ The "size-*", "group-*", "graph-*", "hub-*", and "grid-*" options control the
|
|
|
138
174
|
rendering geometry (canvas margin, node box sizing, edge routing, group box
|
|
139
175
|
spacing, and the per-diagram-type layout) and take non-negative numbers, except
|
|
140
176
|
the boolean "grid-node-width-equal", which forces all node boxes of a "grid"
|
|
141
|
-
diagram to the width of the widest one.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
177
|
+
diagram to the width of the widest one.
|
|
178
|
+
|
|
179
|
+
The "size-node-width-max" option additionally enables the word-wrapping of the
|
|
180
|
+
node box texts: given a positive value, the node name, its type, and its
|
|
181
|
+
attribute lines are greedily broken at whitespace, so that the node box stays
|
|
182
|
+
within the given width and grows in height instead. This is the only way to
|
|
183
|
+
produce multi-line node boxes. The default "0" disables the wrapping entirely
|
|
184
|
+
and hence lets a node box become as wide as its longest text. A single word
|
|
185
|
+
wider than the maximum is never broken and still widens the box beyond it.
|
|
186
|
+
|
|
187
|
+
The "grid-columns-min" and "grid-columns-max" options control the column count
|
|
188
|
+
of a "grid" diagram, which by default is derived from the node count as a
|
|
189
|
+
roughly square grid: "grid-columns-min" (default 3) raises the derived count,
|
|
190
|
+
so that a few nodes still share a single row instead of being stacked, while
|
|
191
|
+
"grid-columns-max" (default 4) caps it, so that larger diagrams grow in height
|
|
192
|
+
only. The column count never exceeds the node count and the maximum always
|
|
193
|
+
wins over the minimum.
|
|
194
|
+
|
|
195
|
+
The "font-family" and "color-*" options are embedded into the generated SVG:
|
|
196
|
+
when such an option is not explicitly configured, the SVG instead references
|
|
197
|
+
the CSS custom property "--gradia-<option>" (definable by an embedding HTML
|
|
198
|
+
document) and falls back to the built-in default. Leave them unset unless a
|
|
199
|
+
particular look is explicitly requested.
|
|
200
|
+
|
|
201
|
+
The "font-family" option is either the built-in font family "Source Sans 3" or
|
|
202
|
+
a plain font family name resolved by the displaying document. A path to a
|
|
203
|
+
WOFF2 file is rejected in this context, exactly like the option "font-embed":
|
|
204
|
+
both are available only through the (trusted) command-line options of the
|
|
205
|
+
service.
|
|
147
206
|
|
|
148
207
|
## Result
|
|
149
208
|
|
|
@@ -151,6 +210,10 @@ The tool returns the rendered diagram as a single text result, in the requested
|
|
|
151
210
|
output format. For the "svg:*" formats this is the SVG/XML document, which is
|
|
152
211
|
usually written to a "*.svg" file; for the "url:*" formats it is a
|
|
153
212
|
"data:image/svg+xml" URL, usable as the "src" of an HTML "<img>" element.
|
|
213
|
+
|
|
214
|
+
On a malformed input, a violated diagram type constraint, or an invalid "config"
|
|
215
|
+
argument, the tool instead returns an error result whose text starts with
|
|
216
|
+
"gradia: ERROR: ", followed by the reason.
|
|
154
217
|
`.trim();
|
|
155
218
|
/* the arguments of the "gradia_render" tool */
|
|
156
219
|
const toolArguments = {
|
|
@@ -178,11 +241,17 @@ const validateConfig = (raw) => {
|
|
|
178
241
|
if (!Object.hasOwn(configDefaults, key))
|
|
179
242
|
throw new Error(`unknown configuration option "${key}"`);
|
|
180
243
|
const k = key;
|
|
181
|
-
if (
|
|
244
|
+
if (k === "font-embed")
|
|
182
245
|
throw new Error(`configuration option "${key}" is not available in this context`);
|
|
246
|
+
else if (typeof configDefaults[k] === "boolean") {
|
|
247
|
+
if (typeof val !== "boolean" && val !== "true" && val !== "false")
|
|
248
|
+
throw new Error(`invalid value "${String(val)}" for boolean configuration option "${key}" ` +
|
|
249
|
+
"(expected \"true\" or \"false\")");
|
|
250
|
+
config[k] = typeof val === "boolean" ? val : val === "true";
|
|
251
|
+
}
|
|
183
252
|
else if (typeof configDefaults[k] === "number") {
|
|
184
253
|
const num = Number(val);
|
|
185
|
-
if (typeof val === "boolean" || !Number.isFinite(num) || num < 0)
|
|
254
|
+
if (typeof val === "boolean" || val === "" || !Number.isFinite(num) || num < 0)
|
|
186
255
|
throw new Error(`invalid value "${String(val)}" for numeric configuration option "${key}" ` +
|
|
187
256
|
"(expected a non-negative number)");
|
|
188
257
|
config[k] = num;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Object Graph Diagram Rendering",
|
|
4
4
|
"keywords": [ "object", "graph", "diagram", "rendering", "svg", "layout", "api", "cli" ],
|
|
5
5
|
"publishConfig": { "access": "public" },
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "1.0.0",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"commander": "15.0.0",
|
|
28
28
|
"@antv/layout": "2.0.0",
|
|
29
|
-
"
|
|
29
|
+
"pure-uuid": "2.0.0",
|
|
30
30
|
"source-sans": "3.52.0",
|
|
31
31
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
32
32
|
"zod": "4.4.3"
|