Two reports, one cause: the script's fetch had no .catch. When the server had exited the promise rejected, the chain never ran, and not even the status line moved — so a dead server and a click that did nothing were indistinguishable. play again read as a dead button, and the linger timeout was invisible. Now a rejection says the session is gone and seals the page, and a 5-second heartbeat against a new /alive notices it without needing a click, which is what the timeout case requires. The beat carries the token, and does not extend the linger: that deadline is absolute. The harness had the same hole. jsrun's fetch stub had no .catch, so the branch that notices a dead server would have been unreachable in every test — the very defect the stub's own comment records from CB-WP-0024. Teaching it __failing, .catch and a recorded setInterval was the fix; writing the script defensively would have repeated the trap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c2a7e40b67
commit
82aead6cea
9 changed files with 448 additions and 7 deletions
|
|
@ -91,7 +91,9 @@ pub const SCRIPT: &str = r#"
|
|||
//
|
||||
// No game vocabulary here (ADR-0007 D5). 'closed' is a server
|
||||
// lifecycle word, exactly like the 'ok' branch below it.
|
||||
var sealed = false;
|
||||
function seal() {
|
||||
sealed = true;
|
||||
// The WHOLE page goes inert, not only the controls. A greyed-out
|
||||
// button beside a full-colour table still reads as a live game with
|
||||
// one broken control; the session has ended and everything on screen
|
||||
|
|
@ -206,8 +208,33 @@ pub const SCRIPT: &str = r#"
|
|||
status(t);
|
||||
if (t.indexOf('ok') === 0) { window.location.reload(); }
|
||||
else if (t.indexOf('closed') === 0) { seal(); }
|
||||
});
|
||||
}).catch(gone);
|
||||
});
|
||||
|
||||
// CB-WP-0035. The session ended without this page being told.
|
||||
//
|
||||
// There was no rejection handler at all, so when the server had exited
|
||||
// the promise rejected and the chain simply never ran -- not even the
|
||||
// status line moved. `play again` looked like a dead button, and the
|
||||
// linger timeout was invisible. A request that cannot be answered is
|
||||
// information, and it was being thrown away.
|
||||
function gone() {
|
||||
status('the session is no longer available \u2014 the game server has '
|
||||
+ 'stopped. Nothing on this page can act; it is a record now.');
|
||||
seal();
|
||||
}
|
||||
|
||||
// And notice it WITHOUT being clicked, which is what the timeout needs:
|
||||
// a player who walks away comes back to a sealed page rather than to a
|
||||
// live-looking table that answers nothing.
|
||||
if (window.CB_ALIVE) {
|
||||
setInterval(function () {
|
||||
if (sealed) { return; }
|
||||
fetch(window.CB_ALIVE).then(function (r) {
|
||||
if (!r.ok) { gone(); }
|
||||
}).catch(gone);
|
||||
}, 5000);
|
||||
}
|
||||
})();
|
||||
"#;
|
||||
|
||||
|
|
@ -949,6 +976,7 @@ pub fn document(
|
|||
Endpoints {
|
||||
command: endpoint,
|
||||
note: "/note",
|
||||
alive: "/alive",
|
||||
},
|
||||
seat,
|
||||
may_pass,
|
||||
|
|
@ -969,6 +997,8 @@ pub struct Endpoints<'a> {
|
|||
pub command: &'a str,
|
||||
/// Free text. Nothing turns these into commands.
|
||||
pub note: &'a str,
|
||||
/// Where the page checks the session is still there (CB-WP-0035).
|
||||
pub alive: &'a str,
|
||||
}
|
||||
|
||||
pub fn document_with_log(
|
||||
|
|
@ -980,7 +1010,7 @@ pub fn document_with_log(
|
|||
log: Account<'_>,
|
||||
meta: &[String],
|
||||
) -> String {
|
||||
let (endpoint, note_to) = (to.command, to.note);
|
||||
let (endpoint, note_to, alive) = (to.command, to.note, to.alive);
|
||||
let mut s = String::with_capacity(8192);
|
||||
let _ = write!(
|
||||
s,
|
||||
|
|
@ -1019,9 +1049,10 @@ pub fn document_with_log(
|
|||
let _ = write!(
|
||||
s,
|
||||
"<div id=\"cb-status\">ready</div>\
|
||||
<script>window.CB_ENDPOINT={endpoint}</script><script>{SCRIPT}</script>\
|
||||
</body></html>",
|
||||
<script>window.CB_ENDPOINT={endpoint};window.CB_ALIVE={alive}</script>\
|
||||
<script>{SCRIPT}</script></body></html>",
|
||||
endpoint = json_string(endpoint),
|
||||
alive = json_string(alive),
|
||||
);
|
||||
s
|
||||
}
|
||||
|
|
@ -1733,6 +1764,7 @@ pub fn ending(
|
|||
message: &str,
|
||||
endpoint: &str,
|
||||
note_to: Option<&str>,
|
||||
alive: &str,
|
||||
log: Account<'_>,
|
||||
series: &[String],
|
||||
) -> String {
|
||||
|
|
@ -1814,8 +1846,10 @@ pub fn ending(
|
|||
let _ = write!(
|
||||
s,
|
||||
"<div id=\"cb-status\">the game is over</div>\
|
||||
<script>window.CB_ENDPOINT={endpoint}</script><script>{SCRIPT}</script>",
|
||||
<script>window.CB_ENDPOINT={endpoint};window.CB_ALIVE={alive}</script>\
|
||||
<script>{SCRIPT}</script>",
|
||||
endpoint = json_string(endpoint),
|
||||
alive = json_string(alive),
|
||||
);
|
||||
s
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue