@takodotid/circuit 0.42.0 → 0.43.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.
@@ -83,6 +83,11 @@ export class RouterOsPlan {
83
83
  /** Menus where an entry that keeps its name but changes what it is known by is changed with a `set`, so what points at it by name, such as an address on a VLAN interface, stays attached. In every other menu it is removed and added again. */
84
84
  private static readonly CHANGED_IN_PLACE = new Set(["/interface vlan"]);
85
85
 
86
+ /**
87
+ * Properties a running entry takes only when it restarts. A BGP session given a new `routing-table` stops announcing at once and keeps its routes in the old table until it restarts (TRAPS.md), so the plan disables the connection, changes it and enables it again: one restart, written in the plan.
88
+ */
89
+ private static readonly RESTART_ON: Record<string, string[]> = { "/routing bgp connection": ["routing-table"] };
90
+
86
91
  /** The variable holding the old rules of an ordered menu while the new ones are added. Removed when the rewrite ends. */
87
92
  private static readonly OLD_RULES = "frameworkOldRules";
88
93
 
@@ -314,8 +319,19 @@ export class RouterOsPlan {
314
319
  const update = this.changes(entry, current, `${menu} ${entryKey}`);
315
320
  if (!Object.keys(update).length) continue;
316
321
 
322
+ const selector = RouterOsPlan.find(menu, RouterOsExport.identity(current));
323
+ const restartsOn = (RouterOsPlan.RESTART_ON[menu] ?? []).filter((property) => property in update);
324
+ if (restartsOn.length) {
325
+ changeStep.show.push(
326
+ `~ ${entryKey} ${RouterOsPlan.describe(update)}, restarting the session: it takes a new ${restartsOn.join(" and ")} only when it restarts`
327
+ );
328
+ changeStep.send.push(RouterOsPlan.line(menu, "set", selector, [], { disabled: "yes", ...update }));
329
+ changeStep.send.push(RouterOsPlan.line(menu, "set", selector, [], { disabled: "no" }));
330
+ continue;
331
+ }
332
+
317
333
  changeStep.show.push(`~ ${entryKey} ${RouterOsPlan.describe(update)}`);
318
- changeStep.send.push(RouterOsPlan.line(menu, "set", RouterOsPlan.find(menu, RouterOsExport.identity(current)), [], update));
334
+ changeStep.send.push(RouterOsPlan.line(menu, "set", selector, [], update));
319
335
  }
320
336
 
321
337
  const removed = existingEntries.filter((entry) => !kept.has(key(entry)));
@@ -1,6 +1,7 @@
1
1
  // The neutral model as a RouterOS 7 export, written in the form `/export` prints so the two compare line for line.
2
2
 
3
3
  import { X509Certificate } from "node:crypto";
4
+ import { Utils } from "../../core/utils";
4
5
  import { Address } from "../../core/address";
5
6
  import { Secrets } from "../../core/secrets";
6
7
  import type {
@@ -1147,7 +1148,7 @@ export class RouterOsRender {
1147
1148
  distance: route.distance,
1148
1149
  "dst-address": route.prefix,
1149
1150
  gateway,
1150
- "routing-table": route.vrf ?? route.table,
1151
+ "routing-table": route.vrf ?? Utils.otherTable(route.table),
1151
1152
  });
1152
1153
  }
1153
1154
  }
@@ -1292,7 +1293,7 @@ export class RouterOsRender {
1292
1293
  "output.network": originates ? RouterOsRender.NETWORKS[family] : undefined,
1293
1294
  "remote.address": `${neighbor.address}/${addressFamily === "ipv4" ? 32 : 128}`,
1294
1295
  "remote.as": settings.remote_as,
1295
- "routing-table": settings.table,
1296
+ "routing-table": Utils.otherTable(settings.table),
1296
1297
  "tcp-md5-key": settings.password && Secrets.marker(settings.password),
1297
1298
  "use-bfd": settings.bfd ? "yes" : undefined,
1298
1299
  });
@@ -16,8 +16,8 @@ export type StaticRoute<I extends string, F extends string, T extends string = n
16
16
  distance?: number;
17
17
  /** The VRF the route is in. The main table when absent. */
18
18
  vrf?: F;
19
- /** The table the route is in, one of `routing.tables`. The main table when absent. A route has a `vrf` or a `table`, not both. */
20
- table?: T;
19
+ /** The table the route is in: `main`, or one of `routing.tables`. The main table when absent; write `main` to say so. A route has a `vrf` or a `table`, not both. */
20
+ table?: T | "main";
21
21
  /** Why the route exists. */
22
22
  description?: string;
23
23
  };
@@ -57,8 +57,8 @@ export type NeighborSettings<P extends string, T extends string = never> = {
57
57
  passive?: boolean;
58
58
  /** Detect a dead link in under a second with BFD. */
59
59
  bfd?: boolean;
60
- /** The table the routes learned are installed in, one of `routing.tables`. The main table when absent. What is announced still comes from `networks` and the main table. */
61
- table?: T;
60
+ /** The table the routes learned are installed in: `main`, or one of `routing.tables`. The main table when absent; write `main` to say so. On RouterOS the session also announces from that table, so each prefix of `networks` it announces needs a route in the table too, and a running session takes a new table only when it restarts. */
61
+ table?: T | "main";
62
62
  };
63
63
 
64
64
  /** One BGP session. Settings it does not set come from its group. */