@afokapu/atdd-bun 0.10.9 → 0.10.10

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/integrity.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.10.9",
2
+ "version": "0.10.10",
3
3
  "files": {
4
4
  "HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
5
5
  "PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
@@ -1193,7 +1193,7 @@
1193
1193
  "relationships.yaml": "1cb713364aff54fb6e299e0737fe75a5d7b2e1dbd0a79b16b793aa1d269207b5",
1194
1194
  "src/agent.ts": "65e4579adb5a54977f557f8c783aba0e2a2a1d5cf3cea9f017b67ffe598b2f65",
1195
1195
  "src/board-ui.ts": "16a843705552f8f18e0e328e90e4a2a9148d771ca69f2708fe4a91302947dfad",
1196
- "src/board.ts": "115c6cff12fb2b20e6471013161aef020a65331ecb97fa323e9b08f55053149f",
1196
+ "src/board.ts": "a3a8efe923858b9b5cf5ca3682dacfceb5557f8303d818687eb1325a15fa1782",
1197
1197
  "src/ci.ts": "67e54d2cbfc44a9e42837d5e750af526d6255c539d3c452a4d5fca3226c0d9ac",
1198
1198
  "src/cli.ts": "e45f2666334503a91eb65b1b5ee4dd8acab6b99f62d333e2ee2b65963db6358b",
1199
1199
  "src/delivery.ts": "5688356e26bf5aa49ad940d1bcd42e41b9f3dc1c21dc833b328fcb38a3f04fb8",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afokapu/atdd-bun",
3
- "version": "0.10.9",
3
+ "version": "0.10.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/afokapu/atdd-bun.git"
package/src/board.ts CHANGED
@@ -97,8 +97,8 @@ export async function post(url: string, topic: string, header: Header, body: str
97
97
  }
98
98
 
99
99
  /** Every message on the topics after `since` (a message id, or "all"). */
100
- export async function read(url: string, topics: string[], since = "all"): Promise<Message[]> {
101
- const res = await fetch(`${url}/${topics.join(",")}/json?poll=1&since=${since}`);
100
+ export async function read(url: string, topics: string[], since = "all", signal?: AbortSignal): Promise<Message[]> {
101
+ const res = await fetch(`${url}/${topics.join(",")}/json?poll=1&since=${since}`, { signal });
102
102
  if (!res.ok) throw new Error(`reading ${topics.join(", ")} failed: ${res.status} ${await res.text()}`);
103
103
  return (await res.text()).split("\n").filter(Boolean).map(line => JSON.parse(line)).filter(m => m.event === "message")
104
104
  .map(m => ({ id: m.id, time: m.time, topic: m.topic, raw: m.message, ...parseEnvelope(m.message) }));
@@ -108,11 +108,17 @@ export async function read(url: string, topics: string[], since = "all"): Promis
108
108
  * It polls rather than streams: a live stream can drop a message published while it connects. ntfy reads `since` (a
109
109
  * message id) as that message's time on every topic, so one cursor serves several topics. Returns null after
110
110
  * `timeoutSeconds`, so an agent's tool call stays bounded. */
111
+ /** The longest one poll may take. ntfy answers a poll at once, so only a stalled request ever reaches it. */
112
+ const POLL_LIMIT_MS = 10_000;
113
+
111
114
  export async function waitFor(url: string, topics: string[], agent: string, since = "all", timeoutSeconds = 0, skip: string[] = [], intervalMs = 500, seen: (id: string) => void = () => {}): Promise<Message | null> {
112
115
  const deadline = timeoutSeconds > 0 ? Date.now() + timeoutSeconds * 1000 : Infinity;
113
116
  for (;;) {
114
117
  let messages: Message[] = [];
115
- try { messages = await read(url, topics, since); } catch { /* the board is briefly unavailable: keep waiting */ }
118
+ // Each poll is bounded: a request that never answers (a half-open connection after the board restarts or the
119
+ // machine sleeps) would otherwise hold the wait past its deadline and deliver nothing. It counts as unavailable.
120
+ const bound = AbortSignal.timeout(Math.max(1, Math.min(POLL_LIMIT_MS, deadline - Date.now())));
121
+ try { messages = await read(url, topics, since, bound); } catch { /* the board is briefly unavailable: keep waiting */ }
116
122
  for (const message of messages) { since = message.id; seen(since); if (addressedTo(message, agent) && !skip.includes(message.header.kind ?? "")) return message; }
117
123
  if (Date.now() >= deadline) return null;
118
124
  await Bun.sleep(intervalMs);