@takodotid/circuit 0.20.0 → 0.21.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/dist/cli/index.js CHANGED
@@ -1,33 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ portsOf,
4
+ spell,
3
5
  LAYERS,
4
- addressesOf,
5
6
  deviceId,
6
- freeOfLength,
7
- graphOf,
8
- holdersOf,
9
7
  keyOf,
8
+ holdersOf,
9
+ graphOf,
10
10
  only,
11
- portsOf,
12
- spell,
11
+ addressesOf,
12
+ freeOfLength,
13
13
  studioData
14
- } from "../chunks/index-jtzfw679.js";
14
+ } from "../chunks/index-n4zpd25n.js";
15
15
  import {
16
- contains,
17
- familyOf,
18
- host,
19
- isAddress,
20
- isCidr,
21
- isGlobal,
22
- netmask,
23
- network,
16
+ familyOf2,
24
17
  split,
25
- wildcard
26
- } from "../chunks/index-xm9nma2t.js";
27
- import {
28
- __require,
29
- __toESM
30
- } from "../chunks/index-fq57hya4.js";
18
+ isAddress2,
19
+ isCidr2,
20
+ network,
21
+ contains2,
22
+ netmask,
23
+ wildcard,
24
+ host2,
25
+ isGlobal2
26
+ } from "../chunks/index-v8zgr7wm.js";
31
27
 
32
28
  // src/cli/index.ts
33
29
  import { createPrivateKey, createPublicKey } from "node:crypto";
@@ -98,10 +94,10 @@ function valueOf(reference) {
98
94
  const fullPath = join(root, path);
99
95
  if (!existsSync(fullPath))
100
96
  throw new Error(`secret file ${path} does not exist`);
101
- const value2 = readFileSync(fullPath, "utf8").trimEnd();
102
- if (!value2)
97
+ const value = readFileSync(fullPath, "utf8").trimEnd();
98
+ if (!value)
103
99
  throw new Error(`secret file ${path} is empty`);
104
- return value2;
100
+ return value;
105
101
  }
106
102
  const value = process.env[reference] || dotenv()[reference];
107
103
  if (!value)
