Plugin DeveloperDev Server Service API
← Documentation

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 }

Frozen Page Refresh (Dev)

In development, full-page frozen views (?_jay_freeze=<id> in iframe or tab) self-reload when jay-html or CSS changes — same jay:page-reload Hot Module Replacement (HMR) path as live pages. No host application wiring required. Saved ViewState is preserved across reload.

Fragment format (format=fragment for shadow DOM embedders) does not auto-reload; those hosts must re-fetch the fragment.

Freeze Changed Event (Fragment Hosts)

The freezeChanged socket event was described for design board fragment refresh but is not emitted by the current dev server. Fragment embedders should re-fetch on file changes themselves, or wait for a future fetch-and-swap protocol. Dev full-page frozen views do not need this event.

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

See also Dev-only routes in plugin-routes.md — settings pages loaded in AIditor Project settings use _jay_embed but are not blocked from direct URL access.


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 →