@takodotid/circuit 0.34.3 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -15
- package/dist/chunks/index-20kcjxjg.js +174 -0
- package/dist/chunks/index-2r4bbhac.js +977 -0
- package/dist/chunks/index-n7w3tg5y.js +149 -0
- package/dist/chunks/new-w3zys5y4.js +172 -0
- package/dist/chunks/server-gzsf5me5.js +259 -0
- package/dist/cli/index.js +11345 -9024
- package/dist/helpers/index.js +594 -0
- package/dist/index.js +9 -28
- package/dist/types/core/{addresses.d.ts → address-space.d.ts} +23 -7
- package/dist/types/core/address.d.ts +44 -0
- package/dist/types/core/cabling.d.ts +41 -0
- package/dist/types/core/cross-checks.d.ts +28 -0
- package/dist/types/core/define.d.ts +25 -3
- package/dist/types/core/project-files.d.ts +12 -0
- package/dist/types/core/schema-docs.d.ts +13 -4
- package/dist/types/core/secrets.d.ts +62 -21
- package/dist/types/core/{graph.d.ts → topology.d.ts} +32 -12
- package/dist/types/core/utils.d.ts +8 -0
- package/dist/types/core/validator.d.ts +19 -0
- package/dist/types/helpers/bgp.d.ts +97 -0
- package/dist/types/helpers/checks.d.ts +32 -0
- package/dist/types/helpers/exchange.d.ts +49 -0
- package/dist/types/helpers/filters.d.ts +51 -0
- package/dist/types/helpers/index.d.ts +10 -0
- package/dist/types/helpers/route-server.d.ts +52 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/integrations/librenms.d.ts +60 -0
- package/dist/types/integrations/netbox.d.ts +89 -0
- package/dist/types/integrations/peeringdb.d.ts +24 -0
- package/dist/types/integrations/prometheus.d.ts +24 -0
- package/dist/types/integrations/registries.d.ts +8 -0
- package/dist/types/platforms/bgp-wait.d.ts +18 -0
- package/dist/types/platforms/catalog.d.ts +109 -0
- package/dist/types/platforms/derive.d.ts +17 -0
- package/dist/types/platforms/fortios/firewall.d.ts +45 -0
- package/dist/types/platforms/fortios/index.d.ts +134 -0
- package/dist/types/platforms/fortios/names.d.ts +52 -0
- package/dist/types/platforms/fortios/plan.d.ts +104 -0
- package/dist/types/platforms/fortios/render.d.ts +75 -0
- package/dist/types/platforms/fortios/routing.d.ts +56 -0
- package/dist/types/platforms/fortios/tree.d.ts +37 -0
- package/dist/types/platforms/index.d.ts +13 -0
- package/dist/types/platforms/junos/bgp.d.ts +44 -0
- package/dist/types/platforms/junos/index.d.ts +114 -0
- package/dist/types/platforms/junos/render.d.ts +154 -0
- package/dist/types/platforms/line-diff.d.ts +67 -0
- package/dist/types/platforms/platform.d.ts +120 -0
- package/dist/types/platforms/raisecom-ros/index.d.ts +71 -0
- package/dist/types/platforms/raisecom-ros/render.d.ts +41 -0
- package/dist/types/platforms/routeros/export.d.ts +59 -0
- package/dist/types/platforms/routeros/index.d.ts +96 -0
- package/dist/types/{adapters/devices → platforms}/routeros/models.d.ts +4 -0
- package/dist/types/platforms/routeros/plan.d.ts +54 -0
- package/dist/types/platforms/routeros/render.d.ts +240 -0
- package/dist/types/platforms/vrp/index.d.ts +83 -0
- package/dist/types/platforms/vrp/render.d.ts +96 -0
- package/dist/types/schema/common.d.ts +12 -6
- package/dist/types/schema/index.d.ts +1 -0
- package/dist/types/schema/interfaces.d.ts +14 -6
- package/dist/types/schema/policy.d.ts +18 -10
- package/dist/types/schema/routing.d.ts +14 -1
- package/dist/types/schema/system.d.ts +16 -6
- package/dist/types/schema/time-zones.d.ts +2 -0
- package/dist/types/transport/console.d.ts +79 -23
- package/dist/types/transport/path-check.d.ts +32 -0
- package/dist/types/transport/ssh.d.ts +17 -15
- package/package.json +18 -12
- package/src/cli/graph-format.ts +91 -0
- package/src/cli/index.ts +1074 -484
- package/src/cli/new.ts +208 -175
- package/src/cli/rename.ts +97 -91
- package/src/core/address-space.ts +320 -0
- package/src/core/address.ts +202 -0
- package/src/core/cabling.ts +154 -0
- package/src/core/cross-checks.ts +364 -0
- package/src/core/define.ts +26 -3
- package/src/core/project-files.ts +35 -0
- package/src/core/schema-docs.ts +138 -130
- package/src/core/secrets.ts +170 -76
- package/src/core/topology.ts +656 -0
- package/src/core/utils.ts +37 -0
- package/src/core/validator.ts +407 -0
- package/src/helpers/bgp.ts +301 -0
- package/src/helpers/checks.ts +178 -0
- package/src/helpers/exchange.ts +117 -0
- package/src/helpers/filters.ts +107 -0
- package/src/helpers/index.ts +12 -0
- package/src/helpers/route-server.ts +212 -0
- package/src/index.ts +6 -3
- package/src/integrations/librenms.ts +291 -0
- package/src/integrations/netbox.ts +620 -0
- package/src/integrations/peeringdb.ts +173 -0
- package/src/integrations/prometheus.ts +90 -0
- package/src/integrations/registries.ts +25 -0
- package/src/platforms/bgp-wait.ts +41 -0
- package/src/platforms/catalog.ts +69 -0
- package/src/platforms/derive.ts +117 -0
- package/src/{adapters/devices → platforms}/fortios/TRAPS.md +6 -0
- package/src/platforms/fortios/firewall.ts +377 -0
- package/src/platforms/fortios/index.ts +1060 -0
- package/src/platforms/fortios/names.ts +130 -0
- package/src/platforms/fortios/plan.ts +929 -0
- package/src/platforms/fortios/render.ts +779 -0
- package/src/platforms/fortios/routing.ts +476 -0
- package/src/platforms/fortios/tree.ts +129 -0
- package/src/platforms/index.ts +36 -0
- package/src/{adapters/devices → platforms}/junos/TRAPS.md +8 -0
- package/src/platforms/junos/bgp.ts +293 -0
- package/src/platforms/junos/index.ts +1006 -0
- package/src/platforms/junos/render.ts +967 -0
- package/src/platforms/line-diff.ts +244 -0
- package/src/platforms/platform.ts +122 -0
- package/src/platforms/raisecom-ros/TRAPS.md +40 -0
- package/src/platforms/raisecom-ros/index.ts +731 -0
- package/src/platforms/raisecom-ros/render.ts +328 -0
- package/src/{adapters/devices → platforms}/routeros/TRAPS.md +16 -2
- package/src/platforms/routeros/export.ts +342 -0
- package/src/platforms/routeros/index.ts +636 -0
- package/src/{adapters/devices → platforms}/routeros/models.ts +2 -0
- package/src/platforms/routeros/plan.ts +444 -0
- package/src/platforms/routeros/render.ts +1483 -0
- package/src/platforms/vrp/TRAPS.md +45 -0
- package/src/platforms/vrp/index.ts +652 -0
- package/src/platforms/vrp/render.ts +615 -0
- package/src/schema/common.ts +15 -6
- package/src/schema/index.ts +1 -0
- package/src/schema/interfaces.ts +15 -5
- package/src/schema/policy.ts +22 -10
- package/src/schema/routing.ts +16 -1
- package/src/schema/system.ts +16 -6
- package/src/schema/time-zones.ts +482 -0
- package/src/studio/data.ts +172 -168
- package/src/studio/git.ts +95 -90
- package/src/studio/public/app.js +161 -150
- package/src/studio/public/compare.js +219 -212
- package/src/studio/public/diff.js +77 -75
- package/src/studio/public/dom.js +118 -108
- package/src/studio/public/evaluate.js +584 -545
- package/src/studio/public/export.js +120 -118
- package/src/studio/public/graph.js +1343 -1266
- package/src/studio/public/net.js +115 -109
- package/src/studio/public/tables.js +1135 -1083
- package/src/studio/public/tools.js +561 -529
- package/src/studio/server.ts +202 -196
- package/src/transport/console.ts +363 -271
- package/src/transport/path-check.ts +91 -0
- package/src/transport/ssh.ts +98 -94
- package/templates/colocation/checks.ts +11 -6
- package/templates/colocation/edge-01.ts +9 -9
- package/templates/colocation/routing.ts +2 -2
- package/templates/colocation/tenancy.ts +3 -3
- package/templates/colocation/tor-01.ts +3 -3
- package/templates/edge-router/checks.ts +4 -4
- package/templates/edge-router/edge-01.ts +5 -5
- package/templates/edge-router/routing.ts +2 -2
- package/templates/project/AGENTS.md +5 -1
- package/templates/single-site/checks.ts +2 -2
- package/dist/chunks/index-47debt16.js +0 -956
- package/dist/chunks/index-v8zgr7wm.js +0 -128
- package/dist/chunks/new-ny89s7y7.js +0 -166
- package/dist/chunks/server-tjbb3rqc.js +0 -251
- package/dist/presets/index.js +0 -394
- package/dist/types/adapters/devices/catalog.d.ts +0 -86
- package/dist/types/adapters/devices/derive.d.ts +0 -7
- package/dist/types/adapters/devices/fortios/firewall.d.ts +0 -23
- package/dist/types/adapters/devices/fortios/index.d.ts +0 -58
- package/dist/types/adapters/devices/fortios/names.d.ts +0 -48
- package/dist/types/adapters/devices/fortios/plan.d.ts +0 -28
- package/dist/types/adapters/devices/fortios/render.d.ts +0 -38
- package/dist/types/adapters/devices/fortios/routing.d.ts +0 -32
- package/dist/types/adapters/devices/fortios/tree.d.ts +0 -33
- package/dist/types/adapters/devices/index.d.ts +0 -3
- package/dist/types/adapters/devices/junos/bgp.d.ts +0 -11
- package/dist/types/adapters/devices/junos/index.d.ts +0 -42
- package/dist/types/adapters/devices/junos/render.d.ts +0 -52
- package/dist/types/adapters/devices/lines.d.ts +0 -56
- package/dist/types/adapters/devices/raisecom-ros/index.d.ts +0 -28
- package/dist/types/adapters/devices/routeros/export.d.ts +0 -39
- package/dist/types/adapters/devices/routeros/index.d.ts +0 -30
- package/dist/types/adapters/devices/routeros/plan.d.ts +0 -4
- package/dist/types/adapters/devices/routeros/render.d.ts +0 -100
- package/dist/types/adapters/devices/types.d.ts +0 -40
- package/dist/types/adapters/devices/vrp/index.d.ts +0 -27
- package/dist/types/adapters/registries/index.d.ts +0 -3
- package/dist/types/adapters/registries/librenms.d.ts +0 -15
- package/dist/types/adapters/registries/peeringdb.d.ts +0 -16
- package/dist/types/adapters/registries/prometheus.d.ts +0 -16
- package/dist/types/core/across.d.ts +0 -4
- package/dist/types/core/addr.d.ts +0 -34
- package/dist/types/core/files.d.ts +0 -6
- package/dist/types/core/merge.d.ts +0 -4
- package/dist/types/core/time.d.ts +0 -2
- package/dist/types/core/validate.d.ts +0 -2
- package/dist/types/presets/index.d.ts +0 -183
- package/src/adapters/devices/catalog.ts +0 -41
- package/src/adapters/devices/derive.ts +0 -59
- package/src/adapters/devices/fortios/firewall.ts +0 -362
- package/src/adapters/devices/fortios/index.ts +0 -923
- package/src/adapters/devices/fortios/names.ts +0 -115
- package/src/adapters/devices/fortios/plan.ts +0 -888
- package/src/adapters/devices/fortios/render.ts +0 -768
- package/src/adapters/devices/fortios/routing.ts +0 -444
- package/src/adapters/devices/fortios/tree.ts +0 -121
- package/src/adapters/devices/index.ts +0 -9
- package/src/adapters/devices/junos/bgp.ts +0 -269
- package/src/adapters/devices/junos/index.ts +0 -862
- package/src/adapters/devices/junos/render.ts +0 -872
- package/src/adapters/devices/lines.ts +0 -212
- package/src/adapters/devices/raisecom-ros/TRAPS.md +0 -26
- package/src/adapters/devices/raisecom-ros/index.ts +0 -700
- package/src/adapters/devices/routeros/export.ts +0 -321
- package/src/adapters/devices/routeros/index.ts +0 -346
- package/src/adapters/devices/routeros/plan.ts +0 -398
- package/src/adapters/devices/routeros/render.ts +0 -1490
- package/src/adapters/devices/types.ts +0 -38
- package/src/adapters/devices/vrp/TRAPS.md +0 -26
- package/src/adapters/devices/vrp/index.ts +0 -865
- package/src/adapters/registries/index.ts +0 -19
- package/src/adapters/registries/librenms.ts +0 -92
- package/src/adapters/registries/peeringdb.ts +0 -169
- package/src/adapters/registries/prometheus.ts +0 -71
- package/src/cli/graph.ts +0 -89
- package/src/core/across.ts +0 -350
- package/src/core/addr.ts +0 -174
- package/src/core/addresses.ts +0 -316
- package/src/core/files.ts +0 -32
- package/src/core/graph.ts +0 -640
- package/src/core/merge.ts +0 -19
- package/src/core/time.ts +0 -15
- package/src/core/validate.ts +0 -348
- package/src/presets/index.ts +0 -579
- /package/dist/types/{adapters/devices → platforms}/fortios/models.d.ts +0 -0
- /package/dist/types/{adapters/devices → platforms}/junos/models.d.ts +0 -0
- /package/dist/types/{adapters/devices → platforms}/raisecom-ros/models.d.ts +0 -0
- /package/dist/types/{adapters/devices → platforms}/vrp/models.d.ts +0 -0
- /package/src/{adapters/devices → platforms}/fortios/models.ts +0 -0
- /package/src/{adapters/devices → platforms}/junos/models.ts +0 -0
- /package/src/{adapters/devices → platforms}/raisecom-ros/models.ts +0 -0
- /package/src/{adapters/devices → platforms}/vrp/models.ts +0 -0
|
@@ -2,242 +2,249 @@
|
|
|
2
2
|
/// <reference lib="dom" />
|
|
3
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
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
9
|
|
|
10
10
|
/** @typedef {import("../data").StudioData} StudioData */
|
|
11
11
|
/** @typedef {import("../git").Commit} Commit */
|
|
12
12
|
/** @typedef {{ commit: Commit, network: StudioData & { error?: undefined }, error?: undefined } | { error: string }} Comparison */
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
+
}
|
|
32
34
|
}
|
|
33
|
-
}
|
|
34
35
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The picker: a branch, tag or recent commit, or any name typed.
|
|
45
|
-
*
|
|
46
|
-
* @param {string | undefined} current
|
|
47
|
-
*/
|
|
48
|
-
function picker(current) {
|
|
49
|
-
const input = h("input", { type: "text", value: current ?? "", placeholder: "main, a tag, or a commit", spellcheck: "false" });
|
|
50
|
-
const list = h("select", {}, h("option", { value: "" }, "Pick one…"));
|
|
51
|
-
list.addEventListener("change", () => {
|
|
52
|
-
if (list.value) go(`compare/${encodeURIComponent(list.value)}`);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
references ??= fetchJson("refs");
|
|
56
|
-
void references.then((found) => {
|
|
57
|
-
if ("error" in found) return list.replaceChildren(h("option", { value: "" }, "No git history here"));
|
|
58
|
-
const names = found.names.map((name) => h("option", { value: name }, name));
|
|
59
|
-
const commits = found.commits.map((commit) => h("option", { value: commit.commit }, describeCommit(commit)));
|
|
60
|
-
list.append(h("optgroup", { label: "Branches and tags" }, ...names), h("optgroup", { label: "Latest commits" }, ...commits));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const compare = h("button", { type: "submit", class: "primary" }, "Compare");
|
|
64
|
-
compare.addEventListener("click", (event) => {
|
|
65
|
-
event.preventDefault();
|
|
66
|
-
if (input.value.trim()) go(`compare/${encodeURIComponent(input.value.trim())}`);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return h(
|
|
70
|
-
"form",
|
|
71
|
-
{ class: "tool-form" },
|
|
72
|
-
h("label", { class: "field" }, h("span", {}, "Branch, tag or commit"), list),
|
|
73
|
-
h("label", { class: "field" }, h("span", {}, "Or a name"), input),
|
|
74
|
-
compare
|
|
75
|
-
);
|
|
76
|
-
}
|
|
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
|
+
}
|
|
77
44
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
head: ["Device", "Change"].map((label) => ({ label })),
|
|
114
|
-
rows: [
|
|
115
|
-
...devices.added.map((device) => ({ cells: [deviceLink(device), "added"] })),
|
|
116
|
-
...devices.removed.map((device) => ({ cells: [device, "removed"] })),
|
|
117
|
-
],
|
|
118
|
-
})
|
|
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
|
|
119
80
|
);
|
|
120
81
|
}
|
|
121
82
|
|
|
122
|
-
|
|
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
|
+
|
|
123
102
|
area.append(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
h(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
+
)
|
|
132
112
|
);
|
|
133
113
|
|
|
134
|
-
|
|
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) {
|
|
135
128
|
area.append(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
h("
|
|
140
|
-
|
|
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
|
+
"."
|
|
141
136
|
)
|
|
142
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
|
+
}
|
|
143
149
|
}
|
|
144
|
-
}
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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;
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
+
}
|
|
177
200
|
}
|
|
178
201
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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)
|
|
193
221
|
);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
222
|
|
|
197
|
-
|
|
198
|
-
* Draw the comparison the address names, such as `["compare", "main"]`, into a container.
|
|
199
|
-
*
|
|
200
|
-
* @param {string[]} route
|
|
201
|
-
* @param {HTMLElement} container
|
|
202
|
-
* @param {StudioData} now
|
|
203
|
-
*/
|
|
204
|
-
export function showCompare(route, container, now) {
|
|
205
|
-
container.replaceChildren();
|
|
206
|
-
const [, name] = route;
|
|
207
|
-
|
|
208
|
-
container.append(
|
|
209
|
-
heading("Compare", "h2"),
|
|
210
|
-
prose(
|
|
211
|
-
"Your files now, beside another branch, tag or commit: which devices change and how, what the graph gains and loses, and what ",
|
|
212
|
-
h("code", {}, "circuit validate"),
|
|
213
|
-
" finds newly or no longer. Studio reads the other commit with the Circuit installed now."
|
|
214
|
-
),
|
|
215
|
-
picker(name)
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
if (!name) return;
|
|
219
|
-
|
|
220
|
-
const area = h("div", {}, empty(`Reading the network at ${name}…`));
|
|
221
|
-
container.append(area);
|
|
222
|
-
|
|
223
|
-
if (!asked.has(name)) asked.set(name, /** @type {Promise<Comparison>} */ (fetchJson(`compare?ref=${encodeURIComponent(name)}`)));
|
|
224
|
-
const shown = location.hash;
|
|
225
|
-
|
|
226
|
-
void asked.get(name)?.then((result) => {
|
|
227
|
-
// The reader may have gone elsewhere while it was read.
|
|
228
|
-
if (location.hash !== shown) return;
|
|
229
|
-
area.replaceChildren();
|
|
230
|
-
|
|
231
|
-
if (result.error !== undefined) {
|
|
232
|
-
asked.delete(name);
|
|
233
|
-
return area.append(empty(`It could not be read: ${result.error}`));
|
|
234
|
-
}
|
|
223
|
+
if (!name) return;
|
|
235
224
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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;
|
|
240
231
|
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
}
|
|
243
250
|
}
|
|
@@ -3,95 +3,97 @@
|
|
|
3
3
|
|
|
4
4
|
/** @typedef {{ kind: "same" | "removed" | "added", text: string }} DiffLine */
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The lines of `before` and `after` in order, each marked as kept, removed or added. Undefined when they differ in more than a few thousand lines.
|
|
11
|
-
*
|
|
12
|
-
* @param {readonly string[]} before
|
|
13
|
-
* @param {readonly string[]} after
|
|
14
|
-
* @returns {DiffLine[] | undefined}
|
|
15
|
-
*/
|
|
16
|
-
export function diffLines(before, after) {
|
|
17
|
-
const n = before.length;
|
|
18
|
-
const m = after.length;
|
|
19
|
-
const limit = Math.min(n + m, MOST_DIFFERENCES);
|
|
20
|
-
const offset = limit + 1;
|
|
21
|
-
|
|
22
|
-
// How far along `before` each diagonal reaches, and a copy of that for each number of differences, to walk back through.
|
|
23
|
-
const reach = new Int32Array(2 * offset + 1);
|
|
24
|
-
/** @type {Int32Array[]} */
|
|
25
|
-
const trace = [];
|
|
6
|
+
export class Diff {
|
|
7
|
+
/** Past this many differences, two texts are too far apart to show line by line. */
|
|
8
|
+
static #MOST_DIFFERENCES = 4000;
|
|
26
9
|
|
|
27
10
|
/**
|
|
28
|
-
*
|
|
11
|
+
* The lines of `before` and `after` in order, each marked as kept, removed or added. Undefined when they differ in more than a few thousand lines.
|
|
29
12
|
*
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
13
|
+
* @param {readonly string[]} before
|
|
14
|
+
* @param {readonly string[]} after
|
|
15
|
+
* @returns {DiffLine[] | undefined}
|
|
32
16
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
17
|
+
static lines(before, after) {
|
|
18
|
+
const n = before.length;
|
|
19
|
+
const m = after.length;
|
|
20
|
+
const limit = Math.min(n + m, Diff.#MOST_DIFFERENCES);
|
|
21
|
+
const offset = limit + 1;
|
|
22
|
+
|
|
23
|
+
// How far along `before` each diagonal reaches, and a copy of that for each number of differences, to walk back through.
|
|
24
|
+
const reach = new Int32Array(2 * offset + 1);
|
|
25
|
+
/** @type {Int32Array[]} */
|
|
26
|
+
const trace = [];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Where diagonal k reached after d differences.
|
|
30
|
+
*
|
|
31
|
+
* @param {number} d
|
|
32
|
+
* @param {number} k
|
|
33
|
+
*/
|
|
34
|
+
const reached = (d, k) => /** @type {Int32Array} */ (trace[d])[k + d] ?? 0;
|
|
35
|
+
|
|
36
|
+
for (let d = 0; d <= limit; d++) {
|
|
37
|
+
for (let k = -d; k <= d; k += 2) {
|
|
38
|
+
const down = k === -d || (k !== d && (reach[k - 1 + offset] ?? 0) < (reach[k + 1 + offset] ?? 0));
|
|
39
|
+
let x = down ? (reach[k + 1 + offset] ?? 0) : (reach[k - 1 + offset] ?? 0) + 1;
|
|
40
|
+
let y = x - k;
|
|
41
|
+
|
|
42
|
+
while (x < n && y < m && before[x] === after[y]) {
|
|
43
|
+
x++;
|
|
44
|
+
y++;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
reach[k + offset] = x;
|
|
48
|
+
|
|
49
|
+
if (x >= n && y >= m) {
|
|
50
|
+
trace.push(reach.slice(offset - d, offset + d + 1));
|
|
51
|
+
return Diff.#walkBack(before, after, trace, reached);
|
|
52
|
+
}
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
reach
|
|
47
|
-
|
|
48
|
-
if (x >= n && y >= m) {
|
|
49
|
-
trace.push(reach.slice(offset - d, offset + d + 1));
|
|
50
|
-
return walkBack(before, after, trace, reached);
|
|
51
|
-
}
|
|
55
|
+
trace.push(reach.slice(offset - d, offset + d + 1));
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
return undefined;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
/**
|
|
62
|
+
* The edits, found by walking back from the end through what each step reached.
|
|
63
|
+
*
|
|
64
|
+
* @param {readonly string[]} before
|
|
65
|
+
* @param {readonly string[]} after
|
|
66
|
+
* @param {Int32Array[]} trace
|
|
67
|
+
* @param {(d: number, k: number) => number} reached
|
|
68
|
+
* @returns {DiffLine[]}
|
|
69
|
+
*/
|
|
70
|
+
static #walkBack(before, after, trace, reached) {
|
|
71
|
+
/** @type {DiffLine[]} */
|
|
72
|
+
const lines = [];
|
|
73
|
+
let x = before.length;
|
|
74
|
+
let y = after.length;
|
|
75
|
+
|
|
76
|
+
for (let d = trace.length - 1; d > 0; d--) {
|
|
77
|
+
const k = x - y;
|
|
78
|
+
const down = k === -d || (k !== d && reached(d - 1, k - 1) < reached(d - 1, k + 1));
|
|
79
|
+
const previousK = down ? k + 1 : k - 1;
|
|
80
|
+
const previousX = reached(d - 1, previousK);
|
|
81
|
+
const previousY = previousX - previousK;
|
|
82
|
+
|
|
83
|
+
while (x > previousX && y > previousY) {
|
|
84
|
+
lines.push({ kind: "same", text: before[--x] ?? "" });
|
|
85
|
+
y--;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (down) lines.push({ kind: "added", text: after[--y] ?? "" });
|
|
89
|
+
else lines.push({ kind: "removed", text: before[--x] ?? "" });
|
|
90
|
+
}
|
|
59
91
|
|
|
60
|
-
|
|
61
|
-
* The edits, found by walking back from the end through what each step reached.
|
|
62
|
-
*
|
|
63
|
-
* @param {readonly string[]} before
|
|
64
|
-
* @param {readonly string[]} after
|
|
65
|
-
* @param {Int32Array[]} trace
|
|
66
|
-
* @param {(d: number, k: number) => number} reached
|
|
67
|
-
* @returns {DiffLine[]}
|
|
68
|
-
*/
|
|
69
|
-
function walkBack(before, after, trace, reached) {
|
|
70
|
-
/** @type {DiffLine[]} */
|
|
71
|
-
const lines = [];
|
|
72
|
-
let x = before.length;
|
|
73
|
-
let y = after.length;
|
|
74
|
-
|
|
75
|
-
for (let d = trace.length - 1; d > 0; d--) {
|
|
76
|
-
const k = x - y;
|
|
77
|
-
const down = k === -d || (k !== d && reached(d - 1, k - 1) < reached(d - 1, k + 1));
|
|
78
|
-
const previousK = down ? k + 1 : k - 1;
|
|
79
|
-
const previousX = reached(d - 1, previousK);
|
|
80
|
-
const previousY = previousX - previousK;
|
|
81
|
-
|
|
82
|
-
while (x > previousX && y > previousY) {
|
|
92
|
+
while (x > 0 && y > 0) {
|
|
83
93
|
lines.push({ kind: "same", text: before[--x] ?? "" });
|
|
84
94
|
y--;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
else lines.push({ kind: "removed", text: before[--x] ?? "" });
|
|
97
|
+
return lines.reverse();
|
|
89
98
|
}
|
|
90
|
-
|
|
91
|
-
while (x > 0 && y > 0) {
|
|
92
|
-
lines.push({ kind: "same", text: before[--x] ?? "" });
|
|
93
|
-
y--;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return lines.reverse();
|
|
97
99
|
}
|