@chaos-overlords/server 0.9.1 → 0.9.3

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.
Files changed (53) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +4 -1
  3. package/dist/app.js.map +1 -1
  4. package/dist/configEnv.d.ts +22 -0
  5. package/dist/configEnv.d.ts.map +1 -0
  6. package/dist/configEnv.js +46 -0
  7. package/dist/configEnv.js.map +1 -0
  8. package/dist/container.d.ts +27 -1
  9. package/dist/container.d.ts.map +1 -1
  10. package/dist/container.js +16 -1
  11. package/dist/container.js.map +1 -1
  12. package/dist/http/errorHandler.js +5 -0
  13. package/dist/http/errorHandler.js.map +1 -1
  14. package/dist/http/middleware.d.ts +11 -0
  15. package/dist/http/middleware.d.ts.map +1 -1
  16. package/dist/http/middleware.js +28 -9
  17. package/dist/http/middleware.js.map +1 -1
  18. package/dist/http/types.d.ts +9 -4
  19. package/dist/http/types.d.ts.map +1 -1
  20. package/dist/index.d.ts +4 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +4 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/routes/bugReports.d.ts.map +1 -1
  25. package/dist/routes/bugReports.js +17 -11
  26. package/dist/routes/bugReports.js.map +1 -1
  27. package/dist/routes/events.d.ts.map +1 -1
  28. package/dist/routes/events.js +17 -0
  29. package/dist/routes/events.js.map +1 -1
  30. package/dist/routes/lobby.d.ts.map +1 -1
  31. package/dist/routes/lobby.js +10 -1
  32. package/dist/routes/lobby.js.map +1 -1
  33. package/dist/sse/LocalEventHub.d.ts +23 -5
  34. package/dist/sse/LocalEventHub.d.ts.map +1 -1
  35. package/dist/sse/LocalEventHub.js +68 -15
  36. package/dist/sse/LocalEventHub.js.map +1 -1
  37. package/dist/sse/MatchLog.d.ts +6 -0
  38. package/dist/sse/MatchLog.d.ts.map +1 -1
  39. package/dist/sse/MatchLog.js +10 -0
  40. package/dist/sse/MatchLog.js.map +1 -1
  41. package/dist/sse/closeLog.d.ts +12 -0
  42. package/dist/sse/closeLog.d.ts.map +1 -0
  43. package/dist/sse/closeLog.js +21 -0
  44. package/dist/sse/closeLog.js.map +1 -0
  45. package/dist/sse/createSseResponse.d.ts +40 -1
  46. package/dist/sse/createSseResponse.d.ts.map +1 -1
  47. package/dist/sse/createSseResponse.js +79 -8
  48. package/dist/sse/createSseResponse.js.map +1 -1
  49. package/dist/sse/membership.d.ts +11 -0
  50. package/dist/sse/membership.d.ts.map +1 -0
  51. package/dist/sse/membership.js +13 -0
  52. package/dist/sse/membership.js.map +1 -0
  53. package/package.json +5 -5
@@ -1,5 +1,15 @@
1
1
  import { MATCH_EVENT_SSE_NAME, matchEventSchema, SSE_HEARTBEAT_COMMENT, } from '@chaos-overlords/contracts';
2
2
  import { validateSync } from '@toad-contracts/core';
3
+ /**
4
+ * Whether a close is one a healthy client should never see: the server dropped a stream that was
5
+ * still being read, for a reason of its own.
6
+ *
7
+ * `client_gone`, `replaced` and `shutdown` are routine, and a revoked membership is the stream
8
+ * doing its job. The rest are worth an operator's attention.
9
+ */
10
+ export function isUnexpectedClose(reason) {
11
+ return reason === 'membership_unverified' || reason === 'stalled' || reason === 'read_failed';
12
+ }
3
13
  /**
4
14
  * Heartbeats between two catch-up reads a caught-up stream cannot talk its way out of.
5
15
  *
@@ -19,6 +29,17 @@ const CATCH_UP_EVERY_HEARTBEATS = 5;
19
29
  * cannot recover: it reconnects and resumes from its `Last-Event-ID`.
20
30
  */
21
31
  const STALLED_HEARTBEATS_BEFORE_DROP = 3;
