fix(web): default the socket to TLS, and cache-bust the deployed bundles
?ws= now takes a host:port and the page supplies the scheme: wss:// when it
is itself served over https, ws:// when over http. An explicit ws:// from an
https page is UPGRADED rather than passed through, because an https origin
refuses it as mixed content -- so passing it through can only produce a
failure that reads like 'the server is not running'. The scheme is not the
caller's to get wrong.
And the deploy now stamps ?v=<sha> onto both bundles. Pages serves everything
max-age=600 and the wasm-pack filenames carry no content hash, so for ten
minutes after a deploy a returning visitor runs the PREVIOUS build -- which
made a shipped fix look like it had not shipped, twice, during the first real
browser debugging session.
BOTH halves are stamped, because a fresh glue file does not pull a fresh
.wasm: the glue locates it with new URL('..._bg.wasm', import.meta.url), and
a URL built against a base does not inherit the base's query. Stamping only
the import specifier would refresh the wrapper and keep the stale binary --
exactly the failure this is here to end.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
This commit is contained in:
+32
-3
@@ -29,11 +29,12 @@
|
||||
// default an in-page Web Worker hosting the whole App over OPFS
|
||||
// (improvise-9x6). No server process; this is what makes a
|
||||
// GitHub Pages copy of this directory a working application.
|
||||
// ?ws=URL a websocket to `improvise-ws-server`. That process is how a
|
||||
// ?ws=host:port
|
||||
// a websocket to `improvise-ws-server`. That process is how a
|
||||
// document opened in the TUI is shared, so the path is kept; the
|
||||
// presence of the parameter is the whole selection mechanism,
|
||||
// because a second knob naming the same fact is a second thing to
|
||||
// get wrong.
|
||||
// get wrong. The scheme defaults to `wss://` — see `socketUrl`.
|
||||
//
|
||||
// Neither is reachable silently. Every transport writes what it is doing to
|
||||
// `#status` — "starting", "connected", "cannot reach …", "disconnected" — so
|
||||
@@ -42,7 +43,35 @@
|
||||
import init, { Client } from "./pkg/improvise_wasm_client.js";
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
const WS_URL = params.get("ws"); // present => socket, absent => in-page worker
|
||||
// present => socket, absent => in-page worker. The value is a host:port, and
|
||||
// the scheme is NOT the caller's to get wrong: see `socketUrl`.
|
||||
const WS_PEER = params.get("ws");
|
||||
|
||||
/**
|
||||
* The websocket URL for `?ws=`, defaulting to TLS.
|
||||
*
|
||||
* `wss://` is the default and a bare `host:port` gets it, because the address
|
||||
* that works from where this page is actually served is the one it should be
|
||||
* easiest to write. This page ships to GitHub Pages, which is https, and an
|
||||
* https origin refuses a `ws://` address as mixed content — so a `ws://`
|
||||
* default would hand the browser a URL that cannot work and report it as a
|
||||
* connection failure, which reads like "the server is not running".
|
||||
*
|
||||
* An explicit `ws://` is honoured only where it can function: a page served
|
||||
* over plain http, which is the local `python3 -m http.server` case that pairs
|
||||
* with `improvise-ws-server` (which speaks plain `ws://` only — it has no TLS,
|
||||
* so reaching it from https means terminating TLS in front of it). From an
|
||||
* https page an explicit `ws://` is upgraded rather than passed through, since
|
||||
* passing it through can only produce a failure the user cannot act on.
|
||||
*/
|
||||
function socketUrl(peer) {
|
||||
const secure = location.protocol === "https:";
|
||||
if (/^wss:\/\//i.test(peer)) return peer;
|
||||
if (/^ws:\/\//i.test(peer)) return secure ? peer.replace(/^ws:/i, "wss:") : peer;
|
||||
return `${secure ? "wss" : "ws"}://${peer}`;
|
||||
}
|
||||
|
||||
const WS_URL = WS_PEER ? socketUrl(WS_PEER) : null;
|
||||
const DOC = params.get("doc") ?? "demo.improv";
|
||||
const SEED = params.get("seed") ?? "demo.improv";
|
||||
|
||||
|
||||
+9
-9
@@ -29,8 +29,8 @@
|
||||
?doc= and ?seed= name the document and the file it is first seeded from.
|
||||
|
||||
To talk to a separate `improvise-ws-server` process instead — which is
|
||||
how a document open in the TUI is shared — pass ?ws=wss://host:port
|
||||
(or ws:// when this page is served over http://localhost).
|
||||
how a document open in the TUI is shared — pass ?ws=host:port; the
|
||||
scheme defaults to wss:// on https and ws:// on http.
|
||||
-->
|
||||
<header>
|
||||
<h1>improvise</h1>
|
||||
@@ -60,13 +60,13 @@
|
||||
<code>?seed=url</code>. To open a document hosted by a separate
|
||||
process instead, run
|
||||
<code>improvise-ws-server model.improv --port 8787</code> and reload
|
||||
with <code>?ws=wss://host:port</code>. From this https page the
|
||||
address must be <code>wss://</code> — a <code>ws://</code> one is
|
||||
blocked as mixed content — and
|
||||
<code>improvise-ws-server</code> speaks plain <code>ws://</code> only,
|
||||
so it needs TLS terminated in front of it. Served over
|
||||
<code>http://localhost</code> the same page takes
|
||||
<code>?ws=ws://127.0.0.1:8787</code> directly.
|
||||
with <code>?ws=host:port</code>. <strong>The scheme defaults to
|
||||
<code>wss://</code></strong>, because this page is served over https and
|
||||
an https origin refuses a <code>ws://</code> address as mixed content.
|
||||
Note that <code>improvise-ws-server</code> speaks plain
|
||||
<code>ws://</code> only, so reaching it from here means terminating TLS
|
||||
in front of it. Served over <code>http://localhost</code> the same page
|
||||
defaults to <code>ws://</code> and pairs with it directly.
|
||||
</p>
|
||||
|
||||
<main>
|
||||
|
||||
Reference in New Issue
Block a user