@scp-dev/broker 0.1.0 → 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 +6 -1
- package/dist/index.js +65 -11
- package/package.json +3 -3
- package/src/bridge.ts +18 -2
- package/src/client.ts +2 -1
- package/src/server.ts +68 -8
package/dist/index.d.ts
CHANGED
|
@@ -143,7 +143,8 @@ declare class RelayClient {
|
|
|
143
143
|
}>;
|
|
144
144
|
/**
|
|
145
145
|
* at a stop: the spawned workers' unread messages, held for the hook to print, waiting up to `wait_s` while workers still run, none came,
|
|
146
|
-
* pushes are left and no prompt moved the `prompts` mark inject handed out
|
|
146
|
+
* pushes are left and no prompt moved the `prompts` mark inject handed out; with `wait_s` above 0 and other workers still running, the
|
|
147
|
+
* broker gathers more of their results into the same push for up to ${LIMITS.pushGraceS} s, which may run past `wait_s`
|
|
147
148
|
*/
|
|
148
149
|
stopWait(waitS?: number, prompts?: number): Promise<StopWaitResult>;
|
|
149
150
|
group(params: GroupParams): Promise<unknown>;
|
|
@@ -221,6 +222,8 @@ interface Bridge {
|
|
|
221
222
|
/** the endpoints of other accounts this account's contacts let it reach */
|
|
222
223
|
contacts(): Promise<XListed[]>;
|
|
223
224
|
status(): BridgeStatus;
|
|
225
|
+
/** reads the credential again and reconnects now, also after the hub revoked or replaced this bridge */
|
|
226
|
+
relink(): void;
|
|
224
227
|
stop(): Promise<void>;
|
|
225
228
|
}
|
|
226
229
|
/** a WebSocket constructor that takes request headers, as Bun's and Node's do */
|
|
@@ -400,6 +403,8 @@ interface BrokerOptions {
|
|
|
400
403
|
machine?: string | undefined;
|
|
401
404
|
/** the link to the hub; without it the broker never opens a network connection */
|
|
402
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;
|
|
403
408
|
/** how often presence that decays without a request (a hook-only endpoint) is checked for changes (default 60 s) */
|
|
404
409
|
presenceEveryMs?: number | undefined;
|
|
405
410
|
/** where a judge model's key_env is read (default the broker's environment) */
|
package/dist/index.js
CHANGED
|
@@ -408,7 +408,8 @@ var RelayClient = class _RelayClient {
|
|
|
408
408
|
}
|
|
409
409
|
/**
|
|
410
410
|
* at a stop: the spawned workers' unread messages, held for the hook to print, waiting up to `wait_s` while workers still run, none came,
|
|
411
|
-
* pushes are left and no prompt moved the `prompts` mark inject handed out
|
|
411
|
+
* pushes are left and no prompt moved the `prompts` mark inject handed out; with `wait_s` above 0 and other workers still running, the
|
|
412
|
+
* broker gathers more of their results into the same push for up to ${LIMITS.pushGraceS} s, which may run past `wait_s`
|
|
412
413
|
*/
|
|
413
414
|
stopWait(waitS = 0, prompts) {
|
|
414
415
|
return this.request("stop_wait", { wait_s: waitS, ...prompts !== void 0 ? { prompts } : {} });
|
|
@@ -562,6 +563,7 @@ function startBridge(options) {
|
|
|
562
563
|
let connected = false;
|
|
563
564
|
let ready = false;
|
|
564
565
|
let stopped = false;
|
|
566
|
+
let shutDown = false;
|
|
565
567
|
let lastError = options.token ? null : options.tokenError ?? "no hub credential; the broker runs local-only";
|
|
566
568
|
let since = (/* @__PURE__ */ new Date()).toISOString();
|
|
567
569
|
let attempt = 0;
|
|
@@ -602,7 +604,7 @@ function startBridge(options) {
|
|
|
602
604
|
});
|
|
603
605
|
}
|
|
604
606
|
function connect() {
|
|
605
|
-
if (stopped || !token) return;
|
|
607
|
+
if (stopped || !token || ws) return;
|
|
606
608
|
let socket;
|
|
607
609
|
try {
|
|
608
610
|
socket = new Socket(relaySocketUrl(options.url), { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -1008,8 +1010,19 @@ function startBridge(options) {
|
|
|
1008
1010
|
function status() {
|
|
1009
1011
|
return { enabled: true, machine, url: options.url, connected, last_error: lastError, since };
|
|
1010
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
|
+
}
|
|
1011
1023
|
async function stop() {
|
|
1012
1024
|
stopped = true;
|
|
1025
|
+
shutDown = true;
|
|
1013
1026
|
clearTimeout(retryTimer);
|
|
1014
1027
|
for (const fn of waitingForLink.splice(0)) fn();
|
|
1015
1028
|
const socket = ws;
|
|
@@ -1021,7 +1034,7 @@ function startBridge(options) {
|
|
|
1021
1034
|
if (socket && socket.readyState <= 1) socket.close(1e3, "the broker stopped");
|
|
1022
1035
|
}
|
|
1023
1036
|
connect();
|
|
1024
|
-
return { forward, changed, resync, activity, contacts, status, stop };
|
|
1037
|
+
return { forward, changed, resync, activity, contacts, status, relink, stop };
|
|
1025
1038
|
}
|
|
1026
1039
|
|
|
1027
1040
|
// src/server.ts
|
|
@@ -1576,7 +1589,7 @@ function openDatabase(path) {
|
|
|
1576
1589
|
// src/server.ts
|
|
1577
1590
|
var SWEEP_MS = 10 * 6e4;
|
|
1578
1591
|
var PRESENCE_MS = 6e4;
|
|
1579
|
-
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"]);
|
|
1580
1593
|
var SCOPE_METHODS = /* @__PURE__ */ new Set([
|
|
1581
1594
|
"rules",
|
|
1582
1595
|
"rules_set",
|
|
@@ -1598,6 +1611,13 @@ var ASK_RECHECK_MS = 1e3;
|
|
|
1598
1611
|
var STOP_RECHECK_MS = 1e3;
|
|
1599
1612
|
var TRANSIENT_METHODS = /* @__PURE__ */ new Set(["event", "status", "whoami", "inject", "injected", "peek_turn", "stop_wait", "prompted"]);
|
|
1600
1613
|
var GRANT_UNBOUND = `a spawn-env grant binds only a new harness process: run the harness with it, or name the child (${CLI_NAME} spawn-env --name / --as)`;
|
|
1614
|
+
function workerResults(result) {
|
|
1615
|
+
return result.messages.length - (result.others_shown ?? 0) + result.more;
|
|
1616
|
+
}
|
|
1617
|
+
function othersRunning(result) {
|
|
1618
|
+
const senders = new Set(result.messages.slice(0, result.messages.length - (result.others_shown ?? 0)).map((m) => m.from.address));
|
|
1619
|
+
return result.workers.filter((address) => !senders.has(address)).length + result.starting;
|
|
1620
|
+
}
|
|
1601
1621
|
function running(pid) {
|
|
1602
1622
|
try {
|
|
1603
1623
|
process.kill(pid, 0);
|
|
@@ -1726,6 +1746,15 @@ async function startBroker(options) {
|
|
|
1726
1746
|
}
|
|
1727
1747
|
});
|
|
1728
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
|
+
}
|
|
1729
1758
|
function activity() {
|
|
1730
1759
|
if (idleTimer) clearTimeout(idleTimer);
|
|
1731
1760
|
idleTimer = void 0;
|
|
@@ -1886,6 +1915,21 @@ async function startBroker(options) {
|
|
|
1886
1915
|
if (s.data.closed) done();
|
|
1887
1916
|
});
|
|
1888
1917
|
}
|
|
1918
|
+
async function gather(me, result, peek, s) {
|
|
1919
|
+
const gathering = (r) => workerResults(r) > 0 && !r.more && !r.prompted && othersRunning(r) > 0;
|
|
1920
|
+
const cap = Date.now() + LIMITS2.pushGraceS * 1e3;
|
|
1921
|
+
let quiet = Date.now() + LIMITS2.pushQuietS * 1e3;
|
|
1922
|
+
let results = workerResults(result);
|
|
1923
|
+
while (gathering(result) && !s.data.closed && Date.now() < Math.min(quiet, cap)) {
|
|
1924
|
+
await waitFor(me, Math.min(quiet, cap) - Date.now(), s);
|
|
1925
|
+
if (closing) throw new RelayError4("unauthorized", "the relay broker is stopping");
|
|
1926
|
+
result = core.stopWait(me, peek);
|
|
1927
|
+
if (workerResults(result) > results) {
|
|
1928
|
+
results = workerResults(result);
|
|
1929
|
+
quiet = Date.now() + LIMITS2.pushQuietS * 1e3;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1889
1933
|
function onData(s, chunk) {
|
|
1890
1934
|
const { lines, tooLong } = s.data.reader.push(chunk);
|
|
1891
1935
|
for (const line of lines) s.data.queue.push(line);
|
|
@@ -2206,22 +2250,28 @@ async function startBroker(options) {
|
|
|
2206
2250
|
...hookOf(s)
|
|
2207
2251
|
});
|
|
2208
2252
|
case "stop_wait": {
|
|
2209
|
-
const
|
|
2253
|
+
const waitMs = waitSeconds(params.wait_s);
|
|
2254
|
+
const deadline = Date.now() + waitMs;
|
|
2210
2255
|
const options2 = { ...hookOf(s), ...typeof params.prompts === "number" ? { prompts: params.prompts } : {} };
|
|
2211
|
-
|
|
2256
|
+
const peek = { ...options2, peek: true };
|
|
2257
|
+
let result = core.stopWait(me, peek);
|
|
2212
2258
|
const idle = (r) => !r.messages.length && !r.more && r.workers.length + r.starting > 0 && r.pushes_left > 0 && !r.prompted;
|
|
2213
2259
|
while (idle(result) && !s.data.closed && Date.now() < deadline) {
|
|
2214
2260
|
await waitFor(me, Math.min(STOP_RECHECK_MS, deadline - Date.now()), s);
|
|
2215
2261
|
if (closing) throw new RelayError4("unauthorized", "the relay broker is stopping");
|
|
2216
|
-
result = core.stopWait(me,
|
|
2262
|
+
result = core.stopWait(me, peek);
|
|
2217
2263
|
}
|
|
2218
|
-
|
|
2264
|
+
if (waitMs > 0) await gather(me, result, peek, s);
|
|
2265
|
+
return core.stopWait(me, options2);
|
|
2266
|
+
}
|
|
2267
|
+
case "prompted": {
|
|
2268
|
+
const prompted = core.prompted(me, hookOf(s));
|
|
2269
|
+
if (prompted) wake(me);
|
|
2270
|
+
return { prompted };
|
|
2219
2271
|
}
|
|
2220
|
-
case "prompted":
|
|
2221
|
-
return { prompted: core.prompted(me, hookOf(s)) };
|
|
2222
2272
|
case "injected":
|
|
2223
2273
|
return {
|
|
2224
|
-
injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read }),
|
|
2274
|
+
injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read, deferred: params.deferred === true }),
|
|
2225
2275
|
...params.stop === true ? { held: core.stopHeld(me, hookOf(s)) } : {},
|
|
2226
2276
|
...params.push === true ? { pushed: core.pushed(me, params.read, hookOf(s)) } : {},
|
|
2227
2277
|
...params.nudged !== void 0 ? { nudged: core.nudged(me, params.nudged, hookOf(s)) } : {},
|
|
@@ -2231,6 +2281,8 @@ async function startBroker(options) {
|
|
|
2231
2281
|
};
|
|
2232
2282
|
case "message":
|
|
2233
2283
|
return { message: core.delivered(me, params.id) ?? (params.member === true ? core.threadMessage(me, params.id) : null) };
|
|
2284
|
+
case "receipts":
|
|
2285
|
+
return core.receipts(me, params.id);
|
|
2234
2286
|
}
|
|
2235
2287
|
throw new RelayError4("method_not_found", `no method ${method}`);
|
|
2236
2288
|
}
|
|
@@ -2304,6 +2356,8 @@ async function startBroker(options) {
|
|
|
2304
2356
|
}
|
|
2305
2357
|
case "cloud_status":
|
|
2306
2358
|
return bridge?.status() ?? { enabled: false };
|
|
2359
|
+
case "bridge_relink":
|
|
2360
|
+
return relink();
|
|
2307
2361
|
}
|
|
2308
2362
|
throw new RelayError4("method_not_found", `no method ${method}`);
|
|
2309
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",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"typecheck": "tsc -p ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@scp-dev/core": "0.1.
|
|
41
|
-
"@scp-dev/hub": "0.1.
|
|
40
|
+
"@scp-dev/core": "0.1.1",
|
|
41
|
+
"@scp-dev/hub": "0.1.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.5.1"
|
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/client.ts
CHANGED
|
@@ -196,7 +196,8 @@ export class RelayClient {
|
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
198
|
* at a stop: the spawned workers' unread messages, held for the hook to print, waiting up to `wait_s` while workers still run, none came,
|
|
199
|
-
* pushes are left and no prompt moved the `prompts` mark inject handed out
|
|
199
|
+
* pushes are left and no prompt moved the `prompts` mark inject handed out; with `wait_s` above 0 and other workers still running, the
|
|
200
|
+
* broker gathers more of their results into the same push for up to ${LIMITS.pushGraceS} s, which may run past `wait_s`
|
|
200
201
|
*/
|
|
201
202
|
stopWait(waitS = 0, prompts?: number): Promise<StopWaitResult> {
|
|
202
203
|
return this.request<StopWaitResult>("stop_wait", { wait_s: waitS, ...(prompts !== undefined ? { prompts } : {}) })
|
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",
|
|
@@ -135,6 +137,17 @@ const STOP_RECHECK_MS = 1000
|
|
|
135
137
|
const TRANSIENT_METHODS = new Set(["event", "status", "whoami", "inject", "injected", "peek_turn", "stop_wait", "prompted"])
|
|
136
138
|
const GRANT_UNBOUND = `a spawn-env grant binds only a new harness process: run the harness with it, or name the child (${CLI_NAME} spawn-env --name / --as)`
|
|
137
139
|
|
|
140
|
+
/** the workers' messages and handbacks a stop's push would show, and those of theirs left unread past it */
|
|
141
|
+
function workerResults(result: StopWaitResult): number {
|
|
142
|
+
return result.messages.length - (result.others_shown ?? 0) + result.more
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** the workers still running that sent none of the messages a stop's push would show, and those not joined yet */
|
|
146
|
+
function othersRunning(result: StopWaitResult): number {
|
|
147
|
+
const senders = new Set(result.messages.slice(0, result.messages.length - (result.others_shown ?? 0)).map((m) => m.from.address))
|
|
148
|
+
return result.workers.filter((address) => !senders.has(address)).length + result.starting
|
|
149
|
+
}
|
|
150
|
+
|
|
138
151
|
type Request = { jsonrpc?: string; id?: string | number | null; method?: unknown; params?: unknown }
|
|
139
152
|
|
|
140
153
|
/** whether a process runs (one of another user's counts too) */
|
|
@@ -270,6 +283,17 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
270
283
|
})
|
|
271
284
|
}
|
|
272
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
|
+
|
|
273
297
|
function activity() {
|
|
274
298
|
if (idleTimer) clearTimeout(idleTimer)
|
|
275
299
|
idleTimer = undefined
|
|
@@ -461,6 +485,32 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
461
485
|
})
|
|
462
486
|
}
|
|
463
487
|
|
|
488
|
+
/**
|
|
489
|
+
* a stop's push with worker results to show while other workers still run: waits on, peeking, for more of theirs to show with them until
|
|
490
|
+
* none came for ${LIMITS.pushQuietS} s, ${LIMITS.pushGraceS} s after it began (which may run past the stop's wait), the push budget is
|
|
491
|
+
* full (`more`), no other worker runs ({@link othersRunning}), a prompt came or the connection closed
|
|
492
|
+
*/
|
|
493
|
+
async function gather(
|
|
494
|
+
me: string,
|
|
495
|
+
result: StopWaitResult,
|
|
496
|
+
peek: ReturnType<typeof hookOf> & { prompts?: number; peek: boolean },
|
|
497
|
+
s: Conn<Connection>,
|
|
498
|
+
): Promise<void> {
|
|
499
|
+
const gathering = (r: StopWaitResult) => workerResults(r) > 0 && !r.more && !r.prompted && othersRunning(r) > 0
|
|
500
|
+
const cap = Date.now() + LIMITS.pushGraceS * 1000
|
|
501
|
+
let quiet = Date.now() + LIMITS.pushQuietS * 1000
|
|
502
|
+
let results = workerResults(result)
|
|
503
|
+
while (gathering(result) && !s.data.closed && Date.now() < Math.min(quiet, cap)) {
|
|
504
|
+
await waitFor(me, Math.min(quiet, cap) - Date.now(), s)
|
|
505
|
+
if (closing) throw new RelayError("unauthorized", "the relay broker is stopping")
|
|
506
|
+
result = core.stopWait(me, peek)
|
|
507
|
+
if (workerResults(result) > results) {
|
|
508
|
+
results = workerResults(result)
|
|
509
|
+
quiet = Date.now() + LIMITS.pushQuietS * 1000
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
464
514
|
function onData(s: Conn<Connection>, chunk: Uint8Array) {
|
|
465
515
|
const { lines, tooLong } = s.data.reader.push(chunk)
|
|
466
516
|
for (const line of lines) s.data.queue.push(line)
|
|
@@ -796,22 +846,28 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
796
846
|
...hookOf(s),
|
|
797
847
|
})
|
|
798
848
|
case "stop_wait": {
|
|
799
|
-
const
|
|
849
|
+
const waitMs = waitSeconds(params.wait_s)
|
|
850
|
+
const deadline = Date.now() + waitMs
|
|
800
851
|
const options = { ...hookOf(s), ...(typeof params.prompts === "number" ? { prompts: params.prompts } : {}) }
|
|
801
|
-
|
|
852
|
+
const peek = { ...options, peek: true }
|
|
853
|
+
let result = core.stopWait(me, peek)
|
|
802
854
|
const idle = (r: StopWaitResult) => !r.messages.length && !r.more && r.workers.length + r.starting > 0 && r.pushes_left > 0 && !r.prompted
|
|
803
855
|
while (idle(result) && !s.data.closed && Date.now() < deadline) {
|
|
804
856
|
await waitFor(me, Math.min(STOP_RECHECK_MS, deadline - Date.now()), s)
|
|
805
857
|
if (closing) throw new RelayError("unauthorized", "the relay broker is stopping")
|
|
806
|
-
result = core.stopWait(me,
|
|
858
|
+
result = core.stopWait(me, peek)
|
|
807
859
|
}
|
|
808
|
-
|
|
860
|
+
if (waitMs > 0) await gather(me, result, peek, s)
|
|
861
|
+
return core.stopWait(me, options)
|
|
862
|
+
}
|
|
863
|
+
case "prompted": {
|
|
864
|
+
const prompted = core.prompted(me, hookOf(s))
|
|
865
|
+
if (prompted) wake(me)
|
|
866
|
+
return { prompted }
|
|
809
867
|
}
|
|
810
|
-
case "prompted":
|
|
811
|
-
return { prompted: core.prompted(me, hookOf(s)) }
|
|
812
868
|
case "injected":
|
|
813
869
|
return {
|
|
814
|
-
injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read }),
|
|
870
|
+
injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read, deferred: params.deferred === true }),
|
|
815
871
|
...(params.stop === true ? { held: core.stopHeld(me, hookOf(s)) } : {}),
|
|
816
872
|
...(params.push === true ? { pushed: core.pushed(me, params.read, hookOf(s)) } : {}),
|
|
817
873
|
...(params.nudged !== undefined ? { nudged: core.nudged(me, params.nudged, hookOf(s)) } : {}),
|
|
@@ -821,6 +877,8 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
821
877
|
}
|
|
822
878
|
case "message":
|
|
823
879
|
return { message: core.delivered(me, params.id) ?? (params.member === true ? core.threadMessage(me, params.id) : null) }
|
|
880
|
+
case "receipts":
|
|
881
|
+
return core.receipts(me, params.id)
|
|
824
882
|
}
|
|
825
883
|
throw new RelayError("method_not_found", `no method ${method}`)
|
|
826
884
|
}
|
|
@@ -903,6 +961,8 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
903
961
|
}
|
|
904
962
|
case "cloud_status":
|
|
905
963
|
return bridge?.status() ?? { enabled: false }
|
|
964
|
+
case "bridge_relink":
|
|
965
|
+
return relink()
|
|
906
966
|
}
|
|
907
967
|
throw new RelayError("method_not_found", `no method ${method}`)
|
|
908
968
|
}
|