Skip to content

v0.13.0

Latest

Choose a tag to compare

@raymondfeng raymondfeng released this 30 Sep 05:29
· 48 commits to main since this release

A small feature release with one job: WebSocket endpoints on the app's own
port.
RestServer.upgrade() lets a ws endpoint live beside the REST routes
on the same port and origin, without reading a private field or opening a
second listener (#60).
It is opt-in — an app that registers no upgrade path behaves exactly as it did
on 0.12.0.

✨ New

RestServer.upgrade(path, handler, {close?}) — @agentback/rest

Node never routes an HTTP Upgrade request through the request handler; it
emits a separate 'upgrade' event on the server with the raw socket, and
RestServer kept that server private. upgrade() is now the supported seam,
on both the Express and native listeners:

import {WebSocketServer} from 'ws';

const server = await app.restServer;
const wss = new WebSocketServer({noServer: true});

server.upgrade(
  '/ws',
  (req, socket, head) => {
    // Authenticate here: no REST middleware, auth or rate limiting runs.
    wss.handleUpgrade(req, socket, head, ws => wss.emit('connection', ws, req));
  },
  {close: () => wss.close()},
);
await app.start();
  • Exact pathname match (query ignored, basePath not applied). Register
    before start() — afterwards it throws. It returns a remover, which
    works at any time.
  • Nothing changes until you register a path. The 'upgrade' listener is
    installed only when at least one path exists; with none, Node keeps
    downgrading upgrade requests to plain requests, as before.
  • Once a path is registered, an upgrade on another path gets 404 and a
    malformed request-target 400. Both are parsed inside a try/catch,
    because a throw escaping an 'upgrade' listener is an uncaught exception
    that would kill the process before any auth ran.
  • A handler that throws or rejects loses its socket; the error is logged
    and the process stays up.
  • stop() awaits each close hook, then destroys every accepted socket
    still open.
    An upgraded socket is no longer an HTTP connection, so without
    that sweep server.close() was measured to wait on it forever. A test pins
    the sweep and fails when it is removed.
  • Handlers are not middleware — authentication is theirs.
  • Node listener only: nothing is accepted under listen: false or on a
    fetch host (fetchHandler(), Workers, Bun, Deno).

New exported types: UpgradeHandler and UpgradeOptions. ws is a
devDependency of @agentback/rest (for its tests) — bring your own
WebSocket library; no runtime or peer dependency was added.

No other package changed behavior in this release; the lockstep bump carries
them at 0.13.0 unchanged.