@scp-dev/broker 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -222,6 +222,8 @@ interface Bridge {
222
222
  /** the endpoints of other accounts this account's contacts let it reach */
223
223
  contacts(): Promise<XListed[]>;
224
224
  status(): BridgeStatus;
225
+ /** reads the credential again and reconnects now, also after the hub revoked or replaced this bridge */
226
+ relink(): void;
225
227
  stop(): Promise<void>;
226
228
  }
227
229
  /** a WebSocket constructor that takes request headers, as Bun's and Node's do */
@@ -401,6 +403,8 @@ interface BrokerOptions {
401
403
  machine?: string | undefined;
402
404
  /** the link to the hub; without it the broker never opens a network connection */
403
405
  bridge?: BridgeConfig | undefined;
406
+ /** reads the bridge configuration again for bridge_relink; one that appeared or names another hub or machine restarts the broker */
407
+ bridgeConfig?: (() => Promise<BridgeConfig | undefined>) | undefined;
404
408
  /** how often presence that decays without a request (a hook-only endpoint) is checked for changes (default 60 s) */
405
409
  presenceEveryMs?: number | undefined;
406
410
  /** where a judge model's key_env is read (default the broker's environment) */
package/dist/index.js CHANGED
@@ -563,6 +563,7 @@ function startBridge(options) {
563
563
  let connected = false;
564
564
  let ready = false;
565
565
  let stopped = false;
566
+ let shutDown = false;
566
567
  let lastError = options.token ? null : options.tokenError ?? "no hub credential; the broker runs local-only";
567
568
  let since = (/* @__PURE__ */ new Date()).toISOString();
568
569
  let attempt = 0;
@@ -603,7 +604,7 @@ function startBridge(options) {
603
604
  });
604
605
  }
