@takodotid/circuit 0.43.1 → 1.0.0-rc.2

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.
Files changed (256) hide show
  1. package/README.md +115 -37
  2. package/package.json +21 -58
  3. package/src/checks/across.ts +402 -0
  4. package/src/checks/built-in.ts +256 -0
  5. package/src/checks/device.ts +436 -0
  6. package/src/checks/index.ts +53 -0
  7. package/src/cli/advisories.ts +108 -0
  8. package/src/cli/app.ts +71 -0
  9. package/src/cli/editor-types.generated.ts +30 -0
  10. package/src/cli/editor-types.ts +69 -0
  11. package/src/cli/format.ts +124 -0
  12. package/src/cli/git-hooks.ts +79 -0
  13. package/src/cli/index.ts +350 -1170
  14. package/src/cli/migrate.ts +554 -0
  15. package/src/cli/project-commands.ts +656 -0
  16. package/src/cli/releases.ts +374 -0
  17. package/src/cli/remember.ts +339 -0
  18. package/src/cli/rename.ts +27 -33
  19. package/src/cli/secrets-password.ts +60 -0
  20. package/src/cli/secrets.ts +258 -0
  21. package/src/cli/setup.ts +300 -0
  22. package/src/cli/signing.ts +23 -0
  23. package/src/cli/text-editor.ts +268 -0
  24. package/src/cli/ui.ts +298 -0
  25. package/src/cli/version.ts +2 -0
  26. package/src/cli/versions.ts +158 -0
  27. package/src/config/address.ts +102 -0
  28. package/src/config/circuit.d.ts +30 -0
  29. package/src/config/common.ts +65 -0
  30. package/src/config/communities.ts +479 -0
  31. package/src/config/define.ts +684 -0
  32. package/src/config/device.ts +164 -0
  33. package/src/config/firewall.ts +150 -0
  34. package/src/config/index.ts +22 -0
  35. package/src/{schema → config}/interfaces.ts +121 -85
  36. package/src/config/keys.ts +434 -0
  37. package/src/config/management.ts +61 -0
  38. package/src/config/merge.ts +335 -0
  39. package/src/config/models.ts +70 -0
  40. package/src/config/monitoring.ts +64 -0
  41. package/src/config/network.ts +180 -0
  42. package/src/config/presets.ts +149 -0
  43. package/src/config/routing.ts +401 -0
  44. package/src/config/services.ts +51 -0
  45. package/src/config/switching.ts +84 -0
  46. package/src/config/system.ts +103 -0
  47. package/src/{schema → config}/time-zones.ts +1 -1
  48. package/src/core/address.ts +1 -1
  49. package/src/core/cabling.ts +8 -43
  50. package/src/core/graph.ts +40 -0
  51. package/src/{integrations → core}/registries.ts +5 -3
  52. package/src/core/secrets-file.ts +539 -0
  53. package/src/core/secrets.ts +83 -64
  54. package/src/core/topology.ts +103 -146
  55. package/src/core/utils.ts +12 -1
  56. package/src/model.ts +83 -0
  57. package/src/platforms/derive.ts +64 -37
  58. package/src/platforms/finding.ts +9 -0
  59. package/src/platforms/fortios/firewall.ts +76 -64
  60. package/src/platforms/fortios/index.ts +189 -125
  61. package/src/platforms/fortios/names.ts +51 -18
  62. package/src/platforms/fortios/render.ts +69 -62
  63. package/src/platforms/fortios/routing.ts +89 -58
  64. package/src/platforms/fortios/vdoms.ts +47 -16
  65. package/src/platforms/junos/TRAPS.md +2 -2
  66. package/src/platforms/junos/bgp.ts +63 -42
  67. package/src/platforms/junos/index.ts +122 -89
  68. package/src/platforms/junos/render.ts +139 -104
  69. package/src/platforms/platform.ts +10 -8
  70. package/src/platforms/raisecom-ros/TRAPS.md +1 -1
  71. package/src/platforms/raisecom-ros/index.ts +89 -70
  72. package/src/platforms/raisecom-ros/render.ts +49 -45
  73. package/src/platforms/routeros/TRAPS.md +3 -3
  74. package/src/platforms/routeros/index.ts +121 -60
  75. package/src/platforms/routeros/render.ts +214 -195
  76. package/src/platforms/vrp/TRAPS.md +1 -1
  77. package/src/platforms/vrp/index.ts +74 -50
  78. package/src/platforms/vrp/render.ts +67 -56
  79. package/src/project/project.ts +725 -0
  80. package/src/studio/app/env.d.ts +9 -0
  81. package/src/studio/app/index.html +18 -0
  82. package/src/studio/app/src/App.vue +95 -0
  83. package/src/studio/app/src/components/AddressesView.vue +38 -0
  84. package/src/studio/app/src/components/CablesView.vue +29 -0
  85. package/src/studio/app/src/components/CompareView.vue +160 -0
  86. package/src/studio/app/src/components/DataTable.vue +49 -0
  87. package/src/studio/app/src/components/DeviceView.vue +237 -0
  88. package/src/studio/app/src/components/DevicesView.vue +32 -0
  89. package/src/studio/app/src/components/Faceplate.vue +75 -0
  90. package/src/studio/app/src/components/GraphView.vue +282 -0
  91. package/src/studio/app/src/components/LineDiff.vue +24 -0
  92. package/src/studio/app/src/components/ProblemsView.vue +42 -0
  93. package/src/studio/app/src/components/SearchView.vue +72 -0
  94. package/src/studio/app/src/components/SecretsView.vue +112 -0
  95. package/src/studio/app/src/components/SessionsView.vue +35 -0
  96. package/src/studio/app/src/components/Settings.vue +95 -0
  97. package/src/studio/app/src/components/ToolsView.vue +118 -0
  98. package/src/studio/app/src/components/VlansView.vue +50 -0
  99. package/src/studio/app/src/lib/csv.ts +19 -0
  100. package/src/studio/{public/diff.js → app/src/lib/diff.ts} +40 -35
  101. package/src/studio/app/src/lib/format.ts +38 -0
  102. package/src/studio/app/src/lib/net.ts +68 -0
  103. package/src/studio/app/src/lib/secrets-crypto.ts +35 -0
  104. package/src/studio/app/src/lib/store.ts +71 -0
  105. package/src/studio/app/src/lib/tools.ts +217 -0
  106. package/src/studio/app/src/main.ts +9 -0
  107. package/src/studio/{public → app/src}/style.css +14 -4
  108. package/src/studio/app/tsconfig.json +17 -0
  109. package/src/studio/app/vite.config.ts +16 -0
  110. package/src/studio/assets.generated.ts +3 -0
  111. package/src/studio/data.ts +96 -188
  112. package/src/studio/git.ts +27 -43
  113. package/src/studio/server.ts +196 -107
  114. package/src/studio/types.ts +141 -0
  115. package/src/transport/console.ts +3 -3
  116. package/src/transport/ssh.ts +1 -1
  117. package/dist/chunks/index-3b343xva.js +0 -177
  118. package/dist/chunks/index-6wb41q67.js +0 -62
  119. package/dist/chunks/index-n7w3tg5y.js +0 -149
  120. package/dist/chunks/index-yzw86rc8.js +0 -921
  121. package/dist/chunks/new-w3zys5y4.js +0 -172
  122. package/dist/chunks/server-cxr9ynxc.js +0 -259
  123. package/dist/cli/index.js +0 -12922
  124. package/dist/helpers/index.js +0 -881
  125. package/dist/index.js +0 -28
  126. package/dist/types/core/address-space.d.ts +0 -62
  127. package/dist/types/core/address.d.ts +0 -44
  128. package/dist/types/core/cabling.d.ts +0 -41
  129. package/dist/types/core/cross-checks.d.ts +0 -28
  130. package/dist/types/core/define.d.ts +0 -79
  131. package/dist/types/core/project-files.d.ts +0 -12
  132. package/dist/types/core/schema-docs.d.ts +0 -25
  133. package/dist/types/core/secrets.d.ts +0 -62
  134. package/dist/types/core/topology.d.ts +0 -96
  135. package/dist/types/core/utils.d.ts +0 -10
  136. package/dist/types/core/validator.d.ts +0 -23
  137. package/dist/types/helpers/bgp.d.ts +0 -195
  138. package/dist/types/helpers/cables.d.ts +0 -69
  139. package/dist/types/helpers/checks.d.ts +0 -36
  140. package/dist/types/helpers/exchange.d.ts +0 -55
  141. package/dist/types/helpers/filters.d.ts +0 -51
  142. package/dist/types/helpers/index.d.ts +0 -14
  143. package/dist/types/helpers/route-server.d.ts +0 -52
  144. package/dist/types/helpers/upstreams.d.ts +0 -192
  145. package/dist/types/index.d.ts +0 -10
  146. package/dist/types/integrations/librenms.d.ts +0 -60
  147. package/dist/types/integrations/netbox.d.ts +0 -89
  148. package/dist/types/integrations/peeringdb.d.ts +0 -24
  149. package/dist/types/integrations/prometheus.d.ts +0 -24
  150. package/dist/types/integrations/registries.d.ts +0 -8
  151. package/dist/types/platforms/bgp-wait.d.ts +0 -24
  152. package/dist/types/platforms/catalog.d.ts +0 -109
  153. package/dist/types/platforms/derive.d.ts +0 -17
  154. package/dist/types/platforms/fortios/firewall.d.ts +0 -47
  155. package/dist/types/platforms/fortios/index.d.ts +0 -140
  156. package/dist/types/platforms/fortios/models.d.ts +0 -14
  157. package/dist/types/platforms/fortios/names.d.ts +0 -52
  158. package/dist/types/platforms/fortios/plan.d.ts +0 -114
  159. package/dist/types/platforms/fortios/render.d.ts +0 -77
  160. package/dist/types/platforms/fortios/routing.d.ts +0 -56
  161. package/dist/types/platforms/fortios/tree.d.ts +0 -37
  162. package/dist/types/platforms/fortios/vdoms.d.ts +0 -50
  163. package/dist/types/platforms/index.d.ts +0 -13
  164. package/dist/types/platforms/junos/bgp.d.ts +0 -44
  165. package/dist/types/platforms/junos/index.d.ts +0 -114
  166. package/dist/types/platforms/junos/models.d.ts +0 -14
  167. package/dist/types/platforms/junos/render.d.ts +0 -154
  168. package/dist/types/platforms/line-diff.d.ts +0 -67
  169. package/dist/types/platforms/platform.d.ts +0 -135
  170. package/dist/types/platforms/raisecom-ros/index.d.ts +0 -71
  171. package/dist/types/platforms/raisecom-ros/models.d.ts +0 -14
  172. package/dist/types/platforms/raisecom-ros/render.d.ts +0 -41
  173. package/dist/types/platforms/routeros/export.d.ts +0 -59
  174. package/dist/types/platforms/routeros/index.d.ts +0 -116
  175. package/dist/types/platforms/routeros/models.d.ts +0 -18
  176. package/dist/types/platforms/routeros/plan.d.ts +0 -58
  177. package/dist/types/platforms/routeros/render.d.ts +0 -250
  178. package/dist/types/platforms/vrp/index.d.ts +0 -83
  179. package/dist/types/platforms/vrp/models.d.ts +0 -12
  180. package/dist/types/platforms/vrp/render.d.ts +0 -96
  181. package/dist/types/schema/common.d.ts +0 -30
  182. package/dist/types/schema/device.d.ts +0 -126
  183. package/dist/types/schema/firewall.d.ts +0 -131
  184. package/dist/types/schema/index.d.ts +0 -8
  185. package/dist/types/schema/interfaces.d.ts +0 -205
  186. package/dist/types/schema/policy.d.ts +0 -107
  187. package/dist/types/schema/routing.d.ts +0 -161
  188. package/dist/types/schema/system.d.ts +0 -198
  189. package/dist/types/schema/time-zones.d.ts +0 -2
  190. package/dist/types/transport/console.d.ts +0 -115
  191. package/dist/types/transport/path-check.d.ts +0 -32
  192. package/dist/types/transport/ssh.d.ts +0 -27
  193. package/src/cli/graph-format.ts +0 -91
  194. package/src/cli/new.ts +0 -234
  195. package/src/core/address-space.ts +0 -320
  196. package/src/core/cross-checks.ts +0 -369
  197. package/src/core/define.ts +0 -124
  198. package/src/core/project-files.ts +0 -35
  199. package/src/core/schema-docs.ts +0 -174
  200. package/src/core/validator.ts +0 -508
  201. package/src/helpers/bgp.ts +0 -473
  202. package/src/helpers/cables.ts +0 -74
  203. package/src/helpers/checks.ts +0 -201
  204. package/src/helpers/exchange.ts +0 -123
  205. package/src/helpers/filters.ts +0 -107
  206. package/src/helpers/index.ts +0 -25
  207. package/src/helpers/route-server.ts +0 -212
  208. package/src/helpers/upstreams.ts +0 -369
  209. package/src/index.ts +0 -13
  210. package/src/integrations/librenms.ts +0 -291
  211. package/src/integrations/netbox.ts +0 -620
  212. package/src/integrations/peeringdb.ts +0 -173
  213. package/src/integrations/prometheus.ts +0 -90
  214. package/src/schema/common.ts +0 -41
  215. package/src/schema/device.ts +0 -145
  216. package/src/schema/firewall.ts +0 -140
  217. package/src/schema/index.ts +0 -8
  218. package/src/schema/policy.ts +0 -115
  219. package/src/schema/routing.ts +0 -173
  220. package/src/schema/system.ts +0 -203
  221. package/src/studio/public/app.js +0 -186
  222. package/src/studio/public/compare.js +0 -250
  223. package/src/studio/public/dom.js +0 -138
  224. package/src/studio/public/evaluate.js +0 -687
  225. package/src/studio/public/export.js +0 -130
  226. package/src/studio/public/graph.js +0 -1541
  227. package/src/studio/public/index.html +0 -83
  228. package/src/studio/public/net.js +0 -134
  229. package/src/studio/public/tables.js +0 -1289
  230. package/src/studio/public/tools.js +0 -610
  231. package/templates/colocation/checks.ts +0 -53
  232. package/templates/colocation/circuit.config.ts +0 -9
  233. package/templates/colocation/edge-01.ts +0 -133
  234. package/templates/colocation/routing.ts +0 -13
  235. package/templates/colocation/tenancy.ts +0 -76
  236. package/templates/colocation/tenants/acme.ts +0 -19
  237. package/templates/colocation/tenants/index.ts +0 -6
  238. package/templates/colocation/tor-01.ts +0 -33
  239. package/templates/edge-router/checks.ts +0 -20
  240. package/templates/edge-router/circuit.config.ts +0 -8
  241. package/templates/edge-router/edge-01.ts +0 -110
  242. package/templates/edge-router/routing.ts +0 -13
  243. package/templates/project/AGENTS.md +0 -51
  244. package/templates/project/CLAUDE.md +0 -1
  245. package/templates/project/README.md +0 -16
  246. package/templates/project/gitignore +0 -5
  247. package/templates/project/tsconfig.json +0 -13
  248. package/templates/single-site/checks.ts +0 -26
  249. package/templates/single-site/circuit.config.ts +0 -8
  250. package/templates/single-site/router.ts +0 -86
  251. package/templates/single-site/site.ts +0 -18
  252. package/templates/single-site/switch.ts +0 -34
  253. /package/src/studio/{public → app/public}/favicon.svg +0 -0
  254. /package/src/studio/{public → app/public}/special-gothic-latin-ext.woff2 +0 -0
  255. /package/src/studio/{public → app/public}/special-gothic-latin.woff2 +0 -0
  256. /package/src/studio/{public → app/public}/special-gothic-license.txt +0 -0
