@itsthecsguy/linework 0.1.1 → 0.1.3
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 +46 -3
- package/dist/drawio/theme.js +79 -52
- package/dist/drawio/write.js +152 -22
- package/dist/layout/layout.js +225 -17
- package/dist/layout/polish.js +281 -0
- package/dist/layout/sequence.js +7 -4
- package/dist/lint/check.js +6 -1
- package/dist/spec/model.js +3 -1
- package/package.json +1 -1
- package/skills/linework/SKILL.md +9 -2
- package/skills/linework/examples/auth-seq.yaml +35 -0
- package/skills/linework/examples/deploy-flow.yaml +32 -0
- package/skills/linework/examples/gcp-ingest.yaml +21 -0
- package/skills/linework/examples/job-state.yaml +25 -0
- package/skills/linework/examples/platform.yaml +45 -0
- package/skills/linework/examples/shop-er.yaml +26 -0
- package/skills/linework/references/spec.md +3 -3
- package/skills/linework/references/style-guide.md +9 -1
package/skills/linework/SKILL.md
CHANGED
|
@@ -33,7 +33,14 @@ Never fall back to writing SVG or draw.io XML by hand. Tell the user what failed
|
|
|
33
33
|
- `er`: database tables, their columns and how they relate.
|
|
34
34
|
|
|
35
35
|
If the doc already has a Mermaid diagram, convert it instead of rewriting it: `linework import doc.md --block 1 -o docs/diagrams/x.drawio.svg`.
|
|
36
|
-
3. **Write the spec** to `/tmp/spec.yaml`, or pipe it on stdin with `-` as the file. Start from the closest file in `examples/` next to this guide:
|
|
36
|
+
3. **Write the spec** to `/tmp/spec.yaml`, or pipe it on stdin with `-` as the file. Start from the closest file in `examples/` next to this guide:
|
|
37
|
+
- architecture: `checkout.yaml` (small), `platform.yaml` (clients, nested groups, 17 nodes), `serverless-aws.yaml` (AWS icons), `gcp-ingest.yaml` (GCP icons)
|
|
38
|
+
- flow: `refund-flow.yaml` (branches), `deploy-flow.yaml` (a retry loop back to an earlier step)
|
|
39
|
+
- state: `payment-state.yaml`, `job-state.yaml` (pause/resume pairs and a self-transition)
|
|
40
|
+
- sequence: `pay-sequence.yaml` (alt, note), `auth-seq.yaml` (loop with a nested opt)
|
|
41
|
+
- ER: `billing-er.yaml` (small), `shop-er.yaml` (7 tables, composite keys)
|
|
42
|
+
|
|
43
|
+
The `.drawio.svg` is the source of truth, so you don't need to keep the YAML.
|
|
37
44
|
4. **Build with a preview:**
|
|
38
45
|
```bash
|
|
39
46
|
linework build /tmp/spec.yaml -o docs/diagrams/checkout.drawio.svg --png /tmp/checkout.png
|
|
@@ -78,7 +85,7 @@ edges: # "from ARROW to: label"
|
|
|
78
85
|
- `soft`: borderless pastel cards, tinted by kind. For slides and docs that want color coding.
|
|
79
86
|
- `dark`: for dark-mode docs and slides.
|
|
80
87
|
- `sketch`: hand-drawn lines and handwriting. For early proposals ("this is a draft") and whiteboard-style explainers.
|
|
81
|
-
- `blueprint`:
|
|
88
|
+
- `blueprint`: blue ink on pale drafting paper, monospace. For technical deep dives that want a distinct look.
|
|
82
89
|
- `c4`: C4-model conventions; labels each box with its element type.
|
|
83
90
|
- Color is for meaning, so most nodes stay neutral:
|
|
84
91
|
- `highlight: true` on a node, group or participant, or `==>` on an edge or message, makes it the first thing a reader sees. Highlight the one path or component the surrounding text is about: often one node and the 2 or 3 edges into and out of it. `build` warns when you highlight so much that nothing stands out.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type: sequence
|
|
2
|
+
title: OAuth login with refresh
|
|
3
|
+
participants:
|
|
4
|
+
user: { label: User, kind: person }
|
|
5
|
+
app: { label: SPA, kind: client }
|
|
6
|
+
bff: { label: BFF, tech: Node.js }
|
|
7
|
+
idp: { label: Identity provider, kind: external }
|
|
8
|
+
api: { label: Resource API }
|
|
9
|
+
cache: { label: Token cache, kind: cache }
|
|
10
|
+
messages:
|
|
11
|
+
- user -> app: Sign in
|
|
12
|
+
- app -> bff: GET /login
|
|
13
|
+
- bff --> app: 302 to /authorize
|
|
14
|
+
- app -> idp: /authorize?code_challenge=...
|
|
15
|
+
- idp -> user: Login form
|
|
16
|
+
- user -> idp: Credentials + MFA
|
|
17
|
+
- idp --> app: redirect with code
|
|
18
|
+
- app -> bff: GET /callback?code
|
|
19
|
+
- bff ==> idp: POST /token (code, verifier)
|
|
20
|
+
- idp --> bff: access + refresh tokens
|
|
21
|
+
- bff -> cache: store refresh token
|
|
22
|
+
- note right of bff: tokens never reach the browser
|
|
23
|
+
- bff --> app: Set-Cookie session
|
|
24
|
+
- loop: every API call
|
|
25
|
+
do:
|
|
26
|
+
- app -> bff: GET /orders
|
|
27
|
+
- opt: access token expired
|
|
28
|
+
do:
|
|
29
|
+
- bff -> cache: read refresh token
|
|
30
|
+
- bff -> idp: POST /token (refresh)
|
|
31
|
+
- idp --> bff: new access token
|
|
32
|
+
- bff -> api: GET /orders + Bearer
|
|
33
|
+
- api -> api: validate JWT
|
|
34
|
+
- api --> bff: 200 orders
|
|
35
|
+
- bff --> app: orders
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type: flow
|
|
2
|
+
title: Deploy pipeline
|
|
3
|
+
nodes:
|
|
4
|
+
push: { label: Push to main, kind: terminal }
|
|
5
|
+
build: { label: Build and unit test }
|
|
6
|
+
ok: { label: Tests pass?, kind: decision }
|
|
7
|
+
fix: { label: Notify author, kind: error }
|
|
8
|
+
stage: { label: Deploy to staging }
|
|
9
|
+
smoke: { label: Run smoke tests against staging }
|
|
10
|
+
smokeok: { label: Healthy?, kind: decision }
|
|
11
|
+
canary: { label: Canary 5% of traffic }
|
|
12
|
+
metrics: { label: Error rate under 0.1%?, kind: decision }
|
|
13
|
+
roll: { label: Roll back canary, kind: error }
|
|
14
|
+
full: { label: Promote to 100% }
|
|
15
|
+
done: { label: Released, kind: terminal }
|
|
16
|
+
artifact: { label: Container image, kind: data }
|
|
17
|
+
edges:
|
|
18
|
+
- push -> build
|
|
19
|
+
- build -> ok
|
|
20
|
+
- ok -> fix: "no"
|
|
21
|
+
- ok -> artifact: "yes"
|
|
22
|
+
- artifact -> stage
|
|
23
|
+
- stage -> smoke
|
|
24
|
+
- smoke -> smokeok
|
|
25
|
+
- smokeok -> canary: "yes"
|
|
26
|
+
- smokeok -> roll: "no"
|
|
27
|
+
- canary -> metrics
|
|
28
|
+
- metrics -> full: "yes"
|
|
29
|
+
- metrics -> roll: "no"
|
|
30
|
+
- roll -> fix
|
|
31
|
+
- fix -> build: retry after fix
|
|
32
|
+
- full -> done
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type: architecture
|
|
2
|
+
title: Ingest pipeline on GCP
|
|
3
|
+
groups:
|
|
4
|
+
gcp: { label: Google Cloud project }
|
|
5
|
+
nodes:
|
|
6
|
+
devices: { label: Sensors, kind: client, tech: MQTT }
|
|
7
|
+
lb: { label: Load balancer, icon: gcp.cloud_load_balancing, in: gcp }
|
|
8
|
+
run: { label: Ingest API, icon: gcp.cloud_run, tech: Cloud Run, in: gcp }
|
|
9
|
+
ps: { label: telemetry, kind: queue, icon: gcp.pubsub, tech: Pub/Sub, in: gcp }
|
|
10
|
+
df: { label: Stream job, icon: gcp.dataflow, tech: Dataflow, in: gcp }
|
|
11
|
+
bq: { label: Warehouse, kind: database, icon: gcp.bigquery, tech: BigQuery, in: gcp }
|
|
12
|
+
gcs: { label: Raw archive, kind: storage, icon: gcp.cloud_storage, tech: GCS, in: gcp }
|
|
13
|
+
looker: { label: Looker, kind: external, tech: Dashboards }
|
|
14
|
+
edges:
|
|
15
|
+
- devices -> lb: HTTPS
|
|
16
|
+
- lb -> run
|
|
17
|
+
- run -.-> ps: publish
|
|
18
|
+
- ps -.-> df: subscribe
|
|
19
|
+
- df -> bq: stream insert
|
|
20
|
+
- df -> gcs: write Avro
|
|
21
|
+
- looker -> bq: SQL
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type: state
|
|
2
|
+
title: Background job
|
|
3
|
+
nodes:
|
|
4
|
+
queued: {}
|
|
5
|
+
running: {}
|
|
6
|
+
paused: {}
|
|
7
|
+
retrying: { label: Waiting to retry }
|
|
8
|
+
succeeded: { kind: success }
|
|
9
|
+
failed: { kind: error }
|
|
10
|
+
cancelled: {}
|
|
11
|
+
edges:
|
|
12
|
+
- "[*] -> queued"
|
|
13
|
+
- queued -> running: worker picks up
|
|
14
|
+
- running -> succeeded: exit 0
|
|
15
|
+
- running -> retrying: error, attempts < 5
|
|
16
|
+
- retrying -> queued: backoff elapsed
|
|
17
|
+
- running -> failed: error, attempts = 5
|
|
18
|
+
- running -> paused: pause
|
|
19
|
+
- paused -> running: resume
|
|
20
|
+
- queued -> cancelled: cancel
|
|
21
|
+
- paused -> cancelled: cancel
|
|
22
|
+
- running -> running: heartbeat
|
|
23
|
+
- "succeeded -> [*]"
|
|
24
|
+
- "failed -> [*]"
|
|
25
|
+
- "cancelled -> [*]"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
type: architecture
|
|
2
|
+
title: Platform
|
|
3
|
+
groups:
|
|
4
|
+
edge: { label: Edge }
|
|
5
|
+
cluster: { label: Kubernetes cluster }
|
|
6
|
+
core: { label: Core services, in: cluster }
|
|
7
|
+
data: { label: Data tier }
|
|
8
|
+
nodes:
|
|
9
|
+
user: { label: Customer, kind: person }
|
|
10
|
+
admin: { label: Support agent, kind: person }
|
|
11
|
+
mobile: { label: Mobile app, kind: client, tech: Swift }
|
|
12
|
+
web: { label: Web dashboard, kind: client, tech: React }
|
|
13
|
+
cdn: { label: CDN, kind: cache, tech: CloudFront, in: edge }
|
|
14
|
+
gw: { label: API gateway, tech: Kong, in: edge }
|
|
15
|
+
auth: { label: Auth service, tech: Go, in: core }
|
|
16
|
+
orders: { label: Orders service, tech: Kotlin, in: core, highlight: true }
|
|
17
|
+
search: { label: Search service, tech: Rust, in: core }
|
|
18
|
+
notify: { label: Notification worker, kind: function, tech: Node.js, in: cluster }
|
|
19
|
+
bus: { label: domain-events, kind: queue, tech: Kafka, in: cluster }
|
|
20
|
+
pg: { label: Orders DB, kind: database, tech: Postgres 16, in: data }
|
|
21
|
+
es: { label: Search index, kind: database, tech: OpenSearch, in: data }
|
|
22
|
+
redis: { label: Session store, kind: cache, tech: Redis, in: data }
|
|
23
|
+
s3: { label: Media bucket, kind: storage, tech: S3, in: data }
|
|
24
|
+
twilio: { label: Twilio, kind: external, tech: SMS }
|
|
25
|
+
idp: { label: Okta, kind: external, tech: OIDC }
|
|
26
|
+
edges:
|
|
27
|
+
- user -> mobile
|
|
28
|
+
- user -> web
|
|
29
|
+
- admin -> web
|
|
30
|
+
- mobile -> cdn: assets
|
|
31
|
+
- web -> cdn: assets
|
|
32
|
+
- mobile ==> gw: HTTPS
|
|
33
|
+
- web ==> gw: HTTPS
|
|
34
|
+
- cdn -> s3: origin fetch
|
|
35
|
+
- gw -> auth: verify token
|
|
36
|
+
- auth -> idp: OIDC
|
|
37
|
+
- auth -> redis: sessions
|
|
38
|
+
- gw ==> orders: gRPC
|
|
39
|
+
- gw -> search: gRPC
|
|
40
|
+
- orders ==> pg: SQL
|
|
41
|
+
- orders -.-> bus: OrderPlaced
|
|
42
|
+
- bus -.-> search: reindex
|
|
43
|
+
- bus -.-> notify: consume
|
|
44
|
+
- search -> es: query
|
|
45
|
+
- notify -> twilio: send SMS
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type: er
|
|
2
|
+
title: Storefront schema
|
|
3
|
+
nodes:
|
|
4
|
+
customers:
|
|
5
|
+
fields: [id: uuid pk, email: text unique, name: text, created_at: timestamptz]
|
|
6
|
+
addresses:
|
|
7
|
+
fields: [id: uuid pk, customer_id: uuid fk, line1: text, city: text, country: char(2)]
|
|
8
|
+
orders:
|
|
9
|
+
fields: [id: uuid pk, customer_id: uuid fk, shipping_address_id: uuid fk, status: order_status, total_cents: bigint, placed_at: timestamptz null]
|
|
10
|
+
order_items:
|
|
11
|
+
fields: [order_id: uuid pk fk, product_id: uuid pk fk, quantity: int, unit_price_cents: bigint]
|
|
12
|
+
products:
|
|
13
|
+
fields: [id: uuid pk, sku: text unique, title: text, price_cents: bigint]
|
|
14
|
+
inventory:
|
|
15
|
+
fields: [product_id: uuid pk fk, warehouse: text pk, on_hand: int]
|
|
16
|
+
payments:
|
|
17
|
+
kind: external
|
|
18
|
+
fields: [id: text pk, order_id: uuid fk, provider: text]
|
|
19
|
+
edges:
|
|
20
|
+
- addresses }o--|| customers: has
|
|
21
|
+
- orders }o--|| customers: placed by
|
|
22
|
+
- orders }o--|| addresses: ships to
|
|
23
|
+
- order_items }|--|| orders: contains
|
|
24
|
+
- order_items }o--|| products: of
|
|
25
|
+
- inventory }o--|| products: stocks
|
|
26
|
+
- payments |o..|| orders: pays
|
|
@@ -40,9 +40,9 @@ nodes:
|
|
|
40
40
|
| `clean` | white boxes with hairline borders, groups drawn as gray grounds, cobalt accent. Nodes are neutral unless the kind means something (error, success). | IBM Plex Sans |
|
|
41
41
|
| `soft` | borderless pastel cards, each kind its own tint | IBM Plex Sans |
|
|
42
42
|
| `dark` | `clean` in GitHub's dark colors, so it sits flush on a dark README | IBM Plex Sans |
|
|
43
|
-
| `sketch` | hand-drawn strokes, pastel fills,
|
|
44
|
-
| `blueprint` |
|
|
45
|
-
| `c4` | C4
|
|
43
|
+
| `sketch` | hand-drawn strokes, pastel fills, blue accent | Kalam (handwriting) |
|
|
44
|
+
| `blueprint` | blue ink on pale drafting paper, drafting capitals for group titles | IBM Plex Mono |
|
|
45
|
+
| `c4` | C4's colors (dark person, blue containers, gray external systems) in quiet tones; each box says its element type | IBM Plex Sans |
|
|
46
46
|
|
|
47
47
|
The "drawn as" columns below describe `soft`. In `clean`, `dark` and `blueprint`, kinds differ by shape and dash only, and color comes from `highlight`, `color` and the error/success kinds.
|
|
48
48
|
|
|
@@ -38,9 +38,17 @@ Linework handles the layout and the look. These rules are about content, which i
|
|
|
38
38
|
- **Keep error and success kinds for real errors and successes.** They're red and green in every style.
|
|
39
39
|
- **Pick the style for the medium**: `clean` for docs and READMEs, `dark` for dark-mode pages, `soft` for slides, `sketch` for early drafts, `blueprint` for technical deep dives. Use one style across all the diagrams in a doc.
|
|
40
40
|
|
|
41
|
+
## Order
|
|
42
|
+
|
|
43
|
+
Layout reads the spec top to bottom, so the order you write things in shows up in the picture.
|
|
44
|
+
|
|
45
|
+
- **List nodes and edges in reading order**: the entry point first, then each step as the request or flow reaches it. A flow's main path then runs straight down, and an edge back to an earlier node (a retry, a resume) is drawn as the loop it is.
|
|
46
|
+
- **Write a reply after its call** (`api -> stripe`, then `stripe -.-> api`). The pair is drawn side by side.
|
|
47
|
+
- **In ER, name foreign keys after the table they point at**: `customer_id` for `customers`, `shipping_address_id` for `addresses`. The relationship line then attaches to that row and to the other table's primary key. A key named anything else attaches to the table as a whole.
|
|
48
|
+
|
|
41
49
|
## Direction
|
|
42
50
|
|
|
43
|
-
- Leave out `direction` on the first build. `auto` tries both and keeps the one that fits a doc page, which is about 800px wide.
|
|
51
|
+
- Leave out `direction` on the first build. `auto` tries both and keeps the one that fits a doc page, which is about 800px wide. ER diagrams always run left to right, since relationships attach to the sides of the key rows.
|
|
44
52
|
- Set it only after looking at the result, and only if it reads wrong. A short linear pipeline of 5 or fewer steps can read better as LR.
|
|
45
53
|
- Don't force LR on a diagram with more than about 6 nodes in a row. It comes out 1,500px or more wide, and embedded in a doc its text shrinks to unreadable. `check` warns about this as `too-wide`.
|
|
46
54
|
|