@webpieces/nx-webpieces-rules 0.4.529 → 0.4.531

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,61 @@
1
+ /**
2
+ * The runtime graph's VISUAL VOCABULARY, and the legend that explains it.
3
+ *
4
+ * Every colour, shape and fill the drawing uses lives here, next to the legend swatches that teach
5
+ * the reader what each one means. They are one unit on purpose: the legend's job is to be a true
6
+ * account of the picture, and the fastest way for a legend to start lying is for it to sit in a
7
+ * different file from the constants it describes and drift as those change.
8
+ *
9
+ * Imported by runtime-visualizer.ts, which owns the DRAWING (which nodes and edges to emit). This
10
+ * module knows nothing about the graph model, which is what keeps the dependency one-way and
11
+ * cycle-free.
12
+ */
13
+ export declare const LEVEL_COLORS: Record<number, string>;
14
+ export declare const QUEUE_FILL = "#FFF3E0";
15
+ /**
16
+ * The queue node is an `Mrecord` whose FIRST field is empty, which draws a rounded outline with a
17
+ * vertical cap line near one end — a cylinder lying on its side, distinguishing a queue from the
18
+ * upright cylinder that now means a database.
19
+ *
20
+ * Two things here are load-bearing and easy to break:
21
+ *
22
+ * 1. NO surrounding `{}`. Record fields lay out along the rank direction, and this graph is
23
+ * `rankdir=TB` (see {@link generateRuntimeDot}), where the default is horizontal — which is what
24
+ * we want. Adding braces TOGGLES that, turning the cap line into a band across the top.
25
+ * 2. The leading space is the empty field. The record parser trims it to nothing, which is the
26
+ * point; it must survive as its own field, so the `|` cannot be dropped.
27
+ *
28
+ * Graphviz has no sideways cylinder and never has: `orientation=` is documented as rotating POLYGON
29
+ * shapes, and `cylinder` is drawn with beziers, so it silently ignores the attribute (graphviz issue
30
+ * #2244, open since 2022 and still reproducible on 13.0.0). This is the closest native shape.
31
+ */
32
+ export declare const QUEUE_SHAPE = "Mrecord";
33
+ export declare const QUEUE_LABEL_PREFIX = " |";
34
+ /**
35
+ * Marker class stamped on every queue node. Graphviz copies `class` straight into the rendered
36
+ * `<g class="node wp_queue">`, which is how runtime-visualizer.client.js finds these nodes and
37
+ * redraws them as true horizontal cylinders in the browser.
38
+ *
39
+ * A CLASS rather than an id prefix, because queue-kind EXTERNAL systems are queues too and share the
40
+ * `system__` id space with databases — which must stay upright. Underscored, not hyphenated: DOT
41
+ * emits a hyphen as `&#45;`, which is harmless but needlessly surprising to anyone reading the SVG.
42
+ */
43
+ export declare const QUEUE_CLASS = "wp_queue";
44
+ /** Fill for the upright cylinder standing for an external DATASTORE (firestore, postgres, ...). */
45
+ export declare const DATABASE_FILL = "#E1F5FE";
46
+ /** Shape per external-system kind. Anything unrecognised falls back to the generic dashed box. */
47
+ export declare const EXTERNAL_SHAPES: Record<string, string>;
48
+ /** Fill per external-system kind, paired with {@link EXTERNAL_SHAPES}. */
49
+ export declare const EXTERNAL_FILLS: Record<string, string>;
50
+ /** Fill + border for the dashed terminal node standing for a system outside this repo. */
51
+ export declare const EXTERNAL_FILL = "#FAFAFA";
52
+ export declare const EXTERNAL_BORDER = "#9E9E9E";
53
+ /** Fill + border for the clock node standing for a scheduler-driven endpoint. */
54
+ export declare const CRON_FILL = "#FFF9C4";
55
+ export declare const CRON_BORDER = "#F9A825";
56
+ /**
57
+ * The legend. Three columns — what a box IS, what a line MEANS, how to read a box — replacing the
58
+ * three paragraphs of prose that used to restate the picture in words. Styled after
59
+ * {@link GraphVisualizer}'s legend so the two graphs in this repo look like one tool.
60
+ */
61
+ export declare function legendHtml(): string;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ /**
3
+ * The runtime graph's VISUAL VOCABULARY, and the legend that explains it.
4
+ *
5
+ * Every colour, shape and fill the drawing uses lives here, next to the legend swatches that teach
6
+ * the reader what each one means. They are one unit on purpose: the legend's job is to be a true
7
+ * account of the picture, and the fastest way for a legend to start lying is for it to sit in a
8
+ * different file from the constants it describes and drift as those change.
9
+ *
10
+ * Imported by runtime-visualizer.ts, which owns the DRAWING (which nodes and edges to emit). This
11
+ * module knows nothing about the graph model, which is what keeps the dependency one-way and
12
+ * cycle-free.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CRON_BORDER = exports.CRON_FILL = exports.EXTERNAL_BORDER = exports.EXTERNAL_FILL = exports.EXTERNAL_FILLS = exports.EXTERNAL_SHAPES = exports.DATABASE_FILL = exports.QUEUE_CLASS = exports.QUEUE_LABEL_PREFIX = exports.QUEUE_SHAPE = exports.QUEUE_FILL = exports.LEVEL_COLORS = void 0;
16
+ exports.legendHtml = legendHtml;
17
+ exports.LEVEL_COLORS = {
18
+ 0: '#E8F5E9',
19
+ 1: '#E3F2FD',
20
+ 2: '#FFF3E0',
21
+ 3: '#FCE4EC',
22
+ };
23
+ exports.QUEUE_FILL = '#FFF3E0';
24
+ /**
25
+ * The queue node is an `Mrecord` whose FIRST field is empty, which draws a rounded outline with a
26
+ * vertical cap line near one end — a cylinder lying on its side, distinguishing a queue from the
27
+ * upright cylinder that now means a database.
28
+ *
29
+ * Two things here are load-bearing and easy to break:
30
+ *
31
+ * 1. NO surrounding `{}`. Record fields lay out along the rank direction, and this graph is
32
+ * `rankdir=TB` (see {@link generateRuntimeDot}), where the default is horizontal — which is what
33
+ * we want. Adding braces TOGGLES that, turning the cap line into a band across the top.
34
+ * 2. The leading space is the empty field. The record parser trims it to nothing, which is the
35
+ * point; it must survive as its own field, so the `|` cannot be dropped.
36
+ *
37
+ * Graphviz has no sideways cylinder and never has: `orientation=` is documented as rotating POLYGON
38
+ * shapes, and `cylinder` is drawn with beziers, so it silently ignores the attribute (graphviz issue
39
+ * #2244, open since 2022 and still reproducible on 13.0.0). This is the closest native shape.
40
+ */
41
+ exports.QUEUE_SHAPE = 'Mrecord';
42
+ exports.QUEUE_LABEL_PREFIX = ' |';
43
+ /**
44
+ * Marker class stamped on every queue node. Graphviz copies `class` straight into the rendered
45
+ * `<g class="node wp_queue">`, which is how runtime-visualizer.client.js finds these nodes and
46
+ * redraws them as true horizontal cylinders in the browser.
47
+ *
48
+ * A CLASS rather than an id prefix, because queue-kind EXTERNAL systems are queues too and share the
49
+ * `system__` id space with databases — which must stay upright. Underscored, not hyphenated: DOT
50
+ * emits a hyphen as `&#45;`, which is harmless but needlessly surprising to anyone reading the SVG.
51
+ */
52
+ exports.QUEUE_CLASS = 'wp_queue';
53
+ /** Fill for the upright cylinder standing for an external DATASTORE (firestore, postgres, ...). */
54
+ exports.DATABASE_FILL = '#E1F5FE';
55
+ /** Shape per external-system kind. Anything unrecognised falls back to the generic dashed box. */
56
+ exports.EXTERNAL_SHAPES = {
57
+ database: 'cylinder',
58
+ cache: 'cylinder',
59
+ queue: 'Mrecord',
60
+ storage: 'folder',
61
+ };
62
+ /** Fill per external-system kind, paired with {@link EXTERNAL_SHAPES}. */
63
+ exports.EXTERNAL_FILLS = {
64
+ database: exports.DATABASE_FILL,
65
+ cache: exports.DATABASE_FILL,
66
+ queue: exports.QUEUE_FILL,
67
+ storage: '#F3E5F5',
68
+ };
69
+ /** Fill + border for the dashed terminal node standing for a system outside this repo. */
70
+ exports.EXTERNAL_FILL = '#FAFAFA';
71
+ exports.EXTERNAL_BORDER = '#9E9E9E';
72
+ /** Fill + border for the clock node standing for a scheduler-driven endpoint. */
73
+ exports.CRON_FILL = '#FFF9C4';
74
+ exports.CRON_BORDER = '#F9A825';
75
+ class LegendSwatches {
76
+ service = '<svg width="46" height="26"><rect x="1" y="3" width="44" height="20" rx="7" fill="#E8F5E9" stroke="#333"/></svg>';
77
+ /**
78
+ * A cylinder on its side — the SAME geometry runtime-visualizer.client.js draws on the real
79
+ * node, so the legend cannot drift from the picture it explains.
80
+ */
81
+ queue = `<svg width="46" height="26"><path d="M9,5 H37 A8,8 0 0 1 37,21 H9 A8,8 0 0 1 9,5 Z" fill="${exports.QUEUE_FILL}" stroke="#333"/>` +
82
+ '<path d="M9,5 A8,8 0 0 1 9,21" fill="none" stroke="#333"/></svg>';
83
+ database = `<svg width="46" height="26"><path d="M8,7 a15,4 0 0 1 30,0 v12 a15,4 0 0 1 -30,0 z" fill="${exports.DATABASE_FILL}" stroke="#333"/>` +
84
+ '<path d="M8,7 a15,4 0 0 0 30,0" fill="none" stroke="#333"/></svg>';
85
+ storage = '<svg width="46" height="26"><path d="M2,22 V6 H16 l3,3 H44 V22 Z" fill="#F3E5F5" stroke="#333"/></svg>';
86
+ external = `<svg width="46" height="26"><rect x="1" y="3" width="44" height="20" fill="${exports.EXTERNAL_FILL}" ` +
87
+ `stroke="${exports.EXTERNAL_BORDER}" stroke-dasharray="4,3"/></svg>`;
88
+ cron = `<svg width="46" height="26"><circle cx="23" cy="13" r="11" fill="${exports.CRON_FILL}" stroke="${exports.CRON_BORDER}"/>` +
89
+ '<text x="23" y="18" font-size="12" text-anchor="middle">&#9200;</text></svg>';
90
+ solid = '<svg width="60" height="20"><line x1="2" y1="10" x2="48" y2="10" stroke="#333" stroke-width="1.5"/>' +
91
+ '<path d="M48,6 L57,10 L48,14 Z" fill="#333"/></svg>';
92
+ dashed = '<svg width="60" height="20"><line x1="2" y1="10" x2="48" y2="10" stroke="#333" stroke-width="1.5" ' +
93
+ 'stroke-dasharray="5,4"/><path d="M48,6 L57,10 L48,14 Z" fill="#333"/></svg>';
94
+ scheduled = `<svg width="60" height="20"><line x1="2" y1="10" x2="48" y2="10" stroke="${exports.CRON_BORDER}" stroke-width="1.5"/>` +
95
+ `<path d="M48,6 L57,10 L48,14 Z" fill="${exports.CRON_BORDER}"/></svg>`;
96
+ }
97
+ /**
98
+ * The legend. Three columns — what a box IS, what a line MEANS, how to read a box — replacing the
99
+ * three paragraphs of prose that used to restate the picture in words. Styled after
100
+ * {@link GraphVisualizer}'s legend so the two graphs in this repo look like one tool.
101
+ */
102
+ // webpieces-disable no-function-outside-class -- HTML builder, matching the sibling builders in this file
103
+ function legendHtml() {
104
+ const sw = new LegendSwatches();
105
+ const item = (swatch, text) => `<div class="legend-item"><span class="sw">${swatch}</span><span>${text}</span></div>`;
106
+ return `<div class="legend">
107
+ <h2>Legend</h2>
108
+ <div class="legend-columns">
109
+ <div class="legend-col">
110
+ <h3>Node shapes &mdash; <em>what a box is</em></h3>
111
+ ${item(sw.service, '<strong>service</strong> &mdash; a deployable in this repo; fill is its dependency level')}
112
+ ${item(sw.queue, '<strong>queue</strong> &mdash; each <em>line</em> in the box is one Cloud Tasks queue, the unit Terraform actually creates. Queues of one contract that flow between the <em>same</em> producer and consumer share a box; every one is still named, so you can always see <em>which</em> queue is stuck.')}
113
+ ${item(sw.database, '<strong>database</strong> &mdash; a datastore outside this repo')}
114
+ ${item(sw.storage, '<strong>object storage</strong> &mdash; a bucket outside this repo')}
115
+ ${item(sw.external, '<strong>external system</strong> &mdash; outside this repo; nothing here implements it. Pointing <strong>OUT</strong> = a system this repo calls (firestore, gmail). Pointing <strong>IN</strong> = an endpoint driven from outside, and the box names the <strong>CALLER</strong> (<code>twilio</code>), not our contract &mdash; the contract and method are on the arrow. One vendor is ONE box however many endpoints it posts to, and the same box whether we call it or it calls us. A dotted box labelled <code>? unknown caller</code> means the endpoint never declared one.')}
116
+ ${item(sw.cron, '<strong>cron</strong> &mdash; a scheduler fires this endpoint')}
117
+ </div>
118
+ <div class="legend-col">
119
+ <h3>Lines &mdash; <em>what a call is</em></h3>
120
+ ${item(sw.solid, '<strong>solid = rpc</strong> &mdash; the request follows the arrow, the response flows back')}
121
+ ${item(sw.dashed, '<strong>dashed = event</strong> &mdash; asynchronous: the event flows in the direction of the arrow and returns once it is in the queue')}
122
+ ${item(sw.scheduled, '<strong>scheduled</strong> &mdash; a cron invocation')}
123
+ <div class="legend-note"><em>Every line is labeled with the contract the call flows over. A service that enqueues to itself loops through its own queue &mdash; a queue decouples the two sides, so it is not a dependency cycle.</em></div>
124
+ </div>
125
+ <div class="legend-col">
126
+ <h3>Reading a box</h3>
127
+ <pre class="legend-box-anatomy">name
128
+ (server|client, L#)
129
+ implements: &lt;contracts it serves&gt;
130
+ </pre>
131
+ <div class="legend-note">A box lists only what it <strong>serves</strong>. What it <em>calls</em> is on its outgoing arrows.</div>
132
+ <div class="legend-note"><code>(via &lt;lib&gt;)</code> = served through an embedded library, not its own source.</div>
133
+ </div>
134
+ </div>
135
+ </div>`;
136
+ }
137
+ //# sourceMappingURL=runtime-viz-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-viz-theme.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-viz-theme.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AA2GH,gCAkCC;AA3IY,QAAA,YAAY,GAA2B;IAChD,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,SAAS;CACf,CAAC;AAEW,QAAA,UAAU,GAAG,SAAS,CAAC;AAEpC;;;;;;;;;;;;;;;;GAgBG;AACU,QAAA,WAAW,GAAG,SAAS,CAAC;AACxB,QAAA,kBAAkB,GAAG,IAAI,CAAC;AAEvC;;;;;;;;GAQG;AACU,QAAA,WAAW,GAAG,UAAU,CAAC;AAEtC,mGAAmG;AACtF,QAAA,aAAa,GAAG,SAAS,CAAC;AAEvC,kGAAkG;AACrF,QAAA,eAAe,GAA2B;IACnD,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,QAAQ;CACpB,CAAC;AAEF,0EAA0E;AAC7D,QAAA,cAAc,GAA2B;IAClD,QAAQ,EAAE,qBAAa;IACvB,KAAK,EAAE,qBAAa;IACpB,KAAK,EAAE,kBAAU;IACjB,OAAO,EAAE,SAAS;CACrB,CAAC;AAEF,0FAA0F;AAC7E,QAAA,aAAa,GAAG,SAAS,CAAC;AAC1B,QAAA,eAAe,GAAG,SAAS,CAAC;AAEzC,iFAAiF;AACpE,QAAA,SAAS,GAAG,SAAS,CAAC;AACtB,QAAA,WAAW,GAAG,SAAS,CAAC;AAErC,MAAM,cAAc;IACP,OAAO,GACZ,kHAAkH,CAAC;IACvH;;;OAGG;IACM,KAAK,GACV,6FAA6F,kBAAU,mBAAmB;QAC1H,kEAAkE,CAAC;IAC9D,QAAQ,GACb,6FAA6F,qBAAa,mBAAmB;QAC7H,mEAAmE,CAAC;IAC/D,OAAO,GACZ,wGAAwG,CAAC;IACpG,QAAQ,GACb,8EAA8E,qBAAa,IAAI;QAC/F,WAAW,uBAAe,kCAAkC,CAAC;IACxD,IAAI,GACT,oEAAoE,iBAAS,aAAa,mBAAW,KAAK;QAC1G,8EAA8E,CAAC;IAC1E,KAAK,GACV,qGAAqG;QACrG,qDAAqD,CAAC;IACjD,MAAM,GACX,oGAAoG;QACpG,6EAA6E,CAAC;IACzE,SAAS,GACd,4EAA4E,mBAAW,wBAAwB;QAC/G,yCAAyC,mBAAW,WAAW,CAAC;CACvE;AAED;;;;GAIG;AACH,0GAA0G;AAC1G,SAAgB,UAAU;IACtB,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,IAAY,EAAU,EAAE,CAClD,6CAA6C,MAAM,gBAAgB,IAAI,eAAe,CAAC;IAC3F,OAAO;;;;;kBAKO,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,0FAA0F,CAAC;kBAC5G,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,0SAA0S,CAAC;kBAC1T,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,iEAAiE,CAAC;kBACpF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,oEAAoE,CAAC;kBACtF,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,ujBAAujB,CAAC;kBAC1kB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,+DAA+D,CAAC;;;;kBAI9E,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,6FAA6F,CAAC;kBAC7G,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,yIAAyI,CAAC;kBAC1J,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,sDAAsD,CAAC;;;;;;;;;;;;;WAajF,CAAC;AACZ,CAAC","sourcesContent":["/**\n * The runtime graph's VISUAL VOCABULARY, and the legend that explains it.\n *\n * Every colour, shape and fill the drawing uses lives here, next to the legend swatches that teach\n * the reader what each one means. They are one unit on purpose: the legend's job is to be a true\n * account of the picture, and the fastest way for a legend to start lying is for it to sit in a\n * different file from the constants it describes and drift as those change.\n *\n * Imported by runtime-visualizer.ts, which owns the DRAWING (which nodes and edges to emit). This\n * module knows nothing about the graph model, which is what keeps the dependency one-way and\n * cycle-free.\n */\n\nexport const LEVEL_COLORS: Record<number, string> = {\n 0: '#E8F5E9',\n 1: '#E3F2FD',\n 2: '#FFF3E0',\n 3: '#FCE4EC',\n};\n\nexport const QUEUE_FILL = '#FFF3E0';\n\n/**\n * The queue node is an `Mrecord` whose FIRST field is empty, which draws a rounded outline with a\n * vertical cap line near one end — a cylinder lying on its side, distinguishing a queue from the\n * upright cylinder that now means a database.\n *\n * Two things here are load-bearing and easy to break:\n *\n * 1. NO surrounding `{}`. Record fields lay out along the rank direction, and this graph is\n * `rankdir=TB` (see {@link generateRuntimeDot}), where the default is horizontal — which is what\n * we want. Adding braces TOGGLES that, turning the cap line into a band across the top.\n * 2. The leading space is the empty field. The record parser trims it to nothing, which is the\n * point; it must survive as its own field, so the `|` cannot be dropped.\n *\n * Graphviz has no sideways cylinder and never has: `orientation=` is documented as rotating POLYGON\n * shapes, and `cylinder` is drawn with beziers, so it silently ignores the attribute (graphviz issue\n * #2244, open since 2022 and still reproducible on 13.0.0). This is the closest native shape.\n */\nexport const QUEUE_SHAPE = 'Mrecord';\nexport const QUEUE_LABEL_PREFIX = ' |';\n\n/**\n * Marker class stamped on every queue node. Graphviz copies `class` straight into the rendered\n * `<g class=\"node wp_queue\">`, which is how runtime-visualizer.client.js finds these nodes and\n * redraws them as true horizontal cylinders in the browser.\n *\n * A CLASS rather than an id prefix, because queue-kind EXTERNAL systems are queues too and share the\n * `system__` id space with databases — which must stay upright. Underscored, not hyphenated: DOT\n * emits a hyphen as `&#45;`, which is harmless but needlessly surprising to anyone reading the SVG.\n */\nexport const QUEUE_CLASS = 'wp_queue';\n\n/** Fill for the upright cylinder standing for an external DATASTORE (firestore, postgres, ...). */\nexport const DATABASE_FILL = '#E1F5FE';\n\n/** Shape per external-system kind. Anything unrecognised falls back to the generic dashed box. */\nexport const EXTERNAL_SHAPES: Record<string, string> = {\n database: 'cylinder',\n cache: 'cylinder',\n queue: 'Mrecord',\n storage: 'folder',\n};\n\n/** Fill per external-system kind, paired with {@link EXTERNAL_SHAPES}. */\nexport const EXTERNAL_FILLS: Record<string, string> = {\n database: DATABASE_FILL,\n cache: DATABASE_FILL,\n queue: QUEUE_FILL,\n storage: '#F3E5F5',\n};\n\n/** Fill + border for the dashed terminal node standing for a system outside this repo. */\nexport const EXTERNAL_FILL = '#FAFAFA';\nexport const EXTERNAL_BORDER = '#9E9E9E';\n\n/** Fill + border for the clock node standing for a scheduler-driven endpoint. */\nexport const CRON_FILL = '#FFF9C4';\nexport const CRON_BORDER = '#F9A825';\n\nclass LegendSwatches {\n readonly service =\n '<svg width=\"46\" height=\"26\"><rect x=\"1\" y=\"3\" width=\"44\" height=\"20\" rx=\"7\" fill=\"#E8F5E9\" stroke=\"#333\"/></svg>';\n /**\n * A cylinder on its side — the SAME geometry runtime-visualizer.client.js draws on the real\n * node, so the legend cannot drift from the picture it explains.\n */\n readonly queue =\n `<svg width=\"46\" height=\"26\"><path d=\"M9,5 H37 A8,8 0 0 1 37,21 H9 A8,8 0 0 1 9,5 Z\" fill=\"${QUEUE_FILL}\" stroke=\"#333\"/>` +\n '<path d=\"M9,5 A8,8 0 0 1 9,21\" fill=\"none\" stroke=\"#333\"/></svg>';\n readonly database =\n `<svg width=\"46\" height=\"26\"><path d=\"M8,7 a15,4 0 0 1 30,0 v12 a15,4 0 0 1 -30,0 z\" fill=\"${DATABASE_FILL}\" stroke=\"#333\"/>` +\n '<path d=\"M8,7 a15,4 0 0 0 30,0\" fill=\"none\" stroke=\"#333\"/></svg>';\n readonly storage =\n '<svg width=\"46\" height=\"26\"><path d=\"M2,22 V6 H16 l3,3 H44 V22 Z\" fill=\"#F3E5F5\" stroke=\"#333\"/></svg>';\n readonly external =\n `<svg width=\"46\" height=\"26\"><rect x=\"1\" y=\"3\" width=\"44\" height=\"20\" fill=\"${EXTERNAL_FILL}\" ` +\n `stroke=\"${EXTERNAL_BORDER}\" stroke-dasharray=\"4,3\"/></svg>`;\n readonly cron =\n `<svg width=\"46\" height=\"26\"><circle cx=\"23\" cy=\"13\" r=\"11\" fill=\"${CRON_FILL}\" stroke=\"${CRON_BORDER}\"/>` +\n '<text x=\"23\" y=\"18\" font-size=\"12\" text-anchor=\"middle\">&#9200;</text></svg>';\n readonly solid =\n '<svg width=\"60\" height=\"20\"><line x1=\"2\" y1=\"10\" x2=\"48\" y2=\"10\" stroke=\"#333\" stroke-width=\"1.5\"/>' +\n '<path d=\"M48,6 L57,10 L48,14 Z\" fill=\"#333\"/></svg>';\n readonly dashed =\n '<svg width=\"60\" height=\"20\"><line x1=\"2\" y1=\"10\" x2=\"48\" y2=\"10\" stroke=\"#333\" stroke-width=\"1.5\" ' +\n 'stroke-dasharray=\"5,4\"/><path d=\"M48,6 L57,10 L48,14 Z\" fill=\"#333\"/></svg>';\n readonly scheduled =\n `<svg width=\"60\" height=\"20\"><line x1=\"2\" y1=\"10\" x2=\"48\" y2=\"10\" stroke=\"${CRON_BORDER}\" stroke-width=\"1.5\"/>` +\n `<path d=\"M48,6 L57,10 L48,14 Z\" fill=\"${CRON_BORDER}\"/></svg>`;\n}\n\n/**\n * The legend. Three columns — what a box IS, what a line MEANS, how to read a box — replacing the\n * three paragraphs of prose that used to restate the picture in words. Styled after\n * {@link GraphVisualizer}'s legend so the two graphs in this repo look like one tool.\n */\n// webpieces-disable no-function-outside-class -- HTML builder, matching the sibling builders in this file\nexport function legendHtml(): string {\n const sw = new LegendSwatches();\n const item = (swatch: string, text: string): string =>\n `<div class=\"legend-item\"><span class=\"sw\">${swatch}</span><span>${text}</span></div>`;\n return `<div class=\"legend\">\n <h2>Legend</h2>\n <div class=\"legend-columns\">\n <div class=\"legend-col\">\n <h3>Node shapes &mdash; <em>what a box is</em></h3>\n ${item(sw.service, '<strong>service</strong> &mdash; a deployable in this repo; fill is its dependency level')}\n ${item(sw.queue, '<strong>queue</strong> &mdash; each <em>line</em> in the box is one Cloud Tasks queue, the unit Terraform actually creates. Queues of one contract that flow between the <em>same</em> producer and consumer share a box; every one is still named, so you can always see <em>which</em> queue is stuck.')}\n ${item(sw.database, '<strong>database</strong> &mdash; a datastore outside this repo')}\n ${item(sw.storage, '<strong>object storage</strong> &mdash; a bucket outside this repo')}\n ${item(sw.external, '<strong>external system</strong> &mdash; outside this repo; nothing here implements it. Pointing <strong>OUT</strong> = a system this repo calls (firestore, gmail). Pointing <strong>IN</strong> = an endpoint driven from outside, and the box names the <strong>CALLER</strong> (<code>twilio</code>), not our contract &mdash; the contract and method are on the arrow. One vendor is ONE box however many endpoints it posts to, and the same box whether we call it or it calls us. A dotted box labelled <code>? unknown caller</code> means the endpoint never declared one.')}\n ${item(sw.cron, '<strong>cron</strong> &mdash; a scheduler fires this endpoint')}\n </div>\n <div class=\"legend-col\">\n <h3>Lines &mdash; <em>what a call is</em></h3>\n ${item(sw.solid, '<strong>solid = rpc</strong> &mdash; the request follows the arrow, the response flows back')}\n ${item(sw.dashed, '<strong>dashed = event</strong> &mdash; asynchronous: the event flows in the direction of the arrow and returns once it is in the queue')}\n ${item(sw.scheduled, '<strong>scheduled</strong> &mdash; a cron invocation')}\n <div class=\"legend-note\"><em>Every line is labeled with the contract the call flows over. A service that enqueues to itself loops through its own queue &mdash; a queue decouples the two sides, so it is not a dependency cycle.</em></div>\n </div>\n <div class=\"legend-col\">\n <h3>Reading a box</h3>\n <pre class=\"legend-box-anatomy\">name\n(server|client, L#)\nimplements: &lt;contracts it serves&gt;\n</pre>\n <div class=\"legend-note\">A box lists only what it <strong>serves</strong>. What it <em>calls</em> is on its outgoing arrows.</div>\n <div class=\"legend-note\"><code>(via &lt;lib&gt;)</code> = served through an embedded library, not its own source.</div>\n </div>\n </div>\n </div>`;\n}\n"]}