@@ -131,9 +127,9 @@ function offset(zone) {
131
127
  // src/transport/ssh.ts
132
128
  import { Client } from "ssh2";
133
129
  function connect(login) {
134
- return new Promise((resolve2, reject) => {
130
+ return new Promise((resolve, reject) => {
135
131
  const client = new Client;
136
- client.on("ready", () => resolve2(client)).on("error", reject).connect({
132
+ client.on("ready", () => resolve(client)).on("error", reject).connect({
137
133
  host: login.host,
138
134
  port: login.port ?? 22,
139
135
  username: login.user,
@@ -144,29 +140,29 @@ function connect(login) {
144
140
  });
145
141
  }
146
142
  function exec(client, command, timeout = 120000) {
147
- return new Promise((resolve2, reject) => {
143
+ return new Promise((resolve, reject) => {
148
144
  client.exec(command, (error, stream) => {
149
145
  if (error)
150
146
  return reject(error);
151
147
  let output = "";
152
148
  const append = (chunk) => output += chunk.toString("utf8");
153
- const timer = setTimeout(() => resolve2(output.trim()), timeout);
149
+ const timer = setTimeout(() => resolve(output.trim()), timeout);
154
150
  stream.on("data", append);
155
151
  stream.stderr.on("data", append);
156
152
  stream.on("close", () => {
157
153
  clearTimeout(timer);
158
- resolve2(output.trim());
154
+ resolve(output.trim());
159
155
  });
160
156
  });
161
157
  });
162
158
  }
163
159
  function shell(client, columns = 200, rows = 1000) {
164
- return new Promise((resolve2, reject) => {
165
- client.shell({ cols: columns, rows }, (error, channel) => error ? reject(error) : resolve2(channel));
160
+ return new Promise((resolve, reject) => {
161
+ client.shell({ cols: columns, rows }, (error, channel) => error ? reject(error) : resolve(channel));
166
162
  });
167
163
  }
168
164
  function readUntil(channel, done, timeout, settle = 400) {
169
- return new Promise((resolve2) => {
165
+ return new Promise((resolve) => {
170
166
  let output = "";
171
167
  let finished = false;
172
168
  const finish = () => {
@@ -174,7 +170,7 @@ function readUntil(channel, done, timeout, settle = 400) {
174
170
  return;
175
171
  finished = true;
176
172
  channel.removeListener("data", onData);
177
- resolve2(output);
173
+ resolve(output);
178
174
  };
179
175
  const onData = (chunk) => {
180
176
  output += chunk.toString("utf8");
@@ -188,12 +184,12 @@ function readUntil(channel, done, timeout, settle = 400) {
188
184
  function send(channel, line, timeout = 20000, settle = 1500) {
189
185
  channel.write(line + `
190
186
  `);
191
- return new Promise((resolve2) => {
187
+ return new Promise((resolve) => {
192
188
  let output = "";
193
189
  let timer;
194
190
  const finish = () => {
195
191
  channel.removeListener("data", onData);
196
- resolve2(output);
192
+ resolve(output);
197
193
  };
198
194
  const onData = (chunk) => {
199
195
  output += chunk.toString("utf8");
@@ -205,7 +201,7 @@ function send(channel, line, timeout = 20000, settle = 1500) {
205
201
  });
206
202
  }
207
203
  function upload(client, path, content) {
208
- return new Promise((resolve2, reject) => {
204
+ return new Promise((resolve, reject) => {
209
205
  client.sftp((error, sftp) => {
210
206
  if (error)
211
207
  return reject(error);
@@ -214,7 +210,7 @@ function upload(client, path, content) {
214
210
  if (writeError)
215
211
  reject(writeError);
216
212
  else
217
- resolve2();
213
+ resolve();
218
214
  });
219
215
  });
220
216
  });
@@ -235,7 +231,7 @@ function lagIds(device) {
235
231
  function ipv4Networks(device, iface) {
236
232
  const settings = device.interfaces?.[iface] ?? device.ports?.[iface];
237
233
  const addresses = (settings?.addresses ?? []).map((entry) => typeof entry === "string" ? entry : entry.address);
238
- return addresses.filter((address) => familyOf(address) === "ipv4").map((address) => network(address));
234
+ return addresses.filter((address) => familyOf2(address) === "ipv4").map((address) => network(address));
239
235
  }
240
236
  var SWITCH_SERVICES = ["ssh", "telnet", "snmp"];
241
237
  function switchUnsupported(device) {
@@ -312,12 +308,12 @@ function head(line) {
312
308
  }
313
309
  function compare(desired, current, dialect) {
314
310
  const canon = dialect.canon ?? ((line) => line);
315
- const keyOf2 = (entry) => [...entry.path, canon(entry.line)].join("\x00");
311
+ const keyOf = (entry) => [...entry.path, canon(entry.line)].join("\x00");
316
312
  const pathKey = (path) => path.join("\x00");
317
313
  const wanted = entries(desired, dialect);
318
314
  const existing = entries(current, dialect);
319
- const wantedKeys = new Set(wanted.map(keyOf2));
320
- const existingKeys = new Set(existing.map(keyOf2));
315
+ const wantedKeys = new Set(wanted.map(keyOf));
316
+ const existingKeys = new Set(existing.map(keyOf));
321
317
  const wantedBlocks = new Set(wanted.map((entry) => pathKey([...entry.path, entry.line])));
322
318
  const groups = new Map;
323
319
  const groupFor = (path) => {
@@ -327,7 +323,7 @@ function compare(desired, current, dialect) {
327
323
  return groups.get(key);
328
324
  };
329
325
  for (const entry of wanted) {
330
- if (existingKeys.has(keyOf2(entry)))
326
+ if (existingKeys.has(keyOf(entry)))
331
327
  continue;
332
328
  const ownBlock = pathKey([...entry.path, entry.line]);
333
329
  const opensBlock = wanted.some((other) => {
@@ -339,7 +335,7 @@ function compare(desired, current, dialect) {
339
335
  groupFor(entry.path).add.push(entry.line);
340
336
  }
341
337
  for (const entry of [...existing].reverse()) {
342
- if (wantedKeys.has(keyOf2(entry)))
338
+ if (wantedKeys.has(keyOf(entry)))
343
339
  continue;
344
340
  const insideRemovedBlock = entry.path.some((_, depth) => {
345
341
  const enclosing = pathKey(entry.path.slice(0, depth + 1));
@@ -349,7 +345,7 @@ function compare(desired, current, dialect) {
349
345
  continue;
350
346
  const replaced = wanted.some((other) => {
351
347
  const sameBlock = pathKey(other.path) === pathKey(entry.path);
352
- const isNew = !existingKeys.has(keyOf2(other));
348
+ const isNew = !existingKeys.has(keyOf(other));
353
349
  return sameBlock && isNew && head(other.line) === head(entry.line);
354
350
  });
355
351
  if (!replaced)
@@ -504,7 +500,7 @@ function render(device) {
504
500
  }
505
501
  if (allow.length) {
506
502
  lines.push(`access-list ${MANAGEMENT_ACL}`, " description management");
507
- allow.forEach((cidr, index) => lines.push(` rule ${(index + 1) * 10} permit ${host(cidr)} ${netmask(split(cidr).len)}`));
503
+ allow.forEach((cidr, index) => lines.push(` rule ${(index + 1) * 10} permit ${host2(cidr)} ${netmask(split(cidr).len)}`));
508
504
  lines.push(" rule 100 deny any", "!");
509
505
  }
510
506
  for (const [name, user] of Object.entries(device.users ?? {}))
@@ -529,8 +525,8 @@ function render(device) {
529
525
  lines.push(`interface vlan ${vlanId(iface.vlan)}`);
530
526
  for (const entry of iface.addresses ?? []) {
531
527
  const address = typeof entry === "string" ? entry : entry.address;
532
- if (familyOf(address) === "ipv4")
533
- lines.push(`ip address ${host(address)} ${netmask(split(address).len)}`);
528
+ if (familyOf2(address) === "ipv4")
529
+ lines.push(`ip address ${host2(address)} ${netmask(split(address).len)}`);
534
530
  }
535
531
  const ospfSettings = Object.values(ospf?.areas ?? {}).find((area) => area.interfaces[name])?.interfaces[name];
536
532
  if (ospfSettings?.network === "point-to-point")
@@ -549,7 +545,7 @@ function render(device) {
549
545
  for (const [areaId, area] of Object.entries(ospf.areas)) {
550
546
  for (const iface of Object.keys(area.interfaces)) {
551
547
  for (const prefix of ipv4Networks(device, iface)) {
552
- lines.push(`network ${host(prefix)} ${wildcard(split(prefix).len)} area ${areaId}`);
548
+ lines.push(`network ${host2(prefix)} ${wildcard(split(prefix).len)} area ${areaId}`);
553
549
  }
554
550
  }
555
551
  }
@@ -584,7 +580,7 @@ function render(device) {
584
580
  lines.push(`snmp-server access-list ${MANAGEMENT_ACL}`);
585
581
  for (const route of device.routing?.static ?? []) {
586
582
  const { addr, len } = split(route.prefix);
587
- if (familyOf(addr) === "ipv4" && route.via)
583
+ if (familyOf2(addr) === "ipv4" && route.via)
588
584
  lines.push(`ip route ${addr} ${netmask(len)} ${route.via}`);
589
585
  }
590
586
  if (management.telnet) {
@@ -726,11 +722,11 @@ function unsupported(device) {
726
722
  }
727
723
  for (const entry of iface.addresses ?? []) {
728
724
  const address = typeof entry === "string" ? entry : entry.address;
729
- if (familyOf(address) === "ipv6")
725
+ if (familyOf2(address) === "ipv6")
730
726
  found.push(`interfaces.${name}.addresses ${address}, only IPv4 here`);
731
727
  }
732
728
  for (const group of iface.vrrp ?? []) {
733
- if (familyOf(group.address) === "ipv6")
729
+ if (familyOf2(group.address) === "ipv6")
734
730
  found.push(`interfaces.${name}.vrrp for IPv6`);
735
731
  }
736
732
  }
@@ -751,7 +747,7 @@ function unsupported(device) {
751
747
  }
752
748
  }
753
749
  for (const route of routing?.static ?? []) {
754
- if (!route.via || route.vrf || familyOf(route.prefix) === "ipv6") {
750
+ if (!route.via || route.vrf || familyOf2(route.prefix) === "ipv6") {
755
751
  found.push(`routing.static ${route.prefix}, only an IPv4 next hop in the default table`);
756
752
  }
757
753
  if (route.description || route.distance)
@@ -995,9 +991,9 @@ function parse(text) {
995
991
  menu = line;
996
992
  continue;
997
993
  }
998
- const marker2 = /^# generated (\S+)$/.exec(line);
999
- if (marker2)
1000
- generated.add(marker2[1]);
994
+ const marker = /^# generated (\S+)$/.exec(line);
995
+ if (marker)
996
+ generated.add(marker[1]);
1001
997
  if (menu && /^(add|set)( |$)/.test(line))
1002
998
  commands.push(parseLine(menu, line));
1003
999
  }
@@ -1087,12 +1083,12 @@ function normalValue(command, property, value) {
1087
1083
  if (property === "tagged" || property === "untagged" || property === "slaves")
1088
1084
  return value.split(",").sort().join(",");
1089
1085
  const onInterface = property === "address" && (command.menu === "/ip address" || command.menu === "/ipv6 address");
1090
- const isAddress2 = /(^|-)address$/.test(property) && !property.endsWith("mac-address");
1086
+ const isAddress = /(^|-)address$/.test(property) && !property.endsWith("mac-address");
1091
1087
  if (onInterface && !value.includes("/"))
1092
1088
  return `${value}/${value.includes(":") ? 64 : 32}`;
1093
- if (!onInterface && isAddress2 && /^[\d.]+\/32$/.test(value))
1089
+ if (!onInterface && isAddress && /^[\d.]+\/32$/.test(value))
1094
1090
  return value.slice(0, -3);
1095
- if (!onInterface && isAddress2 && value.includes(":") && !value.includes("/") && !value.includes(","))
1091
+ if (!onInterface && isAddress && value.includes(":") && !value.includes("/") && !value.includes(","))
1096
1092
  return `${value}/128`;
1097
1093
  return value;
1098
1094
  }
@@ -1421,13 +1417,13 @@ function planOrdered(menu, wanted, existing) {
1421
1417
  const inKeptChain = `[:typeof [:find $keep [${menu} get $id chain]]] != "nil"`;
1422
1418
  removeOld = `:local keep {${keep}}; :foreach id in=$${OLD_RULES} do={ :if (${inKeptChain}) do={ ${menu} remove $id } }`;
1423
1419
  }
1424
- const send2 = [
1420
+ const send = [
1425
1421
  `:global ${OLD_RULES} [${menu} find${where}]`,
1426
1422
  ...wanted.map((item) => line(menu, "add", "", item.flags, item.attrs)),
1427
1423
  `:global ${OLD_RULES}; ${removeOld}; /system script environment remove [find name="${OLD_RULES}"]`
1428
1424
  ];
1429
1425
  const show = [...existing.map((item) => `- ${canonical(item)}`), ...wanted.map((item) => `+ ${canonical(item)}`)];
1430
- result.first.push({ title: `${menu}, in the order of the config`, show, send: send2 });
1426
+ result.first.push({ title: `${menu}, in the order of the config`, show, send });
1431
1427
  if (hasChains) {
1432
1428
  for (const chain of droppedChains) {
1433
1429
  const selector = `[ find chain=${quote(chain)}${DYNAMIC.has(menu) ? " and !dynamic" : ""} ]`;
@@ -1483,22 +1479,22 @@ class Output {
1483
1479
  `;
1484
1480
  }
1485
1481
  }
1486
- function quote2(value2) {
1487
- const plain = /^[\w.:/@-]+$/.test(value2);
1482
+ function quote2(value) {
1483
+ const plain = /^[\w.:/@-]+$/.test(value);
1488
1484
  if (plain)
1489
- return value2;
1490
- const escaped = value2.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\$/g, "\\$");
1485
+ return value;
1486
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\$/g, "\\$");
1491
1487
  return `"${escaped}"`;
1492
1488
  }
1493
1489
  function command(verb, properties) {
1494
- const words2 = [verb];
1495
- for (const [name, value2] of Object.entries(properties)) {
1496
- if (value2 === undefined || value2 === false)
1490
+ const words = [verb];
1491
+ for (const [name, value] of Object.entries(properties)) {
1492
+ if (value === undefined || value === false)
1497
1493
  continue;
1498
- const written = typeof value2 === "number" ? String(value2) : quote2(value2);
1499
- words2.push(`${name}=${written}`);
1494
+ const written = typeof value === "number" ? String(value) : quote2(value);
1495
+ words.push(`${name}=${written}`);
1500
1496
  }
1501
- return words2.join(" ");
1497
+ return words.join(" ");
1502
1498
  }
1503
1499
  var yesNo = (on) => on ? "yes" : "no";
1504
1500
  var familySuffix = (family) => family === "ipv4" ? "V4" : "V6";
@@ -1761,7 +1757,7 @@ function splitEndpoint(endpoint) {
1761
1757
  function vrrp(out, ctx) {
1762
1758
  for (const { name, settings } of ctx.routed()) {
1763
1759
  for (const group of settings.vrrp ?? []) {
1764
- const ipv6 = familyOf(group.address) === "ipv6";
1760
+ const ipv6 = familyOf2(group.address) === "ipv6";
1765
1761
  out.add("/interface vrrp", command("add", {
1766
1762
  interface: name,
1767
1763
  interval: group.interval ? duration(group.interval) : undefined,
@@ -1887,7 +1883,7 @@ function addresses(out, ctx) {
1887
1883
  for (const entry of settings.addresses ?? []) {
1888
1884
  const address = typeof entry === "string" ? entry : entry.address;
1889
1885
  const peer = typeof entry === "string" ? undefined : entry.peer;
1890
- if (familyOf(address) === "ipv4") {
1886
+ if (familyOf2(address) === "ipv4") {
1891
1887
  out.add("/ip address", command("add", { address, comment: settings.description, interface: name, network: peer }));
1892
1888
  } else {
1893
1889
  out.add("/ipv6 address", command("add", { address, advertise: yesNo(settings.ipv6_ra), comment: settings.description, interface: name }));
@@ -1896,7 +1892,7 @@ function addresses(out, ctx) {
1896
1892
  for (const group of settings.vrrp ?? []) {
1897
1893
  const vrrpInterface = vrrpName(name, group.id);
1898
1894
  const comment = `VRRP ${group.id} on ${name}`;
1899
- if (familyOf(group.address) === "ipv4") {
1895
+ if (familyOf2(group.address) === "ipv4") {
1900
1896
  out.add("/ip address", command("add", { address: `${group.address}/32`, comment, interface: vrrpInterface }));
1901
1897
  } else {
1902
1898
  out.add("/ipv6 address", command("add", {
@@ -1938,30 +1934,30 @@ function dhcp(out, ctx) {
1938
1934
  }
1939
1935
  }
1940
1936
  function dns(out, ctx) {
1941
- const dns2 = ctx.device.system?.dns;
1942
- out.add("/ip dns", command("set", { "allow-remote-requests": yesNo(dns2?.serve), servers: dns2?.servers.join(",") ?? "" }));
1937
+ const dns = ctx.device.system?.dns;
1938
+ out.add("/ip dns", command("set", { "allow-remote-requests": yesNo(dns?.serve), servers: dns?.servers.join(",") ?? "" }));
1943
1939
  }
1944
1940
  function addressLists(out, ctx) {
1945
1941
  const { device } = ctx;
1946
- const entries2 = [];
1942
+ const entries = [];
1947
1943
  for (const [list, members] of Object.entries(device.firewall?.address_sets ?? {})) {
1948
1944
  for (const address of members)
1949
- entries2.push({ list, address });
1945
+ entries.push({ list, address });
1950
1946
  }
1951
1947
  for (const [list, set] of Object.entries(device.prefix_sets ?? {})) {
1952
1948
  if ("prefixes" in set)
1953
1949
  for (const address of set.prefixes)
1954
- entries2.push({ list, address });
1950
+ entries.push({ list, address });
1955
1951
  else
1956
1952
  out.add("/ip firewall address-list", `# generated ${list}`);
1957
1953
  }
1958
1954
  for (const prefix of device.routing?.bgp?.networks ?? []) {
1959
- entries2.push({ list: NETWORKS[familyOf(host(prefix))], address: prefix });
1955
+ entries.push({ list: NETWORKS[familyOf2(host2(prefix))], address: prefix });
1960
1956
  }
1961
1957
  for (const family of ["ipv4", "ipv6"]) {
1962
1958
  const menu = family === "ipv4" ? "/ip firewall address-list" : "/ipv6 firewall address-list";
1963
- for (const { list, address } of entries2) {
1964
- if (familyOf(host(address)) !== family)
1959
+ for (const { list, address } of entries) {
1960
+ if (familyOf2(host2(address)) !== family)
1965
1961
  continue;
1966
1962
  out.add(menu, command("add", { address, list }));
1967
1963
  }
@@ -1981,7 +1977,7 @@ function renderFirewall(out, ctx) {
1981
1977
  function untracked(out, ctx, family) {
1982
1978
  const menu = family === "ipv4" ? "/ip firewall raw" : "/ipv6 firewall raw";
1983
1979
  for (const prefix of ctx.device.firewall?.untracked ?? []) {
1984
- if (familyOf(prefix) !== family)
1980
+ if (familyOf2(prefix) !== family)
1985
1981
  continue;
1986
1982
  for (const [direction, property] of [
1987
1983
  ["to", "dst-address"],
@@ -1998,8 +1994,8 @@ function families(ctx, match) {
1998
1994
  if (match?.family)
1999
1995
  candidates = [match.family];
2000
1996
  else if (address)
2001
- candidates = [familyOf(address)];
2002
- const setHas = (set, family) => !set || (sets[set] ?? []).some((member) => familyOf(member) === family);
1997
+ candidates = [familyOf2(address)];
1998
+ const setHas = (set, family) => !set || (sets[set] ?? []).some((member) => familyOf2(member) === family);
2003
1999
  return candidates.filter((family) => setHas(match?.src_set, family) && setHas(match?.dst_set, family));
2004
2000
  }
2005
2001
  function matchProperties(ctx, match, family) {
@@ -2050,7 +2046,7 @@ function nat(out, ctx, family) {
2050
2046
  for (const rule of rules ?? []) {
2051
2047
  if (!families(ctx, rule.match).includes(family))
2052
2048
  continue;
2053
- if (rule.action !== "masquerade" && familyOf(rule.to) !== family)
2049
+ if (rule.action !== "masquerade" && familyOf2(rule.to) !== family)
2054
2050
  continue;
2055
2051
  const action = rule.action === "masquerade" ? {
2056
2052
  action: "masquerade"
@@ -2072,7 +2068,7 @@ function switchAcls(out, ctx) {
2072
2068
  continue;
2073
2069
  for (const rule of ctx.device.acls?.[settings.acl] ?? []) {
2074
2070
  const match = rule.match ?? {};
2075
- const ipv6 = match.family === "ipv6" || [match.src, match.dst].some((address2) => address2 && familyOf(address2) === "ipv6");
2071
+ const ipv6 = match.family === "ipv6" || [match.src, match.dst].some((address) => address && familyOf2(address) === "ipv6");
2076
2072
  const address = (prefix) => prefix && !ipv6 ? dottedMask(prefix) : prefix;
2077
2073
  for (const srcPort of each(match.src_port)) {
2078
2074
  for (const dstPort of each(match.dst_port)) {
@@ -2117,7 +2113,7 @@ function renderRouting(out, ctx) {
2117
2113
  }
2118
2114
  function staticRoutes(out, ctx) {
2119
2115
  for (const route of ctx.device.routing?.static ?? []) {
2120
- const menu = familyOf(host(route.prefix)) === "ipv4" ? "/ip route" : "/ipv6 route";
2116
+ const menu = familyOf2(host2(route.prefix)) === "ipv4" ? "/ip route" : "/ipv6 route";
2121
2117
  const verb = route.blackhole ? "add blackhole" : "add";
2122
2118
  const gateway = route.via ?? (route.interface ? ctx.name(route.interface) : undefined);
2123
2119
  out.add(menu, command(verb, {
@@ -2148,21 +2144,21 @@ function settingsOf(ctx, neighbor) {
2148
2144
  return { ...group, ...neighbor };
2149
2145
  }
2150
2146
  function familiesOf(ctx, neighbor) {
2151
- return settingsOf(ctx, neighbor).families ?? [familyOf(neighbor.address)];
2147
+ return settingsOf(ctx, neighbor).families ?? [familyOf2(neighbor.address)];
2152
2148
  }
2153
2149
  var chainName = (policy, family) => `${policy}-${familySuffix(family)}`;
2154
2150
  function appliesTo(ctx, rule, family) {
2155
2151
  const match = rule.match;
2156
2152
  if (match?.family && match.family !== family)
2157
2153
  return false;
2158
- if (match?.prefix && familyOf(host(match.prefix)) !== family)
2154
+ if (match?.prefix && familyOf2(host2(match.prefix)) !== family)
2159
2155
  return false;
2160
2156
  if (match?.prefix_set) {
2161
2157
  const set = ctx.device.prefix_sets?.[match.prefix_set];
2162
2158
  if (!set)
2163
2159
  return false;
2164
2160
  const firstPrefix = "source" in set ? undefined : set.prefixes[0];
2165
- const setFamily = "source" in set ? set.source.family : firstPrefix && familyOf(host(firstPrefix));
2161
+ const setFamily = "source" in set ? set.source.family : firstPrefix && familyOf2(host2(firstPrefix));
2166
2162
  if (setFamily !== family)
2167
2163
  return false;
2168
2164
  }
@@ -2193,35 +2189,35 @@ function conditions(rule) {
2193
2189
  }
2194
2190
  function actions(rule, family) {
2195
2191
  const set = rule.set ?? {};
2196
- const steps2 = [];
2192
+ const steps = [];
2197
2193
  if (set.local_pref !== undefined)
2198
- steps2.push(`set bgp-local-pref ${set.local_pref}`);
2194
+ steps.push(`set bgp-local-pref ${set.local_pref}`);
2199
2195
  if (set.med !== undefined)
2200
- steps2.push(`set bgp-med ${set.med}`);
2196
+ steps.push(`set bgp-med ${set.med}`);
2201
2197
  if (set.prepend !== undefined)
2202
- steps2.push(`set bgp-path-prepend ${set.prepend}`);
2198
+ steps.push(`set bgp-path-prepend ${set.prepend}`);
2203
2199
  for (const pattern of set.remove_communities ?? [])
2204
- steps2.push(`delete bgp-communities regexp "${communityPattern(pattern)}"`);
2200
+ steps.push(`delete bgp-communities regexp "${communityPattern(pattern)}"`);
2205
2201
  if (set.communities)
2206
- steps2.push(`set bgp-communities ${set.communities.join(",")}`);
2202
+ steps.push(`set bgp-communities ${set.communities.join(",")}`);
2207
2203
  if (set.add_communities)
2208
- steps2.push(`append bgp-communities ${set.add_communities.join(",")}`);
2204
+ steps.push(`append bgp-communities ${set.add_communities.join(",")}`);
2209
2205
  for (const pattern of set.remove_large_communities ?? []) {
2210
- steps2.push(`delete bgp-large-communities regexp "${communityPattern(pattern)}"`);
2206
+ steps.push(`delete bgp-large-communities regexp "${communityPattern(pattern)}"`);
2211
2207
  }
2212
2208
  if (set.large_communities)
2213
- steps2.push(`set bgp-large-communities ${set.large_communities.join(",")}`);
2209
+ steps.push(`set bgp-large-communities ${set.large_communities.join(",")}`);
2214
2210
  if (set.add_large_communities)
2215
- steps2.push(`append bgp-large-communities ${set.add_large_communities.join(",")}`);
2216
- if (set.next_hop && familyOf(set.next_hop) === family)
2217
- steps2.push(`set gw ${set.next_hop}`);
2218
- if (set.preferred_source && familyOf(set.preferred_source) === family)
2219
- steps2.push(`set pref-src ${set.preferred_source}`);
2211
+ steps.push(`append bgp-large-communities ${set.add_large_communities.join(",")}`);
2212
+ if (set.next_hop && familyOf2(set.next_hop) === family)
2213
+ steps.push(`set gw ${set.next_hop}`);
2214
+ if (set.preferred_source && familyOf2(set.preferred_source) === family)
2215
+ steps.push(`set pref-src ${set.preferred_source}`);
2220
2216
  if (set.blackhole)
2221
- steps2.push("set blackhole yes");
2217
+ steps.push("set blackhole yes");
2222
2218
  if (rule.action)
2223
- steps2.push(rule.action);
2224
- return steps2;
2219
+ steps.push(rule.action);
2220
+ return steps;
2225
2221
  }
2226
2222
  function ruleText(rule, family) {
2227
2223
  const parts = [];
@@ -2230,24 +2226,24 @@ function ruleText(rule, family) {
2230
2226
  if (rule.match?.rpki)
2231
2227
  parts.push(`rpki-verify ${RPKI_GROUP}`);
2232
2228
  const tests = conditions(rule);
2233
- const steps2 = actions(rule, family);
2229
+ const steps = actions(rule, family);
2234
2230
  if (tests.length)
2235
- parts.push(`if (${tests.join(" && ")})${steps2.length ? ` { ${steps2.join("; ")} }` : ""}`);
2231
+ parts.push(`if (${tests.join(" && ")})${steps.length ? ` { ${steps.join("; ")} }` : ""}`);
2236
2232
  else
2237
- parts.push(...steps2);
2233
+ parts.push(...steps);
2238
2234
  return parts.join("; ");
2239
2235
  }
2240
2236
  function policies(out, ctx) {
2241
2237
  const bgp = ctx.device.routing.bgp;
2242
- const policies2 = ctx.device.policies ?? {};
2238
+ const policies = ctx.device.policies ?? {};
2243
2239
  const wanted = new Map;
2244
2240
  const want = (policy, family) => {
2245
- const families2 = wanted.get(policy) ?? new Set;
2246
- if (families2.has(family))
2241
+ const families = wanted.get(policy) ?? new Set;
2242
+ if (families.has(family))
2247
2243
  return;
2248
- families2.add(family);
2249
- wanted.set(policy, families2);
2250
- for (const rule of policies2[policy] ?? [])
2244
+ families.add(family);
2245
+ wanted.set(policy, families);
2246
+ for (const rule of policies[policy] ?? [])
2251
2247
  if (rule.call && appliesTo(ctx, rule, family))
2252
2248
  want(rule.call, family);
2253
2249
  };
@@ -2263,13 +2259,13 @@ function policies(out, ctx) {
2263
2259
  }
2264
2260
  }
2265
2261
  }
2266
- const called = new Set(Object.values(policies2).flatMap((rules) => rules.flatMap((rule) => rule.call ? [rule.call] : [])));
2262
+ const called = new Set(Object.values(policies).flatMap((rules) => rules.flatMap((rule) => rule.call ? [rule.call] : [])));
2267
2263
  for (const policy of [...wanted.keys()].sort()) {
2268
2264
  for (const family of ["ipv4", "ipv6"]) {
2269
2265
  if (!wanted.get(policy).has(family))
2270
2266
  continue;
2271
2267
  const chain = chainName(policy, family);
2272
- const rules = (policies2[policy] ?? []).filter((rule) => appliesTo(ctx, rule, family));
2268
+ const rules = (policies[policy] ?? []).filter((rule) => appliesTo(ctx, rule, family));
2273
2269
  for (const rule of rules) {
2274
2270
  const text = ruleText(rule, family);
2275
2271
  if (text)
@@ -2294,9 +2290,9 @@ function bgpConnections(out, ctx) {
2294
2290
  for (const [name, neighbor] of Object.entries(bgp.neighbors ?? {})) {
2295
2291
  const settings = settingsOf(ctx, neighbor);
2296
2292
  const [family] = familiesOf(ctx, neighbor);
2297
- const addressFamily = familyOf(neighbor.address);
2293
+ const addressFamily = familyOf2(neighbor.address);
2298
2294
  const internal = settings.remote_as === bgp.asn;
2299
- const originates = bgp.networks?.some((prefix) => familyOf(host(prefix)) === family);
2295
+ const originates = bgp.networks?.some((prefix) => familyOf2(host2(prefix)) === family);
2300
2296
  const role = settings.role ? ROLES[settings.role] : internal ? "ibgp" : "ebgp";
2301
2297
  out.add("/routing bgp connection", command("add", {
2302
2298
  afi: addressFamily === family ? undefined : family === "ipv6" ? "ipv6" : "ip",
@@ -2322,19 +2318,19 @@ function bgpConnections(out, ctx) {
2322
2318
  }
2323
2319
  }
2324
2320
  function ospf(out, ctx) {
2325
- const ospf2 = ctx.device.routing?.ospf;
2326
- if (!ospf2)
2321
+ const ospf = ctx.device.routing?.ospf;
2322
+ if (!ospf)
2327
2323
  return;
2328
- for (const family of ospf2.families ?? ["ipv4"]) {
2324
+ for (const family of ospf.families ?? ["ipv4"]) {
2329
2325
  const version = family === "ipv4" ? 2 : 3;
2330
2326
  const instance = `ospf-v${version}`;
2331
2327
  out.add("/routing ospf instance", command("add", {
2332
2328
  name: instance,
2333
- redistribute: ospf2.redistribute?.join(","),
2329
+ redistribute: ospf.redistribute?.join(","),
2334
2330
  "router-id": ctx.device.routing?.router_id,
2335
2331
  version
2336
2332
  }));
2337
- for (const [areaId, area] of Object.entries(ospf2.areas)) {
2333
+ for (const [areaId, area] of Object.entries(ospf.areas)) {
2338
2334
  const areaName = `${instance}-${areaId}`;
2339
2335
  out.add("/routing ospf area", command("add", { "area-id": areaId, instance, name: areaName }));
2340
2336
  for (const [iface, settings] of Object.entries(area.interfaces)) {
@@ -2411,19 +2407,19 @@ function layerTwo(out, ctx) {
2411
2407
  }
2412
2408
  function snmp(out, ctx) {
2413
2409
  const management = ctx.device.management ?? {};
2414
- const snmp2 = management.snmp;
2415
- if (!snmp2) {
2410
+ const snmp = management.snmp;
2411
+ if (!snmp) {
2416
2412
  out.add("/snmp", command("set", { enabled: "no" }));
2417
2413
  out.add("/snmp community", command("set [ find default=yes ]", { addresses: "::/0", name: "public" }));
2418
2414
  return;
2419
2415
  }
2420
- const allow = (snmp2.allow ?? management.allow ?? []).join(",");
2421
- out.add("/snmp", command("set", { contact: snmp2.contact ?? "", enabled: "yes", location: snmp2.location ?? "" }));
2422
- if (snmp2.community)
2423
- out.add("/snmp community", command("set [ find default=yes ]", { addresses: allow, name: marker(snmp2.community) }));
2416
+ const allow = (snmp.allow ?? management.allow ?? []).join(",");
2417
+ out.add("/snmp", command("set", { contact: snmp.contact ?? "", enabled: "yes", location: snmp.location ?? "" }));
2418
+ if (snmp.community)
2419
+ out.add("/snmp community", command("set [ find default=yes ]", { addresses: allow, name: marker(snmp.community) }));
2424
2420
  else
2425
2421
  out.add("/snmp community", command("set [ find default=yes ]", { addresses: "::/0", disabled: "yes", name: "public" }));
2426
- for (const [name, user] of Object.entries(snmp2.users ?? {})) {
2422
+ for (const [name, user] of Object.entries(snmp.users ?? {})) {
2427
2423
  out.add("/snmp community", command("add", {
2428
2424
  addresses: allow,
2429
2425
  "authentication-password": marker(user.auth_password),
@@ -2490,15 +2486,15 @@ function renderSystem(out, ctx) {
2490
2486
  flowExport(out, ctx);
2491
2487
  }
2492
2488
  function logging(out, ctx) {
2493
- const logging2 = ctx.device.system?.logging;
2494
- const local = logging2?.local;
2489
+ const logging = ctx.device.system?.logging;
2490
+ const local = logging?.local;
2495
2491
  out.add("/system logging action", command("set 1", {
2496
2492
  "disk-file-count": local?.files ?? 2,
2497
2493
  "disk-lines-per-file": local?.lines_per_file ?? 1000
2498
2494
  }));
2499
2495
  for (const topic of local ? topicsFrom(local.level) : [])
2500
2496
  out.add("/system logging", command("add", { action: "disk", topics: topic }));
2501
- (logging2?.remote ?? []).forEach((collector, index) => {
2497
+ (logging?.remote ?? []).forEach((collector, index) => {
2502
2498
  const action = `remote${index + 1}`;
2503
2499
  out.add("/system logging action", command("add", {
2504
2500
  name: action,
@@ -2576,17 +2572,17 @@ async function read2(device) {
2576
2572
  try {
2577
2573
  await checkVersion(client, device);
2578
2574
  const config = await exec(client, "/export", 300000);
2579
- const users2 = await exec(client, "/user export");
2580
- const certificates2 = (await exec(client, LIST_CERTIFICATES)).trim();
2581
- const listed = certificates2.split(`
2582
- `).every((line2) => line2.trim().startsWith("add fingerprint="));
2583
- if (certificates2 && !listed) {
2584
- throw new Error(`${device.name}: certificates could not be listed: ${certificates2.slice(0, 120)}`);
2575
+ const users = await exec(client, "/user export");
2576
+ const certificates = (await exec(client, LIST_CERTIFICATES)).trim();
2577
+ const listed = certificates.split(`
2578
+ `).every((line) => line.trim().startsWith("add fingerprint="));
2579
+ if (certificates && !listed) {
2580
+ throw new Error(`${device.name}: certificates could not be listed: ${certificates.slice(0, 120)}`);
2585
2581
  }
2586
- const userMenu = users2.slice(users2.indexOf(`
2582
+ const userMenu = users.slice(users.indexOf(`
2587
2583
  /`)).trim();
2588
- const certificateMenu = certificates2 ? `/certificate
2589
- ${certificates2}` : "";
2584
+ const certificateMenu = certificates ? `/certificate
2585
+ ${certificates}` : "";
2590
2586
  const text = [config.trim(), userMenu, certificateMenu].filter(Boolean).join(`
2591
2587
  `);
2592
2588
  return withoutTimestamp(text) + `
@@ -2622,8 +2618,8 @@ function unsupported2(device) {
2622
2618
  const bgp = device.routing?.bgp;
2623
2619
  for (const [name, neighbor] of Object.entries(bgp?.neighbors ?? {})) {
2624
2620
  const group = neighbor.group ? bgp?.groups?.[neighbor.group] : undefined;
2625
- const families2 = neighbor.families ?? group?.families ?? [familyOf(neighbor.address)];
2626
- if (families2.length > 1)
2621
+ const families = neighbor.families ?? group?.families ?? [familyOf2(neighbor.address)];
2622
+ if (families.length > 1)
2627
2623
  found.push(`routing.bgp.neighbors.${name}.families, one family per session`);
2628
2624
  }
2629
2625
  const management = device.management;
@@ -2649,8 +2645,8 @@ function unsupported2(device) {
2649
2645
  }
2650
2646
  var ROLLBACK_SCHEDULER = "apply-rollback";
2651
2647
  var escapeForString = (text) => text.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\$/g, "\\$");
2652
- async function refusal(client, command2) {
2653
- const compiled = (await exec(client, `:put [:parse "${escapeForString(command2)}"]`)).trim();
2648
+ async function refusal(client, command) {
2649
+ const compiled = (await exec(client, `:put [:parse "${escapeForString(command)}"]`)).trim();
2654
2650
  const refused = !compiled.startsWith("(") || /syntax error|bad parameter|no such|expected/.test(compiled);
2655
2651
  return refused ? compiled.slice(0, 200) : undefined;
2656
2652
  }
@@ -2661,25 +2657,25 @@ async function installCertificate(client, device, name) {
2661
2657
  await upload(client, `${name}.crt`, certificate.certificate);
2662
2658
  await exec(client, `/certificate import file-name=${name}.crt name=${name} passphrase=""`);
2663
2659
  if (certificate.private_key) {
2664
- const value2 = resolve(certificate.private_key);
2665
- const privateKey = value2.includes("-----BEGIN") ? `${value2}
2666
- ` : Buffer.from(value2, "base64").toString("utf8");
2660
+ const value = resolve(certificate.private_key);
2661
+ const privateKey = value.includes("-----BEGIN") ? `${value}
2662
+ ` : Buffer.from(value, "base64").toString("utf8");
2667
2663
  await upload(client, `${name}.key`, privateKey);
2668
2664
  await exec(client, `/certificate import file-name=${name}.key name=${name} passphrase=""`);
2669
2665
  }
2670
2666
  await exec(client, `/file remove [find name="${name}.crt" or name="${name}.key"]`);
2671
2667
  }
2672
- async function apply2(device, _network, changes2, options) {
2668
+ async function apply2(device, _network, changes, options) {
2673
2669
  const client = await login(device);
2674
2670
  await checkVersion(client, device);
2675
- const commands = changes2.steps.flatMap((step) => step.send.map((command2) => ({ title: step.title, command: command2 })));
2676
- for (const { title, command: command2 } of commands) {
2677
- if (command2.startsWith(INSTALL_CERTIFICATE))
2671
+ const commands = changes.steps.flatMap((step) => step.send.map((command) => ({ title: step.title, command })));
2672
+ for (const { title, command } of commands) {
2673
+ if (command.startsWith(INSTALL_CERTIFICATE))
2678
2674
  continue;
2679
- const reason = await refusal(client, substitute(command2));
2675
+ const reason = await refusal(client, substitute(command));
2680
2676
  if (reason) {
2681
2677
  client.end();
2682
- throw new Error(`${title}: the device will not parse ${command2.slice(0, 200)}
2678
+ throw new Error(`${title}: the device will not parse ${command.slice(0, 200)}
2683
2679
  ${reason}`);
2684
2680
  }
2685
2681
  }
@@ -2696,15 +2692,15 @@ async function apply2(device, _network, changes2, options) {
2696
2692
  await exec(client, `/system backup save name=${backup} dont-encrypt=yes`, 120000);
2697
2693
  await exec(client, `/system scheduler remove [find name="${ROLLBACK_SCHEDULER}"]`);
2698
2694
  await exec(client, armRollback);
2699
- for (const { title, command: command2 } of commands) {
2700
- if (command2.startsWith(INSTALL_CERTIFICATE)) {
2701
- const certificateName = command2.slice(INSTALL_CERTIFICATE.length).trim();
2695
+ for (const { title, command } of commands) {
2696
+ if (command.startsWith(INSTALL_CERTIFICATE)) {
2697
+ const certificateName = command.slice(INSTALL_CERTIFICATE.length).trim();
2702
2698
  await installCertificate(client, device, certificateName);
2703
2699
  continue;
2704
2700
  }
2705
- const answer = (await exec(client, substitute(command2), 120000)).trim();
2701
+ const answer = (await exec(client, substitute(command), 120000)).trim();
2706
2702
  if (answer)
2707
- throw new Error(`${title}: ${command2.slice(0, 160)}
2703
+ throw new Error(`${title}: ${command.slice(0, 160)}
2708
2704
  device said: ${answer.slice(0, 300)}`);
2709
2705
  }
2710
2706
  } catch (error) {
@@ -2776,20 +2772,20 @@ var DEVICE_FACTS2 = [
2776
2772
  "Info:"
2777
2773
  ];
2778
2774
  var UNDO_BY_KEYWORD2 = /^(description|name|port default vlan|port link-type|port trunk pvid vlan|set authentication password|eth-trunk|ip binding vpn-instance)\b/;
2779
- function undo2(line2) {
2780
- if (line2.startsWith("undo "))
2781
- return line2.slice("undo ".length);
2782
- if (/^rule \d+/.test(line2))
2783
- return `undo ${line2.split(" ").slice(0, 2).join(" ")}`;
2784
- const keyword = UNDO_BY_KEYWORD2.exec(line2);
2775
+ function undo2(line) {
2776
+ if (line.startsWith("undo "))
2777
+ return line.slice("undo ".length);
2778
+ if (/^rule \d+/.test(line))
2779
+ return `undo ${line.split(" ").slice(0, 2).join(" ")}`;
2780
+ const keyword = UNDO_BY_KEYWORD2.exec(line);
2785
2781
  if (keyword)
2786
2782
  return `undo ${keyword[1]}`;
2787
- if (line2.startsWith("storm control "))
2788
- return `undo ${line2.replace(/ (min-rate|max-rate).*$/, "")}`;
2789
- return `undo ${line2}`;
2783
+ if (line.startsWith("storm control "))
2784
+ return `undo ${line.replace(/ (min-rate|max-rate).*$/, "")}`;
2785
+ return `undo ${line}`;
2790
2786
  }
2791
- function canon(line2) {
2792
- return line2.replace(/(irreversible-cipher|cipher) \S+/g, "cipher <secret>").replace(/<secret:\w+>/g, "<secret>");
2787
+ function canon(line) {
2788
+ return line.replace(/(irreversible-cipher|cipher) \S+/g, "cipher <secret>").replace(/<secret:\w+>/g, "<secret>");
2793
2789
  }
2794
2790
  function order(path) {
2795
2791
  const header = path[0] ?? "";
@@ -2811,7 +2807,7 @@ var dialect2 = {
2811
2807
  block: /^(interface |vlan \d+$|acl number |aaa$|user-interface |ospf |ip vpn-instance |bfd$)/,
2812
2808
  indented: true,
2813
2809
  terminator: "#",
2814
- ignore: (line2) => DEVICE_FACTS2.some((fact) => line2.startsWith(fact)),
2810
+ ignore: (line) => DEVICE_FACTS2.some((fact) => line.startsWith(fact)),
2815
2811
  undo: undo2,
2816
2812
  leave: "quit",
2817
2813
  first: /^authentication-mode /,
@@ -2825,13 +2821,13 @@ var SNMP_GROUP2 = "circuit";
2825
2821
  var USER_LEVEL = { admin: 3, operator: 2, "read-only": 1 };
2826
2822
  var SNMP_AUTH3 = { sha1: "sha", sha256: "sha2-256" };
2827
2823
  var SNMP_PRIVACY2 = { aes128: "aes128", des: "des56" };
2828
- function batchIds(line2) {
2829
- const words2 = line2.replace(/^vlan batch /, "").split(" ");
2824
+ function batchIds(line) {
2825
+ const words = line.replace(/^vlan batch /, "").split(" ");
2830
2826
  const ids = [];
2831
- for (let index = 0;index < words2.length; index++) {
2832
- const first = Number(words2[index]);
2833
- if (words2[index + 1] === "to") {
2834
- const last = Number(words2[index + 2]);
2827
+ for (let index = 0;index < words.length; index++) {
2828
+ const first = Number(words[index]);
2829
+ if (words[index + 1] === "to") {
2830
+ const last = Number(words[index + 2]);
2835
2831
  for (let id = first;id <= last; id++)
2836
2832
  ids.push(id);
2837
2833
  index += 2;
@@ -2968,7 +2964,7 @@ function managementAcl(config, device) {
2968
2964
  const allow = device.management?.allow ?? [];
2969
2965
  if (!allow.length)
2970
2966
  return;
2971
- const rules = allow.map((cidr, index) => `rule ${(index + 1) * 5} permit source ${host(cidr)} ${wildcard(split(cidr).len)}`);
2967
+ const rules = allow.map((cidr, index) => `rule ${(index + 1) * 5} permit source ${host2(cidr)} ${wildcard(split(cidr).len)}`);
2972
2968
  config.block(`acl number ${MANAGEMENT_ACL2}`, ["description Management access", ...rules, `rule ${(allow.length + 1) * 5} deny`]);
2973
2969
  }
2974
2970
  function users2(config, device) {
@@ -3047,8 +3043,8 @@ function vlanInterface(device, name, iface) {
3047
3043
  lines.push(`ip binding vpn-instance ${iface.vrf}`);
3048
3044
  for (const entry of iface.addresses ?? []) {
3049
3045
  const address = typeof entry === "string" ? entry : entry.address;
3050
- if (familyOf(address) === "ipv4")
3051
- lines.push(`ip address ${host(address)} ${netmask(split(address).len)}`);
3046
+ if (familyOf2(address) === "ipv4")
3047
+ lines.push(`ip address ${host2(address)} ${netmask(split(address).len)}`);
3052
3048
  else
3053
3049
  lines.push(`ipv6 address ${address}`);
3054
3050
  }
@@ -3103,7 +3099,7 @@ function staticRoutes2(config, device, nameOf) {
3103
3099
  const vrf = route.vrf ? ` vpn-instance ${route.vrf}` : "";
3104
3100
  const preference = route.distance ? ` preference ${route.distance}` : "";
3105
3101
  const description = route.description ? ` description ${route.description}` : "";
3106
- if (familyOf(addr) === "ipv4")
3102
+ if (familyOf2(addr) === "ipv4")
3107
3103
  config.line(`ip route-static${vrf} ${addr} ${netmask(len)} ${nextHop}${preference}${description}`);
3108
3104
  else
3109
3105
  config.line(`ipv6 route-static${vrf} ${addr} ${len} ${nextHop}${preference}`);
@@ -3111,21 +3107,21 @@ function staticRoutes2(config, device, nameOf) {
3111
3107
  config.separator();
3112
3108
  }
3113
3109
  function ospf2(config, device, nameOf) {
3114
- const ospf3 = device.routing?.ospf;
3115
- if (!ospf3)
3110
+ const ospf = device.routing?.ospf;
3111
+ if (!ospf)
3116
3112
  return;
3117
3113
  const routerId = device.routing?.router_id;
3118
3114
  const lines = [];
3119
- for (const source of ospf3.redistribute ?? [])
3115
+ for (const source of ospf.redistribute ?? [])
3120
3116
  lines.push(`import-route ${source === "connected" ? "direct" : "static"}`);
3121
3117
  const areaLines = [];
3122
- for (const [areaId, area] of Object.entries(ospf3.areas)) {
3118
+ for (const [areaId, area] of Object.entries(ospf.areas)) {
3123
3119
  areaLines.push(`area ${areaId}`);
3124
3120
  for (const [iface, settings] of Object.entries(area.interfaces)) {
3125
3121
  if (settings?.passive)
3126
3122
  lines.push(`silent-interface ${nameOf(iface)}`);
3127
3123
  for (const prefix of ipv4Networks(device, iface))
3128
- areaLines.push(` network ${host(prefix)} ${wildcard(split(prefix).len)}`);
3124
+ areaLines.push(` network ${host2(prefix)} ${wildcard(split(prefix).len)}`);
3129
3125
  }
3130
3126
  }
3131
3127
  config.block(`ospf ${OSPF_PROCESS2}${routerId ? ` router-id ${routerId}` : ""}`, [...lines, ...areaLines]);
@@ -3146,23 +3142,23 @@ function flowExport2(config, device) {
3146
3142
  config.separator();
3147
3143
  }
3148
3144
  function snmp2(config, device) {
3149
- const snmp3 = device.management?.snmp;
3150
- if (!snmp3)
3145
+ const snmp = device.management?.snmp;
3146
+ if (!snmp)
3151
3147
  return;
3152
- const versions = [snmp3.community ? "v2c" : "", snmp3.users ? "v3" : ""].filter(Boolean).join(" ");
3148
+ const versions = [snmp.community ? "v2c" : "", snmp.users ? "v3" : ""].filter(Boolean).join(" ");
3153
3149
  const guarded = (device.management?.allow?.length ?? 0) > 0;
3154
3150
  config.line("snmp-agent");
3155
3151
  config.line(`snmp-agent sys-info version ${versions}`);
3156
- if (snmp3.contact)
3157
- config.line(`snmp-agent sys-info contact ${snmp3.contact}`);
3158
- if (snmp3.location)
3159
- config.line(`snmp-agent sys-info location ${snmp3.location}`);
3160
- if (snmp3.community)
3161
- config.line(`snmp-agent community read cipher ${marker(snmp3.community)}${guarded ? ` acl ${MANAGEMENT_ACL2}` : ""}`);
3162
- if (snmp3.users) {
3152
+ if (snmp.contact)
3153
+ config.line(`snmp-agent sys-info contact ${snmp.contact}`);
3154
+ if (snmp.location)
3155
+ config.line(`snmp-agent sys-info location ${snmp.location}`);
3156
+ if (snmp.community)
3157
+ config.line(`snmp-agent community read cipher ${marker(snmp.community)}${guarded ? ` acl ${MANAGEMENT_ACL2}` : ""}`);
3158
+ if (snmp.users) {
3163
3159
  config.line("snmp-agent mib-view included all iso");
3164
3160
  config.line(`snmp-agent group v3 ${SNMP_GROUP2} privacy read-view all${guarded ? ` acl ${MANAGEMENT_ACL2}` : ""}`);
3165
- for (const [name, user] of Object.entries(snmp3.users)) {
3161
+ for (const [name, user] of Object.entries(snmp.users)) {
3166
3162
  config.line(`snmp-agent usm-user v3 ${name} group ${SNMP_GROUP2}`);
3167
3163
  config.line(`snmp-agent usm-user v3 ${name} authentication-mode ${SNMP_AUTH3[user.auth]} cipher ${marker(user.auth_password)}`);
3168
3164
  config.line(`snmp-agent usm-user v3 ${name} privacy-mode ${SNMP_PRIVACY2[user.privacy]} cipher ${marker(user.privacy_password)}`);
@@ -3171,8 +3167,8 @@ function snmp2(config, device) {
3171
3167
  config.separator();
3172
3168
  }
3173
3169
  function ssh(config, device) {
3174
- const ssh2 = device.management?.ssh;
3175
- if (!ssh2)
3170
+ const ssh = device.management?.ssh;
3171
+ if (!ssh)
3176
3172
  return;
3177
3173
  const guarded = (device.management?.allow?.length ?? 0) > 0;
3178
3174
  config.line("stelnet server enable");
@@ -3185,7 +3181,7 @@ function ssh(config, device) {
3185
3181
  config.line(`ssh server acl ${MANAGEMENT_ACL2}`);
3186
3182
  config.line("ssh authorization-type default aaa");
3187
3183
  config.separator();
3188
- if (!ssh2.weak_crypto) {
3184
+ if (!ssh.weak_crypto) {
3189
3185
  config.line("ssh server cipher aes256_ctr aes128_ctr");
3190
3186
  config.line("ssh server hmac sha2_256_96 sha2_256");
3191
3187
  config.line("ssh server key-exchange dh_group_exchange_sha256 ecdh_sha2_nistp256 ecdh_sha2_nistp384 ecdh_sha2_nistp521");
@@ -3227,10 +3223,10 @@ async function read3(device) {
3227
3223
  channel.end();
3228
3224
  const lines = dump.replace(/\r/g, "").split(`
3229
3225
  `);
3230
- const start = lines.findIndex((line2) => line2.startsWith("!Software Version"));
3226
+ const start = lines.findIndex((line) => line.startsWith("!Software Version"));
3231
3227
  if (start < 0)
3232
3228
  throw new Error(`${device.name} returned no configuration`);
3233
- const body = lines.slice(start).filter((line2) => !/^<.*>$/.test(line2.trim()) && !line2.startsWith("!Last configuration"));
3229
+ const body = lines.slice(start).filter((line) => !/^<.*>$/.test(line.trim()) && !line.startsWith("!Last configuration"));
3234
3230
  return scrub(body.join(`
3235
3231
  `).trim() + `
3236
3232
  `);
@@ -3261,7 +3257,7 @@ function unsupported3(device) {
3261
3257
  if (iface.mtu !== undefined)
3262
3258
  found.push(`interfaces.${name}.mtu`);
3263
3259
  for (const group of iface.vrrp ?? [])
3264
- if (familyOf(group.address) === "ipv6")
3260
+ if (familyOf2(group.address) === "ipv6")
3265
3261
  found.push(`interfaces.${name}.vrrp for IPv6`);
3266
3262
  }
3267
3263
  for (const [name, vrf] of Object.entries(device.vrfs ?? {})) {
@@ -3294,11 +3290,11 @@ function unsupported3(device) {
3294
3290
  found.push("management.ssh.auth_timeout and auth_retries");
3295
3291
  if (device.lldp && device.lldp !== true)
3296
3292
  found.push("lldp.interfaces, use lldp on the ports instead");
3297
- const system2 = device.system ?? {};
3298
- if (system2.dns || system2.logging || system2.release_channel || system2.banner) {
3293
+ const system = device.system ?? {};
3294
+ if (system.dns || system.logging || system.release_channel || system.banner) {
3299
3295
  found.push("system.dns, logging, release_channel and banner");
3300
3296
  }
3301
- if (system2.transceiver_monitoring !== undefined)
3297
+ if (system.transceiver_monitoring !== undefined)
3302
3298
  found.push("system.transceiver_monitoring");
3303
3299
  found.push(...switchUnsupported(device));
3304
3300
  const flow = device.flow_export;
@@ -3313,31 +3309,31 @@ function unsupported3(device) {
3313
3309
  }
3314
3310
  var REFUSED2 = /^\s*Error|Unrecognized command|Incomplete command|Wrong parameter|Too many parameters/m;
3315
3311
  var INTERACTIVE = /^(snmp-agent usm-user v3 \S+ (?:authentication|privacy)-mode \S+) cipher <secret:([A-Za-z0-9_./:-]+)>$/;
3316
- async function sendLine(channel, line2) {
3317
- const interactive = INTERACTIVE.exec(line2.trim());
3312
+ async function sendLine(channel, line) {
3313
+ const interactive = INTERACTIVE.exec(line.trim());
3318
3314
  if (interactive) {
3319
- const [, command2, reference] = interactive;
3315
+ const [, command, reference] = interactive;
3320
3316
  const password = valueOfReference(reference);
3321
- let answer2 = await send(channel, command2, 15000, 900);
3322
- answer2 += await send(channel, password, 15000, 900);
3323
- answer2 += await send(channel, password, 15000, 900);
3324
- return answer2;
3317
+ let answer = await send(channel, command, 15000, 900);
3318
+ answer += await send(channel, password, 15000, 900);
3319
+ answer += await send(channel, password, 15000, 900);
3320
+ return answer;
3325
3321
  }
3326
- let answer = await send(channel, substitute(line2), 20000, 800);
3322
+ let answer = await send(channel, substitute(line), 20000, 800);
3327
3323
  if (/\[Y\/N\]/i.test(answer))
3328
3324
  answer += await send(channel, "Y", 20000, 800);
3329
3325
  return answer;
3330
3326
  }
3331
- async function apply3(device, _network, plan3) {
3327
+ async function apply3(device, _network, plan) {
3332
3328
  const client = await login2(device);
3333
3329
  const channel = await openShell(client);
3334
3330
  try {
3335
3331
  await send(channel, "system-view", 1e4, 1500);
3336
- for (const step of plan3.steps) {
3337
- for (const line2 of step.send) {
3338
- const answer = await sendLine(channel, line2);
3332
+ for (const step of plan.steps) {
3333
+ for (const line of step.send) {
3334
+ const answer = await sendLine(channel, line);
3339
3335
  if (REFUSED2.test(answer))
3340
- throw new Error(`${step.title}: ${line2}
3336
+ throw new Error(`${step.title}: ${line}
3341
3337
  device said: ${answer.trim().slice(0, 300)}`);
3342
3338
  }
3343
3339
  }
@@ -3373,19 +3369,19 @@ function plan3(desired, current) {
3373
3369
  const groups = compare(desired, current, dialect2);
3374
3370
  vlanListChange(groups, "vlan batch ", batchIds, (ids) => [`vlan batch ${vlanBatch(ids)}`], (ids) => [`vlan batch ${vlanBatch(ids)}`]);
3375
3371
  const top = groups.find((group) => group.path.length === 0);
3376
- const removedBatch = top?.remove.find((line2) => line2.startsWith("vlan batch ")) ?? "";
3372
+ const removedBatch = top?.remove.find((line) => line.startsWith("vlan batch ")) ?? "";
3377
3373
  const removedIds = removedBatch ? batchIds(removedBatch) : [];
3378
3374
  const firstRemovals = [
3379
- ...removedIds.map((id) => `interface Vlanif${id}`).filter((line2) => top?.remove.includes(line2)),
3380
- ...removedIds.map((id) => `vlan ${id}`).filter((line2) => top?.remove.includes(line2)),
3375
+ ...removedIds.map((id) => `interface Vlanif${id}`).filter((line) => top?.remove.includes(line)),
3376
+ ...removedIds.map((id) => `vlan ${id}`).filter((line) => top?.remove.includes(line)),
3381
3377
  ...removedBatch ? [removedBatch] : []
3382
3378
  ];
3383
3379
  if (top)
3384
- top.remove = top.remove.filter((line2) => !firstRemovals.includes(line2));
3380
+ top.remove = top.remove.filter((line) => !firstRemovals.includes(line));
3385
3381
  const planned = steps(groups.filter((group) => group.add.length || group.remove.length), dialect2);
3386
3382
  if (firstRemovals.length) {
3387
- const sent = firstRemovals.filter((line2) => !/^vlan \d+$/.test(line2)).map(undo2);
3388
- planned.unshift({ title: "VLANs no longer used", show: firstRemovals.map((line2) => `- ${line2}`), send: sent });
3383
+ const sent = firstRemovals.filter((line) => !/^vlan \d+$/.test(line)).map(undo2);
3384
+ planned.unshift({ title: "VLANs no longer used", show: firstRemovals.map((line) => `- ${line}`), send: sent });
3389
3385
  }
3390
3386
  return { steps: planned, problems: [] };
3391
3387
  }
@@ -3420,17 +3416,17 @@ var registries = { "ripe-stat": ripeStat };
3420
3416
  // src/adapters/registries/librenms.ts
3421
3417
  var AUTH = { sha1: "SHA", sha256: "SHA-256" };
3422
3418
  var PRIVACY = { aes128: "AES", des: "DES" };
3423
- function settingsOf2(network2) {
3424
- if (!network2.librenms)
3419
+ function settingsOf2(network) {
3420
+ if (!network.librenms)
3425
3421
  throw new Error("the network has no librenms settings");
3426
3422
  return {
3427
- base: `${network2.librenms.url.replace(/\/+$/, "")}/api/v0`,
3428
- headers: { "X-Auth-Token": resolve(network2.librenms.api_token), "Content-Type": "application/json" }
3423
+ base: `${network.librenms.url.replace(/\/+$/, "")}/api/v0`,
3424
+ headers: { "X-Auth-Token": resolve(network.librenms.api_token), "Content-Type": "application/json" }
3429
3425
  };
3430
3426
  }
3431
3427
  function credentials(device) {
3432
- const snmp3 = device.management?.snmp;
3433
- const [name, user] = Object.entries(snmp3?.users ?? {})[0] ?? [];
3428
+ const snmp = device.management?.snmp;
3429
+ const [name, user] = Object.entries(snmp?.users ?? {})[0] ?? [];
3434
3430
  if (name && user) {
3435
3431
  return {
3436
3432
  snmpver: "v3",
@@ -3442,26 +3438,26 @@ function credentials(device) {
3442
3438
  cryptoalgo: PRIVACY[user.privacy]
3443
3439
  };
3444
3440
  }
3445
- if (snmp3?.community)
3446
- return { snmpver: "v2c", community: resolve(snmp3.community) };
3441
+ if (snmp?.community)
3442
+ return { snmpver: "v2c", community: resolve(snmp.community) };
3447
3443
  return;
3448
3444
  }
3449
- async function planLibreNms(network2) {
3450
- const { base, headers } = settingsOf2(network2);
3445
+ async function planLibreNms(network) {
3446
+ const { base, headers } = settingsOf2(network);
3451
3447
  const response = await fetch(`${base}/devices?type=all`, { headers, signal: AbortSignal.timeout(30000) });
3452
3448
  if (!response.ok)
3453
3449
  throw new Error(`LibreNMS answered ${response.status} to the device list`);
3454
3450
  const monitored = new Set((await response.json()).devices.map((device) => device.hostname));
3455
- const ours = new Set(network2.devices.map((device) => device.connection.host));
3456
- const missing = network2.devices.filter((device) => !monitored.has(device.connection.host));
3451
+ const ours = new Set(network.devices.map((device) => device.connection.host));
3452
+ const missing = network.devices.filter((device) => !monitored.has(device.connection.host));
3457
3453
  return {
3458
3454
  additions: missing.filter((device) => device.management?.snmp),
3459
3455
  withoutSnmp: missing.filter((device) => !device.management?.snmp),
3460
3456
  unknown: [...monitored].filter((hostname) => !ours.has(hostname)).sort()
3461
3457
  };
3462
3458
  }
3463
- async function sendLibreNms(network2, additions) {
3464
- const { base, headers } = settingsOf2(network2);
3459
+ async function sendLibreNms(network, additions) {
3460
+ const { base, headers } = settingsOf2(network);
3465
3461
  for (const device of additions) {
3466
3462
  const body = { hostname: device.connection.host, display_template: device.name, ...credentials(device) };
3467
3463
  const response = await fetch(`${base}/devices`, {
@@ -3487,48 +3483,48 @@ function peersWithRouteServer(device, iface) {
3487
3483
  return Object.values(bgp?.neighbors ?? {}).some((neighbor) => {
3488
3484
  const group = neighbor.group ? bgp?.groups?.[neighbor.group] : undefined;
3489
3485
  const role = neighbor.role ?? group?.role;
3490
- return role === "rs" && subnets.some((subnet) => contains(subnet, neighbor.address));
3486
+ return role === "rs" && subnets.some((subnet) => contains2(subnet, neighbor.address));
3491
3487
  });
3492
3488
  }
3493
- function wanted(network2) {
3489
+ function wanted(network) {
3494
3490
  const records = [];
3495
- for (const device of network2.devices) {
3491
+ for (const device of network.devices) {
3496
3492
  for (const iface of Object.values(device.interfaces ?? {})) {
3497
3493
  const exchange = iface.exchange;
3498
3494
  if (!exchange?.peeringdb_ixlan)
3499
3495
  continue;
3500
- const published = addressesOf2(iface).map(host).filter(isGlobal);
3496
+ const published = addressesOf2(iface).map(host2).filter(isGlobal2);
3501
3497
  records.push({
3502
3498
  name: exchange.name,
3503
3499
  ixlan_id: exchange.peeringdb_ixlan,
3504
3500
  speed: exchange.speed,
3505
- ipaddr4: published.find((address) => familyOf(address) === "ipv4") ?? null,
3506
- ipaddr6: published.find((address) => familyOf(address) === "ipv6") ?? null,
3501
+ ipaddr4: published.find((address) => familyOf2(address) === "ipv4") ?? null,
3502
+ ipaddr6: published.find((address) => familyOf2(address) === "ipv6") ?? null,
3507
3503
  is_rs_peer: peersWithRouteServer(device, iface)
3508
3504
  });
3509
3505
  }
3510
3506
  }
3511
3507
  return records;
3512
3508
  }
3513
- async function planPeeringDb(network2) {
3514
- if (!network2.asn)
3509
+ async function planPeeringDb(network) {
3510
+ if (!network.asn)
3515
3511
  throw new Error("the network declares no asn");
3516
- const response = await fetch(`${API}/netixlan?asn=${network2.asn}`, {
3512
+ const response = await fetch(`${API}/netixlan?asn=${network.asn}`, {
3517
3513
  headers: { Accept: "application/json" },
3518
3514
  signal: AbortSignal.timeout(30000)
3519
3515
  });
3520
3516
  if (!response.ok)
3521
3517
  throw new Error(`PeeringDB answered ${response.status}`);
3522
3518
  const existing = (await response.json()).data;
3523
- const desired = wanted(network2);
3519
+ const desired = wanted(network);
3524
3520
  const netId = existing[0]?.net_id;
3525
- const changes2 = [];
3521
+ const changes = [];
3526
3522
  for (const record of desired) {
3527
3523
  const current = existing.find((candidate) => candidate.ixlan_id === record.ixlan_id);
3528
3524
  const body = {
3529
3525
  net_id: netId,
3530
3526
  ixlan_id: record.ixlan_id,
3531
- asn: network2.asn,
3527
+ asn: network.asn,
3532
3528
  speed: record.speed,
3533
3529
  ipaddr4: record.ipaddr4,
3534
3530
  ipaddr6: record.ipaddr6,
@@ -3536,27 +3532,27 @@ async function planPeeringDb(network2) {
3536
3532
  operational: current?.operational ?? true
3537
3533
  };
3538
3534
  if (!current) {
3539
- changes2.push({ summary: `+ ${record.name}: ${record.ipaddr4}, ${record.ipaddr6}, ${record.speed} Mbit/s`, body });
3535
+ changes.push({ summary: `+ ${record.name}: ${record.ipaddr4}, ${record.ipaddr6}, ${record.speed} Mbit/s`, body });
3540
3536
  continue;
3541
3537
  }
3542
3538
  const differs = current.ipaddr4 !== record.ipaddr4 || current.ipaddr6 !== record.ipaddr6 || current.speed !== record.speed || current.is_rs_peer !== record.is_rs_peer;
3543
3539
  if (differs) {
3544
3540
  const before = `${current.ipaddr4}, ${current.ipaddr6}, ${current.speed} Mbit/s, route server ${current.is_rs_peer}`;
3545
3541
  const after = `${record.ipaddr4}, ${record.ipaddr6}, ${record.speed} Mbit/s, route server ${record.is_rs_peer}`;
3546
- changes2.push({ summary: `~ ${record.name}: ${before} to ${after}`, id: current.id, body });
3542
+ changes.push({ summary: `~ ${record.name}: ${before} to ${after}`, id: current.id, body });
3547
3543
  }
3548
3544
  }
3549
3545
  const unknown = existing.filter((record) => !desired.some((candidate) => candidate.ixlan_id === record.ixlan_id)).map((record) => record.name);
3550
- if (changes2.some((change) => !change.id) && !netId) {
3546
+ if (changes.some((change) => !change.id) && !netId) {
3551
3547
  throw new Error("PeeringDB holds no presence for this network yet, so its record id is unknown; add the first one by hand");
3552
3548
  }
3553
- return { changes: changes2, unknown };
3549
+ return { changes, unknown };
3554
3550
  }
3555
- async function sendPeeringDb(network2, changes2) {
3556
- if (!network2.peeringdb)
3551
+ async function sendPeeringDb(network, changes) {
3552
+ if (!network.peeringdb)
3557
3553
  throw new Error("the network declares no peeringdb api_key");
3558
- const apiKey = resolve(network2.peeringdb.api_key);
3559
- for (const change of changes2) {
3554
+ const apiKey = resolve(network.peeringdb.api_key);
3555
+ for (const change of changes) {
3560
3556
  const response = await fetch(`${API}/netixlan${change.id ? `/${change.id}` : ""}`, {
3561
3557
  method: change.id ? "PUT" : "POST",
3562
3558
  headers: { Authorization: `Api-Key ${apiKey}`, "Content-Type": "application/json" },
@@ -3574,13 +3570,13 @@ async function sendPeeringDb(network2, changes2) {
3574
3570
  // src/adapters/registries/prometheus.ts
3575
3571
  var AUTH2 = { sha1: "SHA", sha256: "SHA256" };
3576
3572
  var PRIVACY2 = { aes128: "AES", des: "DES" };
3577
- function prometheusTargets(network2) {
3573
+ function prometheusTargets(network) {
3578
3574
  return {
3579
- targets: network2.devices.filter((device) => device.management?.snmp).map((device) => ({
3575
+ targets: network.devices.filter((device) => device.management?.snmp).map((device) => ({
3580
3576
  targets: [device.connection.host],
3581
3577
  labels: { device: device.name, platform: device.platform, model: device.model, __param_auth: device.name }
3582
3578
  })),
3583
- withoutSnmp: network2.devices.filter((device) => !device.management?.snmp)
3579
+ withoutSnmp: network.devices.filter((device) => !device.management?.snmp)
3584
3580
  };
3585
3581
  }
3586
3582
  function fromEnvironment(secret, notes, where) {
@@ -3589,17 +3585,17 @@ function fromEnvironment(secret, notes, where) {
3589
3585
  notes.push(`${where} is read from a file; write its value in place of FILL_IN`);
3590
3586
  return "FILL_IN";
3591
3587
  }
3592
- function snmpExporterAuths(network2) {
3588
+ function snmpExporterAuths(network) {
3593
3589
  const notes = [];
3594
3590
  const lines = [
3595
3591
  "# Written by `circuit prometheus --auths`. Load it beside snmp.yml, with --config.expand-environment-variables.",
3596
3592
  "auths:"
3597
3593
  ];
3598
- for (const device of network2.devices) {
3599
- const snmp3 = device.management?.snmp;
3600
- if (!snmp3)
3594
+ for (const device of network.devices) {
3595
+ const snmp = device.management?.snmp;
3596
+ if (!snmp)
3601
3597
  continue;
3602
- const [name, user] = Object.entries(snmp3.users ?? {})[0] ?? [];
3598
+ const [name, user] = Object.entries(snmp.users ?? {})[0] ?? [];
3603
3599
  lines.push(` ${device.name}:`);
3604
3600
  if (name && user) {
3605
3601
  lines.push(" version: 3", ` username: ${name}`, " security_level: authPriv", ` password: ${fromEnvironment(user.auth_password, notes, `${device.name}'s SNMP password`)}`, ` auth_protocol: ${AUTH2[user.auth]}`, ` priv_protocol: ${PRIVACY2[user.privacy]}`, ` priv_password: ${fromEnvironment(user.privacy_password, notes, `${device.name}'s SNMP privacy password`)}`);
@@ -3618,18 +3614,18 @@ import { X509Certificate as X509Certificate2 } from "node:crypto";
3618
3614
 
3619
3615
  // src/core/across.ts
3620
3616
  function checkAcross(devices) {
3621
- const holders2 = holdersOf(devices);
3617
+ const holders = holdersOf(devices);
3622
3618
  const owners = new Map;
3623
- for (const holder of holders2) {
3624
- const key = keyOf(host(holder.address));
3619
+ for (const holder of holders) {
3620
+ const key = keyOf(host2(holder.address));
3625
3621
  if (!owners.has(key))
3626
3622
  owners.set(key, holder);
3627
3623
  }
3628
3624
  return [
3629
3625
  ...checkLinks(devices),
3630
- ...checkAddresses(holders2),
3626
+ ...checkAddresses(holders),
3631
3627
  ...checkSessions(devices, owners),
3632
- ...checkVrrp(holders2),
3628
+ ...checkVrrp(holders),
3633
3629
  ...checkTunnels(devices, owners)
3634
3630
  ];
3635
3631
  }
@@ -3651,7 +3647,7 @@ function carrierOf(device, portName, port) {
3651
3647
  return { name: port.lag, settings: lag };
3652
3648
  return { name: portName, settings: port };
3653
3649
  }
3654
- var plainAddresses = (settings, family) => (settings.addresses ?? []).filter((entry) => typeof entry === "string" && isCidr(entry) && familyOf(entry) === family);
3650
+ var plainAddresses = (settings, family) => (settings.addresses ?? []).filter((entry) => typeof entry === "string" && isCidr2(entry) && familyOf2(entry) === family);
3655
3651
  function addressDisagreement(here, there, far, farName) {
3656
3652
  for (const family of ["ipv4", "ipv6"]) {
3657
3653
  const ours = plainAddresses(here, family);
@@ -3663,7 +3659,7 @@ function addressDisagreement(here, there, far, farName) {
3663
3659
  return `has ${ours.join(", ")}, and ${far.name} ${farName} has ${theirs.join(", ")}: not one prefix of one length`;
3664
3660
  }
3665
3661
  }
3666
- const farHosts = new Set((there.addresses ?? []).map((entry) => keyOf(host(typeof entry === "string" ? entry : entry.address))));
3662
+ const farHosts = new Set((there.addresses ?? []).map((entry) => keyOf(host2(typeof entry === "string" ? entry : entry.address))));
3667
3663
  for (const entry of here.addresses ?? []) {
3668
3664
  if (typeof entry === "string" || !keyOf(entry.peer) || farHosts.has(keyOf(entry.peer)))
3669
3665
  continue;
@@ -3725,12 +3721,12 @@ function checkLinks(devices) {
3725
3721
  }
3726
3722
  return findings;
3727
3723
  }
3728
- function checkAddresses(holders2) {
3724
+ function checkAddresses(holders) {
3729
3725
  const findings = [];
3730
3726
  const first = new Map;
3731
3727
  const isLoopback = (holder) => holder.device.interfaces?.[holder.name]?.type === "loopback";
3732
- for (const holder of holders2) {
3733
- const key = keyOf(host(holder.address));
3728
+ for (const holder of holders) {
3729
+ const key = keyOf(host2(holder.address));
3734
3730
  const earlier = first.get(key);
3735
3731
  if (!earlier) {
3736
3732
  first.set(key, holder);
@@ -3742,7 +3738,7 @@ function checkAddresses(holders2) {
3742
3738
  findings.push({
3743
3739
  level: anycast ? "warning" : "error",
3744
3740
  device: holder.device.name,
3745
- message: `${host(holder.address)} on ${holder.name} is also on ${earlier.device.name} ${earlier.name}${anycast ? ", which is right only for anycast" : ""}`
3741
+ message: `${host2(holder.address)} on ${holder.name} is also on ${earlier.device.name} ${earlier.name}${anycast ? ", which is right only for anycast" : ""}`
3746
3742
  });
3747
3743
  }
3748
3744
  return findings;
@@ -3775,7 +3771,7 @@ function checkSessions(devices, owners) {
3775
3771
  if (session.remoteAs !== undefined && session.remoteAs !== farBgp.asn) {
3776
3772
  report(`expects AS${session.remoteAs}, and ${far.name} is AS${farBgp.asn}`);
3777
3773
  }
3778
- const back = sessionsOf(far).find((other) => owners.get(keyOf(other.address) ?? "")?.device === device && familyOf(other.address) === familyOf(session.address));
3774
+ const back = sessionsOf(far).find((other) => owners.get(keyOf(other.address) ?? "")?.device === device && familyOf2(other.address) === familyOf2(session.address));
3779
3775
  if (!back) {
3780
3776
  report(`${far.name} has no session back to ${device.name}`);
3781
3777
  continue;
@@ -3787,15 +3783,15 @@ function checkSessions(devices, owners) {
3787
3783
  }
3788
3784
  return findings;
3789
3785
  }
3790
- function checkVrrp(holders2) {
3786
+ function checkVrrp(holders) {
3791
3787
  const findings = [];
3792
3788
  const bySegment = new Map;
3793
- for (const holder of holders2) {
3789
+ for (const holder of holders) {
3794
3790
  const segment = network(holder.address);
3795
3791
  for (const group of holder.vrrp) {
3796
- if (!keyOf(group.address) || familyOf(group.address) !== familyOf(holder.address))
3792
+ if (!keyOf(group.address) || familyOf2(group.address) !== familyOf2(holder.address))
3797
3793
  continue;
3798
- if (!contains(segment, group.address))
3794
+ if (!contains2(segment, group.address))
3799
3795
  continue;
3800
3796
  bySegment.set(segment, [...bySegment.get(segment) ?? [], { holder, id: group.id, address: group.address }]);
3801
3797
  }
@@ -3852,15 +3848,15 @@ function checkTunnels(devices, owners) {
3852
3848
  }
3853
3849
 
3854
3850
  // src/core/validate.ts
3855
- function validate(network2) {
3851
+ function validate(network) {
3856
3852
  const findings = [];
3857
3853
  const seen = new Set;
3858
- for (const device of network2.devices) {
3854
+ for (const device of network.devices) {
3859
3855
  if (seen.has(device.name))
3860
3856
  findings.push({ level: "error", device: device.name, message: "declared twice" });
3861
3857
  seen.add(device.name);
3862
3858
  }
3863
- for (const device of network2.devices) {
3859
+ for (const device of network.devices) {
3864
3860
  const report = (message) => findings.push({ level: "error", device: device.name, message });
3865
3861
  checkDevice(device, report);
3866
3862
  const adapter = adapters[device.platform];
@@ -3870,9 +3866,9 @@ function validate(network2) {
3870
3866
  for (const field of adapter.unsupported(device))
3871
3867
  report(`${device.platform} cannot express ${field}`);
3872
3868
  }
3873
- findings.push(...checkAcross(network2.devices));
3874
- for (const check of network2.checks ?? [])
3875
- findings.push(...check(network2.devices));
3869
+ findings.push(...checkAcross(network.devices));
3870
+ for (const check of network.checks ?? [])
3871
+ findings.push(...check(network.devices));
3876
3872
  return findings;
3877
3873
  }
3878
3874
 
@@ -3904,13 +3900,13 @@ class Names {
3904
3900
  }
3905
3901
  function checkDevice(device, report) {
3906
3902
  const names = new Names(device, report);
3907
- const cidr = (where, value2) => {
3908
- if (!isCidr(value2))
3909
- report(`${where}: '${value2}' is not an address with a length`);
3903
+ const cidr = (where, value) => {
3904
+ if (!isCidr2(value))
3905
+ report(`${where}: '${value}' is not an address with a length`);
3910
3906
  };
3911
- const ip = (where, value2) => {
3912
- if (!isAddress(value2))
3913
- report(`${where}: '${value2}' is not an address`);
3907
+ const ip = (where, value) => {
3908
+ if (!isAddress2(value))
3909
+ report(`${where}: '${value}' is not an address`);
3914
3910
  };
3915
3911
  if (!device.users?.[device.connection.user])
3916
3912
  report(`connection user '${device.connection.user}' is not one of its users`);
@@ -3936,11 +3932,11 @@ function checkVlans(device, report) {
3936
3932
  }
3937
3933
  function checkAddresses2(device, report, cidr, ip) {
3938
3934
  const holderOf = new Map;
3939
- const holders2 = [
3935
+ const holders = [
3940
3936
  ...Object.entries(device.ports ?? {}).map(([name, settings]) => ({ where: `port ${name}`, settings })),
3941
3937
  ...Object.entries(device.interfaces ?? {}).map(([name, settings]) => ({ where: `interface ${name}`, settings }))
3942
3938
  ];
3943
- for (const { where, settings } of holders2) {
3939
+ for (const { where, settings } of holders) {
3944
3940
  for (const entry of settings?.addresses ?? []) {
3945
3941
  const address = typeof entry === "string" ? entry : entry.address;
3946
3942
  cidr(where, address);
@@ -4003,7 +3999,7 @@ function checkInterfaces(device, names, report, ip) {
4003
3999
  if (iface.type === "wireguard") {
4004
4000
  for (const peer of iface.peers ?? []) {
4005
4001
  for (const address of peer.allowed_addresses) {
4006
- if (!isCidr(address))
4002
+ if (!isCidr2(address))
4007
4003
  report(`interface ${name} peer ${peer.name}: '${address}' is not an address with a length`);
4008
4004
  }
4009
4005
  }
@@ -4012,7 +4008,7 @@ function checkInterfaces(device, names, report, ip) {
4012
4008
  }
4013
4009
  function checkRouting(device, names, report, cidr, ip) {
4014
4010
  const routing = device.routing;
4015
- const policies2 = device.policies ?? {};
4011
+ const policies = device.policies ?? {};
4016
4012
  for (const route of routing?.static ?? []) {
4017
4013
  cidr("static route", route.prefix);
4018
4014
  if (!route.via && !route.interface && !route.blackhole)
@@ -4023,7 +4019,7 @@ function checkRouting(device, names, report, cidr, ip) {
4023
4019
  names.expect(`static route ${route.prefix} VRF`, route.vrf, route.vrf !== undefined && route.vrf in (device.vrfs ?? {}));
4024
4020
  }
4025
4021
  const bgp = routing?.bgp;
4026
- const isPolicy = (policy) => policy !== undefined && (policy in policies2);
4022
+ const isPolicy = (policy) => policy !== undefined && (policy in policies);
4027
4023
  for (const prefix of bgp?.networks ?? [])
4028
4024
  cidr("BGP network", prefix);
4029
4025
  for (const [name, group] of Object.entries(bgp?.groups ?? {})) {
@@ -4041,7 +4037,7 @@ function checkRouting(device, names, report, cidr, ip) {
4041
4037
  names.expect(`neighbor ${name} policy`, policy, isPolicy(policy));
4042
4038
  }
4043
4039
  const local = neighbor.local_address ?? group?.local_address;
4044
- if (local && familyOf(local) !== familyOf(neighbor.address))
4040
+ if (local && familyOf2(local) !== familyOf2(neighbor.address))
4045
4041
  report(`neighbor ${name} local_address and address differ in family`);
4046
4042
  }
4047
4043
  for (const [areaId, area] of Object.entries(routing?.ospf?.areas ?? {})) {
@@ -4052,11 +4048,11 @@ function checkRouting(device, names, report, cidr, ip) {
4052
4048
  }
4053
4049
  }
4054
4050
  function checkPolicies(device, names, report, cidr) {
4055
- const policies2 = device.policies ?? {};
4051
+ const policies = device.policies ?? {};
4056
4052
  const sets = device.prefix_sets ?? {};
4057
- for (const [name, rules] of Object.entries(policies2)) {
4053
+ for (const [name, rules] of Object.entries(policies)) {
4058
4054
  for (const rule of rules) {
4059
- names.expect(`policy ${name} call`, rule.call, rule.call !== undefined && rule.call in policies2);
4055
+ names.expect(`policy ${name} call`, rule.call, rule.call !== undefined && rule.call in policies);
4060
4056
  const set = rule.match?.prefix_set;
4061
4057
  names.expect(`policy ${name} prefix set`, set, set !== undefined && set in sets);
4062
4058
  if (rule.match?.prefix)
@@ -4066,11 +4062,11 @@ function checkPolicies(device, names, report, cidr) {
4066
4062
  const visit = (policy, path) => {
4067
4063
  if (path.includes(policy))
4068
4064
  return report(`policy ${[...path, policy].join(" -> ")} calls itself`);
4069
- for (const rule of policies2[policy] ?? [])
4065
+ for (const rule of policies[policy] ?? [])
4070
4066
  if (rule.call)
4071
4067
  visit(rule.call, [...path, policy]);
4072
4068
  };
4073
- for (const policy of Object.keys(policies2))
4069
+ for (const policy of Object.keys(policies))
4074
4070
  visit(policy, []);
4075
4071
  }
4076
4072
  function checkFirewall(device, names, cidr) {
@@ -4078,7 +4074,7 @@ function checkFirewall(device, names, cidr) {
4078
4074
  const sets = firewall?.address_sets ?? {};
4079
4075
  for (const [name, members] of Object.entries(sets)) {
4080
4076
  for (const member of members) {
4081
- if (!isAddress(member))
4077
+ if (!isAddress2(member))
4082
4078
  cidr(`address set ${name}`, member);
4083
4079
  }
4084
4080
  }
@@ -4104,12 +4100,12 @@ function checkServices(device, names, report, cidr) {
4104
4100
  for (const [name, server] of Object.entries(device.dhcp ?? {})) {
4105
4101
  names.expectInterface(`DHCP ${name} interface`, server.interface);
4106
4102
  cidr(`DHCP ${name}`, server.network);
4107
- if (!isCidr(server.network))
4103
+ if (!isCidr2(server.network))
4108
4104
  continue;
4109
4105
  const reserved = (server.reservations ?? []).map((reservation) => reservation.address);
4110
- const addresses2 = [server.gateway, ...server.pool, ...reserved];
4111
- for (const address of addresses2) {
4112
- if (!contains(server.network, address))
4106
+ const addresses = [server.gateway, ...server.pool, ...reserved];
4107
+ for (const address of addresses) {
4108
+ if (!contains2(server.network, address))
4113
4109
  report(`DHCP ${name}: ${address} is outside ${server.network}`);
4114
4110
  }
4115
4111
  }
@@ -4131,8 +4127,8 @@ function checkServices(device, names, report, cidr) {
4131
4127
  report("SNMP is on with neither a community nor a user");
4132
4128
  }
4133
4129
  function checkCertificates(device, names, report) {
4134
- const certificates2 = device.certificates ?? {};
4135
- for (const [name, certificate] of Object.entries(certificates2)) {
4130
+ const certificates = device.certificates ?? {};
4131
+ for (const [name, certificate] of Object.entries(certificates)) {
4136
4132
  try {
4137
4133
  new X509Certificate2(certificate.certificate);
4138
4134
  } catch {
@@ -4141,8 +4137,8 @@ function checkCertificates(device, names, report) {
4141
4137
  }
4142
4138
  for (const service of ["https", "api_tls"]) {
4143
4139
  const name = device.management?.[service]?.certificate;
4144
- names.expect(`${service} certificate`, name, name !== undefined && name in certificates2);
4145
- if (name && certificates2[name] && !certificates2[name].private_key) {
4140
+ names.expect(`${service} certificate`, name, name !== undefined && name in certificates);
4141
+ if (name && certificates[name] && !certificates[name].private_key) {
4146
4142
  report(`${service} certificate ${name} has no private key to serve with`);
4147
4143
  }
4148
4144
  }
@@ -4162,7 +4158,7 @@ function markOf(graph, node) {
4162
4158
  }
4163
4159
  function mermaid(graph) {
4164
4160
  const ids = new Map(graph.nodes.map((node, index) => [node.id, `n${index}`]));
4165
- const text = (...lines2) => `"${lines2.map((line2) => line2.replace(/"/g, "#quot;").replace(/</g, "#lt;").replace(/>/g, "#gt;")).join("<br/>")}"`;
4161
+ const text = (...lines) => `"${lines.map((line) => line.replace(/"/g, "#quot;").replace(/</g, "#lt;").replace(/>/g, "#gt;")).join("<br/>")}"`;
4166
4162
  const shapes = {
4167
4163
  device: (label) => `[${label}]`,
4168
4164
  segment: (label) => `([${label}])`,
@@ -4188,7 +4184,7 @@ function mermaid(graph) {
4188
4184
  `;
4189
4185
  }
4190
4186
  function dot(graph) {
4191
- const text = (value2) => `"${value2.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
4187
+ const text = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
4192
4188
  const colors = { error: "#d1242f", warning: "#bf8700" };
4193
4189
  const shapes = {
4194
4190
  device: "shape=box, style=rounded",
@@ -4226,42 +4222,41 @@ import { basename, dirname, extname, join as join3, resolve as resolve2 } from "
4226
4222
  import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
4227
4223
  import { join as join2, relative } from "node:path";
4228
4224
  var SKIPPED = new Set(["node_modules", ".git", ".circuit", "dist"]);
4229
- function projectSources(root2, directory = root2) {
4230
- const entries2 = readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
4231
- return entries2.flatMap((entry) => {
4225
+ function projectSources(root, directory = root) {
4226
+ const entries = readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
4227
+ return entries.flatMap((entry) => {
4232
4228
  if (entry.isDirectory())
4233
- return SKIPPED.has(entry.name) ? [] : projectSources(root2, join2(directory, entry.name));
4234
- return /\.(ts|mts|cts)$/.test(entry.name) ? [relative(root2, join2(directory, entry.name))] : [];
4229
+ return SKIPPED.has(entry.name) ? [] : projectSources(root, join2(directory, entry.name));
4230
+ return /\.(ts|mts|cts)$/.test(entry.name) ? [relative(root, join2(directory, entry.name))] : [];
4235
4231
  });
4236
4232
  }
4237
- var escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4233
+ var FIRST_NAME = /^[\s\S]*?\bname\s*:\s*(["'`])(.*?)\1/;
4238
4234
  function declaresDevice(text, name) {
4239
- const declares = new RegExp(`\\bname\\s*:\\s*(["'\`])${escape(name)}\\1`);
4240
- return text.includes("defineDevice") && declares.test(text);
4235
+ return [...text.matchAll(/\bdefineDevice\s*\(/g)].some((call) => FIRST_NAME.exec(text.slice(call.index + call[0].length))?.[2] === name);
4241
4236
  }
4242
- function deviceFiles(root2, name, sources = projectSources(root2)) {
4243
- return sources.filter((file) => declaresDevice(readFileSync2(join2(root2, file), "utf8"), name));
4237
+ function deviceFiles(root, name, sources = projectSources(root)) {
4238
+ return sources.filter((file) => declaresDevice(readFileSync2(join2(root, file), "utf8"), name));
4244
4239
  }
4245
4240
 
4246
4241
  // src/cli/rename.ts
4247
- var escape2 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4248
- function planRename(root2, from, to, options) {
4249
- const changes2 = [];
4242
+ var escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4243
+ function planRename(root, from, to, options) {
4244
+ const changes = [];
4250
4245
  const moves = [];
4251
- const exact = new RegExp(`(["'\`])${escape2(from)}\\1`, "g");
4252
- const anywhere = new RegExp(`(^|[^A-Za-z0-9_.-])${escape2(from)}(?![A-Za-z0-9_-])`);
4253
- const sources = projectSources(root2);
4246
+ const exact = new RegExp(`(["'\`])${escape(from)}\\1`, "g");
4247
+ const anywhere = new RegExp(`(^|[^A-Za-z0-9_.-])${escape(from)}(?![A-Za-z0-9_-])`);
4248
+ const sources = projectSources(root);
4254
4249
  const declaring = new Set;
4255
4250
  for (const file of sources) {
4256
- const text = readFileSync3(join3(root2, file), "utf8");
4251
+ const text = readFileSync3(join3(root, file), "utf8");
4257
4252
  const lines = text.split(`
4258
4253
  `);
4259
4254
  if (declaresDevice(text, from))
4260
4255
  declaring.add(file);
4261
4256
  for (const [index, before] of lines.entries()) {
4262
- const after = before.replace(exact, (_match, quote3) => `${quote3}${to}${quote3}`);
4257
+ const after = before.replace(exact, (_match, quote) => `${quote}${to}${quote}`);
4263
4258
  if (after !== before)
4264
- changes2.push({ file, line: index + 1, before, after });
4259
+ changes.push({ file, line: index + 1, before, after });
4265
4260
  }
4266
4261
  }
4267
4262
  if (!declaring.size) {
@@ -4275,58 +4270,58 @@ function planRename(root2, from, to, options) {
4275
4270
  throw new Error(`${[...declaring].join(" and ")} both declare name: "${from}"; rename the file by hand`);
4276
4271
  const [file] = [...declaring];
4277
4272
  const target = join3(dirname(file), `${to}${extname(file)}`);
4278
- if (existsSync2(join3(root2, target)))
4273
+ if (existsSync2(join3(root, target)))
4279
4274
  throw new Error(`${target} already exists`);
4280
4275
  moves.push({ from: file, to: target });
4281
- changes2.push(...importChanges(root2, sources, file, target, changes2));
4276
+ changes.push(...importChanges(root, sources, file, target, changes));
4282
4277
  }
4283
- const mentions = sources.flatMap((file) => readFileSync3(join3(root2, file), "utf8").split(`
4278
+ const mentions = sources.flatMap((file) => readFileSync3(join3(root, file), "utf8").split(`
4284
4279
  `).flatMap((original, index) => {
4285
- const text = changes2.find((change) => change.file === file && change.line === index + 1)?.after ?? original;
4280
+ const text = changes.find((change) => change.file === file && change.line === index + 1)?.after ?? original;
4286
4281
  return anywhere.test(text) ? [{ file, line: index + 1, text: text.trim() }] : [];
4287
4282
  }));
4288
- return { changes: changes2, moves, mentions };
4283
+ return { changes, moves, mentions };
4289
4284
  }
4290
- function importChanges(root2, sources, file, target, earlier) {
4285
+ function importChanges(root, sources, file, target, earlier) {
4291
4286
  const result = [];
4292
4287
  const withoutExtension = (path) => path.replace(/\.(ts|mts|cts|js|mjs|cjs)$/, "");
4293
- const moved = withoutExtension(resolve2(root2, file));
4288
+ const moved = withoutExtension(resolve2(root, file));
4294
4289
  for (const source of sources) {
4295
- const lines = readFileSync3(join3(root2, source), "utf8").split(`
4290
+ const lines = readFileSync3(join3(root, source), "utf8").split(`
4296
4291
  `);
4297
4292
  for (const [index, original] of lines.entries()) {
4298
- const line2 = index + 1;
4299
- const changed = earlier.find((change) => change.file === source && change.line === line2);
4293
+ const line = index + 1;
4294
+ const changed = earlier.find((change) => change.file === source && change.line === line);
4300
4295
  const before = changed?.after ?? original;
4301
- const after = before.replace(/(\bfrom\s+|\bimport\s*\(\s*)(["'])(\.{1,2}\/[^"']+)\2/g, (match, lead, quote3, specifier) => {
4302
- if (withoutExtension(resolve2(root2, dirname(source), specifier)) !== moved)
4296
+ const after = before.replace(/(\bfrom\s+|\bimport\s*\(\s*)(["'])(\.{1,2}\/[^"']+)\2/g, (match, lead, quote, specifier) => {
4297
+ if (withoutExtension(resolve2(root, dirname(source), specifier)) !== moved)
4303
4298
  return match;
4304
4299
  const extension = specifier.match(/\.(ts|mts|cts|js|mjs|cjs)$/)?.[0] ?? "";
4305
4300
  const renamed = `${dirname(specifier)}/${basename(withoutExtension(target))}${extension}`;
4306
- return `${lead}${quote3}${renamed}${quote3}`;
4301
+ return `${lead}${quote}${renamed}${quote}`;
4307
4302
  });
4308
4303
  if (after === before)
4309
4304
  continue;
4310
4305
  if (changed)
4311
4306
  changed.after = after;
4312
4307
  else
4313
- result.push({ file: source, line: line2, before: original, after });
4308
+ result.push({ file: source, line, before: original, after });
4314
4309
  }
4315
4310
  }
4316
4311
  return result;
4317
4312
  }
4318
- function applyRename(root2, plan4) {
4319
- const files = new Set(plan4.changes.map((change) => change.file));
4313
+ function applyRename(root, plan) {
4314
+ const files = new Set(plan.changes.map((change) => change.file));
4320
4315
  for (const file of files) {
4321
- const lines = readFileSync3(join3(root2, file), "utf8").split(`
4316
+ const lines = readFileSync3(join3(root, file), "utf8").split(`
4322
4317
  `);
4323
- for (const change of plan4.changes.filter((each) => each.file === file))
4318
+ for (const change of plan.changes.filter((each) => each.file === file))
4324
4319
  lines[change.line - 1] = change.after;
4325
- writeFileSync(join3(root2, file), lines.join(`
4320
+ writeFileSync(join3(root, file), lines.join(`
4326
4321
  `));
4327
4322
  }
4328
- for (const move of plan4.moves)
4329
- renameSync(join3(root2, move.from), join3(root2, move.to));
4323
+ for (const move of plan.moves)
4324
+ renameSync(join3(root, move.from), join3(root, move.to));
4330
4325
  }
4331
4326
 
4332
4327
  // src/cli/index.ts
@@ -4370,7 +4365,7 @@ var HELP = `usage: circuit <command> [device...] [options]
4370
4365
  var DEFAULT_CONFIG = "circuit.config.ts";
4371
4366
  var [command2, ...args] = process.argv.slice(2);
4372
4367
  if (command2 === "new") {
4373
- const { create } = await import("../chunks/new-7n8qvtjd.js");
4368
+ const { create } = await import("../chunks/new-ny89s7y7.js");
4374
4369
  await create(args);
4375
4370
  process.exit(0);
4376
4371
  }
@@ -4445,18 +4440,18 @@ function guard(devices) {
4445
4440
  fail(`
4446
4441
  ${errors} error(s).`);
4447
4442
  }
4448
- function show(device, plan4) {
4449
- const commands = plan4.steps.reduce((count, step) => count + step.send.length, 0);
4450
- const summary = commands ? `${commands} command(s) in ${plan4.steps.length} step(s)` : "matches";
4443
+ function show(device, plan) {
4444
+ const commands = plan.steps.reduce((count, step) => count + step.send.length, 0);
4445
+ const summary = commands ? `${commands} command(s) in ${plan.steps.length} step(s)` : "matches";
4451
4446
  console.log(`${device.name}: ${summary}`);
4452
- for (const step of plan4.steps) {
4447
+ for (const step of plan.steps) {
4453
4448
  console.log(` ${step.title}`);
4454
- for (const line2 of step.show)
4455
- console.log(` ${line2.slice(0, 220)}`);
4449
+ for (const line of step.show)
4450
+ console.log(` ${line.slice(0, 220)}`);
4456
4451
  }
4457
- for (const problem of plan4.problems)
4452
+ for (const problem of plan.problems)
4458
4453
  console.log(` PROBLEM ${problem}`);
4459
- return commands + plan4.problems.length;
4454
+ return commands + plan.problems.length;
4460
4455
  }
4461
4456
  function validateCommand() {
4462
4457
  guard([...network2.devices]);
@@ -4477,8 +4472,8 @@ function diffCommand() {
4477
4472
  continue;
4478
4473
  }
4479
4474
  const adapter = adapterOf(device);
4480
- const plan4 = adapter.plan(adapter.render(device), readFileSync4(path, "utf8"), options);
4481
- differences += show(device, plan4);
4475
+ const plan = adapter.plan(adapter.render(device), readFileSync4(path, "utf8"), options);
4476
+ differences += show(device, plan);
4482
4477
  }
4483
4478
  process.exit(differences ? 1 : 0);
4484
4479
  }
@@ -4505,10 +4500,10 @@ async function applyCommand() {
4505
4500
  const adapter = adapterOf(device);
4506
4501
  const live = await adapter.read(device, network2.devices);
4507
4502
  writeState(device, live);
4508
- const plan4 = adapter.plan(adapter.render(device), live, options);
4509
- if (!show(device, plan4))
4503
+ const plan = adapter.plan(adapter.render(device), live, options);
4504
+ if (!show(device, plan))
4510
4505
  return;
4511
- if (plan4.problems.length)
4506
+ if (plan.problems.length)
4512
4507
  fail(`
4513
4508
  Not sent: resolve the problems first.`);
4514
4509
  if (!confirmed) {
@@ -4516,13 +4511,13 @@ Not sent: resolve the problems first.`);
4516
4511
  Nothing sent. Re-run with --confirm.`);
4517
4512
  return;
4518
4513
  }
4519
- await adapter.apply(device, network2.devices, plan4, options);
4514
+ await adapter.apply(device, network2.devices, plan, options);
4520
4515
  writeState(device, await adapter.read(device, network2.devices));
4521
4516
  console.log("snapshot updated");
4522
4517
  }
4523
4518
  async function refreshCommand() {
4524
4519
  const device = one();
4525
- const fill2 = adapterOf(device).fill ?? fail(`${device.platform} cannot hold a fetched prefix set`);
4520
+ const fill = adapterOf(device).fill ?? fail(`${device.platform} cannot hold a fetched prefix set`);
4526
4521
  for (const [name, set] of Object.entries(device.prefix_sets ?? {})) {
4527
4522
  if (!("source" in set))
4528
4523
  continue;
@@ -4533,7 +4528,7 @@ async function refreshCommand() {
4533
4528
  }
4534
4529
  console.log(`${name}: ${prefixes.length} prefixes from ${registry} ${query}`);
4535
4530
  if (confirmed)
4536
- await fill2(device, network2.devices, name, family, prefixes);
4531
+ await fill(device, network2.devices, name, family, prefixes);
4537
4532
  }
4538
4533
  if (!confirmed)
4539
4534
  console.log(`
@@ -4581,13 +4576,13 @@ function communitiesCommand() {
4581
4576
  }
4582
4577
  }
4583
4578
  async function peeringdbCommand() {
4584
- const plan4 = await planPeeringDb(network2);
4585
- for (const name of plan4.unknown) {
4579
+ const plan = await planPeeringDb(network2);
4580
+ for (const name of plan.unknown) {
4586
4581
  console.log(` PeeringDB lists ${name}, which the config does not. Remove it by hand if the network left.`);
4587
4582
  }
4588
- for (const change of plan4.changes)
4583
+ for (const change of plan.changes)
4589
4584
  console.log(` ${change.summary}`);
4590
- if (!plan4.changes.length) {
4585
+ if (!plan.changes.length) {
4591
4586
  console.log("PeeringDB matches the config.");
4592
4587
  return;
4593
4588
  }
@@ -4596,17 +4591,17 @@ async function peeringdbCommand() {
4596
4591
  Nothing sent. Re-run with --confirm.`);
4597
4592
  return;
4598
4593
  }
4599
- await sendPeeringDb(network2, plan4.changes);
4594
+ await sendPeeringDb(network2, plan.changes);
4600
4595
  }
4601
4596
  async function librenmsCommand() {
4602
- const plan4 = await planLibreNms(network2);
4603
- for (const hostname of plan4.unknown)
4597
+ const plan = await planLibreNms(network2);
4598
+ for (const hostname of plan.unknown)
4604
4599
  console.log(` LibreNMS monitors ${hostname}, which the config does not. Remove it by hand if it is gone.`);
4605
- for (const device of plan4.withoutSnmp)
4600
+ for (const device of plan.withoutSnmp)
4606
4601
  console.log(` ${device.name} has no SNMP in its config, so LibreNMS cannot poll it. Add management.snmp first.`);
4607
- for (const device of plan4.additions)
4602
+ for (const device of plan.additions)
4608
4603
  console.log(` add ${device.name}, ${device.connection.host}`);
4609
- if (!plan4.additions.length) {
4604
+ if (!plan.additions.length) {
4610
4605
  console.log("LibreNMS monitors every device it can.");
4611
4606
  return;
4612
4607
  }
@@ -4615,7 +4610,7 @@ async function librenmsCommand() {
4615
4610
  Nothing sent. Re-run with --confirm.`);
4616
4611
  return;
4617
4612
  }
4618
- await sendLibreNms(network2, plan4.additions);
4613
+ await sendLibreNms(network2, plan.additions);
4619
4614
  }
4620
4615
  function prometheusCommand() {
4621
4616
  if (flags.includes("--auths")) {
@@ -4630,14 +4625,14 @@ function prometheusCommand() {
4630
4625
  console.error(`${device.name} has no SNMP in its config; left out`);
4631
4626
  console.log(JSON.stringify(targets, null, 4));
4632
4627
  }
4633
- function secretsIn(value2, found = new Set) {
4634
- if (isSecret(value2))
4635
- found.add(referenceOf(value2));
4636
- else if (Array.isArray(value2))
4637
- for (const item of value2)
4628
+ function secretsIn(value, found = new Set) {
4629
+ if (isSecret(value))
4630
+ found.add(referenceOf(value));
4631
+ else if (Array.isArray(value))
4632
+ for (const item of value)
4638
4633
  secretsIn(item, found);
4639
- else if (typeof value2 === "object" && value2 !== null)
4640
- for (const item of Object.values(value2))
4634
+ else if (typeof value === "object" && value !== null)
4635
+ for (const item of Object.values(value))
4641
4636
  secretsIn(item, found);
4642
4637
  return found;
4643
4638
  }
@@ -4665,10 +4660,10 @@ function driftOf(device) {
4665
4660
  return { status: "no snapshot", lines: [] };
4666
4661
  try {
4667
4662
  const adapter = adapterOf(device);
4668
- const plan4 = adapter.plan(adapter.render(device), readFileSync4(path, "utf8"), options);
4663
+ const plan = adapter.plan(adapter.render(device), readFileSync4(path, "utf8"), options);
4669
4664
  const lines = [
4670
- ...plan4.steps.flatMap((step) => [step.title, ...step.show.map((line2) => ` ${line2}`)]),
4671
- ...plan4.problems.map((problem) => `PROBLEM ${problem}`)
4665
+ ...plan.steps.flatMap((step) => [step.title, ...step.show.map((line) => ` ${line}`)]),
4666
+ ...plan.problems.map((problem) => `PROBLEM ${problem}`)
4672
4667
  ];
4673
4668
  return { status: lines.length ? "differs" : "matches", lines };
4674
4669
  } catch (error) {
@@ -4711,7 +4706,7 @@ function printSegment(segment, indent) {
4711
4706
  }
4712
4707
  function addressesCommand() {
4713
4708
  const [within] = names;
4714
- if (within && !isCidr(within))
4709
+ if (within && !isCidr2(within))
4715
4710
  fail(`${within} is not a prefix, such as 10.0.0.0/16`);
4716
4711
  const lengthText = flagValue("length");
4717
4712
  const length = lengthText === undefined ? undefined : Number(lengthText);
@@ -4761,23 +4756,23 @@ function renameCommand() {
4761
4756
  fail(`${to} is not a hostname: letters, digits, dots, dashes and underscores`);
4762
4757
  const snapshot = statePath(device);
4763
4758
  const snapshots = existsSync3(snapshot) ? [relative2(root2, snapshot)] : [];
4764
- let plan4;
4759
+ let plan;
4765
4760
  try {
4766
- plan4 = planRename(root2, from, to, { file: flags.includes("--file"), snapshots });
4761
+ plan = planRename(root2, from, to, { file: flags.includes("--file"), snapshots });
4767
4762
  } catch (error) {
4768
4763
  fail(error.message);
4769
4764
  }
4770
- for (const change of plan4.changes) {
4765
+ for (const change of plan.changes) {
4771
4766
  console.log(`${change.file}:${change.line}`);
4772
4767
  console.log(` - ${change.before.trim()}`);
4773
4768
  console.log(` + ${change.after.trim()}`);
4774
4769
  }
4775
- for (const move of plan4.moves)
4770
+ for (const move of plan.moves)
4776
4771
  console.log(`move ${move.from} to ${move.to}`);
4777
- if (plan4.mentions.length) {
4772
+ if (plan.mentions.length) {
4778
4773
  console.log(`
4779
4774
  Still naming ${from}, left as they are: change them by hand if they mean the device.`);
4780
- for (const mention of plan4.mentions)
4775
+ for (const mention of plan.mentions)
4781
4776
  console.log(` ${mention.file}:${mention.line} ${mention.text.slice(0, 160)}`);
4782
4777
  }
4783
4778
  if (!confirmed) {
@@ -4785,7 +4780,7 @@ Still naming ${from}, left as they are: change them by hand if they mean the dev
4785
4780
  Nothing written. Re-run with --confirm.`);
4786
4781
  return;
4787
4782
  }
4788
- applyRename(root2, plan4);
4783
+ applyRename(root2, plan);
4789
4784
  console.log(`
4790
4785
  Renamed. Run circuit validate, then circuit apply ${to}: the device keeps its old hostname until then.`);
4791
4786
  }
@@ -4806,13 +4801,13 @@ async function studioCommand() {
4806
4801
  const port = Number(flagValue("port") ?? 4600);
4807
4802
  if (!Number.isInteger(port) || port < 1 || port > 65535)
4808
4803
  fail("--port takes a number from 1 to 65535");
4809
- const { isLoopback, studio } = await import("../chunks/server-5shmsmse.js");
4810
- const host2 = flagValue("host") ?? "127.0.0.1";
4804
+ const { isLoopback, studio } = await import("../chunks/server-zapfzhv4.js");
4805
+ const host = flagValue("host") ?? "127.0.0.1";
4811
4806
  const allowHosts = (flagValue("allow-host") ?? "").split(",").filter(Boolean);
4812
- if (!isLoopback(host2) && !allowHosts.length) {
4813
- fail(`Studio on ${host2} needs --allow-host=<name>, the name people reach it by, such as studio.example.com`);
4807
+ if (!isLoopback(host) && !allowHosts.length) {
4808
+ fail(`Studio on ${host} needs --allow-host=<name>, the name people reach it by, such as studio.example.com`);
4814
4809
  }
4815
- await studio({ configPath, root: root2, port, open: !flags.includes("--no-open"), host: host2, allowHosts });
4810
+ await studio({ configPath, root: root2, port, open: !flags.includes("--no-open"), host, allowHosts });
4816
4811
  }
4817
4812
  var commands = {
4818
4813
  validate: validateCommand,