figdown 0.1.0-rc.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/EXPRESSING.md +119 -0
- package/LAYOUT-GUIDE.md +367 -0
- package/LICENSE +21 -0
- package/README.md +201 -0
- package/README.zh-tw.md +185 -0
- package/SHOWCASE.md +421 -0
- package/dist/figdown.js +2239 -0
- package/dist/figdown.mjs +2231 -0
- package/examples/evpn-fabric.svg +54 -0
- package/examples/showcase/arp-resolution.svg +51 -0
- package/examples/showcase/ethernet-frame.svg +37 -0
- package/examples/showcase/l2-forwarding-logic.svg +42 -0
- package/examples/showcase/tcp-handshake.svg +56 -0
- package/examples/showcase/tcp-header.svg +47 -0
- package/examples/showcase/tcp-state-machine.svg +140 -0
- package/package.json +51 -0
- package/skill/figdown/build-svg.js +73 -0
- package/skill/figdown/figdown.html +3290 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 843 984" width="843" height="984" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="68" x1="429.6" y1="76" x2="429.6" y2="170" stroke="#2563eb" stroke-width="1.6"/><path data-edge="69" d="M465.2 58 L780 58 Q790 58 790 68 L790 308 Q790 318 780 318 L692.6 318" fill="none" stroke="#2563eb" stroke-width="1.6"/><line data-edge="70" x1="460.08923076923077" y1="206" x2="619.3107692307692" y2="300" stroke="#2563eb" stroke-width="1.6"/><line data-edge="71" x1="400.8612326629738" y1="209.014657885179" x2="241.49507881681996" y2="303.01465788517896" stroke="#2563eb" stroke-width="1.6"/><line data-edge="72" x1="607" y1="318" x2="266.4" y2="318" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="73" x1="619.3107692307692" y1="336" x2="460.08923076923077" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="74" x1="239.71692307692308" y1="336" x2="399.08307692307693" y2="430" stroke="#2563eb" stroke-width="1.6"/><path data-edge="77" d="M394 170.694 L308.994 129.372 Q300 125 308.883 120.408 L394.782 76" fill="none" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><path data-edge="78" d="M648.809 300 L640.55 149.985 Q640 140 630.683 136.369 L465.2 71.875" fill="none" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="79" x1="237.9387673370262" y1="296.98534211482104" x2="397.30492118318006" y2="202.985342114821" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="82" x1="209.18615384615384" y1="336" x2="209.01384615384615" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="83" x1="399.05538461538464" y1="466" x2="239.54461538461538" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="84" x1="209" y1="596" x2="209" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="85" x1="228.55076923076922" y1="596" x2="330.64923076923077" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="86" x1="218.78923076923076" y1="596" x2="340.6107692307692" y2="820" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="87" x1="228.57846153846154" y1="726" x2="330.82153846153847" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="88" x1="350.2276923076923" y1="726" x2="350.3723076923077" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="89" x1="396.79999999999995" y1="838" x2="614" y2="838" stroke="#dc2626" stroke-width="1.6"/><line data-edge="92" x1="460.1169230769231" y1="466" x2="619.483076923077" y2="560" stroke="#16a34a" stroke-width="1.6"/><line data-edge="93" x1="649.9723076923077" y1="596" x2="649.8276923076922" y2="690" stroke="#16a34a" stroke-width="1.6"/><line data-edge="94" x1="649.7723076923077" y1="726" x2="649.6276923076923" y2="820" stroke="#16a34a" stroke-width="1.6"/><g data-node="closed" data-x="394" data-y="40" style="cursor:move"><rect x="394" y="40" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="62.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><g data-node="listen" data-x="394" data-y="170" style="cursor:move"><rect x="394" y="170" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="192.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LISTEN</text></g><g data-node="synsent" data-x="607" data-y="300" style="cursor:move"><rect x="607" y="300" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="649.8" y="322.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-SENT</text></g><g data-node="synrcvd" data-x="152" data-y="300" style="cursor:move"><rect x="152" y="300" width="114.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209.2" y="322.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-RECEIVED</text></g><g data-node="estab" data-x="376" data-y="430" style="cursor:move"><rect x="376" y="430" width="107.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="452.55" font-size="13" text-anchor="middle" fill="#1d1d1b">ESTABLISHED</text></g><g data-node="fw1" data-x="159" data-y="560" style="cursor:move"><rect x="159" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-1</text></g><g data-node="fw2" data-x="159" data-y="690" style="cursor:move"><rect x="159" y="690" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-2</text></g><g data-node="closing" data-x="311" data-y="690" style="cursor:move"><rect x="311" y="690" width="78.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.2" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSING</text></g><g data-node="closewait" data-x="600" data-y="560" style="cursor:move"><rect x="600" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="650" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSE-WAIT</text></g><g data-node="lastack" data-x="607" data-y="690" style="cursor:move"><rect x="607" y="690" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="649.8" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LAST-ACK</text></g><g data-node="timewait" data-x="304" data-y="820" style="cursor:move"><rect x="304" y="820" width="92.8" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.4" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">TIME-WAIT</text></g><g data-node="closed2" data-x="614" data-y="820" style="cursor:move"><rect x="614" y="820" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="649.6" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><text x="435.6" y="137.57999999999998" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">passive OPEN / create TCB</text><text x="435.6" y="137.57999999999998" font-size="11" text-anchor="start" fill="#2563eb">passive OPEN / create TCB</text><path d="M429.6 170 L424 159.92 L435.20000000000005 159.92 z" fill="#2563eb" stroke="none"/><text x="627.6" y="52.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">active OPEN / create TCB, snd SYN</text><text x="627.6" y="52.25" font-size="11" text-anchor="middle" fill="#2563eb">active OPEN / create TCB, snd SYN</text><path d="M692.5999999999999 318 L702.68 312.4 L702.68 323.6 z" fill="#2563eb" stroke="none"/><text x="539.7" y="247.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">SEND / snd SYN</text><text x="539.7" y="247.25" font-size="11" text-anchor="middle" fill="#2563eb">SEND / snd SYN</text><path d="M619.3107692307692 300 L607.783624515843 299.6977836517479 L613.4775557147959 290.05314019013707 z" fill="#2563eb" stroke="none"/><text x="321.17815573989685" y="250.26465788517896" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="321.17815573989685" y="250.26465788517896" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN / snd SYN,ACK</text><path d="M241.49507881681996 303.01465788517896 L247.33224434230044 293.0701167379896 L253.02234270997042 302.71702197056237 z" fill="#2563eb" stroke="none"/><text x="436.7" y="312.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="436.7" y="312.25" font-size="11" text-anchor="middle" fill="#9333ea">rcv SYN / snd SYN,ACK</text><path d="M266.4 318 L276.47999999999996 312.4 L276.47999999999996 323.6 z" fill="#9333ea" stroke="none"/><text x="539.7" y="377.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN,ACK / snd ACK</text><text x="539.7" y="377.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN,ACK / snd ACK</text><path d="M460.08923076923077 430 L465.9224442852041 420.05314019013707 L471.6163754841569 429.6977836517479 z" fill="#2563eb" stroke="none"/><text x="319.4" y="377.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of SYN / x</text><text x="319.4" y="377.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv ACK of SYN / x</text><path d="M399.08307692307693 430 L387.55581302992647 429.7023640853834 L393.24591139759644 420.05545885281066 z" fill="#2563eb" stroke="none"/><text x="336.01719402985077" y="100.63" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="336.01719402985077" y="100.63" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M394.7820895522388 76 L388.3996077783373 85.60366213513446 L383.25615963242194 75.65454452751315 z" fill="#9333ea" stroke="none"/><text x="552.6" y="100.18726235741445" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="552.6" y="100.18726235741445" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M465.20000000000005 71.8745247148289 L476.6254519472023 70.31713979061396 L472.5583957756856 80.75261074777387 z" fill="#9333ea" stroke="none"/><text x="336.7457827216416" y="232.955342114821" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv RST (note 1) / x</text><text x="336.7457827216416" y="232.955342114821" font-size="11" text-anchor="middle" fill="#9333ea">rcv RST (note 1) / x</text><path d="M397.30492118318006 202.985342114821 L391.46775565769957 212.92988326201035 L385.7776572900296 203.2829780294376 z" fill="#9333ea" stroke="none"/><text x="215.1" y="451.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="215.1" y="451.3" font-size="11" text-anchor="start" fill="#dc2626">CLOSE / snd FIN</text><path d="M209.01384615384615 560 L203.42160165450994 549.915695291214 L214.62159834090195 549.9243106732804 z" fill="#dc2626" stroke="none"/><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#dc2626">CLOSE / snd FIN</text><path d="M239.54461538461538 560 L245.38572668223014 550.0577759626491 L251.0719964792192 559.7069384027706 z" fill="#dc2626" stroke="none"/><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M209 690 L203.4 679.92 L214.6 679.92 z" fill="#dc2626" stroke="none"/><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.64923076923077 690 L319.4405255167678 687.2923577482596 L327.02659217035057 679.0527222752913 z" fill="#dc2626" stroke="none"/><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN,ACK / snd ACK</text><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#9333ea">rcv FIN,ACK / snd ACK</text><path d="M340.6107692307692 820 L330.875382512483 813.820303774018 L340.7144579488292 808.469360440559 z" fill="#9333ea" stroke="none"/><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.82153846153847 820 L319.61092639723944 817.3002635047485 L327.19118005561285 809.0552799101793 z" fill="#dc2626" stroke="none"/><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M350.3723076923077 820 L344.75680664555944 809.9286273033927 L355.9567933911451 809.9113965545532 z" fill="#dc2626" stroke="none"/><text x="505.4" y="832.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">Timeout=2MSL / delete TCB</text><text x="505.4" y="832.25" font-size="11" text-anchor="middle" fill="#dc2626">Timeout=2MSL / delete TCB</text><path d="M614 838 L603.92 843.6 L603.92 832.4 z" fill="#dc2626" stroke="none"/><text x="539.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="539.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#16a34a">rcv FIN / snd ACK</text><path d="M619.483076923077 560 L607.9558130299265 559.7023640853835 L613.6459113975964 550.0554588528106 z" fill="#16a34a" stroke="none"/><text x="655.9" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="655.9" y="646.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">CLOSE / snd FIN</text><path d="M649.8276923076922 690 L644.2432066088548 679.9113965545532 L655.4431933544405 679.9286273033927 z" fill="#16a34a" stroke="none"/><text x="655.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="655.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">rcv ACK of FIN / x</text><path d="M649.6276923076923 820 L644.0432066088549 809.9113965545532 L655.2431933544406 809.9286273033927 z" fill="#16a34a" stroke="none"/><rect x="0" y="877" width="16" height="11" fill="#eef2ff" stroke="#555"/><text x="21" y="886.5" font-size="11" fill="#1d1d1b">A node IS a TCP state (RFC 9293 §3.3.2); the two CLOSED nodes are one state — no node-identity construct yet (OQ-S26)</text><rect x="0" y="897" width="16" height="11" fill="#2563eb" stroke="#555"/><text x="21" y="906.5" font-size="11" fill="#1d1d1b">Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)</text><rect x="0" y="917" width="16" height="11" fill="#dc2626" stroke="#555"/><text x="21" y="926.5" font-size="11" fill="#1d1d1b">Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED</text><rect x="0" y="937" width="16" height="11" fill="#16a34a" stroke="#555"/><text x="21" y="946.5" font-size="11" fill="#1d1d1b">Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED</text><rect x="0" y="957" width="16" height="11" fill="#9333ea" stroke="#555" stroke-dasharray="6 4"/><text x="21" y="966.5" font-size="11" fill="#1d1d1b">Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)</text></g><metadata id="figdown-source" data-sha256="459b9e5cc2a557ccb672ce2b25d576fd8aa07c8384f2ad347a0c2534a2373f54"><![CDATA[
|
|
2
|
+
figdown 0.1 flowchart
|
|
3
|
+
title TCP Connection State Machine (RFC 9293, Figure 5)
|
|
4
|
+
|
|
5
|
+
# ── What this figure is ─────────────────────────────────────────────
|
|
6
|
+
# The complete TCP connection state machine from RFC 9293 §3.3.2,
|
|
7
|
+
# Figure 5: all 11 states and every state transition, each labelled
|
|
8
|
+
# "event / action" exactly as the RFC writes it. `x` is the RFC's own
|
|
9
|
+
# notation for "no action". This is the hardest showcase figure — it
|
|
10
|
+
# answers the maintainer question "can FigDown fully express the TCP
|
|
11
|
+
# state machine with its transitions and conditions?" — and the answer
|
|
12
|
+
# it gives is: yes, every state and every labelled transition rides on
|
|
13
|
+
# an edge, so a reading agent recovers the whole machine from the text.
|
|
14
|
+
#
|
|
15
|
+
# HONEST LIMIT — state-ness is not yet first-class. FigDown has no
|
|
16
|
+
# "state" genre in which a node IS a state as a machine-declarable fact
|
|
17
|
+
# (that genre is the R49 §6 candidate; OQ tracks it). Here each state is
|
|
18
|
+
# a `shape=rounded` node and the fact "this node is a protocol state" is
|
|
19
|
+
# carried by the `states` class label, not by the genre. Interim, ruled.
|
|
20
|
+
#
|
|
21
|
+
# HONEST LIMIT — the CLOSED identity (OQ-S26). The canonical RFC figure
|
|
22
|
+
# draws CLOSED TWICE — top (origin) and bottom (the terminus TIME-WAIT
|
|
23
|
+
# and LAST-ACK return to) — and so do we, because routing every terminal
|
|
24
|
+
# edge back to a single top node produced long lines that struck the
|
|
25
|
+
# setup-abort labels around CLOSED and made the figure unreadable. But
|
|
26
|
+
# FigDown CANNOT declare two nodes to be the SAME entity: there is no
|
|
27
|
+
# node-identity / alias construct (OQ-S26). So `closed` and `closed2`
|
|
28
|
+
# are two nodes that a human reads as one state only because the shared
|
|
29
|
+
# `states` class label SAYS "the two CLOSED nodes are one state."
|
|
30
|
+
# A reading agent sees two ids; the identity is asserted
|
|
31
|
+
# in the label, not machine-declarable. This is the single fact this
|
|
32
|
+
# figure cannot carry that the canonical drawing implies by reusing the
|
|
33
|
+
# name — documented, not hidden.
|
|
34
|
+
#
|
|
35
|
+
# HONEST LIMIT — RFC Note 2. The canonical Figure 5 OMITS the
|
|
36
|
+
# FIN-WAIT-1 → TIME-WAIT transition and says so in its Note 2 (it occurs
|
|
37
|
+
# when a FIN arrives that also ACKs our FIN). We DRAW it (edge fw1→tw),
|
|
38
|
+
# so this figure is strictly MORE complete than the canonical drawing
|
|
39
|
+
# while carrying the same semantics the RFC prose states. Note 1 (rcv
|
|
40
|
+
# RST → LISTEN applies only to a passively-opened SYN-RECEIVED) rides on
|
|
41
|
+
# that edge's label.
|
|
42
|
+
|
|
43
|
+
# ── Semantic classes — the meaning behind every stroke ──────────────
|
|
44
|
+
# A reading agent queries these to group transitions by role; the
|
|
45
|
+
# colour/dash is only presentation (R37), the meaning is the label.
|
|
46
|
+
class states "A node IS a TCP state (RFC 9293 §3.3.2); the two CLOSED nodes are one state — no node-identity construct yet (OQ-S26)" color=#eef2ff
|
|
47
|
+
class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)" color=#2563eb
|
|
48
|
+
class client "Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED" color=#dc2626
|
|
49
|
+
class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" color=#16a34a
|
|
50
|
+
class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)" color=#9333ea style=dashed
|
|
51
|
+
|
|
52
|
+
# ── The 11 states (CLOSED drawn twice; see OQ-S26 note above) ────────
|
|
53
|
+
node closed "CLOSED" shape=rounded class=states
|
|
54
|
+
node listen "LISTEN" shape=rounded class=states
|
|
55
|
+
node synsent "SYN-SENT" shape=rounded class=states
|
|
56
|
+
node synrcvd "SYN-RECEIVED" shape=rounded class=states
|
|
57
|
+
node estab "ESTABLISHED" shape=rounded class=states
|
|
58
|
+
node fw1 "FIN-WAIT-1" shape=rounded class=states
|
|
59
|
+
node fw2 "FIN-WAIT-2" shape=rounded class=states
|
|
60
|
+
node closing "CLOSING" shape=rounded class=states
|
|
61
|
+
node closewait "CLOSE-WAIT" shape=rounded class=states
|
|
62
|
+
node lastack "LAST-ACK" shape=rounded class=states
|
|
63
|
+
node timewait "TIME-WAIT" shape=rounded class=states
|
|
64
|
+
node closed2 "CLOSED" shape=rounded class=states
|
|
65
|
+
|
|
66
|
+
flow down
|
|
67
|
+
|
|
68
|
+
# ── Opening the connection (setup) ──────────────────────────────────
|
|
69
|
+
edge closed -[passive OPEN / create TCB]-> listen class=setup
|
|
70
|
+
edge closed -[active OPEN / create TCB, snd SYN]-> synsent class=setup
|
|
71
|
+
edge listen -[SEND / snd SYN]-> synsent class=setup
|
|
72
|
+
edge listen -[rcv SYN / snd SYN,ACK]-> synrcvd class=setup
|
|
73
|
+
edge synsent -[rcv SYN / snd SYN,ACK]-> synrcvd class=rare
|
|
74
|
+
edge synsent -[rcv SYN,ACK / snd ACK]-> estab class=setup
|
|
75
|
+
edge synrcvd -[rcv ACK of SYN / x]-> estab class=setup
|
|
76
|
+
|
|
77
|
+
# ── Aborting a half-open connection (rare / reset) ──────────────────
|
|
78
|
+
edge listen -[CLOSE / delete TCB]-> closed class=rare
|
|
79
|
+
edge synsent -[CLOSE / delete TCB]-> closed class=rare
|
|
80
|
+
edge synrcvd -[rcv RST (note 1) / x]-> listen class=rare
|
|
81
|
+
|
|
82
|
+
# ── Active close — the endpoint that calls CLOSE first ──────────────
|
|
83
|
+
edge synrcvd -[CLOSE / snd FIN]-> fw1 class=client
|
|
84
|
+
edge estab -[CLOSE / snd FIN]-> fw1 class=client
|
|
85
|
+
edge fw1 -[rcv ACK of FIN / x]-> fw2 class=client
|
|
86
|
+
edge fw1 -[rcv FIN / snd ACK]-> closing class=client
|
|
87
|
+
edge fw1 -[rcv FIN,ACK / snd ACK]-> timewait class=rare
|
|
88
|
+
edge fw2 -[rcv FIN / snd ACK]-> timewait class=client
|
|
89
|
+
edge closing -[rcv ACK of FIN / x]-> timewait class=client
|
|
90
|
+
edge timewait -[Timeout=2MSL / delete TCB]-> closed2 class=client
|
|
91
|
+
|
|
92
|
+
# ── Passive close — the endpoint that receives the peer's FIN ───────
|
|
93
|
+
edge estab -[rcv FIN / snd ACK]-> closewait class=server
|
|
94
|
+
edge closewait -[CLOSE / snd FIN]-> lastack class=server
|
|
95
|
+
edge lastack -[rcv ACK of FIN / x]-> closed2 class=server
|
|
96
|
+
|
|
97
|
+
render
|
|
98
|
+
# ── Presentation only (R25) ─────────────────────────────────────────
|
|
99
|
+
# The strip test holds: delete every line below and the machine is
|
|
100
|
+
# unchanged — all 11 states, all 21 transitions and their event/action
|
|
101
|
+
# labels live in the `node`/`edge`/`class` lines above (closed2 is not
|
|
102
|
+
# an orphan: it is the target of two terminal edges).
|
|
103
|
+
#
|
|
104
|
+
# The grid reproduces the CANONICAL Figure 5 composition, mirrored about
|
|
105
|
+
# one central vertical axis (x≈430, the shared centre of CLOSED, LISTEN
|
|
106
|
+
# and ESTABLISHED) with a uniform 130 px row rhythm:
|
|
107
|
+
# row0 CLOSED · row1 LISTEN · row2 SYN-RECEIVED | SYN-SENT (mirrored
|
|
108
|
+
# at ±220) · row3 ESTABLISHED · row4 FIN-WAIT-1 | CLOSE-WAIT (same
|
|
109
|
+
# ±220 mirror) · row5 FIN-WAIT-2, CLOSING, LAST-ACK · row6 TIME-WAIT,
|
|
110
|
+
# CLOSED (terminus, under LAST-ACK as the RFC draws it).
|
|
111
|
+
# Column discipline: CLOSED→LISTEN, SYN-RECEIVED→FIN-WAIT-1→FIN-WAIT-2,
|
|
112
|
+
# CLOSING→TIME-WAIT, CLOSE-WAIT→LAST-ACK→CLOSED run dead vertical; the
|
|
113
|
+
# simultaneous-open edge SYN-SENT→SYN-RECEIVED runs dead horizontal
|
|
114
|
+
# between the mirrored pair. All meaning is in the edges; this is
|
|
115
|
+
# layout, not knowledge.
|
|
116
|
+
pin closed at=394,20
|
|
117
|
+
pin listen at=394,150
|
|
118
|
+
pin synrcvd at=152,280
|
|
119
|
+
pin synsent at=607,280
|
|
120
|
+
pin estab at=376,410
|
|
121
|
+
pin fw1 at=159,540
|
|
122
|
+
pin closewait at=600,540
|
|
123
|
+
pin fw2 at=159,670
|
|
124
|
+
pin closing at=311,670
|
|
125
|
+
pin lastack at=607,670
|
|
126
|
+
pin timewait at=304,800
|
|
127
|
+
pin closed2 at=614,800
|
|
128
|
+
|
|
129
|
+
# ── Edge routing — the three long transitions, deliberately calm ────
|
|
130
|
+
# active OPEN (closed→synsent) hugs the right margin with two elbows,
|
|
131
|
+
# outside the row-2 band; the two aborts back to the top CLOSED bow out
|
|
132
|
+
# on the outer left (LISTEN's close) and outer right (SYN-SENT's close)
|
|
133
|
+
# so they cross nothing. Every other edge is straight: the FIN-WAIT-1 →
|
|
134
|
+
# TIME-WAIT diagonal threads the gap between FIN-WAIT-2 and CLOSING,
|
|
135
|
+
# and TIME-WAIT → CLOSED is a plain same-row horizontal.
|
|
136
|
+
route closed -> synsent via=790,38;790,298
|
|
137
|
+
route listen -> closed via=300,105
|
|
138
|
+
route synsent -> closed via=640,120
|
|
139
|
+
|
|
140
|
+
]]></metadata></svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "figdown",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"description": "Figures as text — one source, two readers: a closed, deterministic figure language for Markdown, readable by AI agents and humans alike",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "FigDown <hello@figdown.org>",
|
|
7
|
+
"homepage": "https://figdown.org",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/FigDown/figdown.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/FigDown/figdown/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"figdown",
|
|
17
|
+
"diagram",
|
|
18
|
+
"figure",
|
|
19
|
+
"markdown",
|
|
20
|
+
"svg",
|
|
21
|
+
"diagrams-as-code",
|
|
22
|
+
"ai-agent",
|
|
23
|
+
"deterministic"
|
|
24
|
+
],
|
|
25
|
+
"type": "commonjs",
|
|
26
|
+
"main": "dist/figdown.js",
|
|
27
|
+
"module": "dist/figdown.mjs",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./dist/figdown.mjs",
|
|
31
|
+
"require": "./dist/figdown.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"bin": {
|
|
35
|
+
"figdown-svg": "skill/figdown/build-svg.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist/figdown.js",
|
|
39
|
+
"dist/figdown.mjs",
|
|
40
|
+
"skill/figdown/figdown.html",
|
|
41
|
+
"skill/figdown/build-svg.js",
|
|
42
|
+
"SHOWCASE.md",
|
|
43
|
+
"EXPRESSING.md",
|
|
44
|
+
"LAYOUT-GUIDE.md",
|
|
45
|
+
"examples/evpn-fabric.svg",
|
|
46
|
+
"examples/showcase/*.svg"
|
|
47
|
+
],
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// FigDown Stage-0 sidecar generator (R14): X.fd -> X.svg
|
|
3
|
+
// Deterministic: same source -> same SVG, bit-level. The artifact embeds
|
|
4
|
+
// its own source and a SHA-256 of it (spec draft section 7).
|
|
5
|
+
// The engine is never forked: parser+renderer are extracted from the editor
|
|
6
|
+
// single file at build time (the "regenerate, don't fork" rule).
|
|
7
|
+
'use strict';
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
// The engine file is looked up in order: $FIGDOWN_HTML, a co-located
|
|
13
|
+
// copy (the agent-skill bundle layout), then ../editor/figdown.html (the
|
|
14
|
+
// repository layout).
|
|
15
|
+
const ENGINE = [
|
|
16
|
+
process.env.FIGDOWN_HTML,
|
|
17
|
+
path.join(__dirname, 'figdown.html'),
|
|
18
|
+
path.join(__dirname, '..', 'editor', 'figdown.html'),
|
|
19
|
+
].filter(Boolean).find(p => fs.existsSync(p));
|
|
20
|
+
|
|
21
|
+
function loadEngine() {
|
|
22
|
+
if (!ENGINE) throw new Error('figdown.html not found (set FIGDOWN_HTML or keep it next to this script)');
|
|
23
|
+
const h = fs.readFileSync(ENGINE, 'utf8');
|
|
24
|
+
const start = h.indexOf('const SHAPES');
|
|
25
|
+
const end = h.indexOf('// 3. UI');
|
|
26
|
+
if (start < 0 || end < 0) throw new Error('cannot locate engine in ' + ENGINE);
|
|
27
|
+
const factory = new Function(h.slice(start, end) + '\nreturn {parse, render};');
|
|
28
|
+
return factory();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildOne(engine, fdPath) {
|
|
32
|
+
const src = fs.readFileSync(fdPath, 'utf8');
|
|
33
|
+
const { doc, errs } = engine.parse(src);
|
|
34
|
+
if (errs.length) {
|
|
35
|
+
console.error(fdPath + ':');
|
|
36
|
+
for (const e of errs) console.error(' ' + e);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const svg = engine.render(doc, RENDER_OPTS).svg;
|
|
40
|
+
const hash = crypto.createHash('sha256').update(src, 'utf8').digest('hex');
|
|
41
|
+
// render options are recorded in the artifact so a third-party rebuild
|
|
42
|
+
// (same source + same recorded options) stays bit-identical
|
|
43
|
+
const optAttr = RENDER_OPTS.title === true ? ' data-render-options="with-title"' : '';
|
|
44
|
+
const meta = '<metadata id="figdown-source" data-sha256="' + hash + '"' + optAttr + '><![CDATA[\n'
|
|
45
|
+
+ src.replace(/]]>/g, ']]]]><![CDATA[>') + '\n]]></metadata>';
|
|
46
|
+
const artifact = svg.replace(/<\/svg>$/, meta + '</svg>');
|
|
47
|
+
const out = fdPath.replace(/\.fd$/, '') + '.svg';
|
|
48
|
+
fs.writeFileSync(out, artifact);
|
|
49
|
+
console.log('OK ' + out);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const argv = process.argv.slice(2);
|
|
54
|
+
const RENDER_OPTS = {};
|
|
55
|
+
const args = argv.filter(a => {
|
|
56
|
+
if (a === '--with-title') { RENDER_OPTS.title = true; return false; }
|
|
57
|
+
if (a === '--no-title') { return false; } // the default since 0.1-dev.12 (kept for compat)
|
|
58
|
+
return true;
|
|
59
|
+
});
|
|
60
|
+
if (!args.length) {
|
|
61
|
+
console.error('usage: node tools/build-svg.js [--with-title] <file.fd | dir> ...');
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
const engine = loadEngine();
|
|
65
|
+
let fail = false;
|
|
66
|
+
for (const a of args) {
|
|
67
|
+
const st = fs.statSync(a);
|
|
68
|
+
const files = st.isDirectory()
|
|
69
|
+
? fs.readdirSync(a).filter(f => f.endsWith('.fd')).sort().map(f => path.join(a, f))
|
|
70
|
+
: [a];
|
|
71
|
+
for (const f of files) if (!buildOne(engine, f)) fail = true;
|
|
72
|
+
}
|
|
73
|
+
process.exit(fail ? 1 : 0);
|