@scp-dev/broker 0.1.0 → 0.1.1

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
@@ -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>;
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 } : {} });
@@ -1598,6 +1599,13 @@ var ASK_RECHECK_MS = 1e3;
1598
1599
  var STOP_RECHECK_MS = 1e3;
1599
1600
  var TRANSIENT_METHODS = /* @__PURE__ */ new Set(["event", "status", "whoami", "inject", "injected", "peek_turn", "stop_wait", "prompted"]);
1600
1601
  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)`;
1602
+ function workerResults(result) {
1603
+ return result.messages.length - (result.others_shown ?? 0) + result.more;
1604
+ }
1605
+ function othersRunning(result) {
1606
+ const senders = new Set(result.messages.slice(0, result.messages.length - (result.others_shown ?? 0)).map((m) => m.from.address));
1607
+ return result.workers.filter((address) => !senders.has(address)).length + result.starting;
1608
+ }
1601
1609
  function running(pid) {
1602
1610
  try {
1603
1611
  process.kill(pid, 0);
@@ -1886,6 +1894,21 @@ async function startBroker(options) {
1886
1894
  if (s.data.closed) done();
1887
1895
  });
1888
1896
  }
1897
+ async function gather(me, result, peek, s) {
1898
+ const gathering = (r) => workerResults(r) > 0 && !r.more && !r.prompted && othersRunning(r) > 0;
1899
+ const cap = Date.now() + LIMITS2.pushGraceS * 1e3;
1900
+ let quiet = Date.now() + LIMITS2.pushQuietS * 1e3;
1901
+ let results = workerResults(result);
1902
+ while (gathering(result) && !s.data.closed && Date.now() < Math.min(quiet, cap)) {
1903
+ await waitFor(me, Math.min(quiet, cap) - Date.now(), s);
1904
+ if (closing) throw new RelayError4("unauthorized", "the relay broker is stopping");
1905
+ result = core.stopWait(me, peek);
1906
+ if (workerResults(result) > results) {
1907
+ results = workerResults(result);
1908
+ quiet = Date.now() + LIMITS2.pushQuietS * 1e3;
1909
+ }
1910
+ }
1911
+ }
1889
1912
  function onData(s, chunk) {
1890
1913
  const { lines, tooLong } = s.data.reader.push(chunk);
1891
1914
  for (const line of lines) s.data.queue.push(line);
@@ -2206,22 +2229,28 @@ async function startBroker(options) {
2206
2229
  ...hookOf(s)
2207
2230
  });
2208
2231
  case "stop_wait": {
2209
- const deadline = Date.now() + waitSeconds(params.wait_s);
2232
+ const waitMs = waitSeconds(params.wait_s);
2233
+ const deadline = Date.now() + waitMs;
2210
2234
  const options2 = { ...hookOf(s), ...typeof params.prompts === "number" ? { prompts: params.prompts } : {} };
2211
- let result = core.stopWait(me, options2);
2235
+ const peek = { ...options2, peek: true };
2236
+ let result = core.stopWait(me, peek);
2212
2237
  const idle = (r) => !r.messages.length && !r.more && r.workers.length + r.starting > 0 && r.pushes_left > 0 && !r.prompted;
2213
2238
  while (idle(result) && !s.data.closed && Date.now() < deadline) {
2214
2239
  await waitFor(me, Math.min(STOP_RECHECK_MS, deadline - Date.now()), s);
2215
2240
  if (closing) throw new RelayError4("unauthorized", "the relay broker is stopping");
2216
- result = core.stopWait(me, options2);
2241
+ result = core.stopWait(me, peek);
2217
2242
  }
2218
- return result;
2243
+ if (waitMs > 0) await gather(me, result, peek, s);
2244
+ return core.stopWait(me, options2);
2245
+ }
2246
+ case "prompted": {
2247
+ const prompted = core.prompted(me, hookOf(s));
2248
+ if (prompted) wake(me);
2249
+ return { prompted };
2219
2250
  }
2220
- case "prompted":
2221
- return { prompted: core.prompted(me, hookOf(s)) };
2222
2251
  case "injected":
2223
2252
  return {
2224
- injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read }),
2253
+ injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read, deferred: params.deferred === true }),
2225
2254
  ...params.stop === true ? { held: core.stopHeld(me, hookOf(s)) } : {},
2226
2255
  ...params.push === true ? { pushed: core.pushed(me, params.read, hookOf(s)) } : {},
2227
2256
  ...params.nudged !== void 0 ? { nudged: core.nudged(me, params.nudged, hookOf(s)) } : {},
@@ -2231,6 +2260,8 @@ async function startBroker(options) {
2231
2260
  };
2232
2261
  case "message":
2233
2262
  return { message: core.delivered(me, params.id) ?? (params.member === true ? core.threadMessage(me, params.id) : null) };
2263
+ case "receipts":
2264
+ return core.receipts(me, params.id);
2234
2265
  }
2235
2266
  throw new RelayError4("method_not_found", `no method ${method}`);
2236
2267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scp-dev/broker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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.0",
41
- "@scp-dev/hub": "0.1.0"
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/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
@@ -135,6 +135,17 @@ const STOP_RECHECK_MS = 1000
135
135
  const TRANSIENT_METHODS = new Set(["event", "status", "whoami", "inject", "injected", "peek_turn", "stop_wait", "prompted"])
136
136
  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
137
 
138
+ /** the workers' messages and handbacks a stop's push would show, and those of theirs left unread past it */
139
+ function workerResults(result: StopWaitResult): number {
140
+ return result.messages.length - (result.others_shown ?? 0) + result.more
141
+ }
142
+
143
+ /** the workers still running that sent none of the messages a stop's push would show, and those not joined yet */
144
+ function othersRunning(result: StopWaitResult): number {
145
+ const senders = new Set(result.messages.slice(0, result.messages.length - (result.others_shown ?? 0)).map((m) => m.from.address))
146
+ return result.workers.filter((address) => !senders.has(address)).length + result.starting
147
+ }
148
+
138
149
  type Request = { jsonrpc?: string; id?: string | number | null; method?: unknown; params?: unknown }
139
150
 
140
151
  /** whether a process runs (one of another user's counts too) */
@@ -461,6 +472,32 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
461
472
  })
462
473
  }
463
474
 
475
+ /**
476
+ * 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
477
+ * none came for ${LIMITS.pushQuietS} s, ${LIMITS.pushGraceS} s after it began (which may run past the stop's wait), the push budget is
478
+ * full (`more`), no other worker runs ({@link othersRunning}), a prompt came or the connection closed
479
+ */
480
+ async function gather(
481
+ me: string,
482
+ result: StopWaitResult,
483
+ peek: ReturnType<typeof hookOf> & { prompts?: number; peek: boolean },
484
+ s: Conn<Connection>,
485
+ ): Promise<void> {
486
+ const gathering = (r: StopWaitResult) => workerResults(r) > 0 && !r.more && !r.prompted && othersRunning(r) > 0
487
+ const cap = Date.now() + LIMITS.pushGraceS * 1000
488
+ let quiet = Date.now() + LIMITS.pushQuietS * 1000
489
+ let results = workerResults(result)
490
+ while (gathering(result) && !s.data.closed && Date.now() < Math.min(quiet, cap)) {
491
+ await waitFor(me, Math.min(quiet, cap) - Date.now(), s)
492
+ if (closing) throw new RelayError("unauthorized", "the relay broker is stopping")
493
+ result = core.stopWait(me, peek)
494
+ if (workerResults(result) > results) {
495
+ results = workerResults(result)
496
+ quiet = Date.now() + LIMITS.pushQuietS * 1000
497
+ }
498
+ }
499
+ }
500
+
464
501
  function onData(s: Conn<Connection>, chunk: Uint8Array) {
465
502
  const { lines, tooLong } = s.data.reader.push(chunk)
466
503
  for (const line of lines) s.data.queue.push(line)
@@ -796,22 +833,28 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
796
833
  ...hookOf(s),
797
834
  })
798
835
  case "stop_wait": {
799
- const deadline = Date.now() + waitSeconds(params.wait_s)
836
+ const waitMs = waitSeconds(params.wait_s)
837
+ const deadline = Date.now() + waitMs
800
838
  const options = { ...hookOf(s), ...(typeof params.prompts === "number" ? { prompts: params.prompts } : {}) }
801
- let result = core.stopWait(me, options)
839
+ const peek = { ...options, peek: true }
840
+ let result = core.stopWait(me, peek)
802
841
  const idle = (r: StopWaitResult) => !r.messages.length && !r.more && r.workers.length + r.starting > 0 && r.pushes_left > 0 && !r.prompted
803
842
  while (idle(result) && !s.data.closed && Date.now() < deadline) {
804
843
  await waitFor(me, Math.min(STOP_RECHECK_MS, deadline - Date.now()), s)
805
844
  if (closing) throw new RelayError("unauthorized", "the relay broker is stopping")
806
- result = core.stopWait(me, options)
845
+ result = core.stopWait(me, peek)
807
846
  }
808
- return result
847
+ if (waitMs > 0) await gather(me, result, peek, s)
848
+ return core.stopWait(me, options)
849
+ }
850
+ case "prompted": {
851
+ const prompted = core.prompted(me, hookOf(s))
852
+ if (prompted) wake(me)
853
+ return { prompted }
809
854
  }
810
- case "prompted":
811
- return { prompted: core.prompted(me, hookOf(s)) }
812
855
  case "injected":
813
856
  return {
814
- injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read }),
857
+ injected: core.injected(params.seqs, params.hold, { endpoint: me, read: params.read, deferred: params.deferred === true }),
815
858
  ...(params.stop === true ? { held: core.stopHeld(me, hookOf(s)) } : {}),
816
859
  ...(params.push === true ? { pushed: core.pushed(me, params.read, hookOf(s)) } : {}),
817
860
  ...(params.nudged !== undefined ? { nudged: core.nudged(me, params.nudged, hookOf(s)) } : {}),
@@ -821,6 +864,8 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
821
864
  }
822
865
  case "message":
823
866
  return { message: core.delivered(me, params.id) ?? (params.member === true ? core.threadMessage(me, params.id) : null) }
867
+ case "receipts":
868
+ return core.receipts(me, params.id)
824
869
  }
825
870
  throw new RelayError("method_not_found", `no method ${method}`)
826
871
  }