@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.
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +4 -1
- package/dist/app.js.map +1 -1
- package/dist/configEnv.d.ts +22 -0
- package/dist/configEnv.d.ts.map +1 -0
- package/dist/configEnv.js +46 -0
- package/dist/configEnv.js.map +1 -0
- package/dist/container.d.ts +27 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +16 -1
- package/dist/container.js.map +1 -1
- package/dist/http/errorHandler.js +5 -0
- package/dist/http/errorHandler.js.map +1 -1
- package/dist/http/middleware.d.ts +11 -0
- package/dist/http/middleware.d.ts.map +1 -1
- package/dist/http/middleware.js +28 -9
- package/dist/http/middleware.js.map +1 -1
- package/dist/http/types.d.ts +9 -4
- package/dist/http/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/routes/bugReports.d.ts.map +1 -1
- package/dist/routes/bugReports.js +17 -11
- package/dist/routes/bugReports.js.map +1 -1
- package/dist/routes/events.d.ts.map +1 -1
- package/dist/routes/events.js +17 -0
- package/dist/routes/events.js.map +1 -1
- package/dist/routes/lobby.d.ts.map +1 -1
- package/dist/routes/lobby.js +10 -1
- package/dist/routes/lobby.js.map +1 -1
- package/dist/sse/LocalEventHub.d.ts +23 -5
- package/dist/sse/LocalEventHub.d.ts.map +1 -1
- package/dist/sse/LocalEventHub.js +68 -15
- package/dist/sse/LocalEventHub.js.map +1 -1
- package/dist/sse/MatchLog.d.ts +6 -0
- package/dist/sse/MatchLog.d.ts.map +1 -1
- package/dist/sse/MatchLog.js +10 -0
- package/dist/sse/MatchLog.js.map +1 -1
- package/dist/sse/closeLog.d.ts +12 -0
- package/dist/sse/closeLog.d.ts.map +1 -0
- package/dist/sse/closeLog.js +21 -0
- package/dist/sse/closeLog.js.map +1 -0
- package/dist/sse/createSseResponse.d.ts +40 -1
- package/dist/sse/createSseResponse.d.ts.map +1 -1
- package/dist/sse/createSseResponse.js +79 -8
- package/dist/sse/createSseResponse.js.map +1 -1
- package/dist/sse/membership.d.ts +11 -0
- package/dist/sse/membership.d.ts.map +1 -0
- package/dist/sse/membership.js +13 -0
- package/dist/sse/membership.js.map +1 -0
- 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
|
-
|
|
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',
|
|
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',
|
|
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":"AAAA,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;
|
|
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.
|
|
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.
|
|
52
|
-
"@chaos-overlords/contracts": "0.9.
|
|
53
|
-
"@chaos-overlords/kernel": "0.9.
|
|
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.
|
|
59
|
+
"@chaos-overlords/conformance": "0.9.3",
|
|
60
60
|
"typescript": "^7.0.2",
|
|
61
61
|
"vitest": "^4.1.11"
|
|
62
62
|
},
|