605
606
  function connect() {
606
- if (stopped || !token) return;
607
+ if (stopped || !token || ws) return;
607
608
  let socket;
608
609
  try {
609
610
  socket = new Socket(relaySocketUrl(options.url), { headers: { Authorization: `Bearer ${token}` } });
@@ -1009,8 +1010,19 @@ function startBridge(options) {
1009
1010
  function status() {
1010
1011
  return { enabled: true, machine, url: options.url, connected, last_error: lastError, since };
1011
1012
  }
1013
+ function relink() {
1014
+ if (shutDown) return;
1015
+ stopped = false;
1016
+ attempt = 0;
1017
+ clearTimeout(retryTimer);
1018
+ if (ws) return;
1019
+ void reconnect().then(() => {
1020
+ if (!token && !ws) setState(false, "no hub credential; log in again");
1021
+ });
1022
+ }
1012
1023
  async function stop() {
1013
1024
  stopped = true;
1025
+ shutDown = true;
1014
1026
  clearTimeout(retryTimer);
1015
1027
  for (const fn of waitingForLink.splice(0)) fn();
1016
1028
  const socket = ws;
@@ -1022,7 +1034,7 @@ function startBridge(options) {
1022
1034
  if (socket && socket.readyState <= 1) socket.close(1e3, "the broker stopped");
1023
1035
  }
1024
1036
  connect();
1025
- return { forward, changed, resync, activity, contacts, status, stop };
1037
+ return { forward, changed, resync, activity, contacts, status, relink, stop };
1026
1038
  }
1027
1039
 
1028
1040
  // src/server.ts
@@ -1577,7 +1589,7 @@ function openDatabase(path) {
1577
1589
  // src/server.ts
1578
1590
  var SWEEP_MS = 10 * 6e4;
1579
1591
  var PRESENCE_MS = 6e4;
1580
- var OBSERVER_METHODS = /* @__PURE__ */ new Set(["list", "snapshot", "watch", "log", "policy", "policy_check", "cloud_status"]);
1592
+ var OBSERVER_METHODS = /* @__PURE__ */ new Set(["list", "snapshot", "watch", "log", "policy", "policy_check", "cloud_status", "bridge_relink"]);
1581
1593
  var SCOPE_METHODS = /* @__PURE__ */ new Set([
1582
1594
  "rules",
1583
1595
  "rules_set",
@@ -1734,6 +1746,15 @@ async function startBroker(options) {
1734
1746
  }
1735
1747
  });
1736
1748
  }
1749
+ async function relink() {
1750
+ const config = options.bridgeConfig ? await options.bridgeConfig() : options.bridge;
1751
+ if (!config || bridge && config.url === options.bridge?.url && config.machine === options.bridge?.machine) {
1752
+ bridge?.relink();
1753
+ return bridge?.status() ?? { enabled: false };
1754
+ }
1755
+ setImmediate(() => void stop());
1756
+ return { enabled: false, restarting: true };
1757
+ }
1737
1758
  function activity() {
1738
1759
  if (idleTimer) clearTimeout(idleTimer);
1739
1760
  idleTimer = void 0;
@@ -2335,6 +2356,8 @@ async function startBroker(options) {
2335
2356
  }
2336
2357
  case "cloud_status":
2337
2358
  return bridge?.status() ?? { enabled: false };
2359
+ case "bridge_relink":
2360
+ return relink();
2338
2361
  }
2339
2362
  throw new RelayError4("method_not_found", `no method ${method}`);
2340
2363
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scp-dev/broker",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Swarm Communication Protocol local broker: a unix socket JSON-RPC daemon over the relay core, its client and the bridge to an account hub.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://scp.dev",
package/src/bridge.ts CHANGED
@@ -78,6 +78,8 @@ export interface Bridge {
78
78
  /** the endpoints of other accounts this account's contacts let it reach */
79
79
  contacts(): Promise<XListed[]>
80
80
  status(): BridgeStatus
81
+ /** reads the credential again and reconnects now, also after the hub revoked or replaced this bridge */
82
+ relink(): void
81
83
  stop(): Promise<void>
82
84
  }
83
85
 
@@ -132,7 +134,9 @@ export function startBridge(options: BridgeOptions): Bridge {
132
134
  let connected = false
133
135
  /** hello went through on the current socket: deltas may go out */
134
136
  let ready = false
137
+ /** the hub turned this bridge away, or the broker stopped it: no more retries until relink */
135
138
  let stopped = false
139
+ let shutDown = false
136
140
  let lastError: string | null = options.token ? null : (options.tokenError ?? "no hub credential; the broker runs local-only")
137
141
  let since = new Date().toISOString()
138
142
  let attempt = 0
@@ -177,7 +181,7 @@ export function startBridge(options: BridgeOptions): Bridge {
177
181
  }
178
182
 
179
183
  function connect() {
180
- if (stopped || !token) return
184
+ if (stopped || !token || ws) return
181
185
  let socket: WebSocket
182
186
  try {
183
187
  socket = new Socket(relaySocketUrl(options.url), { headers: { Authorization: `Bearer ${token}` } })
@@ -624,8 +628,20 @@ export function startBridge(options: BridgeOptions): Bridge {
624
628
  return { enabled: true, machine, url: options.url, connected, last_error: lastError, since }
625
629
  }
626
630
 
631
+ function relink() {
632
+ if (shutDown) return
633
+ stopped = false
634
+ attempt = 0
635
+ clearTimeout(retryTimer)
636
+ if (ws) return
637
+ void reconnect().then(() => {
638
+ if (!token && !ws) setState(false, "no hub credential; log in again")
639
+ })
640
+ }
641
+
627
642
  async function stop() {
628
643
  stopped = true
644
+ shutDown = true
629
645
  clearTimeout(retryTimer)
630
646
  for (const fn of waitingForLink.splice(0)) fn()
631
647
  const socket = ws
@@ -638,5 +654,5 @@ export function startBridge(options: BridgeOptions): Bridge {
638
654
  }
639
655
 
640
656
  connect()
641
- return { forward, changed, resync, activity, contacts, status, stop }
657
+ return { forward, changed, resync, activity, contacts, status, relink, stop }
642
658
  }
package/src/server.ts CHANGED
@@ -57,6 +57,8 @@ export interface BrokerOptions {
57
57
  machine?: string | undefined
58
58
  /** the link to the hub; without it the broker never opens a network connection */
59
59
  bridge?: BridgeConfig | undefined
60
+ /** reads the bridge configuration again for bridge_relink; one that appeared or names another hub or machine restarts the broker */
61
+ bridgeConfig?: (() => Promise<BridgeConfig | undefined>) | undefined
60
62
  /** how often presence that decays without a request (a hook-only endpoint) is checked for changes (default 60 s) */
61
63
  presenceEveryMs?: number | undefined
62
64
  /** where a judge model's key_env is read (default the broker's environment) */
@@ -110,7 +112,7 @@ interface Connection {
110
112
 
111
113
  const SWEEP_MS = 10 * 60_000
112
114
  const PRESENCE_MS = 60_000
113
- const OBSERVER_METHODS = new Set(["list", "snapshot", "watch", "log", "policy", "policy_check", "cloud_status"])
115
+ const OBSERVER_METHODS = new Set(["list", "snapshot", "watch", "log", "policy", "policy_check", "cloud_status", "bridge_relink"])
114
116
  const SCOPE_METHODS = new Set([
115
117
  "rules",
116
118
  "rules_set",
@@ -281,6 +283,17 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
281
283
  })
282
284
  }
283
285
 
286
+ /** links the bridge again with the credential on disk; a configuration this broker cannot take without a restart stops it */
287
+ async function relink(): Promise<object> {
288
+ const config = options.bridgeConfig ? await options.bridgeConfig() : options.bridge
289
+ if (!config || (bridge && config.url === options.bridge?.url && config.machine === options.bridge?.machine)) {
290
+ bridge?.relink()
291
+ return bridge?.status() ?? { enabled: false }
292
+ }
293
+ setImmediate(() => void stop())
294
+ return { enabled: false, restarting: true }
295
+ }
296
+
284
297
  function activity() {
285
298
  if (idleTimer) clearTimeout(idleTimer)
286
299
  idleTimer = undefined
@@ -948,6 +961,8 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
948
961
  }
949
962
  case "cloud_status":
950
963
  return bridge?.status() ?? { enabled: false }
964
+ case "bridge_relink":
965
+ return relink()
951
966
  }
952
967
  throw new RelayError("method_not_found", `no method ${method}`)
953
968
  }