@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 +4 -0
- package/dist/index.js +26 -3
- package/package.json +1 -1
- package/src/bridge.ts +18 -2
- package/src/server.ts +16 -1
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.
|
|
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
|
}
|