@localpreview/protocol
v0.2.3
Published
Shared protocol contract for LocalPreview packages.
Readme
@localpreview/protocol
Shared protocol contract for LocalPreview packages.
This package contains the stable shapes and helpers used by the CLI, control-plane, and relay. Keep runtime-specific code out of this package; it should stay small, dependency-light, and safe to import from every LocalPreview runtime.
What Lives Here
- Public LocalPreview origin constants.
- Tunnel creation and relay registration response/request shapes.
- Relay WebSocket message encoding and validation.
- Shared error codes and error response shape.
- Header filtering helpers for proxy boundaries.
- Target and requested subdomain validation.
- Protocol and relay snapshot version constants.
Relay Message Flow
The relay protocol multiplexes browser HTTP requests over the CLI WebSocket.
Messages are JSON strings and every message belongs to a requestId.
browser request
-> relay sends request-start/request-chunk/request-end to CLI
-> CLI forwards to local target
-> CLI sends response-start/response-chunk/response-end to relay
-> relay streams response back to browserServer-to-client messages are decoded with decodeServerRelayMessage.
Client-to-server messages are decoded with decodeClientRelayMessage.
import {
decodeServerRelayMessage,
encodeRelayMessage,
type ServerMessage,
} from "@localpreview/protocol";
const message: ServerMessage = {
type: "request-start",
requestId: "req_1",
method: "GET",
path: "/hello",
headers: [["host", "example.test"]],
};
const encoded = encodeRelayMessage(message);
const decoded = decodeServerRelayMessage(encoded);Decoders return { ok: true, message } or { ok: false, error }. They do not
throw on invalid JSON or invalid message shapes.
Request and response body chunks are sent as base64 strings in chunkBase64.
Headers are represented as ordered [name, value] pairs rather than an object
map so duplicate headers such as set-cookie are preserved.
Targets
parseTarget normalizes CLI target input into a TunnelTarget.
import { parseTarget } from "@localpreview/protocol";
parseTarget("4000");
// { ok: true, target: { protocol: "http", hostname: "127.0.0.1", port: 4000 } }
parseTarget("https://localhost:4000");
// { ok: true, target: { protocol: "https", hostname: "localhost", port: 4000 } }Rules:
- A bare port means
http://127.0.0.1:<port>. - URL targets default to
httpwhen no protocol is provided. - URL targets must use
httporhttps. - URL targets must include an explicit port.
- Ports must be integers from
1to65535.
Requested Subdomains
validateRequestedSubdomain trims and lowercases user input, rejects reserved
names, and enforces the public subdomain format.
import { validateRequestedSubdomain } from "@localpreview/protocol";
validateRequestedSubdomain("Proyecto-API");
// { valid: true, subdomain: "proyecto-api" }Valid requested subdomains:
- Start and end with a lowercase letter or number after normalization.
- May contain lowercase letters, numbers, and hyphens.
- Must not use reserved names such as
api,app,auth,dashboard, orwww.
Headers
Proxy boundaries should remove hop-by-hop headers and internal LocalPreview headers before forwarding requests across trust boundaries.
import {
filterEndToEndHeaderPairs,
filterInternalLocalPreviewHeaderPairs,
} from "@localpreview/protocol";
const publicHeaders = filterInternalLocalPreviewHeaderPairs(
filterEndToEndHeaderPairs(headers),
);Use flattenHeaderPairs when an HTTP adapter expects alternating
name, value, name, value entries.
Errors
LOCALPREVIEW_ERROR_CODES is the shared code registry for CLI, control-plane,
and relay failures. API responses should use the LocalPreviewError shape so
clients can branch on error.code instead of parsing messages.
import { LOCALPREVIEW_ERROR_CODES, makeLocalPreviewError } from "@localpreview/protocol";
const error = makeLocalPreviewError({
code: LOCALPREVIEW_ERROR_CODES.TUNNEL_NOT_FOUND,
message: "Tunnel not found.",
tunnelId: "tun_123",
});Versioning
LOCALPREVIEW_PROTOCOL_VERSION identifies the wire and HTTP contract expected
by all LocalPreview packages.
LOCALPREVIEW_RELAY_SNAPSHOT_VERSION identifies the relay snapshot build
contract used by production sandboxes.
Change LOCALPREVIEW_PROTOCOL_VERSION when a deployed CLI, control-plane, or
relay would no longer understand the same request/response/message shapes.
Change LOCALPREVIEW_RELAY_SNAPSHOT_VERSION when the relay snapshot must be
rebuilt or promoted even if the protocol version stays compatible.
See ../../docs/relay-snapshot.md for the production snapshot runbook.
Development
From the repo root:
pnpm --filter @localpreview/protocol test
pnpm --filter @localpreview/protocol typecheck
pnpm --filter @localpreview/protocol build