32
+ /**
33
+ * Consecutive membership checks that may fail or hang before the stream is dropped.
34
+ *
35
+ * A check that FAILS has learned nothing about the membership, only about the database, and D1 in
36
+ * particular answers the odd query with a transient error. Dropping the stream on the first one
37
+ * put a "connection lost" dialog in front of a player whose membership, server and connection were
38
+ * all fine. A kick does not wait on this check — `LocalEventHub.close` hangs the stream up at once —
39
+ * so tolerating one failure only delays the backstop by a catch-up cycle, and a check that answers
40
+ * "not a member" still ends the stream immediately.
41
+ */
42
+ const MEMBERSHIP_CHECK_FAILURES_BEFORE_DROP = 2;
22
43
  const encoder = new TextEncoder();
23
44
  /**
24
45
  * Frames buffered for a consumer that is not keeping up, before the drain parks.
@@ -129,7 +150,7 @@ export function createSseResponse(source, options) {
129
150
  catch {
130
151
  // Already closed by the consumer.
131
152
  }
132
- shutdown();
153
+ shutdown('read_failed');
133
154
  })
134
155
  .finally(() => {
135
156
  draining = null;
@@ -144,11 +165,22 @@ export function createSseResponse(source, options) {
144
165
  // A thunk, not `shutdown` itself: the real one is assigned a few lines below and the
145
166
  // placeholder above it only flips `closed`, so passing the reference here would hand the
146
167
  // hub a close that leaves the heartbeat running and the subscription in place.
147
- const unsubscribe = source.subscribe(() => wake(), () => {
148
- shutdown();
168
+ const unsubscribe = source.subscribe(() => wake(), (reason) => {
169
+ shutdown(reason);
149
170
  });
150
171
  let beats = 0;
151
172
  let stalledBeats = 0;
173
+ let validating = false;
174
+ /** The pending check already counted as failed by an overdue beat; see below. */
175
+ let pendingCounted = false;
176
+ let failedChecks = 0;
177
+ /** A check that learned nothing; enough of them in a row and the stream is not trusted. */
178
+ const checkFailed = () => {
179
+ failedChecks += 1;
180
+ if (failedChecks >= MEMBERSHIP_CHECK_FAILURES_BEFORE_DROP) {
181
+ shutdown('membership_unverified');
182
+ }
183
+ };
152
184
  const heartbeat = setInterval(() => {
153
185
  // A consumer that is not reading is not a consumer. The queue is full, so this frame
154
186
  // would only buffer; after a few beats of that the connection is gone in every way that
@@ -156,7 +188,7 @@ export function createSseResponse(source, options) {
156
188
  if ((controller.desiredSize ?? 1) <= 0) {
157
189
  stalledBeats += 1;
158
190
  if (stalledBeats >= STALLED_HEARTBEATS_BEFORE_DROP)
159
- shutdown();
191
+ shutdown('stalled');
160
192
  return;
161
193
  }
162
194
  stalledBeats = 0;
@@ -167,16 +199,49 @@ export function createSseResponse(source, options) {
167
199
  // idle server was otherwise issuing one query per stream per heartbeat to find nothing.
168
200
  beats += 1;
169
201
  const overdue = beats % CATCH_UP_EVERY_HEARTBEATS === 0;
202
+ if (overdue && options.revalidate) {
203
+ // A lookup still pending a whole catch-up cycle later is a failed one. Waiting on it
204
+ // without counting it left the stream never checked again for as long as that query
205
+ // hung; it is left to settle rather than raced by a second one.
206
+ if (validating) {
207
+ pendingCounted = true;
208
+ checkFailed();
209
+ if (closed)
210
+ return;
211
+ }
212
+ else {
213
+ validating = true;
214
+ pendingCounted = false;
215
+ void Promise.resolve()
216
+ .then(options.revalidate)
217
+ .then((valid) => {
218
+ validating = false;
219
+ if (!valid)
220
+ shutdown('membership_revoked');
221
+ else
222
+ failedChecks = 0;
223
+ }, () => {
224
+ validating = false;
225
+ // One lookup is one failure: a check that hung past a catch-up cycle was
226
+ // counted then, and its eventual rejection is the same failure, not a second.
227
+ if (!pendingCounted)
228
+ checkFailed();
229
+ });
230
+ }
231
+ }
232
+ // The catch-up does not wait on the membership check: notifications keep delivering
233
+ // while it is in flight anyway, so holding this back only delayed the read.
170
234
  if (overdue || !source.caughtUp(lastSeq))
171
235
  wake(overdue);
172
236
  }, options.heartbeatMs);
173
- shutdown = () => {
237
+ const aborted = () => shutdown('client_gone');
238
+ shutdown = (reason) => {
174
239
  if (closed)
175
240
  return;
176
241
  closed = true;
177
242
  // A stream ended by `cancel`, a stall or its hub would otherwise leave this listener, and
178
243
  // everything its closure holds, on the request signal for as long as that lives.
179
- options.signal.removeEventListener('abort', shutdown);
244
+ options.signal.removeEventListener('abort', aborted);
180
245
  clearInterval(heartbeat);
181
246
  unsubscribe();
182
247
  // Release a drain parked on backpressure, so it observes `closed` and stops.
@@ -188,8 +253,14 @@ export function createSseResponse(source, options) {
188
253
  catch {
189
254
  // Already errored, or cancelled by the consumer.
190
255
  }
256
+ try {
257
+ options.onClose?.(reason);
258
+ }
259
+ catch {
260
+ // An observer that throws must not escape into a timer or abort a hub's close loop.
261
+ }
191
262
  };
192
- options.signal.addEventListener('abort', shutdown, { once: true });
263
+ options.signal.addEventListener('abort', aborted, { once: true });
193
264
  send(': connected\n\n');
194
265
  // The opening catch-up always reaches the log: a fresh stream has been told nothing.
195
266
  wake(true);
@@ -202,7 +273,7 @@ export function createSseResponse(source, options) {
202
273
  },
203
274
  // The consumer went away (a dropped TCP connection surfaces here, not as an abort).
204
275
  cancel() {
205
- shutdown();
276
+ shutdown('client_gone');
206
277
  },
207
278
  }, new CountQueuingStrategy({ highWaterMark: STREAM_HIGH_WATER_MARK }));
208
279
  return new Response(stream, { headers: SSE_HEADERS });
@@ -1 +1 @@
1
- {"version":3,"file":"createSseResponse.js","sourceRoot":"","sources":["../../src/sse/createSseResponse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createSseResponse.js","sourceRoot":"","sources":["../../src/sse/createSseResponse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { PlayerRepository } from '@chaos-overlords/kernel';
2
+ /**
3
+ * Whether `playerId` still holds a live membership of `matchId`: the row exists, belongs to that
4
+ * match, and its token has not been revoked. A token is issued once per membership and never
5
+ * rotated, so a non-null hash is the token the stream was authenticated with.
6
+ *
7
+ * The stream route runs this right after subscribing and every hub repeats it on its catch-up
8
+ * heartbeat; one definition keeps the two from drifting apart.
9
+ */
10
+ export declare function isActiveMember(players: PlayerRepository, matchId: string, playerId: string): Promise<boolean>;
11
+ //# sourceMappingURL=membership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../src/sse/membership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAGlB"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Whether `playerId` still holds a live membership of `matchId`: the row exists, belongs to that
3
+ * match, and its token has not been revoked. A token is issued once per membership and never
4
+ * rotated, so a non-null hash is the token the stream was authenticated with.
5
+ *
6
+ * The stream route runs this right after subscribing and every hub repeats it on its catch-up
7
+ * heartbeat; one definition keeps the two from drifting apart.
8
+ */
9
+ export async function isActiveMember(players, matchId, playerId) {
10
+ const player = await players.get(playerId);
11
+ return player?.matchId === matchId && player.tokenHash !== null;
12
+ }
13
+ //# sourceMappingURL=membership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"membership.js","sourceRoot":"","sources":["../../src/sse/membership.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAyB,EACzB,OAAe,EACf,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC1C,OAAO,MAAM,EAAE,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI,CAAA;AACjE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaos-overlords/server",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "The Hono application both runtimes serve: REST intents, the SSE event stream, auth and the error envelope. Takes its ports through a container.",
5
5
  "keywords": [
6
6
  "chaos-overlords",
@@ -48,15 +48,15 @@
48
48
  "provenance": true
49
49
  },
50
50
  "dependencies": {
51
- "@chaos-overlords/bug-reports": "0.9.1",
52
- "@chaos-overlords/contracts": "0.9.1",
53
- "@chaos-overlords/kernel": "0.9.1",
51
+ "@chaos-overlords/bug-reports": "0.9.3",
52
+ "@chaos-overlords/contracts": "0.9.3",
53
+ "@chaos-overlords/kernel": "0.9.3",
54
54
  "@toad-contracts/core": "1.0.0",
55
55
  "@toad-contracts/hono": "1.0.0",
56
56
  "hono": "^4.13.8"
57
57
  },
58
58
  "devDependencies": {
59
- "@chaos-overlords/conformance": "0.9.1",
59
+ "@chaos-overlords/conformance": "0.9.3",
60
60
  "typescript": "^7.0.2",
61
61
  "vitest": "^4.1.11"
62
62
  },