npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kubohiroya/turbowarp-live-reload-controller

v0.2.0

Published

App-neutral live reload controller and preview protocol for TurboWarp apps.

Downloads

320

Readme

turbowarp-live-reload-controller

日本語

@kubohiroya/turbowarp-live-reload-controller provides app-neutral preview protocol and live reload primitives for TurboWarp applications.

It does not parse app DSLs, own diagnostics, read files, or render app-specific UI. App packages inject source frontend results, copy, diagnostics, and runtime actions.

Scope

  • Protocol version negotiation.
  • Capability validation and negotiation.
  • Session id and message type validation.
  • Monotonic revision ordering.
  • Single-connection ownership and stale session rejection.
  • Disconnect callbacks for app-owned cleanup.
  • Serialized operation queues.
  • A small source lifecycle adapter for stage / defer / commit / disconnect.
  • Simple reload anchor resolution.

Boundary

The package owns protocol mechanics only. Consumers remain responsible for file watching, source compilation, diagnostics rendering, app-specific reload policy, source integrity projection, candidate id generation, restart choices, and any TurboWarp runtime mutations. The controller tracks the candidate a connection currently holds, but what a candidate is stays with the app.

Protocol errors use PreviewProtocolError. Codes default to TWRP-PROTOCOL-*; pass errorCodePrefix to report an app's own diagnostic namespace, for example errorCodePrefix: 'K4' to produce K4-PROTOCOL-CANDIDATE.

Core Primitives

  • createPreviewProtocolController(options) owns app-neutral handshake, disconnect, capability negotiation, session ownership, revision ordering, candidate tracking, and queueing.
  • readPreviewProtocolMessage(input, expectedType, allowedKeys) validates generic message shape and rejects unknown keys.
  • validatePreviewSessionId(value) and validatePreviewRevision(value, latestRevision) provide focused validation helpers.
  • negotiatePreviewProtocolVersion(requested, supported) and negotiatePreviewCapabilities(options) expose the handshake validation pieces directly.
  • createPreviewOperationQueue() serializes arbitrary async protocol operations and tracks work that settles outside the queue.
  • previewProtocolErrorCode(suffix, prefix) builds a code in an app's own diagnostic namespace, so an app can raise the same failures the controller raises.

Two-phase operations

An operation whose slow part must not hold the queue runs in two phases. Start it in one queued step, track the completion so whenIdle still waits for it, then re-enter the queue and pass the connectionId you captured so a connection replaced during the await is rejected:

const begun = preview.enqueue(() => {
  const message = preview.readMessage(incoming, 'tm3d.preview.render.patch', ['sessionId', 'revision', 'patch']);
  const active = preview.acceptRevision(message.sessionId, message.revision);
  return {active, applying: applyRenderPatch(active.sessionId, message.patch)};
});

const completion = begun.then(async ({active, applying}) => {
  const applied = await applying;
  return preview.enqueue(() => {
    // Throws TWRP-PROTOCOL-SESSION when another handshake replaced this connection.
    const current = preview.requireConnection(active.sessionId, active.connectionId);
    if (current.latestRevision !== active.revision) return null;
    return preview.acceptCandidate(active.sessionId, {id: applied.candidateId, revision: active.revision});
  });
});

preview.track(completion);

Capabilities and candidates

  • preview.requireCapability(sessionId, capability) rejects an operation the client never negotiated.
  • preview.acceptCandidate(sessionId, {id, revision}) binds a staged candidate to the connection.
  • preview.requireCandidate(sessionId, revision, candidateId) rejects a stale or missing candidate.
  • Accepting a newer revision discards the previous candidate, and preview.clearCandidate(sessionId) drops it explicitly.

Disconnecting a session that is already disconnected is a no-op, so cleanup and reconnect races do not have to know whether a connection is still open. A disconnect naming a different session is still rejected.

Example

import {createPreviewProtocolController} from '@kubohiroya/turbowarp-live-reload-controller';

const preview = createPreviewProtocolController({
  requiredCapabilities: ['transport.v1'],
  optionalCapabilities: ['render.patch.v1'],
  messageTypes: {
    handshake: 'tm3d.preview.hello',
    handshakeAck: 'tm3d.preview.hello.ok',
    disconnect: 'tm3d.preview.disconnect',
    disconnectAck: 'tm3d.preview.disconnect.ok'
  },
  getState: () => ({sceneReady: true}),
  onDisconnect: async () => {
    await disposePreviewCandidate();
  }
});

await preview.handshake({
  type: 'tm3d.preview.hello',
  protocolVersion: {major: 1, minor: 0},
  sessionId: 'dev',
  capabilities: ['transport.v1', 'render.patch.v1']
});

await preview.enqueue(async () => {
  const message = preview.readMessage(incoming, 'tm3d.preview.render.patch', [
    'sessionId',
    'revision',
    'patch'
  ]);
  const active = preview.acceptRevision(message.sessionId, message.revision);
  await applyRenderPatch(active.sessionId, message.patch);
});

Source Lifecycle Adapter

createPreviewProtocolSession(options) is retained as a compatibility adapter for consumers that already model preview work as source stage, defer, commit, and disconnect operations. It is implemented on top of createPreviewProtocolController.

The adapter passes generic commit options through to the injected liveReloadSession.commit(options). It also accepts the legacy restart key as a pass-through {restart} option for existing consumers. App-specific restart policy and source integrity decisions should be projected by the consuming app, not by this package.

Set deferCapability to reject defer unless the client negotiated that capability. session.getState() reports connection, revision, and candidate state, and session.whenIdle() resolves with that state once the queue and any tracked work are quiescent.

An app whose wire messages carry more than the adapter models — its own ack payloads, candidate ids, restart choices, or source integrity — should build directly on createPreviewProtocolController instead of extending this adapter. That keeps the app's payload semantics in the app and the protocol mechanics here.

Development

corepack enable
pnpm install --frozen-lockfile
pnpm run check
pnpm run release:check