@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/README.md CHANGED
@@ -22,7 +22,7 @@ Documentation: [circuit.tako.id](https://circuit.tako.id)
22
22
 
23
23
  <picture>
24
24
  <source media="(prefers-color-scheme: dark)" srcset="docs/public/screenshots/studio-dark.png">
25
- <img src="docs/public/screenshots/studio-light.png" alt="Studio's graph showing the cables between five devices. One device is outlined in amber because it differs from its last snapshot.">
25
+ <img src="docs/public/screenshots/studio-light.png" alt="Studio's VLAN graph with devices that differ from their snapshots marked by orange dashed borders, beside the layer details and legend.">
26
26
  </picture>
27
27
 
28
28
  ## Start
@@ -1,24 +1,62 @@
1
1
  import {
2
- contains,
3
- familyOf,
4
- formatAddress,
5
- host,
6
- isAddress,
7
- isCidr,
8
- network,
2
+ familyOf2,
9
3
  parse,
10
- prefixesBetween,
4
+ split,
5
+ isAddress2,
6
+ isCidr2,
7
+ network,
8
+ contains2,
9
+ formatAddress,
11
10
  rangeOf,
12
- split
13
- } from "./index-xm9nma2t.js";
11
+ prefixesBetween,
12
+ host2
13
+ } from "./index-v8zgr7wm.js";
14
+
15
+ // src/adapters/devices/raisecom-ros/models.ts
16
+ var models = {
17
+ "RAX721-C-6C48": {
18
+ ports: { "100g": 6, "25g": 24, "10g": 24 },
19
+ spelling: { "100g": "hundredgige 1/1/{n}", "25g": "twenty-fivegige 1/2/{n}", "10g": "tengigabitethernet 1/3/{n}" }
20
+ }
21
+ };
22
+
23
+ // src/adapters/devices/routeros/models.ts
24
+ var models2 = {
25
+ "CCR2216-1G-12XS-2XQ": {
26
+ ports: { "100g": 2, "25g": 12, "1g": 1 },
27
+ spelling: { "100g": "qsfp28-{n}-1", "25g": "sfp28-{n}", "1g": "ether{n}" }
28
+ }
29
+ };
30
+
31
+ // src/adapters/devices/vrp/models.ts
32
+ var models3 = {
33
+ "CE6855-48S6Q-HI": { ports: { "10g": 48, "40g": 6 }, spelling: { "10g": "10GE1/0/{n}", "40g": "40GE1/0/{n}" } }
34
+ };
35
+
36
+ // src/adapters/devices/catalog.ts
37
+ var catalog = { routeros: models2, vrp: models3, "raisecom-ros": models };
38
+ function portsOf(platform, model) {
39
+ const spec = catalog[platform]?.[model];
40
+ if (!spec)
41
+ throw new Error(`${platform} has no model ${model}`);
42
+ return Object.entries(spec.ports).flatMap(([speed, count]) => Array.from({ length: count }, (_, i) => `${speed}-${i + 1}`));
43
+ }
44
+ function spell(platform, model, port) {
45
+ const spec = catalog[platform]?.[model];
46
+ const parts = /^(\d+[gm])-(\d+)$/.exec(port);
47
+ const template = parts && spec?.spelling[parts[1]];
48
+ if (!template)
49
+ throw new Error(`${model} has no port ${port}`);
50
+ return template.replace("{n}", parts[2]);
51
+ }
14
52
 
15
53
  // src/core/graph.ts
16
54
  var LAYERS = ["l1", "l2", "l3", "bgp", "ospf", "tunnels"];
17
55
  var deviceId = (name) => `device:${name}`;
18
56
  function keyOf(address) {
19
- if (!address || !isAddress(address))
57
+ if (!address || !isAddress2(address))
20
58
  return;
21
- return `${familyOf(address)}:${parse(address)}`;
59
+ return `${familyOf2(address)}:${parse(address)}`;
22
60
  }