@@ -1,250 +0,0 @@
1
- // @ts-check
2
- /// <reference lib="dom" />
3
- // Comparing the network now with another branch, tag or commit: which devices changed and how their config differs, what the graph gains and loses, and what validate finds newly or no longer.
4
-
5
- import { Dom } from "./dom.js";
6
- import { Evaluate } from "./evaluate.js";
7
- import { Graph } from "./graph.js";
8
- import { Tables } from "./tables.js";
9
-
10
- /** @typedef {import("../data").StudioData} StudioData */
11
- /** @typedef {import("../git").Commit} Commit */
12
- /** @typedef {{ commit: Commit, network: StudioData & { error?: undefined }, error?: undefined } | { error: string }} Comparison */
13
-
14
- export class Compare {
15
- /** Comparisons asked for, by the name asked for, kept while the page is open. @type {Map<string, Promise<Comparison>>} */
16
- static #asked = new Map();
17
-
18
- /** @type {Promise<{ names: string[], commits: Commit[] } | { error: string }> | undefined} */
19
- static #references;
20
-
21
- /**
22
- * Read JSON from the server, or what went wrong.
23
- *
24
- * @template T
25
- * @param {string} path
26
- * @returns {Promise<T | { error: string }>}
27
- */
28
- static async fetchJson(path) {
29
- try {
30
- return await (await fetch(path, { cache: "no-store" })).json();
31
- } catch (error) {
32
- return { error: error instanceof Error ? error.message : String(error) };
33
- }
34
- }
35
-
36
- /**
37
- * A commit in a line: its short name, subject, author and day.
38
- *
39
- * @param {Commit} commit
40
- */
41
- static #describeCommit(commit) {
42
- return `${commit.short}, ${commit.subject}, by ${commit.author} on ${new Date(commit.date).toLocaleDateString()}`;
43
- }
44
-
45
- /**
46
- * The picker: a branch, tag or recent commit, or any name typed.
47
- *
48
- * @param {string | undefined} current
49
- */
50
- static #picker(current) {
51
- const input = Dom.h("input", { type: "text", value: current ?? "", placeholder: "main, a tag, or a commit", spellcheck: "false" });
52
- const list = Dom.h("select", {}, Dom.h("option", { value: "" }, "Pick one…"));
53
- list.addEventListener("change", () => {
54
- if (list.value) Dom.go(`compare/${encodeURIComponent(list.value)}`);
55
- });
56
-
57
- Compare.#references ??= Compare.fetchJson("refs");
58
- void Compare.#references.then((found) => {
59
- if ("error" in found) return list.replaceChildren(Dom.h("option", { value: "" }, "No git history here"));
60
- const names = found.names.map((name) => Dom.h("option", { value: name }, name));
61
- const commits = found.commits.map((commit) => Dom.h("option", { value: commit.commit }, Compare.#describeCommit(commit)));
62
- list.append(
63
- Dom.h("optgroup", { label: "Branches and tags" }, ...names),
64
- Dom.h("optgroup", { label: "Latest commits" }, ...commits)
65
- );
66
- });
67
-
68
- const compare = Dom.h("button", { type: "submit", class: "primary" }, "Compare");
69
- compare.addEventListener("click", (event) => {
70
- event.preventDefault();
71
- if (input.value.trim()) Dom.go(`compare/${encodeURIComponent(input.value.trim())}`);
72
- });
73
-
74
- return Dom.h(
75
- "form",
76
- { class: "tool-form" },
77
- Dom.h("label", { class: "field" }, Dom.h("span", {}, "Branch, tag or commit"), list),
78
- Dom.h("label", { class: "field" }, Dom.h("span", {}, "Or a name"), input),
79
- compare
80
- );
81
- }
82
-
83
- /**
84
- * @param {HTMLElement} area
85
- * @param {string} name
86
- * @param {Commit} commit
87
- * @param {StudioData} before
88
- * @param {StudioData} after
89
- */
90
- static #showDifferences(area, name, commit, before, after) {
91
- const found = Evaluate.compareNetworks(before, after);
92
- const { devices, edges, findings } = found;
93
- const nothing =
94
- !devices.added.length && !devices.removed.length && !devices.changed.length && !edges.added.length && !edges.removed.length;
95
-
96
- const layers = [...new Set([...edges.added, ...edges.removed].map((edge) => edge.layer))];
97
- const showGraph = Dom.link("Show the changes in the graph", () => {
98
- Graph.setComparison({ name, added: edges.added, removed: edges.removed });
99
- Dom.go(`graph/${layers[0] ?? "l1"}`);
100
- });
101
-
102
- area.append(
103
- Dom.h("p", {}, "Your files now, beside ", Dom.h("strong", {}, name), `: ${Compare.#describeCommit(commit)}.`),
104
- nothing
105
- ? Tables.empty("Nothing differs: every device would run the same, and the graph is the same.")
106
- : Dom.h(
107
- "p",
108
- {},
109
- `${devices.changed.length} changed, ${devices.added.length} added and ${devices.removed.length} removed ${devices.changed.length + devices.added.length + devices.removed.length === 1 ? "device" : "devices"}; ${edges.added.length} ${edges.added.length === 1 ? "line" : "lines"} of the graph added and ${edges.removed.length} gone. `,
110
- layers.length ? showGraph : undefined
111
- )
112
- );
113
-
114
- if (devices.added.length || devices.removed.length) {
115
- area.append(
116
- Tables.heading("Devices added and removed"),
117
- Tables.of({
118
- head: ["Device", "Change"].map((label) => ({ label })),
119
- rows: [
120
- ...devices.added.map((device) => ({ cells: [Tables.deviceLink(device), "added"] })),
121
- ...devices.removed.map((device) => ({ cells: [device, "removed"] })),
122
- ],
123
- })
124
- );
125
- }
126
-
127
- if (devices.changed.length) {
128
- area.append(
129
- Tables.heading("Config that changes"),
130
- Tables.prose(
131
- "What each device would run, ",
132
- Dom.h("span", { class: "removed" }, "then -"),
133
- " and ",
134
- Dom.h("span", { class: "added" }, "now +"),
135
- "."
136
- )
137
- );
138
-
139
- for (const device of devices.changed) {
140
- area.append(
141
- Dom.h(
142
- "details",
143
- { class: "change" },
144
- Dom.h("summary", {}, device),
145
- Tables.lineDiff(before.rendered[device] ?? "", after.rendered[device] ?? "")
146
- )
147
- );
148
- }
149
- }
150
-
151
- if (edges.added.length || edges.removed.length) {
152
- const nameOf = (/** @type {string} */ id) =>
153
- [...after.graph.nodes, ...before.graph.nodes].find((node) => node.id === id)?.label ?? id;
154
-
155
- area.append(
156
- Tables.heading("The graph"),
157
- Tables.of({
158
- csv: `compare-${name}-graph`,
159
- head: ["Layer", "Change", "What", "From", "To"].map((label) => ({ label })),
160
- rows: [
161
- ...edges.added.map((edge) => ({
162
- cells: [
163
- Graph.LAYER_NAMES[edge.layer],
164
- Dom.h("span", { class: "added" }, "added"),
165
- edge.label,
166
- nameOf(edge.from),
167
- nameOf(edge.to),
168
- ],
169
- })),
170
- ...edges.removed.map((edge) => ({
171
- cells: [
172
- Graph.LAYER_NAMES[edge.layer],
173
- Dom.h("span", { class: "removed" }, "gone"),
174
- edge.label,
175
- nameOf(edge.from),
176
- nameOf(edge.to),
177
- ],
178
- })),
179
- ],
180
- })
181
- );
182
- }
183
-
184
- if (findings.added.length || findings.resolved.length) {
185
- area.append(
186
- Dom.h("h3", {}, "Found by ", Dom.h("code", {}, "circuit validate")),
187
- Tables.of({
188
- head: ["", "Device", "Message"].map((label) => ({ label })),
189
- rows: [
190
- ...findings.added.map((one) => ({
191
- cells: [Dom.h("span", { class: "removed" }, `new ${one.level}`), one.device ?? "the network", one.message],
192
- })),
193
- ...findings.resolved.map((one) => ({
194
- cells: [Dom.h("span", { class: "added" }, `no longer`), one.device ?? "the network", one.message],
195
- })),
196
- ],
197
- })
198
- );
199
- }
200
- }
201
-
202
- /**
203
- * Draw the comparison the address names, such as `["compare", "main"]`, into a container.
204
- *
205
- * @param {string[]} route
206
- * @param {HTMLElement} container
207
- * @param {StudioData} now
208
- */
209
- static show(route, container, now) {
210
- container.replaceChildren();
211
- const [, name] = route;
212
-
213
- container.append(
214
- Tables.heading("Compare", "h2"),
215
- Tables.prose(
216
- "Your files now, beside another branch, tag or commit: which devices change and how, what the graph gains and loses, and what ",
217
- Dom.h("code", {}, "circuit validate"),
218
- " finds newly or no longer. Studio reads the other commit with the Circuit installed now."
219
- ),
220
- Compare.#picker(name)
221
- );
222
-
223
- if (!name) return;
224
-
225
- const area = Dom.h("div", {}, Tables.empty(`Reading the network at ${name}…`));
226
- container.append(area);
227
-
228
- if (!Compare.#asked.has(name))
229
- Compare.#asked.set(name, /** @type {Promise<Comparison>} */ (Compare.fetchJson(`compare?ref=${encodeURIComponent(name)}`)));
230
- const shown = location.hash;
231
-
232
- void Compare.#asked.get(name)?.then((result) => {
233
- // The reader may have gone elsewhere while it was read.
234
- if (location.hash !== shown) return;
235
- area.replaceChildren();
236
-
237
- if (result.error !== undefined) {
238
- Compare.#asked.delete(name);
239
- return area.append(Tables.empty(`It could not be read: ${result.error}`));
240
- }
241
-
242
- if (result.network.error !== undefined) {
243
- Compare.#asked.delete(name);
244
- return area.append(Tables.empty(`Its config could not be read: ${result.network.error}`));
245
- }
246
-
247
- Compare.#showDifferences(area, name, result.commit, result.network, now);
248
- });
249
- }
250
- }
@@ -1,138 +0,0 @@
1
- // @ts-check
2
- /// <reference lib="dom" />
3
- // Small helpers every part of Studio's page uses: making elements, links that act, and browser storage that may be missing.
4
-
5
- /** @typedef {Node | string | undefined | false} Child */
6
-
7
- export class Dom {
8
- static #SVG = /** @type {const} */ ("http://www.w3.org/2000/svg");
9
-
10
- /**
11
- * An element with attributes and children. Text is always set as text, never parsed as HTML.
12
- *
13
- * @template {keyof HTMLElementTagNameMap} K
14
- * @param {K} tag
15
- * @param {Record<string, string>} [attributes]
16
- * @param {...Child} children
17
- * @returns {HTMLElementTagNameMap[K]}
18
- */
19
- static h(tag, attributes = {}, ...children) {
20
- const element = document.createElement(tag);
21
- for (const [name, value] of Object.entries(attributes)) element.setAttribute(name, value);
22
- for (const child of children) if (child !== undefined && child !== false) element.append(child);
23
- return element;
24
- }
25
-
26
- /**
27
- * An SVG element with attributes.
28
- *
29
- * @template {keyof SVGElementTagNameMap} K
30
- * @param {K} tag
31
- * @param {Record<string, string | number>} [attributes]
32
- * @returns {SVGElementTagNameMap[K]}
33
- */
34
- static s(tag, attributes = {}) {
35
- const element = document.createElementNS(Dom.#SVG, tag);
36
- for (const [name, value] of Object.entries(attributes)) element.setAttribute(name, String(value));
37
- return element;
38
- }
39
-
40
- /**
41
- * A link that runs an action, by mouse or keyboard.
42
- *
43
- * @param {string} text
44
- * @param {() => void} action
45
- */
46
- static link(text, action) {
47
- const anchor = Dom.h("a", { role: "button", tabindex: "0" }, text);
48
- anchor.addEventListener("click", action);
49
- anchor.addEventListener("keydown", (event) => event.key === "Enter" && action());
50
- return anchor;
51
- }
52
-
53
- /**
54
- * The element with this id, which index.html always has.
55
- *
56
- * @param {string} id
57
- */
58
- static byId(id) {
59
- const element = document.getElementById(id);
60
- if (!(element instanceof HTMLElement)) throw new Error(`index.html has no element ${id}`);
61
- return element;
62
- }
63
-
64
- /**
65
- * The SVG element with this id, which index.html always has.
66
- *
67
- * @param {string} id
68
- */
69
- static svgById(id) {
70
- const element = document.getElementById(id);
71
- if (!(element instanceof SVGElement)) throw new Error(`index.html has no SVG element ${id}`);
72
- return element;
73
- }
74
-
75
- /**
76
- * The reader's system asks for less motion: nothing moves for show, and changes happen at once.
77
- *
78
- * @readonly
79
- */
80
- static still = matchMedia("(prefers-reduced-motion: reduce)");
81
-
82
- /**
83
- * Bring a part of the page in as it changes: it fades in and rises a little.
84
- *
85
- * @param {Element} element
86
- */
87
- static enter(element) {
88
- if (Dom.still.matches) return;
89
-
90
- element.animate(
91
- [
92
- { opacity: 0, transform: "translateY(6px)" },
93
- { opacity: 1, transform: "none" },
94
- ],
95
- { duration: 240, easing: "cubic-bezier(0.16, 1, 0.3, 1)" }
96
- );
97
- }
98
-
99
- /**
100
- * Browser storage can be missing or refuse, in a private window for example. The page works without it.
101
- *
102
- * @readonly
103
- */
104
- static store = {
105
- /**
106
- * @template T
107
- * @param {string} key
108
- * @returns {T | undefined}
109
- */
110
- get(key) {
111
- try {
112
- const value = localStorage.getItem(`circuit-studio:${key}`);
113
- return value === null ? undefined : /** @type {T} */ (JSON.parse(value));
114
- } catch {
115
- return undefined;
116
- }
117
- },
118
-
119
- /**
120
- * @param {string} key
121
- * @param {unknown} value
122
- */
123
- set(key, value) {
124
- try {
125
- localStorage.setItem(`circuit-studio:${key}`, JSON.stringify(value));
126
- } catch {}
127
- },
128
- };
129
-
130
- /**
131
- * Go to a place in Studio, such as `graph/bgp` or `devices/edge-01/ports`. The place is the address's fragment, so the back button and a bookmark work.
132
- *
133
- * @param {string} route
134
- */
135
- static go(route) {
136
- location.hash = route;
137
- }
138
- }