Dev Server Service API

Dev Server Service API

Dev Server Service API

Plugin DeveloperPlugin Developer Agent Kit — documentation written for AI agents, readable by humans.

The dev server exposes a DevServerService for plugins, design board applications, and CLI tools. It provides route listing, param discovery, and freeze management.

Service Marker

Registered as DEV_SERVER_SERVICE — inject in actions and components:

import { DEV_SERVER_SERVICE } from '@jay-framework/dev-server';

export const listAllRoutes = makeJayAction('admin.listRoutes')
  .withServices(DEV_SERVER_SERVICE)
  .withHandler(async (_input, devServer) => {
    return devServer.listRoutes();
  });

Or in a component:

makeJayStackComponent()
  .withServices(DEV_SERVER_SERVICE)
  .withFastRender(async (_props, devServer) => {
    const routes = devServer.listRoutes();
    return phaseOutput({ routes, routeCount: routes.length }, {});
  });

Direct Access

Also returned from mkDevServer() for CLI usage:

const { service } = await mkDevServer(options);

Routes

listRoutes()

Returns all page routes in the project (including plugin-provided routes):

const routes = service.listRoutes();
// [{ path: '/products/kitan/[[category]]', jayHtmlPath: '...', compPath: '...' }]

loadRouteParams(route)

Async generator that yields param batches from the route's loadParams:

for await (const batch of service.loadRouteParams('/products/kitan/[[category]]')) {
  console.log(batch); // [{ category: 'shirts' }, { category: 'pants' }]
}

Returns empty if the route has no page.ts or no loadParams. Throws if the route doesn't exist.

Freeze Management

FreezeStore

Accessible via service.freezeStore:

const store = service.freezeStore;

// Save a ViewState snapshot
const entry = await store.save('/products/kitan', viewState);

// List freezes for a route
const freezes = await store.list('/products/kitan');

// Get a specific freeze
const freeze = await store.get('abc123');

// Rename
await store.rename('abc123', 'in-stock state');

// Delete
await store.delete('abc123');

Editor Protocol

These APIs are also exposed via the editor protocol (Socket.IO) for design board applications:

Protocol Message Service Method
listRoutes service.listRoutes()
listFreezes service.freezeStore.list(route)
renameFreeze service.freezeStore.rename(id, name)
deleteFreeze service.freezeStore.delete(id)
loadRouteParams service.loadRouteParams(route, onBatch)

Streaming Events

loadRouteParams streams batches via routeParamsBatch socket events:

// Client sends: { type: 'loadRouteParams', route: '/products/[slug]' }
// Server responds: { type: 'loadRouteParams', success: true }
// Server emits:   { type: 'routeParamsBatch', route: '...', params: [...], hasMore: true }
// Server emits:   { type: 'routeParamsBatch', route: '...', params: [...], hasMore: true }
// Server emits:   { type: 'routeParamsBatch', route: '...', params: [], hasMore: false }

Freeze Changed Event

The freezeChanged socket event is emitted when jay-html or CSS files change. Design board applications should listen for this to refresh their frozen views:

socket.on('freezeChanged', () => {
  // Re-fetch frozen page fragments
});

Iframe / Embed Mode

When a page is loaded inside an iframe with ?_jay_embed=true (e.g., by the AIditor), the Alt+S shortcut is disabled (parent owns it). Freeze is triggered via postMessage:

// Parent → iframe: request freeze
iframe.contentWindow.postMessage({ type: 'jay:requestFreeze' }, '*');

// Iframe → parent: freeze done
// { type: 'jay:freeze', id: string, route: string }

The parent constructs the frozen page URL:

route + '?_jay_freeze=' + id                        // full page
route + '?_jay_freeze=' + id + '&format=fragment'   // shadow DOM fragment

About this document

This page is part of the Jay Stack Agent Kit — documentation generated from the framework source and written primarily for AI agents. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation. Learn more about the Agent Kit →