Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 22x 20x 20x 20x 20x 20x 10x 10x 6x 20x 20x 20x 20x 4x 4x 3x 3x 4x 20x 4x 3x 3x 6x 2x 2x 21x 1x | /**
* Tiny `io()` API shim served at `/socket.io/socket.io.js`.
*
* External embeds (Drupal sheetnode, stale CDN snapshots, etc.) include
* the legacy script tag:
*
* <script src="/socket.io/socket.io.js"></script>
*
* They expect `window.io` to be a factory producing an object with
* `.emit()`, `.on()`, `.disconnect()`. We give them a facade over a
* native `WebSocket` connection to our new `/_ws/:room` endpoint so they
* don't pay for the legacy handshake roundtrip.
*
* The emitted script is deliberately terse. Modern browsers don't need
* polyfills; the legacy clients we care about (IE11-era sheetnode) have
* already died out per §13 Q4's "indefinite" decision.
*
* We export the code both as a string (LEGACY_IO_JS) — which the worker
* serves as JS — and as a function (installLegacyIo) which the test
* suite can eval into a happy-dom window to assert behaviour.
*/
/**
* Install the `io()` API onto a host global. Factored out so tests can
* call it directly against a mocked `window`. In production this is
* emitted as an IIFE string via `LEGACY_IO_JS` below.
*/
export function installLegacyIo(host: {
WebSocket: typeof WebSocket;
location: { host: string; protocol: string };
io?: unknown;
}): void {
// If a real socket.io client is already present (unlikely) do nothing.
if (host.io) return;
type Listener = (payload: unknown) => void;
function io(url?: string): {
emit: (event: string, data: unknown) => void;
on: (event: string, cb: Listener) => void;
disconnect: () => void;
} {
// Derive the native WS URL. The legacy `io()` takes an arbitrary host
// URL; we ignore it and connect to `/_ws/<room>` where `<room>` is
// pulled from the current pathname. Embeds that need a specific room
// pass it via the URL path (`/<room>`), which is already how legacy
// sheetnode worked.
const proto = host.location.protocol === 'https:' ? 'wss:' : 'ws:';
const target =
typeof url === 'string' && url.length > 0
? url
: `${proto}//${host.location.host}/_ws/legacy`;
// Rewrite http(s):// to ws(s):// for convenience — embeds sometimes
// pass `window.location.origin` which starts with http.
const wsUrl = target.replace(/^https?:/, proto);
const ws = new host.WebSocket(wsUrl);
const listeners: Record<string, Listener[]> = {};
function fire(event: string, payload: unknown): void {
const ls = listeners[event];
if (!ls) return;
for (const l of ls) l(payload);
}
ws.addEventListener('open', () => fire('connect', undefined));
ws.addEventListener('close', () => fire('disconnect', undefined));
ws.addEventListener('error', () => fire('error', undefined));
ws.addEventListener('message', (ev: MessageEvent) => {
// The new transport speaks raw JSON; external embeds used to
// receive a single "data" event. We preserve that contract.
let parsed: unknown = ev.data;
if (typeof ev.data === 'string') {
try {
parsed = JSON.parse(ev.data);
} catch {
// Leave it as a string.
}
}
fire('data', parsed);
});
return {
emit(event: string, data: unknown): void {
if (event !== 'data') return;
// Embeds send `socket.emit('data', {type:'execute',…})` — the
// payload is already a native ClientMessage.
try {
ws.send(typeof data === 'string' ? data : JSON.stringify(data));
} catch {
// Ignore send errors on a closed socket; embed will reconnect
// via its own retry loop.
}
},
on(event: string, cb: Listener): void {
(listeners[event] ??= []).push(cb);
},
disconnect(): void {
try {
ws.close();
} catch {
// Already closed.
}
},
};
}
host.io = io;
}
/**
* Pre-built JS string served at `/socket.io/socket.io.js`. The worker
* responds with `Content-Type: application/javascript; charset=utf-8`
* and this body.
*
* Structure: IIFE that calls `installLegacyIo({ WebSocket, location })`
* against the browser's global namespace. We inline the function body by
* stringifying `installLegacyIo` so the output has zero build-time
* dependency on bundler magic.
*/
export const LEGACY_IO_JS = `/* EtherCalc legacy socket.io v0.9 shim — generated, do not edit */
(function(){
var installLegacyIo = ${installLegacyIo.toString()};
installLegacyIo({
WebSocket: window.WebSocket,
location: window.location
});
})();
`;
|