23
61
  var isHostRoute = (cidr) => {
24
62
  const { len, family } = split(cidr);
@@ -51,7 +89,7 @@ function routedOf(device) {
51
89
  function holdersOf(devices) {
52
90
  return devices.flatMap((device) => routedOf(device).flatMap(([name, settings]) => (settings.addresses ?? []).flatMap((entry) => {
53
91
  const address = typeof entry === "string" ? entry : entry.address;
54
- if (!isCidr(address))
92
+ if (!isCidr2(address))
55
93
  return [];
56
94
  const peer = typeof entry === "string" ? undefined : entry.peer;
57
95
  return [{ device, name, address, peer, vrrp: settings.vrrp ?? [] }];
@@ -156,7 +194,7 @@ function segments(holders, owners, graph) {
156
194
  layer: "l3",
157
195
  from: deviceId(holder.device.name),
158
196
  to: deviceId(far.device.name),
159
- label: `${host(holder.address)} to ${host(far.address)}`,
197
+ label: `${host2(holder.address)} to ${host2(far.address)}`,
160
198
  detail: [`${holder.device.name} ${holder.name} ${holder.address}`, `${far.device.name} ${far.name} ${far.address}`]
161
199
  });
162
200
  continue;
@@ -188,7 +226,7 @@ function segments(holders, owners, graph) {
188
226
  layer: "l3",
189
227
  from: deviceId(member.device.name),
190
228
  to: id,
191
- label: `${member.name} ${host(member.address)}`,
229
+ label: `${member.name} ${host2(member.address)}`,
192
230
  detail: [`${member.device.name} ${member.name} ${member.address}`]
193
231
  });
194
232
  }
@@ -229,7 +267,7 @@ function sessions(devices, owners, graph) {
229
267
  continue;
230
268
  const far = ownerOf(session);
231
269
  if (far && far !== session.device) {
232
- const back = all.find((other) => other.device === far && !paired.has(other) && ownerOf(other) === session.device && familyOf(other.address) === familyOf(session.address));
270
+ const back = all.find((other) => other.device === far && !paired.has(other) && ownerOf(other) === session.device && familyOf2(other.address) === familyOf2(session.address));
233
271
  if (back)
234
272
  paired.add(back);
235
273
  graph.edge({
@@ -265,7 +303,7 @@ function adjacencies(devices, graph) {
265
303
  const cost = settings?.cost ? `, cost ${settings.cost}` : "";
266
304
  for (const entry of routed?.addresses ?? []) {
267
305
  const address = typeof entry === "string" ? entry : entry.address;
268
- if (!isCidr(address) || isHostRoute(address))
306
+ if (!isCidr2(address) || isHostRoute(address))
269
307
  continue;
270
308
  const key = `${area} ${network(address)}`;
271
309
  const member = { device, name, line: `${device.name} ${name} ${address}${cost}` };
@@ -345,8 +383,8 @@ function wireguard(devices, owners, graph) {
345
383
  const farEndOf = (device, peer) => interfaces.find(({ device: other, iface }) => {
346
384
  if (other === device)
347
385
  return false;
348
- const tunnelAddresses = (iface.addresses ?? []).map((entry) => host(typeof entry === "string" ? entry : entry.address));
349
- const sendsFrom = tunnelAddresses.some((address) => isAddress(address) && peer.allowed_addresses.some((allowed) => isCidr(allowed) && contains(allowed, address)));
386
+ const tunnelAddresses = (iface.addresses ?? []).map((entry) => host2(typeof entry === "string" ? entry : entry.address));
387
+ const sendsFrom = tunnelAddresses.some((address) => isAddress2(address) && peer.allowed_addresses.some((allowed) => isCidr2(allowed) && contains2(allowed, address)));
350
388
  const endpoint = peer.endpoint ? endpointHost(peer.endpoint) : undefined;
351
389
  const isEndpoint = endpoint !== undefined && owners.get(keyOf(endpoint) ?? "")?.device === other;
352
390
  return sendsFrom || isEndpoint;
@@ -375,11 +413,11 @@ function wireguard(devices, owners, graph) {
375
413
  continue;
376
414
  }
377
415
  const endpoint = endpointHost(peer.endpoint);
378
- const outside2 = graph.node(`address:${endpoint}`, "outside", peer.name, [`WireGuard endpoint ${peer.endpoint}`]);
416
+ const outside = graph.node(`address:${endpoint}`, "outside", peer.name, [`WireGuard endpoint ${peer.endpoint}`]);
379
417
  graph.edge({
380
418
  layer: "tunnels",
381
419
  from: deviceId(device.name),
382
- to: outside2,
420
+ to: outside,
383
421
  label: `WireGuard ${name}`,
384
422
  detail: [`${device.name} ${name} peer ${peer.name}, allowed ${peer.allowed_addresses.join(", ")}`]
385
423
  });
@@ -403,7 +441,7 @@ function graphOf(devices) {
403
441
  const holders = holdersOf(devices);
404
442
  const owners = new Map;
405
443
  for (const holder of holders) {
406
- const key = keyOf(host(holder.address));
444
+ const key = keyOf(host2(holder.address));
407
445
  if (!owners.has(key))
408
446
  owners.set(key, holder);
409
447
  }
@@ -482,24 +520,24 @@ function comparePrefixes(a, b) {
482
520
  }
483
521
  var byPrefix = (a, b) => comparePrefixes(a.prefix, b.prefix);
484
522
  function addressesOf(devices, within) {
485
- const segments2 = new Map;
523
+ const segments = new Map;
486
524
  const blocks = [];
487
- const segmentOf = (address) => [...segments2.keys()].find((prefix) => contains(prefix, address));
525
+ const segmentOf = (address) => [...segments.keys()].find((prefix) => contains2(prefix, address));
488
526
  for (const holder of holdersOf(devices)) {
489
527
  const vrf = holder.device.ports?.[holder.name]?.vrf ?? holder.device.interfaces?.[holder.name]?.vrf;
490
528
  const what = `${holder.device.name} ${holder.name}${vrf ? `, VRF ${vrf}` : ""}`;
491
529
  if (isHostRoute2(holder.address) || holder.peer) {
492
- blocks.push({ prefix: `${host(holder.address)}/${bits(familyOf(holder.address))}`, what });
530
+ blocks.push({ prefix: `${host2(holder.address)}/${bits(familyOf2(holder.address))}`, what });
493
531
  continue;
494
532
  }
495
533
  const prefix = network(holder.address);
496
- const segment = segments2.get(prefix) ?? { uses: [], taken: [] };
497
- const value = numberOf(host(holder.address));
498
- segment.uses.push({ address: host(holder.address), what });
534
+ const segment = segments.get(prefix) ?? { uses: [], taken: [] };
535
+ const value = numberOf(host2(holder.address));
536
+ segment.uses.push({ address: host2(holder.address), what });
499
537
  segment.taken.push({ first: value, last: value });
500
- segments2.set(prefix, segment);
538
+ segments.set(prefix, segment);
501
539
  for (const group of holder.vrrp) {
502
- if (!isAddress(group.address) || !contains(prefix, group.address))
540
+ if (!isAddress2(group.address) || !contains2(prefix, group.address))
503
541
  continue;
504
542
  if (segment.uses.some((use) => use.address === group.address))
505
543
  continue;
@@ -512,7 +550,7 @@ function addressesOf(devices, within) {
512
550
  const prefix = segmentOf(first);
513
551
  if (!prefix)
514
552
  return false;
515
- const segment = segments2.get(prefix);
553
+ const segment = segments.get(prefix);
516
554
  if (segment.uses.some((use) => use.address === address))
517
555
  return true;
518
556
  segment.uses.push({ address, what });
@@ -522,19 +560,19 @@ function addressesOf(devices, within) {
522
560
  for (const device of devices) {
523
561
  for (const [name, server] of Object.entries(device.dhcp ?? {})) {
524
562
  const [start, end] = server.pool;
525
- if (isAddress(start) && isAddress(end))
563
+ if (isAddress2(start) && isAddress2(end))
526
564
  take(`${start}-${end}`, `DHCP pool ${name} on ${device.name}`, start, end);
527
565
  for (const reservation of server.reservations ?? []) {
528
- if (isAddress(reservation.address))
566
+ if (isAddress2(reservation.address))
529
567
  take(reservation.address, `reserved for ${reservation.description ?? reservation.mac} by ${device.name}`);
530
568
  }
531
569
  }
532
570
  for (const route of device.routing?.static ?? []) {
533
- if (route.via && isAddress(route.via))
571
+ if (route.via && isAddress2(route.via))
534
572
  take(route.via, `next hop for ${route.prefix} on ${device.name}`);
535
573
  }
536
574
  for (const [name, neighbor] of Object.entries(device.routing?.bgp?.neighbors ?? {})) {
537
- if (isAddress(neighbor.address))
575
+ if (isAddress2(neighbor.address))
538
576
  take(neighbor.address, `BGP neighbor ${name} of ${device.name}`);
539
577
  }
540
578
  for (const [name, iface] of Object.entries(device.interfaces ?? {})) {
@@ -542,12 +580,12 @@ function addressesOf(devices, within) {
542
580
  continue;
543
581
  for (const peer of iface.peers ?? []) {
544
582
  for (const allowed of peer.allowed_addresses) {
545
- if (!isCidr(allowed))
583
+ if (!isCidr2(allowed))
546
584
  continue;
547
585
  const what = `WireGuard peer ${peer.name} on ${device.name} ${name}`;
548
- if (isHostRoute2(allowed) && take(host(allowed), what))
586
+ if (isHostRoute2(allowed) && take(host2(allowed), what))
549
587
  continue;
550
- if (!segmentOf(host(allowed)))
588
+ if (!segmentOf(host2(allowed)))
551
589
  blocks.push({ prefix: network(allowed), what });
552
590
  }
553
591
  }
@@ -556,17 +594,17 @@ function addressesOf(devices, within) {
556
594
  const owners = new Map;
557
595
  for (const device of devices) {
558
596
  for (const prefix of device.routing?.bgp?.networks ?? []) {
559
- if (!isCidr(prefix))
597
+ if (!isCidr2(prefix))
560
598
  continue;
561
599
  owners.set(network(prefix), [...owners.get(network(prefix)) ?? [], device.name]);
562
600
  }
563
601
  }
564
602
  for (const device of devices) {
565
603
  for (const route of device.routing?.static ?? []) {
566
- if (!isCidr(route.prefix) || split(route.prefix).len === 0)
604
+ if (!isCidr2(route.prefix) || split(route.prefix).len === 0)
567
605
  continue;
568
- const overlapsSegment = [...segments2.keys()].some((prefix) => contains(route.prefix, prefix) || contains(prefix, route.prefix));
569
- const coversOwned = [...owners.keys()].some((owned) => contains(route.prefix, owned));
606
+ const overlapsSegment = [...segments.keys()].some((prefix) => contains2(route.prefix, prefix) || contains2(prefix, route.prefix));
607
+ const coversOwned = [...owners.keys()].some((owned) => contains2(route.prefix, owned));
570
608
  if (overlapsSegment || coversOwned)
571
609
  continue;
572
610
  const how = route.blackhole ? "discarded" : route.via ? `routed via ${route.via}` : `routed out ${route.interface}`;
@@ -575,12 +613,12 @@ function addressesOf(devices, within) {
575
613
  }
576
614
  const asked = within ? network(within) : undefined;
577
615
  const spacePrefixes = asked ? [asked] : [...owners.keys()];
578
- const spaceOf = (prefix) => spacePrefixes.filter((space) => contains(space, prefix)).sort((a, b) => split(b).len - split(a).len)[0];
616
+ const spaceOf = (prefix) => spacePrefixes.filter((space) => contains2(space, prefix)).sort((a, b) => split(b).len - split(a).len)[0];
579
617
  const toSegment = (prefix) => {
580
- const { uses, taken } = segments2.get(prefix);
618
+ const { uses, taken } = segments.get(prefix);
581
619
  const free = gaps(usable(prefix), taken);
582
620
  const total = free.reduce((sum, range) => sum + range.last - range.first + 1n, 0n);
583
- const family = familyOf(prefix);
621
+ const family = familyOf2(prefix);
584
622
  return {
585
623
  prefix,
586
624
  uses: uses.sort(byAddress),
@@ -591,11 +629,11 @@ function addressesOf(devices, within) {
591
629
  const uniqueBlocks = blocks.filter((block, index) => blocks.findIndex((other) => other.prefix === block.prefix && other.what === block.what) === index);
592
630
  const spaces = spacePrefixes.sort(comparePrefixes).map((space) => {
593
631
  const inside = (prefix) => spaceOf(prefix) === space;
594
- const spaceSegments = [...segments2.keys()].filter(inside).sort(comparePrefixes);
632
+ const spaceSegments = [...segments.keys()].filter(inside).sort(comparePrefixes);
595
633
  const spaceBlocks = uniqueBlocks.filter((block) => inside(block.prefix)).sort(byPrefix);
596
- const nested = spacePrefixes.filter((other) => other !== space && contains(space, other));
634
+ const nested = spacePrefixes.filter((other) => other !== space && contains2(space, other));
597
635
  const taken = [...spaceSegments, ...spaceBlocks.map((block) => block.prefix), ...nested].map((prefix) => rangeOf(prefix));
598
- const family = familyOf(space);
636
+ const family = familyOf2(space);
599
637
  return {
600
638
  prefix: space,
601
639
  owner: asked && !owners.has(space) ? "asked for" : `originated by ${(owners.get(space) ?? []).join(", ")}`,
@@ -608,7 +646,7 @@ function addressesOf(devices, within) {
608
646
  return {
609
647
  spaces,
610
648
  elsewhere: asked ? { segments: [], blocks: [] } : {
611
- segments: [...segments2.keys()].filter(outside).sort(comparePrefixes).map(toSegment),
649
+ segments: [...segments.keys()].filter(outside).sort(comparePrefixes).map(toSegment),
612
650
  blocks: uniqueBlocks.filter((block) => outside(block.prefix)).sort(byPrefix)
613
651
  }
614
652
  };
@@ -636,44 +674,6 @@ import { existsSync, readFileSync } from "node:fs";
636
674
  import { dirname, join } from "node:path";
637
675
  import { fileURLToPath } from "node:url";
638
676
 
639
- // src/adapters/devices/raisecom-ros/models.ts
640
- var models = {
641
- "RAX721-C-6C48": {
642
- ports: { "100g": 6, "25g": 24, "10g": 24 },
643
- spelling: { "100g": "hundredgige 1/1/{n}", "25g": "twenty-fivegige 1/2/{n}", "10g": "tengigabitethernet 1/3/{n}" }
644
- }
645
- };
646
-
647
- // src/adapters/devices/routeros/models.ts
648
- var models2 = {
649
- "CCR2216-1G-12XS-2XQ": {
650
- ports: { "100g": 2, "25g": 12, "1g": 1 },
651
- spelling: { "100g": "qsfp28-{n}-1", "25g": "sfp28-{n}", "1g": "ether{n}" }
652
- }
653
- };
654
-
655
- // src/adapters/devices/vrp/models.ts
656
- var models3 = {
657
- "CE6855-48S6Q-HI": { ports: { "10g": 48, "40g": 6 }, spelling: { "10g": "10GE1/0/{n}", "40g": "40GE1/0/{n}" } }
658
- };
659
-
660
- // src/adapters/devices/catalog.ts
661
- var catalog = { routeros: models2, vrp: models3, "raisecom-ros": models };
662
- function portsOf(platform, model) {
663
- const spec = catalog[platform]?.[model];
664
- if (!spec)
665
- throw new Error(`${platform} has no model ${model}`);
666
- return Object.entries(spec.ports).flatMap(([speed, count2]) => Array.from({ length: count2 }, (_, i) => `${speed}-${i + 1}`));
667
- }
668
- function spell(platform, model, port) {
669
- const spec = catalog[platform]?.[model];
670
- const parts = /^(\d+[gm])-(\d+)$/.exec(port);
671
- const template = parts && spec?.spelling[parts[1]];
672
- if (!template)
673
- throw new Error(`${model} has no port ${port}`);
674
- return template.replace("{n}", parts[2]);
675
- }
676
-
677
677
  // src/core/schema-docs.ts
678
678
  var SCHEMA_FILES = [
679
679
  ["device.ts", "Device"],
@@ -825,7 +825,7 @@ function sessionsOf(devices, owners) {
825
825
  return all.map(({ deviceObject, ...session }) => {
826
826
  const far = ownerOf(session.address);
827
827
  if (far && far !== deviceObject) {
828
- const back = all.some((other) => other.deviceObject === far && ownerOf(other.address) === deviceObject && familyOf(other.address) === familyOf(session.address));
828
+ const back = all.some((other) => other.deviceObject === far && ownerOf(other.address) === deviceObject && familyOf2(other.address) === familyOf2(session.address));
829
829
  return { ...session, far: far.name, ...back ? {} : { oneSided: true } };
830
830
  }
831
831
  return { ...session, ...session.remoteAs ? { far: `AS${session.remoteAs}` } : {} };
@@ -834,7 +834,7 @@ function sessionsOf(devices, owners) {
834
834
  function cablesOf(devices) {
835
835
  const byName = new Map(devices.map((device) => [device.name, device]));
836
836
  const drawn = new Set;
837
- const cables2 = [];
837
+ const cables = [];
838
838
  const vlansOf = (device, portName) => {
839
839
  const port = device.ports?.[portName];
840
840
  const lag = port?.lag ? device.interfaces?.[port.lag] : undefined;
@@ -854,7 +854,7 @@ function cablesOf(devices) {
854
854
  drawn.add(`${here}|${there}`);
855
855
  const farPort = byName.get(link.device)?.ports?.[link.port];
856
856
  const linksBack = farPort?.link?.device === device.name && farPort.link.port === portName;
857
- cables2.push({
857
+ cables.push({
858
858
  device: device.name,
859
859
  port: portName,
860
860
  farDevice: link.device,
@@ -867,7 +867,7 @@ function cablesOf(devices) {
867
867
  });
868
868
  }
869
869
  }
870
- return cables2;
870
+ return cables;
871
871
  }
872
872
  function packageRoot() {
873
873
  let directory = dirname(fileURLToPath(import.meta.url));
@@ -899,7 +899,7 @@ function schemaDocs() {
899
899
  function studioData(devices, graph, texts) {
900
900
  const owners = new Map;
901
901
  for (const holder of holdersOf(devices)) {
902
- const key = keyOf(host(holder.address));
902
+ const key = keyOf(host2(holder.address));
903
903
  if (!owners.has(key))
904
904
  owners.set(key, holder);
905
905
  }
@@ -1,11 +1,11 @@
1
1
  // src/core/addr.ts
2
- var familyOf = (address) => address.includes(":") ? "ipv6" : "ipv4";
2
+ var familyOf2 = (address) => address.includes(":") ? "ipv6" : "ipv4";
3
3
  var bitsOf = (family) => family === "ipv4" ? 32 : 128;
4
4
  function parse(address) {
5
5
  if (!address.includes(":")) {
6
6
  const octets = address.split(".");
7
- const valid2 = octets.length === 4 && octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255);
8
- if (!valid2)
7
+ const valid = octets.length === 4 && octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255);
8
+ if (!valid)
9
9
  return null;
10
10
  return octets.reduce((value, octet) => value << 8n | BigInt(octet), 0n);
11
11
  }
@@ -24,15 +24,15 @@ function parse(address) {
24
24
  }
25
25
  function split(cidr) {
26
26
  const [address, lengthText] = cidr.split("/");
27
- const family = familyOf(address);
27
+ const family = familyOf2(address);
28
28
  const length = Number(lengthText);
29
29
  const valid = parse(address) !== null && lengthText !== undefined && Number.isInteger(length) && length >= 0 && length <= bitsOf(family);
30
30
  if (!valid)
31
31
  throw new Error(`'${cidr}' is not an address with a length`);
32
32
  return { addr: address, len: length, family };
33
33
  }
34
- var isAddress = (text) => parse(text) !== null;
35
- function isCidr(text) {
34
+ var isAddress2 = (text) => parse(text) !== null;
35
+ function isCidr2(text) {
36
36
  try {
37
37
  split(text);
38
38
  return true;
@@ -73,10 +73,10 @@ function network(cidr) {
73
73
  const { addr, len, family } = split(cidr);
74
74
  return `${format(parse(addr) & mask(len, family), family)}/${len}`;
75
75
  }
76
- function contains(outer, inner) {
76
+ function contains2(outer, inner) {
77
77
  const outerParts = split(outer);
78
78
  const [innerAddress, innerLengthText] = inner.split("/");
79
- if (familyOf(innerAddress) !== outerParts.family)
79
+ if (familyOf2(innerAddress) !== outerParts.family)
80
80
  return false;
81
81
  const innerLength = innerLengthText === undefined ? bitsOf(outerParts.family) : Number(innerLengthText);
82
82
  if (innerLength < outerParts.len)
@@ -105,7 +105,7 @@ function prefixesBetween(first, last, family) {
105
105
  }
106
106
  var netmask = (length) => format(mask(length, "ipv4"), "ipv4");
107
107
  var wildcard = (length) => format(mask(length, "ipv4") ^ 0xffffffffn, "ipv4");
108
- var host = (cidr) => cidr.split("/")[0];
108
+ var host2 = (cidr) => cidr.split("/")[0];
109
109
  var NOT_GLOBAL_V4 = [
110
110
  "0.0.0.0/8",
111
111
  "10.0.0.0/8",
@@ -119,10 +119,10 @@ var NOT_GLOBAL_V4 = [
119
119
  "198.51.100.0/24",
120
120
  "203.0.113.0/24"
121
121
  ];
122
- function isGlobal(address) {
123
- if (familyOf(address) === "ipv6")
124
- return contains("2000::/3", address) && !contains("2001:db8::/32", address);
125
- return !NOT_GLOBAL_V4.some((prefix) => contains(prefix, address));
122
+ function isGlobal2(address) {
123
+ if (familyOf2(address) === "ipv6")
124
+ return contains2("2000::/3", address) && !contains2("2001:db8::/32", address);
125
+ return !NOT_GLOBAL_V4.some((prefix) => contains2(prefix, address));
126
126
  }
127
127
 
128
- export { familyOf, parse, split, isAddress, isCidr, network, contains, formatAddress, rangeOf, prefixesBetween, netmask, wildcard, host, isGlobal };
128
+ export { familyOf2, parse, split, isAddress2, isCidr2, network, contains2, formatAddress, rangeOf, prefixesBetween, netmask, wildcard, host2, isGlobal2 };
@@ -1,5 +1,3 @@
1
- import"./index-fq57hya4.js";
2
-
3
1
  // src/cli/new.ts
4
2
  import { spawnSync } from "node:child_process";
5
3
  import { cpSync, existsSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -87,7 +85,7 @@ async function create(args) {
87
85
  if (terminal) {
88
86
  console.log(`
89
87
  Which is closest to your network?`);
90
- names.forEach((name2, index) => console.log(` ${index + 1}. ${PATTERNS[name2].title}: ${PATTERNS[name2].fits}`));
88
+ names.forEach((name, index) => console.log(` ${index + 1}. ${PATTERNS[name].title}: ${PATTERNS[name].fits}`));
91
89
  }
92
90
  pattern = names[Number(await ask("Number", "1")) - 1];
93
91
  }
@@ -120,14 +118,14 @@ Which is closest to your network?`);
120
118
  let text = readFileSync(path, "utf8");
121
119
  if (asn && asn !== EXAMPLE_ASN)
122
120
  text = text.replaceAll(new RegExp(`\\b${EXAMPLE_ASN}\\b`, "g"), asn);
123
- for (const [, name2] of text.matchAll(/secret\("([A-Z0-9_]+)"\)/g))
124
- secrets.add(name2);
121
+ for (const [, name] of text.matchAll(/secret\("([A-Z0-9_]+)"\)/g))
122
+ secrets.add(name);
125
123
  writeFileSync(path, text);
126
124
  }
127
125
  const env = [
128
126
  "# Secrets for Circuit. Never commit this file.",
129
127
  "# A value is the secret itself, or a 1Password reference, op://vault/item/field, read with the 1Password CLI.",
130
- ...[...secrets].sort().map((name2) => onePassword ? `${name2}=op://Network/${name2}/password` : `${name2}=`),
128
+ ...[...secrets].sort().map((name) => onePassword ? `${name}=op://Network/${name}/password` : `${name}=`),
131
129
  ""
132
130
  ];
133
131
  writeFileSync(join(target, ".env"), env.join(`
@@ -1,8 +1,6 @@
1
1
  import {
2
2
  packageRoot
3
- } from "./index-jtzfw679.js";
4
- import"./index-xm9nma2t.js";
5
- import"./index-fq57hya4.js";
3
+ } from "./index-n4zpd25n.js";
6
4
 
7
5
  // src/studio/server.ts
8
6
  import { execFile as execFile2, spawn } from "node:child_process";
@@ -106,7 +104,8 @@ var CONTENT_TYPES = {
106
104
  ".html": "text/html; charset=utf-8",
107
105
  ".css": "text/css; charset=utf-8",
108
106
  ".js": "text/javascript; charset=utf-8",
109
- ".svg": "image/svg+xml"
107
+ ".svg": "image/svg+xml",
108
+ ".woff2": "font/woff2"
110
109
  };
111
110
  function gather(configPath) {
112
111
  const cli = process.argv[1];
@@ -160,11 +159,11 @@ async function studio(options) {
160
159
  const path = url.pathname;
161
160
  if (path === "/history") {
162
161
  answer(response, async () => {
163
- const name2 = url.searchParams.get("device") ?? "";
164
- const files = JSON.parse(data).files?.[name2];
162
+ const name = url.searchParams.get("device") ?? "";
163
+ const files = JSON.parse(data).files?.[name];
165
164
  if (!files)
166
- throw new Error(`no device ${name2}`);
167
- return history(root, [files.source, files.snapshot].filter((file2) => Boolean(file2)));
165
+ throw new Error(`no device ${name}`);
166
+ return history(root, [files.source, files.snapshot].filter((file) => Boolean(file)));
168
167
  });
169
168
  return;
170
169
  }
@@ -208,7 +207,7 @@ async function studio(options) {
208
207
  const name = path === "/" ? "index.html" : path.slice(1);
209
208
  const type = CONTENT_TYPES[extname(name)];
210
209
  const file = join2(pages, name);
211
- if (!/^[a-z-]+\.[a-z]+$/.test(name) || !type || !existsSync(file)) {
210
+ if (!/^[a-z-]+\.[a-z0-9]+$/.test(name) || !type || !existsSync(file)) {
212
211
  response.writeHead(404).end();
213
212
  return;
214
213
  }
@@ -240,7 +239,6 @@ async function studio(options) {
240
239
  });
241
240
  }
242
241
  export {
243
- studio,
244
242
  isLoopback,
245
- hostAllowed
243
+ studio
246
244
  };