create-idocs 0.0.0-stage → 0.3.1
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/LICENSE +21 -0
- package/README.md +9 -2
- package/dist/chunk-YVMC6WCS.js +43 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +49 -0
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.js +8 -0
- package/package.json +32 -3
- package/template/.claude/skills/idocs/SKILL.md +108 -0
- package/template/.claude/skills/idocs/assets/examples/shop.diagram.yaml +164 -0
- package/template/.claude/skills/idocs/references/format.md +123 -0
- package/template/.claude/skills/idocs/references/layout.md +39 -0
- package/template/.claude/skills/idocs/references/scenarios.md +104 -0
- package/template/.claude/skills/idocs/references/setup.md +95 -0
- package/template/.claude/skills/idocs/references/troubleshooting.md +49 -0
- package/template/.github/workflows/deploy.yml +60 -0
- package/template/CLAUDE.md +27 -0
- package/template/README.md +30 -0
- package/template/_gitignore +6 -0
- package/template/_pnpm-workspace.yaml +4 -0
- package/template/astro.config.mjs +32 -0
- package/template/diagrams/diagram.schema.json +647 -0
- package/template/diagrams/shop/diagram.yaml +105 -0
- package/template/diagrams/shop/nodes/gateway.md +1 -0
- package/template/diagrams/shop/nodes/orders.md +9 -0
- package/template/package.json +29 -0
- package/template/public/favicon.svg +1 -0
- package/template/src/content/docs/architecture.mdx +13 -0
- package/template/src/content/docs/editing.mdx +62 -0
- package/template/src/content/docs/index.mdx +18 -0
- package/template/src/content.config.ts +7 -0
- package/template/src/env.d.ts +2 -0
- package/template/src/styles/custom.css +16 -0
- package/template/tsconfig.json +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ahmed Basem
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# create-idocs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```sh
|
|
4
|
+
npm create idocs@latest my-docs
|
|
5
|
+
cd my-docs
|
|
6
|
+
pnpm install
|
|
7
|
+
pnpm dev
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Creates an Astro + Starlight site with an example diagram and three scenarios, a GitHub Pages workflow, and a Claude Code skill that knows the diagram format. The folder must not exist or must be empty. The project uses pnpm.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/scaffold.ts
|
|
2
|
+
import {
|
|
3
|
+
cpSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
renameSync,
|
|
9
|
+
writeFileSync
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { basename, resolve } from "node:path";
|
|
12
|
+
var RENAMES = {
|
|
13
|
+
_gitignore: ".gitignore",
|
|
14
|
+
"_pnpm-workspace.yaml": "pnpm-workspace.yaml"
|
|
15
|
+
};
|
|
16
|
+
function packageName(folder) {
|
|
17
|
+
const name = folder.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-._]+|[-._]+$/g, "");
|
|
18
|
+
return name || "my-docs";
|
|
19
|
+
}
|
|
20
|
+
function scaffold(templateDir, target) {
|
|
21
|
+
const dir = resolve(target);
|
|
22
|
+
if (!existsSync(templateDir)) throw new Error(`Template not found at ${templateDir}`);
|
|
23
|
+
if (existsSync(dir) && readdirSync(dir).length > 0) {
|
|
24
|
+
throw new Error(`${dir} is not empty. Choose another folder, or empty it first.`);
|
|
25
|
+
}
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
cpSync(templateDir, dir, { recursive: true });
|
|
28
|
+
for (const [from, to] of Object.entries(RENAMES)) {
|
|
29
|
+
if (existsSync(resolve(dir, from))) renameSync(resolve(dir, from), resolve(dir, to));
|
|
30
|
+
}
|
|
31
|
+
const name = packageName(basename(dir));
|
|
32
|
+
const file = resolve(dir, "package.json");
|
|
33
|
+
const pkg = JSON.parse(readFileSync(file, "utf8"));
|
|
34
|
+
pkg.name = name;
|
|
35
|
+
writeFileSync(file, `${JSON.stringify(pkg, null, 2)}
|
|
36
|
+
`);
|
|
37
|
+
return { dir, name };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
packageName,
|
|
42
|
+
scaffold
|
|
43
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
scaffold
|
|
4
|
+
} from "./chunk-YVMC6WCS.js";
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
import { dirname, join, relative } from "node:path";
|
|
8
|
+
import { createInterface } from "node:readline/promises";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
var HELP = `create-idocs: start a docs site with interactive architecture diagrams
|
|
11
|
+
|
|
12
|
+
Usage
|
|
13
|
+
npm create idocs@latest [folder]
|
|
14
|
+
pnpm create idocs [folder]
|
|
15
|
+
|
|
16
|
+
The folder must not exist or must be empty. The project uses pnpm.
|
|
17
|
+
`;
|
|
18
|
+
async function main() {
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
21
|
+
console.log(HELP);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let target = args.find((a) => !a.startsWith("-"));
|
|
25
|
+
if (!target) {
|
|
26
|
+
if (process.stdin.isTTY) {
|
|
27
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
28
|
+
target = (await rl.question("Where should the project go? (my-docs) ")).trim() || "my-docs";
|
|
29
|
+
rl.close();
|
|
30
|
+
} else target = "my-docs";
|
|
31
|
+
}
|
|
32
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
const templateDir = join(here, "..", "template");
|
|
34
|
+
const { dir, name } = scaffold(templateDir, target);
|
|
35
|
+
const where = relative(process.cwd(), dir) || ".";
|
|
36
|
+
console.log(`
|
|
37
|
+
Created ${name} in ${where}
|
|
38
|
+
`);
|
|
39
|
+
console.log("Next:");
|
|
40
|
+
if (where !== ".") console.log(` cd ${where}`);
|
|
41
|
+
console.log(" pnpm install");
|
|
42
|
+
console.log(" pnpm dev\n");
|
|
43
|
+
console.log("Then edit diagrams/shop/diagram.yaml and watch the page reload.");
|
|
44
|
+
console.log("No pnpm? npm install -g pnpm\n");
|
|
45
|
+
}
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.error(err instanceof Error ? err.message : err);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ScaffoldResult {
|
|
2
|
+
dir: string;
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
/** A valid npm package name for a folder name. */
|
|
6
|
+
export declare function packageName(folder: string): string;
|
|
7
|
+
/** Copy the template into `target`, which must not exist or must be empty. */
|
|
8
|
+
export declare function scaffold(templateDir: string, target: string): ScaffoldResult;
|
package/dist/scaffold.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-idocs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Scaffold a docs site with interactive diagrams: npm create idocs@latest my-docs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-idocs": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/scaffold.d.ts",
|
|
12
|
+
"default": "./dist/scaffold.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"template"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/The-Package-Labs/idocs.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/The-Package-Labs/idocs#readme",
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@the-package-labs/idocs-core": "^0.3.1"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "node ../../scripts/build.mjs",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
34
|
+
}
|
|
6
35
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: idocs
|
|
3
|
+
license: MIT
|
|
4
|
+
description: Draw, document and explain systems as interactive architecture diagrams with idocs. Diagrams are YAML files (diagrams/<name>/diagram.yaml) laid out automatically and rendered in an Astro + Starlight docs site, with playable scenarios that show a request travelling through the system. Use when asked to diagram, document or explain a system, service, request path or data flow; to add, change or fix an idocs diagram, scenario or story; when `idocs check` or `pnpm check` reports diagram errors; or to set up an idocs docs site.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# idocs
|
|
8
|
+
|
|
9
|
+
idocs turns a YAML description of a system into an interactive diagram: readers click a box for its docs, hover to follow connections, and press play on a **scenario** to watch a request travel through the system, as packets over the architecture, as a sequence diagram, or as a swimlane story. You write the model. You never write coordinates; layout is automatic.
|
|
10
|
+
|
|
11
|
+
## 1. Find out where you are
|
|
12
|
+
|
|
13
|
+
Look at the project before writing anything.
|
|
14
|
+
|
|
15
|
+
| You see | Do |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `@the-package-labs/idocs-astro` in `package.json` and a `diagrams/` folder | Authoring. Go to step 2. |
|
|
18
|
+
| An Astro or Starlight site without idocs | Add it: [references/setup.md](references/setup.md), "Add idocs to an existing site". |
|
|
19
|
+
| No docs site at all | Create one: [references/setup.md](references/setup.md), "Start a new site". |
|
|
20
|
+
| The user only wants a picture, with no site | Say that idocs needs a small site to render in, offer to scaffold one, and carry on if they agree. |
|
|
21
|
+
|
|
22
|
+
Use the project's package manager for every command below (`pnpm idocs ...`, `npx idocs ...`, `yarn idocs ...`). In a project made from the idocs template, `pnpm check` is a shortcut for `idocs check`.
|
|
23
|
+
|
|
24
|
+
## 2. Authoring workflow
|
|
25
|
+
|
|
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
|
+
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.
|
|
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
|
+
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
|
+
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).
|
|
32
|
+
7. **Put it on a page and look at it.** Add `<Diagram name="<name>" />` to an `.mdx` page (see below), run the dev server, and view the page with a browser tool if you have one. A diagram that validates can still look wrong; fix layout with hints: [references/layout.md](references/layout.md).
|
|
33
|
+
|
|
34
|
+
If you are not sure what a finished diagram looks like, read [assets/examples/shop.diagram.yaml](assets/examples/shop.diagram.yaml). It is complete and is checked by tests.
|
|
35
|
+
|
|
36
|
+
## The format at a glance
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
# yaml-language-server: $schema=../diagram.schema.json
|
|
40
|
+
title: Online store checkout
|
|
41
|
+
description: How a shopper's request travels. # markdown, shown above the diagram
|
|
42
|
+
|
|
43
|
+
groups:
|
|
44
|
+
backend: { label: Backend, caption: Kubernetes }
|
|
45
|
+
|
|
46
|
+
nodes:
|
|
47
|
+
browser: { kind: client, icon: react, title: Shopper browser }
|
|
48
|
+
api:
|
|
49
|
+
kind: service # default icon and colour (list in references/format.md)
|
|
50
|
+
icon: nodejs # a name, logos:x, lucide:x, or ./file.svg
|
|
51
|
+
title: catalog-service
|
|
52
|
+
sub: ":4001 · Node.js" # mono line under the title
|
|
53
|
+
status: planned # built (default) | planned | legacy | optional
|
|
54
|
+
in: backend
|
|
55
|
+
refs: [services/catalog/server.ts]
|
|
56
|
+
db: { kind: database, icon: postgresql, title: PostgreSQL, in: backend }
|
|
57
|
+
|
|
58
|
+
edges:
|
|
59
|
+
- browser -> api: HTTPS # shorthand: from -> to: label
|
|
60
|
+
- { from: api, to: db, kind: data, label: SQL, auth: password }
|
|
61
|
+
|
|
62
|
+
scenarios:
|
|
63
|
+
browse:
|
|
64
|
+
title: Browse a product
|
|
65
|
+
steps:
|
|
66
|
+
- browser -> api: GET /products/42 # flies along the edges, hop by hop
|
|
67
|
+
- api -> db: { label: SELECT, kind: lookup }
|
|
68
|
+
- db -> api: rows # flying against an edge makes it a response
|
|
69
|
+
- api -> browser: 200 OK
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Click-through documentation for a box goes in `diagrams/<name>/nodes/<id>.md` (markdown, tables and code fences work). Long scenarios can live in `diagrams/<name>/scenarios/<id>.yaml`.
|
|
73
|
+
|
|
74
|
+
On a page (`src/content/docs/*.mdx`):
|
|
75
|
+
|
|
76
|
+
```mdx
|
|
77
|
+
import Diagram from '@the-package-labs/idocs-astro/Diagram.astro'
|
|
78
|
+
|
|
79
|
+
<Diagram name="shop" />
|
|
80
|
+
<Diagram name="shop" scenario="browse" mode="story" />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Conventions that make diagrams good
|
|
84
|
+
|
|
85
|
+
- **Titles are the names people use in conversation.** `sub` carries the technical detail: port, stack, version.
|
|
86
|
+
- **Edge labels are short**: a protocol, a route, an event name. Detail goes in `payload:` and `auth:`.
|
|
87
|
+
- **Mark unbuilt work `status: planned`**, never only in prose. It draws dashed and readers can hide it.
|
|
88
|
+
- **One story per scenario.** Show failure paths as their own scenarios (`payment-declined`), not as footnotes.
|
|
89
|
+
- **Steps: one verb each**, label what travels (`POST /orders`, `order.created`), explain why in `note:`. Group into two to five phases with plain titles ("Submit", "Charge", "Finish").
|
|
90
|
+
- **Do not restate the diagram in prose on the page.** Use the page for what the diagram cannot say: why a choice was made, what to watch for, who to ask.
|
|
91
|
+
|
|
92
|
+
## Commands
|
|
93
|
+
|
|
94
|
+
| Command | What it does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `idocs check [root...] [--json] [--strict]` | Validate every diagram. `--strict` also fails on warnings. |
|
|
97
|
+
| `idocs list [--json]` | Diagrams, views, scenarios. |
|
|
98
|
+
| `idocs icons search <query>` | Find an icon name (`postgresql`, `logos:redis`, `lucide:server`). |
|
|
99
|
+
| `idocs new <name>` | Scaffold `diagrams/<name>/`. |
|
|
100
|
+
| `idocs schema` | Write `diagrams/diagram.schema.json` so editors autocomplete and validate the YAML. |
|
|
101
|
+
|
|
102
|
+
## Before you say you are done
|
|
103
|
+
|
|
104
|
+
- [ ] `idocs check --strict` passes with no errors or warnings.
|
|
105
|
+
- [ ] Every node and flow comes from the code or from the user, not from a guess.
|
|
106
|
+
- [ ] The page renders and the diagram is readable at its default zoom. If it is tiny, it has too much in it.
|
|
107
|
+
- [ ] Planned work is marked `status: planned`.
|
|
108
|
+
- [ ] Each scenario tells one story and has a one-sentence `summary`.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# A complete, valid diagram that uses most of the format. Checked by the test suite.
|
|
2
|
+
# yaml-language-server: $schema=../diagram.schema.json
|
|
3
|
+
title: Online store
|
|
4
|
+
description: |
|
|
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.
|
|
7
|
+
|
|
8
|
+
groups:
|
|
9
|
+
backend:
|
|
10
|
+
label: Backend
|
|
11
|
+
caption: Kubernetes · namespace shop
|
|
12
|
+
rows: # explicit rows, top to bottom
|
|
13
|
+
- [gateway]
|
|
14
|
+
- [catalog, cart, orders]
|
|
15
|
+
- [stores]
|
|
16
|
+
stores:
|
|
17
|
+
label: Data and messaging
|
|
18
|
+
in: backend
|
|
19
|
+
layout: row # packed cards; they need no arrows of their own
|
|
20
|
+
|
|
21
|
+
nodes:
|
|
22
|
+
browser:
|
|
23
|
+
kind: client
|
|
24
|
+
icon: react
|
|
25
|
+
title: Shopper browser
|
|
26
|
+
sub: React storefront
|
|
27
|
+
cdn:
|
|
28
|
+
kind: external
|
|
29
|
+
icon: cloudflare
|
|
30
|
+
title: CDN
|
|
31
|
+
sub: static assets · TLS
|
|
32
|
+
gateway:
|
|
33
|
+
kind: gateway
|
|
34
|
+
icon: nginx
|
|
35
|
+
title: API gateway
|
|
36
|
+
sub: ":443"
|
|
37
|
+
lines: [verifies the JWT, routes by path]
|
|
38
|
+
in: backend
|
|
39
|
+
catalog:
|
|
40
|
+
kind: service
|
|
41
|
+
icon: nodejs
|
|
42
|
+
title: catalog-service
|
|
43
|
+
sub: ":4001 · Node.js"
|
|
44
|
+
uses: [redis, postgres]
|
|
45
|
+
in: backend
|
|
46
|
+
cart:
|
|
47
|
+
kind: service
|
|
48
|
+
icon: go
|
|
49
|
+
title: cart-service
|
|
50
|
+
sub: ":4002 · Go"
|
|
51
|
+
in: backend
|
|
52
|
+
orders:
|
|
53
|
+
kind: service
|
|
54
|
+
icon: dotnet
|
|
55
|
+
title: order-service
|
|
56
|
+
sub: ":4003 · .NET"
|
|
57
|
+
uses: [postgres, rabbitmq]
|
|
58
|
+
in: backend
|
|
59
|
+
redis:
|
|
60
|
+
kind: cache
|
|
61
|
+
icon: redis
|
|
62
|
+
title: Redis
|
|
63
|
+
sub: catalog cache
|
|
64
|
+
w: 132
|
|
65
|
+
in: stores
|
|
66
|
+
postgres:
|
|
67
|
+
kind: database
|
|
68
|
+
icon: postgresql
|
|
69
|
+
title: PostgreSQL
|
|
70
|
+
sub: orders · products
|
|
71
|
+
w: 132
|
|
72
|
+
in: stores
|
|
73
|
+
rabbit:
|
|
74
|
+
kind: queue
|
|
75
|
+
icon: rabbitmq
|
|
76
|
+
title: RabbitMQ
|
|
77
|
+
sub: order.events
|
|
78
|
+
w: 132
|
|
79
|
+
in: stores
|
|
80
|
+
stripe:
|
|
81
|
+
kind: external
|
|
82
|
+
icon: stripe
|
|
83
|
+
title: Stripe
|
|
84
|
+
sub: payment provider
|
|
85
|
+
notifier:
|
|
86
|
+
kind: worker
|
|
87
|
+
icon: lucide:mail
|
|
88
|
+
title: notification-worker
|
|
89
|
+
status: planned # drawn dashed, with a PLANNED tag
|
|
90
|
+
|
|
91
|
+
edges:
|
|
92
|
+
- browser -> cdn: HTTPS
|
|
93
|
+
- cdn -> gateway
|
|
94
|
+
- gateway -> catalog: /products
|
|
95
|
+
- gateway -> cart: /cart
|
|
96
|
+
- gateway -> orders: /orders
|
|
97
|
+
- { from: orders, to: stripe, kind: channel, label: charge, auth: API key, payload: payment intent }
|
|
98
|
+
- { from: rabbit, to: notifier, kind: queue, status: planned }
|
|
99
|
+
# Relationships scenarios need but the picture does not: not drawn until a scenario uses them.
|
|
100
|
+
- { from: catalog, to: redis, kind: data, hidden: true }
|
|
101
|
+
- { from: catalog, to: postgres, kind: data, hidden: true }
|
|
102
|
+
- { from: orders, to: postgres, kind: data, hidden: true }
|
|
103
|
+
- { from: orders, to: rabbit, kind: queue, hidden: true }
|
|
104
|
+
|
|
105
|
+
views:
|
|
106
|
+
overview:
|
|
107
|
+
title: Overview
|
|
108
|
+
keys: true # letter keys on edges
|
|
109
|
+
interfaces: true # a table of every edge under the diagram
|
|
110
|
+
backend:
|
|
111
|
+
title: Backend only
|
|
112
|
+
include: [backend, stripe]
|
|
113
|
+
direction: DOWN
|
|
114
|
+
|
|
115
|
+
scenarios:
|
|
116
|
+
browse:
|
|
117
|
+
title: Browse a product
|
|
118
|
+
summary: The catalogue answers from the cache, so the database is never touched.
|
|
119
|
+
steps:
|
|
120
|
+
- browser -> catalog: GET /products/42 # flies browser → CDN → gateway → catalog
|
|
121
|
+
- catalog -> redis: { label: GET product:42, kind: lookup }
|
|
122
|
+
- redis -> catalog: { label: hit, note: A hit is served in about a millisecond. }
|
|
123
|
+
- catalog -> browser: 200 + JSON # flies back against the edges: a response
|
|
124
|
+
|
|
125
|
+
place-order:
|
|
126
|
+
title: Place an order
|
|
127
|
+
mode: story # opens as a swimlane story; Flow and Sequence stay available
|
|
128
|
+
summary: The order is saved first, charged second, and announced last.
|
|
129
|
+
lanes:
|
|
130
|
+
- { title: Shopper, nodes: [browser] }
|
|
131
|
+
- { title: Order service, sub: .NET, nodes: [orders] }
|
|
132
|
+
- { title: Stores, sub: Postgres · RabbitMQ, nodes: [postgres, rabbit] }
|
|
133
|
+
- { title: Payments, nodes: [stripe] }
|
|
134
|
+
phases:
|
|
135
|
+
- title: Save
|
|
136
|
+
caption: before any money moves
|
|
137
|
+
steps:
|
|
138
|
+
- browser -> orders:
|
|
139
|
+
title: Submit the order
|
|
140
|
+
detail: POST /orders
|
|
141
|
+
note: The request carries an idempotency key, so a retry cannot charge twice.
|
|
142
|
+
- orders -> postgres: { label: INSERT order (pending), kind: lookup }
|
|
143
|
+
- postgres -> orders: saved
|
|
144
|
+
- title: Charge
|
|
145
|
+
steps:
|
|
146
|
+
- orders -> stripe: { title: Charge the card, detail: create payment intent }
|
|
147
|
+
- at stripe: { title: Authorise, detail: the one step we do not control }
|
|
148
|
+
- stripe -> orders: succeeded
|
|
149
|
+
- title: Finish
|
|
150
|
+
steps:
|
|
151
|
+
- par: # these two start together
|
|
152
|
+
- orders -> postgres: { label: mark paid, kind: lookup }
|
|
153
|
+
- orders -> rabbit: { label: order.created, kind: event }
|
|
154
|
+
- orders -> browser: { title: Confirm, detail: 201 Created }
|
|
155
|
+
|
|
156
|
+
declined:
|
|
157
|
+
title: Card declined
|
|
158
|
+
summary: Stripe refuses the card. The order stays pending and the shopper is told why.
|
|
159
|
+
steps:
|
|
160
|
+
- browser -> orders: POST /orders
|
|
161
|
+
- orders -> stripe: create payment intent
|
|
162
|
+
- stripe -> orders: { label: card_declined, kind: error }
|
|
163
|
+
- at orders: { label: keep order pending, kind: error }
|
|
164
|
+
- orders -> browser: { label: 402 Payment Required, kind: error }
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# diagram.yaml reference
|
|
2
|
+
|
|
3
|
+
One file per diagram: `diagrams/<name>/diagram.yaml`. The folder name is what `<Diagram name="...">` refers to.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
diagrams/
|
|
7
|
+
diagram.schema.json editor autocomplete (idocs schema)
|
|
8
|
+
<name>/
|
|
9
|
+
diagram.yaml the model, views and scenarios
|
|
10
|
+
nodes/<node-id>.md shown when a reader clicks that node (markdown)
|
|
11
|
+
scenarios/<id>.yaml optional: a long scenario in its own file; the file name is its id
|
|
12
|
+
```
|
|
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.
|
|
15
|
+
|
|
16
|
+
## Nodes
|
|
17
|
+
|
|
18
|
+
`nodes:` is a map from id to node. Ids use letters, digits, `_`, `.` and `-`. A bare string is a title: `api: Catalog service`.
|
|
19
|
+
|
|
20
|
+
| Field | Meaning |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `title` | Display name. Defaults to the id. |
|
|
23
|
+
| `sub` | One mono line under the title: port, stack, version. |
|
|
24
|
+
| `lines` | List of short body lines in the card. |
|
|
25
|
+
| `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
|
+
| `kind` | Gives a default icon and colour family (list below). |
|
|
27
|
+
| `family` | Accent colour: `blue amber violet green teal red slate`. Overrides the kind's. |
|
|
28
|
+
| `in` | Id of the group the node sits in. |
|
|
29
|
+
| `status` | `built` (default), `planned`, `legacy`, `optional`. Non-built nodes draw dashed with a tag. |
|
|
30
|
+
| `badge` | A number or short string shown on the card corner. |
|
|
31
|
+
| `uses` | List of short tags drawn as "uses: a · b". Entries that match a node id become links in the drawer. |
|
|
32
|
+
| `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. |
|
|
34
|
+
| `description` | Markdown for the detail drawer, inline. |
|
|
35
|
+
| `doc` | Path to a markdown file for the drawer. Defaults to `nodes/<id>.md` when that file exists. |
|
|
36
|
+
| `links` | `- { label: Runbook, url: https://... }` |
|
|
37
|
+
| `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
|
+
| `tags` | Free-form tags. |
|
|
39
|
+
|
|
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 }`.
|
|
41
|
+
|
|
42
|
+
## Groups
|
|
43
|
+
|
|
44
|
+
`groups:` is a map from id to group. A top-level group is a filled zone; a group with `in:` is a dashed frame inside another.
|
|
45
|
+
|
|
46
|
+
| Field | Meaning |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `label` | Required. Shown in capitals in the zone's corner. |
|
|
49
|
+
| `caption` | Muted text after the label: network, runtime, namespace. |
|
|
50
|
+
| `in` | Parent group id. |
|
|
51
|
+
| `layout` | `flow` (default: automatic), `row` or `grid`: pack members tightly. Use for stores and other cards that need no arrows of their own; arrows to a member end on the group's border. |
|
|
52
|
+
| `columns` | Column count for `grid` (and wrapping for `row`). |
|
|
53
|
+
| `rows` | Explicit rows of member ids, top to bottom: `rows: [[proxy], [auth, api], [stores]]`. Each row is spread across the group's width. Members must be direct children of the group. |
|
|
54
|
+
| `direction` | `AUTO` (default), `RIGHT` or `DOWN`: flow direction inside a top-level group. |
|
|
55
|
+
| `family`, `status`, `icon` | As for nodes. |
|
|
56
|
+
|
|
57
|
+
More on layout choices: [layout.md](layout.md).
|
|
58
|
+
|
|
59
|
+
## Edges
|
|
60
|
+
|
|
61
|
+
`edges:` is a list. Forms:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
edges:
|
|
65
|
+
- browser -> cdn: HTTPS # from -> to: label
|
|
66
|
+
- api <-> cache # arrows at both ends
|
|
67
|
+
- { from: api, to: db, kind: data, label: SQL, auth: password, payload: queries }
|
|
68
|
+
- { from: api, to: audit, hidden: true } # not drawn until a scenario uses it
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Field | Meaning |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `from`, `to` | Node ids (required in the object form). |
|
|
74
|
+
| `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:`. |
|
|
76
|
+
| `both` | Arrow at both ends (same as `<->`). |
|
|
77
|
+
| `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
|
+
| `status` | As for nodes. |
|
|
79
|
+
| `key` | A letter shown on the edge and in the interfaces table. Views with `keys: true` assign them for you. |
|
|
80
|
+
| `auth`, `payload`, `transport` | Columns in the interfaces table. |
|
|
81
|
+
| `note` | Markdown. |
|
|
82
|
+
| `id` | Optional; defaults to its position. |
|
|
83
|
+
|
|
84
|
+
Custom edge kinds: `edgeKinds: { grpc: { label: gRPC, color: accent, width: 2, dash: "6 4" } }`. `color` is a theme token (`ink accent tunnel muted ok warn bad`) or any CSS colour.
|
|
85
|
+
|
|
86
|
+
In flow-style `{ ... }` maps, **quote any value that contains a comma or `: `**: `{ label: "device → URL, org", kind: lookup }`.
|
|
87
|
+
|
|
88
|
+
## Views
|
|
89
|
+
|
|
90
|
+
Without `views:` there is one view with everything. Add views to show different slices of the same model.
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
views:
|
|
94
|
+
overview:
|
|
95
|
+
title: Overview
|
|
96
|
+
keys: true # letter keys on edges
|
|
97
|
+
interfaces: true # table of every edge under the diagram (key, from → to, kind, auth, payload)
|
|
98
|
+
summary: Markdown shown above the diagram.
|
|
99
|
+
backend:
|
|
100
|
+
title: Backend only
|
|
101
|
+
include: [backend, stripe] # node or group ids; a group includes its members
|
|
102
|
+
exclude: [metrics]
|
|
103
|
+
direction: DOWN # AUTO (default), RIGHT, DOWN, LEFT, UP
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
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
|
+
|
|
108
|
+
## Pages
|
|
109
|
+
|
|
110
|
+
```mdx
|
|
111
|
+
import Diagram from '@the-package-labs/idocs-astro/Diagram.astro'
|
|
112
|
+
|
|
113
|
+
<Diagram name="shop" /> # first view, overview
|
|
114
|
+
<Diagram name="shop" view="backend" />
|
|
115
|
+
<Diagram name="shop" scenario="checkout" mode="story" /> # mode: flow | sequence | story
|
|
116
|
+
<Diagram name="shop" showDescription /> # the diagram's `description`
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Other props: `maxHeight` (px, default 780), `showTitle`. Readers can link to a moment with `#<diagram>=<scenario>[.<step>]`, for example `#shop=checkout.3`.
|
|
120
|
+
|
|
121
|
+
## Theming
|
|
122
|
+
|
|
123
|
+
Every colour and font is a CSS variable starting `--idocs-` (for example `--idocs-accent`, `--idocs-zone`, `--idocs-sans`, `--idocs-dim` for how faded unrelated items get). Override them under `.idocs` in the site's CSS, and under `:root[data-theme='dark'] .idocs` for dark mode.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Layout
|
|
2
|
+
|
|
3
|
+
Layout is automatic, and you steer it with hints, never with coordinates. If a diagram looks wrong, change the model or add a hint, then look again.
|
|
4
|
+
|
|
5
|
+
## How it works, briefly
|
|
6
|
+
|
|
7
|
+
Each top-level group (a **zone**) is laid out on its own, in whichever of left-to-right or top-to-bottom comes out squarer. Zones and ungrouped nodes are then arranged side by side. Edges that cross between zones are routed afterwards around the boxes. Two things follow:
|
|
8
|
+
|
|
9
|
+
- **Declaration order matters.** It decides the order of cards within a layer and which edges count as "backwards". Declare nodes in reading order: entry points first, stores last.
|
|
10
|
+
- **Zones are the unit of layout.** Group related nodes into zones (`groups:`) so they travel together.
|
|
11
|
+
|
|
12
|
+
## The hints
|
|
13
|
+
|
|
14
|
+
| Hint | Use it for |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `rows: [[a], [b, c], [d]]` on a group | A stack you want exactly as drawn: gateway, then services, then stores. Each row is spread across the zone's width. Members must be direct children. |
|
|
17
|
+
| `layout: row` or `layout: grid` (with `columns:`) on a group | Cards that need no arrows of their own, such as a row of data stores or a grid of services. Arrows to a member end on the group's border. |
|
|
18
|
+
| `direction: DOWN` or `RIGHT` on a view | Force the main direction. The default `AUTO` tries both and keeps the one that reads larger. |
|
|
19
|
+
| `direction:` on a top-level group | Force the flow direction inside one zone. |
|
|
20
|
+
| `w: 132` on a node | A narrower or wider card (default 178). Narrow the cards in dense rows so a row of five stores does not make the zone huge. |
|
|
21
|
+
| `hidden: true` on an edge | Keep a relationship out of the picture but available to scenarios (service to queue, service to audit log). |
|
|
22
|
+
| A second `view` with `include:` | Show a slice: the backend only, one team's part, one device. |
|
|
23
|
+
|
|
24
|
+
## Symptoms and fixes
|
|
25
|
+
|
|
26
|
+
| You see | Try |
|
|
27
|
+
|---|---|
|
|
28
|
+
| The whole diagram is tiny at default zoom | Too much in one picture. Move detail into a second view, hide minor edges, or split the system. Aim for 8 to 25 nodes. |
|
|
29
|
+
| One zone is much wider than the rest | A long row of cards. Narrow them with `w:`, use `layout: grid` with `columns: 2` or `3`, or wrap with `rows:`. |
|
|
30
|
+
| Arrows cross all over | Reorder the nodes so the flow reads one way. Move cards that many things connect to into a packed group (`layout: row`) with a `uses:` tag instead of drawing every edge. |
|
|
31
|
+
| A zone is tall and thin | Set its `direction: RIGHT`, or break a long chain with `rows:`. |
|
|
32
|
+
| Two cards should sit side by side but do not | Put them in one `rows:` row. |
|
|
33
|
+
| An arrow stops at a group border instead of the card | That is how packed groups (`row`, `grid`) work. Put the connected cards in a `flow` group or a `rows` group. |
|
|
34
|
+
| Planned work clutters the picture | Mark it `status: planned`. Readers get a "Hide planned" toggle. |
|
|
35
|
+
| Labels overlap | Shorten them; move detail into `payload:` and `auth:`. |
|
|
36
|
+
|
|
37
|
+
## When to stop adjusting
|
|
38
|
+
|
|
39
|
+
If a diagram needs more than a few hints, the model is probably too big for one picture. Split it into views of the same model, or into two diagrams, before reaching for more hints.
|