// The one rule `web/worker.js` exists to keep: one frame at a time, in arrival // order. No DOM, no worker globals — context/code/clients.md. /** Chain every arrival onto one tail promise, starting from `head`. * * An `async` listener does NOT serialize: the event loop is free to start the * next message while an earlier one is suspended at an `await`, so two * handler runs interleave and a later frame's replies can be posted before an * earlier frame's — which the client's fold answers by DROPPING the overtaken * update, not by replaying it. Chaining is also what makes "frames that * arrived before `start()` finished" an ordinary case of this rule rather * than a drain loop with the race in it twice over. */ export function serialize(head, handler, onError) { let tail = head.catch(() => {}); return (value) => { tail = tail.then(() => handler(value)).catch((err) => onError(err, value)); return tail; }; }