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.
@@ -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);