create-idocs 0.3.1 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-idocs",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "Scaffold a docs site with interactive diagrams: npm create idocs@latest my-docs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,7 @@
25
25
  "access": "public"
26
26
  },
27
27
  "devDependencies": {
28
- "@the-package-labs/idocs-core": "^0.3.1"
28
+ "@the-package-labs/idocs-core": "^0.5.0"
29
29
  },
30
30
  "license": "MIT",
31
31
  "scripts": {
@@ -25,7 +25,7 @@ Use the project's package manager for every command below (`pnpm idocs ...`, `np
25
25
 
26
26
  1. **Look at what exists.** `idocs list --json` shows the diagrams, views and scenarios. Extend a diagram rather than starting a second one for the same system.
27
27
  2. **Read the code you are documenting.** Never invent components, ports, routes or flows. Put the source files a node is based on in its `refs:` so readers can find them and `idocs check` warns when they move.
28
- 3. **Pick a scope.** One diagram answers one question ("how does checkout work?"). Aim for 8 to 25 nodes. If it needs more, make a second **view** of the same model, not a second diagram.
28
+ 3. **Pick a scope.** One diagram answers one question ("how does checkout work?"). Aim for 8 to 25 nodes. If it needs more, make a second **view** of the same model. If one node has an inside worth a diagram of its own, give it `diagram: <name>` ("Nested diagrams" in [references/format.md](references/format.md)) instead of crowding the parent.
29
29
  4. **Write the model**: nodes, then groups, then edges. Declare nodes in reading order: order decides layout. Format: [references/format.md](references/format.md).
30
30
  5. **Add scenarios** for the flows a reader will ask about: the happy path first, then the failures that teach something. Format and writing advice: [references/scenarios.md](references/scenarios.md).
31
31
  6. **Check.** `idocs check --json`. Fix every error and every warning; messages carry `file:line:column` and a suggestion. Common ones: [references/troubleshooting.md](references/troubleshooting.md).
@@ -3,7 +3,7 @@
3
3
  title: Online store
4
4
  description: |
5
5
  How a shopper's request reaches the store and what the backend does with it.
6
- Pick a scenario above the diagram and press play.
6
+ Pick a scenario on the bar under the diagram and press play.
7
7
 
8
8
  groups:
9
9
  backend:
@@ -11,7 +11,7 @@ diagrams/
11
11
  scenarios/<id>.yaml optional: a long scenario in its own file; the file name is its id
12
12
  ```
13
13
 
14
- Top-level keys: `title` (required), `description`, `nodes` (required), `groups`, `edges`, `views`, `scenarios`, `kinds`, `edgeKinds`. Unknown keys are errors with a "did you mean" hint.
14
+ Top-level keys: `title` (required), `description`, `nodes` (required), `groups`, `edges`, `views`, `scenarios`, `defaults`, `kinds`, `edgeKinds`. Unknown keys are errors with a "did you mean" hint.
15
15
 
16
16
  ## Nodes
17
17
 
@@ -21,7 +21,9 @@ Top-level keys: `title` (required), `description`, `nodes` (required), `groups`,
21
21
  |---|---|
22
22
  | `title` | Display name. Defaults to the id. |
23
23
  | `sub` | One mono line under the title: port, stack, version. |
24
+ | `pill` | Short label in the card header: `TABLE`, `ES INDEX`. |
24
25
  | `lines` | List of short body lines in the card. |
26
+ | `fields` | Makes the card an entity: one row per field. `- id: { type: uuid, key: true }`, `- email: text`, `- customer_id: { type: uuid, ref: customers.id }`, or just `- name`. See "Entities" below. |
25
27
  | `icon` | `postgresql` (looks in brand logos, then monochrome brand marks, then Lucide), `logos:redis`, `simple-icons:keycloak`, `lucide:server`, or a path to your own SVG relative to the diagram folder. |
26
28
  | `kind` | Gives a default icon and colour family (list below). |
27
29
  | `family` | Accent colour: `blue amber violet green teal red slate`. Overrides the kind's. |
@@ -30,14 +32,45 @@ Top-level keys: `title` (required), `description`, `nodes` (required), `groups`,
30
32
  | `badge` | A number or short string shown on the card corner. |
31
33
  | `uses` | List of short tags drawn as "uses: a · b". Entries that match a node id become links in the drawer. |
32
34
  | `chips` | Small boxes inside the card, such as virtual hosts: `- { label: app.*, sub: "→ backend :9100" }`. |
33
- | `w` | Card width in px (default 178). Narrow it in dense rows. |
35
+ | `w` | Card width in px (default 178, or 280 with `fields`). Narrow it in dense rows. |
34
36
  | `description` | Markdown for the detail drawer, inline. |
35
37
  | `doc` | Path to a markdown file for the drawer. Defaults to `nodes/<id>.md` when that file exists. |
36
38
  | `links` | `- { label: Runbook, url: https://... }` |
37
39
  | `refs` | Source files or URLs the node is based on. `idocs check` warns when a path no longer exists. `src/api.ts`, `src/api.ts:40` and `src/api.ts#L40` all work. |
38
40
  | `tags` | Free-form tags. |
41
+ | `diagram` | Name of another diagram (its folder under `diagrams/`) that shows the inside of this node: `diagram: payments`. The card gets **Expand** (show it in place, as a group around the whole inner diagram) and **Open** (move into it, with a trail and Back button). Clicking the node shows the inner diagram as a picture in the drawer. Long form: `diagram: { name: payments, view: refunds, scenario: pay, mode: both, expanded: false }`. See "Nested diagrams" below. |
39
42
 
40
- Node kinds: `client user ui external service gateway worker container function database cache storage queue agent network vm auth monitor`. Add or override your own with a top-level `kinds:` map: `ledger: { icon: lucide:book-open, family: amber }`.
43
+ Node kinds: `client user ui external service gateway worker container function database cache storage queue agent network vm auth monitor entity`. Add or override your own with a top-level `kinds:` map: `ledger: { icon: lucide:book-open, family: amber }`.
44
+
45
+ ## Entities
46
+
47
+ A node with `fields` is an entity: a table, a collection, an index. Use `kind: entity` (blue, a table icon); the card is wider by default.
48
+
49
+ ```yaml
50
+ nodes:
51
+ orders:
52
+ kind: entity
53
+ title: orders
54
+ pill: TABLE
55
+ lines: [One row per checkout.]
56
+ fields:
57
+ - id: { type: uuid, key: true } # `#` in front
58
+ - customer_id: { type: uuid, ref: customers.id } # blue, with an arrow; draws the relation
59
+ - status: pending | paid | shipped
60
+ - total_cents: { type: integer, note: In the smallest currency unit. }
61
+ - placed_at: timestamp
62
+ - notes # a field with no type
63
+ customers: { kind: entity, title: customers, fields: [{ id: { type: uuid, key: true } }] }
64
+ edges:
65
+ - orders.customer_id -> customers.id: N:1 # the same edge by hand, to give it a label
66
+ ```
67
+
68
+ - **One field per list item**: `- name`, `- name: type` or `- name: { type, key, ref, note }`. A field is never written as `{ name: id, type: uuid }`, because a field called `name` would be ambiguous.
69
+ - **`ref`** is a node id or `node.field`. Each one draws a `relation` edge from this entity to that one, unless you write the same edge by hand (then yours wins, which is how you add a label such as `N:1`). A field that points at its own entity is marked but draws nothing.
70
+ - **Edges** may name a field on either end: `orders.customer_id -> customers.id`. Names with dots (`meta.version`) work; a node id always wins over a split. Names with spaces need the `{ from: ..., to: ... }` form.
71
+ - An edge between two entities is a `relation` unless it has another `kind`.
72
+ - The line runs between the two cards. The field names say which columns the relation is about.
73
+ - Entities are plain nodes: `in:`, `status`, `uses`, `description` and scenarios all work. A dashed look for something you do not own is `status: optional`.
41
74
 
42
75
  ## Groups
43
76
 
@@ -70,9 +103,9 @@ edges:
70
103
 
71
104
  | Field | Meaning |
72
105
  |---|---|
73
- | `from`, `to` | Node ids (required in the object form). |
106
+ | `from`, `to` | Node ids (required in the object form). For an entity, `node.field` names the field the relation is about. |
74
107
  | `label` | Short: a protocol, route or event name. |
75
- | `kind` | Line style: `http` (default), `channel` (persistent: websocket, gRPC), `tunnel`, `queue`, `ws`, `data`, or one you define in `edgeKinds:`. |
108
+ | `kind` | Line style: `http` (default), `channel` (persistent: websocket, gRPC), `tunnel`, `queue`, `ws`, `data`, `relation` (between entities), or one you define in `edgeKinds:`. |
76
109
  | `both` | Arrow at both ends (same as `<->`). |
77
110
  | `hidden` | Not drawn and not in the legend, but scenarios can fly along it. Use it for relationships a scenario needs and the picture does not. |
78
111
  | `status` | As for nodes. |
@@ -94,7 +127,7 @@ views:
94
127
  overview:
95
128
  title: Overview
96
129
  keys: true # letter keys on edges
97
- interfaces: true # table of every edge under the diagram (key, from → to, kind, auth, payload)
130
+ interfaces: true # table of every edge under the diagram (key, from → to, kind, auth, payload), folded until opened
98
131
  summary: Markdown shown above the diagram.
99
132
  backend:
100
133
  title: Backend only
@@ -105,6 +138,44 @@ views:
105
138
 
106
139
  Edges whose two ends are both in the view are included. A scenario plays on the first view unless it sets `view:`; every node it uses must be in that view.
107
140
 
141
+ ## Defaults
142
+
143
+ By default a diagram opens on its first view, on its overview, with every scenario waiting to be chosen. `defaults:` changes how this one opens; every field is optional.
144
+
145
+ ```yaml
146
+ defaults:
147
+ scenario: sign-in # selected first; its view comes with it. It waits for play unless autoplay is on
148
+ view: overview # shown first (when it names a scenario too, that scenario must play on this view)
149
+ mode: sequence # flow, sequence or story: how a scenario with no `mode:` of its own is drawn
150
+ sidebar: false # the panel over the picture (outline, views) starts folded to a button
151
+ subtitles: false # the bar under the picture shows no title and description for the step
152
+ autoplay: true # play `scenario` once the diagram is on screen (needs `scenario`)
153
+ ```
154
+
155
+ A page's own props on `<Diagram>` (`view`, `scenario`, `mode`, `autoplay`, `sidebar`, `subtitles`) win over these, and a scenario's own `mode:` wins over `defaults.mode`. Use it for the way a diagram nearly always opens, not to hide anything: the reader can still reach every view and scenario.
156
+
157
+ ## Nested diagrams
158
+
159
+ A node can hold a whole diagram of its own. Write the inside as an ordinary diagram in its own folder, then point the node at it:
160
+
161
+ ```yaml
162
+ # diagrams/shop/diagram.yaml
163
+ nodes:
164
+ orders: { kind: service, title: Order service, diagram: orders }
165
+ payments: { kind: service, title: Payments, diagram: { name: payments, scenario: pay } }
166
+ ```
167
+
168
+ Readers can **expand** the card, which shows the whole inner diagram in place as a group (and nodes inside can be expanded too), or **open** it, which moves into that diagram with its own views, scenarios and player; **Back**, the trail above it, or Esc return to the parent exactly as they left it. Diagrams can nest as deep as the system does.
169
+
170
+ - `mode: both` (default), `expand` or `open` limits which of the two the reader gets. `expanded: true` starts the node expanded, so a page shows the whole picture at once.
171
+ - An expanded diagram is a preview of one view (the one named, else the first). Its scenarios are played by opening it.
172
+
173
+ - Nest when the inside of a node is a story of its own. If it only needs a paragraph, use a node doc (`nodes/<id>.md`); if it only needs a different slice of the same model, use a view.
174
+ - Several nodes may open the same diagram. A diagram cannot be inside itself: a node that opens its own diagram, or diagrams that open each other in a circle, are errors.
175
+ - `idocs check` verifies the name, `view` and `scenario` across diagrams, at the line that wrote them. `idocs list` shows what opens what.
176
+ - Nothing else is needed on the page: a compiled diagram carries the diagrams it can open, so `<Diagram name="shop" />` just works.
177
+ - Links: `#shop~=orders/stock` opens `orders` then `stock`; `#shop+=orders,orders/stock` has those expanded; each level's scenario is `#shop/orders=create.2`.
178
+
108
179
  ## Pages
109
180
 
110
181
  ```mdx
@@ -16,7 +16,7 @@ scenarios:
16
16
  steps:
17
17
  - browser -> orders:
18
18
  label: POST /orders
19
- note: The request carries an idempotency key. # explanation shown in the step list
19
+ note: The request carries an idempotency key. # explanation shown in the timeline
20
20
  - orders -> db: { label: INSERT order, kind: lookup }
21
21
  - title: Charge
22
22
  steps:
@@ -41,7 +41,7 @@ Every step can be written compactly (`a -> b: label`) or as a map (`a -> b: { la
41
41
  |---|---|
42
42
  | `label` | What travels, shown on the packet. Keep it short. |
43
43
  | `kind` | `request` (default), `response`, `error`, `lookup`, `event`. Changes the packet's colour and style. |
44
- | `note` | Markdown explanation shown in the step list. |
44
+ | `note` | Markdown explanation shown in the timeline. |
45
45
  | `status` | `planned` draws the step dashed with a tag. |
46
46
  | `via` | Force the route through these nodes: `via: [gateway]`. |
47
47
  | `hold` | Extra pause in seconds. |
@@ -98,7 +98,7 @@ scenarios:
98
98
  - **One question per scenario.** "Pay with a card" and "Card declined" are two scenarios.
99
99
  - **Failures get their own scenario.** That is where readers learn the most.
100
100
  - **Label what travels** (`POST /payments`, `payment.succeeded`) and put the reasoning in `note`.
101
- - **Two to five phases**, with plain names. They become the chapter headings in the step list.
101
+ - **Two to five phases**, with plain names. They become the chapter headings in the timeline.
102
102
  - **Do not narrate the obvious.** A note should answer "why" or "what could go wrong here".
103
103
  - **Start at the actor.** The first step is usually `browser -> gateway`, or an `at` step describing the trigger.
104
104
  - If a scenario needs a relationship you do not want drawn, add the edge with `hidden: true`.
@@ -15,6 +15,11 @@ Run `idocs check --json` first. Each message has a file, line, column and often
15
15
  | `A step with at needs a label or a title` | `at x:` needs text: `at x: what happens`. |
16
16
  | `"x" is not a direct member of group "g"` | Entries in a group's `rows:` must be nodes or groups whose `in:` is that group. |
17
17
  | `Groups are nested in a cycle` | A group's `in:` chain loops back on itself. |
18
+ | `Node "x" opens diagram "y", which does not exist` | The `diagram:` name must be a folder under `diagrams/`. The hint names the nearest one. |
19
+ | `Diagram "y" has no view "v"` / `... no scenario "s"` | The `view` or `scenario` in a node's long-form `diagram:` is not in that diagram. The hint lists what it has. |
20
+ | `Scenario "s" plays on view "a", not "b"` | A node's `diagram:` names both, and they disagree. Drop `view` and it opens on the scenario's own view. |
21
+ | `Node "x" opens "y", which leads back to "z"` / `... opens its own diagram` | Diagrams cannot nest in a circle. The hint shows the loop; break it at one node. |
22
+ | warning `` `expanded` has no effect with `mode: open` `` | A node limited to `mode: open` cannot start expanded. Use `mode: expand` or `both`, or drop `expanded`. |
18
23
  | `Edge id "e" is used twice` | Give edges distinct ids or drop the ids. |
19
24
  | `Unknown edge kind "x"` | Use a built-in kind or define it under `edgeKinds:`. |
20
25
  | warning `Unknown icon "x"` | `idocs icons search x`, then use the suggested name. |
@@ -13,6 +13,44 @@
13
13
  "description": "Markdown shown above the first view.",
14
14
  "type": "string"
15
15
  },
16
+ "defaults": {
17
+ "description": "How the diagram opens: its view, scenario and mode, and whether the panel and subtitles show. A page can still choose otherwise with the props of `<Diagram>`.",
18
+ "type": "object",
19
+ "properties": {
20
+ "view": {
21
+ "description": "View shown first. Defaults to the view of `scenario`, or else the first view.",
22
+ "type": "string",
23
+ "pattern": "^[A-Za-z0-9_.-]+$"
24
+ },
25
+ "scenario": {
26
+ "description": "Scenario selected first. Without it the diagram opens on its overview. It does not play until a reader presses play, unless `autoplay` is on.",
27
+ "type": "string",
28
+ "pattern": "^[A-Za-z0-9_.-]+$"
29
+ },
30
+ "mode": {
31
+ "description": "How a scenario is drawn when it has no `mode` of its own: packets over the diagram, a sequence diagram, or a swimlane story.",
32
+ "type": "string",
33
+ "enum": [
34
+ "flow",
35
+ "sequence",
36
+ "story"
37
+ ]
38
+ },
39
+ "sidebar": {
40
+ "description": "Whether the panel over the picture starts open. By default it does when there is a scenario or another view to choose, and the frame is wide enough.",
41
+ "type": "boolean"
42
+ },
43
+ "subtitles": {
44
+ "description": "Words over the picture saying what the scenario is doing. On by default.",
45
+ "type": "boolean"
46
+ },
47
+ "autoplay": {
48
+ "description": "Play `scenario` as soon as the diagram is on screen. Needs `scenario`, and is ignored when the system asks for reduced motion.",
49
+ "type": "boolean"
50
+ }
51
+ },
52
+ "additionalProperties": false
53
+ },
16
54
  "kinds": {
17
55
  "type": "object",
18
56
  "propertyNames": {
@@ -185,6 +223,10 @@
185
223
  "description": "One line under the title, rendered in mono (ports, stack, version).",
186
224
  "type": "string"
187
225
  },
226
+ "pill": {
227
+ "description": "Short label in the card header, like `TABLE` or `ES INDEX`.",
228
+ "type": "string"
229
+ },
188
230
  "lines": {
189
231
  "description": "Short body lines inside the card.",
190
232
  "type": "array",
@@ -192,6 +234,57 @@
192
234
  "type": "string"
193
235
  }
194
236
  },
237
+ "fields": {
238
+ "description": "Makes the card an entity: a table of fields, each `name: type`. `ref` points a field at another entity.",
239
+ "type": "array",
240
+ "items": {
241
+ "anyOf": [
242
+ {
243
+ "type": "string",
244
+ "minLength": 1
245
+ },
246
+ {
247
+ "type": "object",
248
+ "propertyNames": {
249
+ "type": "string",
250
+ "minLength": 1
251
+ },
252
+ "additionalProperties": {
253
+ "anyOf": [
254
+ {
255
+ "type": "string"
256
+ },
257
+ {
258
+ "type": "null"
259
+ },
260
+ {
261
+ "type": "object",
262
+ "properties": {
263
+ "type": {
264
+ "description": "Its type: `keyword`, `uuid`, `string[]`.",
265
+ "type": "string"
266
+ },
267
+ "key": {
268
+ "description": "Part of the primary key. Shown with a `#` in front.",
269
+ "type": "boolean"
270
+ },
271
+ "ref": {
272
+ "description": "The `node` or `node.field` this one points at, like a foreign key. Draws a relation between the two cards.",
273
+ "type": "string"
274
+ },
275
+ "note": {
276
+ "description": "Shown on hover and in the drawer.",
277
+ "type": "string"
278
+ }
279
+ },
280
+ "additionalProperties": false
281
+ }
282
+ ]
283
+ }
284
+ }
285
+ ]
286
+ }
287
+ },
195
288
  "icon": {
196
289
  "description": "`postgresql`, `logos:redis`, `lucide:server` or ./path/to.svg.",
197
290
  "type": "string"
@@ -312,6 +405,52 @@
312
405
  "items": {
313
406
  "type": "string"
314
407
  }
408
+ },
409
+ "diagram": {
410
+ "description": "Another diagram (its folder name) that shows the inside of this node. The card gets buttons to expand it in place, as a group, or to open it one level deeper, with a way back.",
411
+ "anyOf": [
412
+ {
413
+ "type": "string",
414
+ "minLength": 1
415
+ },
416
+ {
417
+ "type": "object",
418
+ "properties": {
419
+ "name": {
420
+ "type": "string",
421
+ "minLength": 1,
422
+ "description": "Folder name of the diagram, under diagrams/."
423
+ },
424
+ "view": {
425
+ "description": "View to open first. Defaults to the scenario view, then the first.",
426
+ "type": "string",
427
+ "pattern": "^[A-Za-z0-9_.-]+$"
428
+ },
429
+ "scenario": {
430
+ "description": "Scenario to select when the diagram opens.",
431
+ "type": "string",
432
+ "pattern": "^[A-Za-z0-9_.-]+$"
433
+ },
434
+ "mode": {
435
+ "description": "both (default): the reader can expand it in place or open it. expand: only in place. open: only move into it.",
436
+ "type": "string",
437
+ "enum": [
438
+ "both",
439
+ "expand",
440
+ "open"
441
+ ]
442
+ },
443
+ "expanded": {
444
+ "description": "Start expanded in place, so the whole inner diagram shows as a group.",
445
+ "type": "boolean"
446
+ }
447
+ },
448
+ "required": [
449
+ "name"
450
+ ],
451
+ "additionalProperties": false
452
+ }
453
+ ]
315
454
  }
316
455
  },
317
456
  "additionalProperties": false
@@ -14,14 +14,14 @@
14
14
  "@astrojs/starlight": "^0.42.0",
15
15
  "@fontsource/ibm-plex-mono": "^5.3.0",
16
16
  "@fontsource/ibm-plex-sans": "^5.3.0",
17
- "@the-package-labs/idocs-astro": "^0.3.1",
18
- "@the-package-labs/idocs-react": "^0.3.1",
17
+ "@the-package-labs/idocs-astro": "^0.5.0",
18
+ "@the-package-labs/idocs-react": "^0.5.0",
19
19
  "astro": "^7.0.0",
20
20
  "react": "^19.3.0",
21
21
  "react-dom": "^19.3.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@the-package-labs/idocs": "^0.3.1",
24
+ "@the-package-labs/idocs": "^0.5.0",
25
25
  "@types/react": "^19.2.0",
26
26
  "@types/react-dom": "^19.2.0"
27
27
  },