@xstate-devtools/adapter 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts","../src/logging.ts","../src/sanitize.ts","../src/serialize.ts","../src/core.ts"],"sourcesContent":["// Server entrypoint — exposes a WebSocket bridge so the DevTools panel\n// can connect to actors running in Node.\nimport { createServer as createTcpServer } from 'node:net'\nimport type {\n ExtensionToPageMessage,\n PageToExtensionMessage,\n} from '../../extension/src/shared/types.js'\nimport { createInspector, type Transport } from './core.js'\nimport {\n debugLog as baseDebugLog,\n infoLog as baseInfoLog,\n warnLog as baseWarnLog,\n} from './logging.js'\nimport { sanitize } from './sanitize.js'\n\n/**\n * Find the lowest TCP port >= `start` that is not currently in use.\n * Uses a temporary TCP server to probe; the port is released before resolving.\n */\nfunction getAvailablePort(start: number): Promise<number> {\n return new Promise((resolve, reject) => {\n const probe = createTcpServer()\n probe.unref()\n probe.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n resolve(getAvailablePort(start + 1))\n } else {\n reject(err)\n }\n })\n probe.listen(start, '127.0.0.1', () => {\n const port = (probe.address() as { port: number }).port\n probe.close(() => resolve(port))\n })\n })\n}\n\nexport interface ServerAdapterOptions {\n /** Port to listen on. Defaults to env XSTATE_DEVTOOLS_PORT or 9301. */\n port?: number\n /** Host to bind. Defaults to '127.0.0.1'. */\n host?: string\n /** Max messages to buffer while no panel is connected. Default 200. */\n bufferSize?: number\n}\n\ninterface ClientLike {\n send(data: string): void\n on(event: string, listener: (...args: unknown[]) => void): void\n readyState: number\n}\n\nconst OPEN_STATE = 1\n\nfunction summarizeMessage(message: ExtensionToPageMessage | PageToExtensionMessage) {\n const summary: Record<string, unknown> = { type: message.type }\n if ('sessionId' in message) summary.sessionId = message.sessionId\n if ('parentSessionId' in message && message.parentSessionId) {\n summary.parentSessionId = message.parentSessionId\n }\n if ('globalSeq' in message) summary.globalSeq = message.globalSeq\n if ('timestamp' in message) summary.timestamp = message.timestamp\n if (\n 'event' in message &&\n message.event &&\n typeof message.event === 'object' &&\n 'type' in message.event\n ) {\n summary.eventType = message.event.type\n }\n return summary\n}\n\nfunction debugLog(message: string, details?: unknown) {\n baseDebugLog('server', message, details)\n}\n\nfunction infoLog(message: string, details?: unknown) {\n baseInfoLog('server', message, details)\n}\n\nfunction warnLog(message: string, details?: unknown) {\n baseWarnLog('server', message, details)\n}\n\nfunction stringifyOutgoingMessage(message: PageToExtensionMessage): string | null {\n const payload = { ...message, __xstateDevtools: true as const }\n\n try {\n return JSON.stringify(payload)\n } catch (error) {\n warnLog('failed to stringify adapter message; retrying with sanitized payload', {\n error,\n message: summarizeMessage(message),\n })\n }\n\n try {\n return JSON.stringify(sanitize(payload))\n } catch (error) {\n warnLog('dropping adapter message that could not be stringified', {\n error,\n message: summarizeMessage(message),\n })\n return null\n }\n}\n\ninterface CachedServer {\n clients: Set<ClientLike>\n dispatchHandlers: Set<(msg: ExtensionToPageMessage) => void>\n buffer: string[]\n bufferSize: number\n activated: boolean\n /** Resolves with the actual TCP port once the WS server is listening. */\n port: Promise<number>\n close: () => void\n}\n\n/**\n * Start a local WebSocket server that the DevTools panel can connect to.\n * Returns the inspector callback. Multiple panels can connect simultaneously.\n *\n * The WS server, connected clients, dispatch handlers, and pre-connection\n * buffer are all stashed on globalThis keyed by port. This makes the function\n * idempotent across HMR re-evaluation: subsequent calls reuse the existing\n * server and only register new inspector hooks.\n *\n * Inspection events emitted before the first panel connects are buffered (up\n * to `bufferSize`, default 200) and flushed to the first connecting client so\n * actors registered at boot are visible.\n */\nexport function createServerAdapter(options: ServerAdapterOptions = {}) {\n const port = options.port ?? (Number(process.env.XSTATE_DEVTOOLS_PORT) || 9301)\n const host = options.host ?? '127.0.0.1'\n const bufferSize = options.bufferSize ?? 200\n infoLog('createServerAdapter called', { host, port, bufferSize })\n\n const key = `__xstate_devtools_server_${port}__`\n const cache = (globalThis as Record<string, unknown>)[key] as CachedServer | undefined\n\n let server: CachedServer\n if (cache) {\n server = cache\n infoLog('reusing cached WebSocket server', {\n host,\n port,\n clientCount: server.clients.size,\n bufferedMessages: server.buffer.length,\n })\n // honour the most recent caller's buffer size if larger\n if (bufferSize > server.bufferSize) server.bufferSize = bufferSize\n } else {\n const clients = new Set<ClientLike>()\n const dispatchHandlers = new Set<(msg: ExtensionToPageMessage) => void>()\n const buffer: string[] = []\n let wss: any = null\n let closed = false\n\n let portResolve!: (port: number) => void\n let portReject!: (err: unknown) => void\n const portPromise = new Promise<number>((res, rej) => {\n portResolve = res\n portReject = rej\n })\n\n server = {\n clients,\n dispatchHandlers,\n buffer,\n bufferSize,\n activated: false,\n port: portPromise,\n close: () => {\n closed = true\n infoLog('closing WebSocket server', { host, port, clientCount: clients.size })\n try {\n wss?.close()\n } catch {\n /* noop */\n }\n clients.clear()\n dispatchHandlers.clear()\n buffer.length = 0\n delete (globalThis as Record<string, unknown>)[key]\n },\n }\n\n // Lazily import ws so this module is import-safe in environments that\n // never use the server entrypoint (or where ws isn't installed).\n void (async () => {\n try {\n const mod = await import('ws')\n const WSServer = (mod as any).WebSocketServer ?? (mod as any).Server\n if (closed) return\n const actualPort = await getAvailablePort(port)\n if (closed) return\n wss = new WSServer({ port: actualPort, host })\n process.env.XSTATE_DEVTOOLS_PORT = String(actualPort)\n portResolve(actualPort)\n infoLog('WebSocket server listening', { host, port: actualPort })\n wss.on('connection', (ws: ClientLike) => {\n infoLog('panel connected to WebSocket server', {\n host,\n port,\n activated: server.activated,\n bufferedMessages: server.buffer.length,\n })\n // Drain bootstrap buffer to the first client only.\n if (!server.activated) {\n server.activated = true\n infoLog('flushing bootstrap buffer to first panel', {\n host,\n port,\n bufferedMessages: server.buffer.length,\n })\n for (const payload of server.buffer) {\n try {\n ws.send(payload)\n } catch {\n /* ignore */\n }\n }\n server.buffer.length = 0\n }\n server.clients.add(ws)\n ws.on('message', (raw: unknown) => {\n try {\n const text = typeof raw === 'string' ? raw : (raw as Buffer).toString('utf8')\n const msg = JSON.parse(text) as ExtensionToPageMessage\n debugLog('received dispatch from panel', summarizeMessage(msg))\n for (const cb of server.dispatchHandlers) cb(msg)\n } catch (error) {\n warnLog('failed to parse panel message', { error })\n }\n })\n ws.on('close', () => {\n server.clients.delete(ws)\n infoLog('panel disconnected from WebSocket server', {\n host,\n port,\n clientCount: server.clients.size,\n })\n })\n ws.on('error', (error: unknown) => {\n server.clients.delete(ws)\n warnLog('WebSocket client error', { error })\n })\n })\n wss.on('error', (err: Error) => {\n warnLog('WS server error', { host, port, message: err.message })\n })\n } catch (e) {\n portReject(e)\n warnLog('could not start server adapter — install `ws` to enable', {\n host,\n port,\n message: (e as Error).message,\n })\n }\n })()\n\n ;(globalThis as Record<string, unknown>)[key] = server\n }\n\n const transport: Transport = {\n send(message: PageToExtensionMessage) {\n const payload = stringifyOutgoingMessage(message)\n if (payload === null) return\n\n if (!server.activated) {\n // No panel has connected yet — buffer for the first one.\n if (server.buffer.length >= server.bufferSize) server.buffer.shift()\n server.buffer.push(payload)\n debugLog('buffered outgoing adapter message; no panel connected yet', {\n bufferedMessages: server.buffer.length,\n message: summarizeMessage(message),\n })\n return\n }\n let sentCount = 0\n for (const ws of server.clients) {\n if (ws.readyState === OPEN_STATE) {\n try {\n ws.send(payload)\n sentCount += 1\n } catch {\n /* ignore */\n }\n }\n }\n debugLog('sent adapter message to connected panels', {\n sentCount,\n clientCount: server.clients.size,\n message: summarizeMessage(message),\n })\n },\n subscribe(handler) {\n server.dispatchHandlers.add(handler)\n debugLog('registered dispatch handler', { handlerCount: server.dispatchHandlers.size })\n return () => {\n server.dispatchHandlers.delete(handler)\n debugLog('removed dispatch handler', { handlerCount: server.dispatchHandlers.size })\n }\n },\n }\n\n const inspector = createInspector(transport, 'srv')\n return { ...inspector, close: server.close, port: server.port }\n}\n","type LogLevel = 'debug' | 'info' | 'warn'\n\ndeclare global {\n var __XSTATE_DEVTOOLS_LOGGING__: boolean | undefined\n}\n\nfunction hasProcessEnv() {\n return typeof process !== 'undefined' && typeof process.env !== 'undefined'\n}\n\nexport function isLoggingEnabled() {\n if (globalThis.__XSTATE_DEVTOOLS_LOGGING__ === true) return true\n if (!hasProcessEnv()) return false\n\n const value = process.env.XSTATE_DEVTOOLS_LOGGING\n return value === '1' || value === 'true'\n}\n\nfunction log(level: LogLevel, scope: string, message: string, details?: unknown) {\n if (!isLoggingEnabled()) return\n\n if (details === undefined) {\n console[level](`[xstate-devtools:${scope}] ${message}`)\n return\n }\n\n console[level](`[xstate-devtools:${scope}] ${message}`, details)\n}\n\nexport function debugLog(scope: string, message: string, details?: unknown) {\n log('debug', scope, message, details)\n}\n\nexport function infoLog(scope: string, message: string, details?: unknown) {\n log('info', scope, message, details)\n}\n\nexport function warnLog(scope: string, message: string, details?: unknown) {\n log('warn', scope, message, details)\n}\n","// packages/adapter/src/sanitize.ts\n\nconst MAX_DEPTH = 10\nconst MAX_STRING_LENGTH = 500\nconst MAX_ARRAY_LENGTH = 100\n\nfunction sanitizeValue(value: unknown, depth: number, seen: WeakSet<object>): unknown {\n if (depth > MAX_DEPTH) return '[MaxDepth]'\n if (value === null || value === undefined) return value\n if (typeof value === 'boolean' || typeof value === 'number') return value\n if (typeof value === 'string') {\n return value.length > MAX_STRING_LENGTH ? `${value.slice(0, MAX_STRING_LENGTH)}…` : value\n }\n if (typeof value === 'function') return `[Function: ${value.name || '(anonymous)'}]`\n if (typeof value === 'symbol') return `[Symbol: ${value.description ?? ''}]`\n if (typeof value === 'bigint') return `[BigInt: ${value}]`\n if (value instanceof Error) return { __type: 'Error', name: value.name, message: value.message }\n if (value instanceof Date) return { __type: 'Date', iso: value.toISOString() }\n if (value instanceof RegExp) return { __type: 'RegExp', source: value.source, flags: value.flags }\n if (typeof value === 'object') {\n if (seen.has(value)) return '[Circular]'\n seen.add(value)\n }\n if (value instanceof Map) {\n const entries: [unknown, unknown][] = []\n for (const [k, v] of value as Map<unknown, unknown>) {\n if (entries.length >= MAX_ARRAY_LENGTH) break\n entries.push([sanitizeValue(k, depth + 1, seen), sanitizeValue(v, depth + 1, seen)])\n }\n return { __type: 'Map', entries }\n }\n if (value instanceof Set) {\n const values: unknown[] = []\n for (const v of value as Set<unknown>) {\n if (values.length >= MAX_ARRAY_LENGTH) break\n values.push(sanitizeValue(v, depth + 1, seen))\n }\n return { __type: 'Set', values }\n }\n if (value instanceof Promise) return '[Promise]'\n if (value instanceof WeakMap || value instanceof WeakSet) return '[WeakCollection]'\n if (ArrayBuffer.isView(value)) return `[TypedArray: ${(value as any).constructor.name}]`\n // Detect DOM nodes (works in browser and is safe to check)\n if (typeof Node !== 'undefined' && value instanceof Node) {\n return `[DOMNode: ${(value as Element).tagName ?? value.nodeName}]`\n }\n if (Array.isArray(value)) {\n const sliced = value.slice(0, MAX_ARRAY_LENGTH)\n const result = sliced.map((v) => sanitizeValue(v, depth + 1, seen))\n if (value.length > MAX_ARRAY_LENGTH) result.push(`[…${value.length - MAX_ARRAY_LENGTH} more]`)\n return result\n }\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {}\n let count = 0\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (count++ >= MAX_ARRAY_LENGTH) {\n result['…'] = '[truncated]'\n break\n }\n result[k] = sanitizeValue(v, depth + 1, seen)\n }\n return result\n }\n return String(value)\n}\n\nexport function sanitize(value: unknown, depth = 0): unknown {\n return sanitizeValue(value, depth, new WeakSet<object>())\n}\n","// packages/adapter/src/serialize.ts\nimport type { AnyStateMachine } from 'xstate'\nimport type {\n SerializedInvoke,\n SerializedMachine,\n SerializedStateNode,\n SerializedTransition,\n} from '../../extension/src/shared/types.js'\n\nconst MAX_SERIALIZED_NODES = 500\nconst MAX_TRANSITIONS_PER_NODE = 100\nconst MAX_CHILD_STATES = 100\nconst MAX_ACTIONS_PER_TRANSITION = 20\nconst MAX_ENTRY_EXIT_ACTIONS = 20\nconst MAX_INVOKES_PER_NODE = 20\n\nfunction serializeGuard(guard: unknown): string | undefined {\n if (!guard) return undefined\n if (typeof guard === 'string') return guard\n if (typeof guard === 'function') return (guard as Function).name || '(inline)'\n if (typeof guard === 'object' && guard !== null) {\n const g = guard as any\n return g.type ?? g.name ?? '(inline)'\n }\n return '(inline)'\n}\n\nfunction serializeAction(action: unknown): string {\n if (typeof action === 'string') return action\n if (typeof action === 'function') return (action as Function).name || '(anonymous)'\n if (typeof action === 'object' && action !== null) {\n const a = action as any\n return a.type ?? a.name ?? String(action)\n }\n return String(action)\n}\n\nfunction serializeTransitionList(transitions: any[]): SerializedTransition[] {\n return transitions.slice(0, MAX_TRANSITIONS_PER_NODE).map((t: any) => ({\n eventType: t.eventType ?? '',\n targets: (t.target ?? []).map((n: any) => n?.id ?? String(n)).filter(Boolean),\n guard: serializeGuard(t.guard),\n actions: (t.actions ?? [])\n .slice(0, MAX_ACTIONS_PER_TRANSITION)\n .map(serializeAction)\n .filter(Boolean),\n }))\n}\n\nfunction serializeInvokes(node: any): SerializedInvoke[] {\n return (node.invoke as any[]).slice(0, MAX_INVOKES_PER_NODE).map((inv: any) => ({\n id: inv.id ?? '(unknown)',\n src: typeof inv.src === 'string' ? inv.src : (inv.src?.id ?? inv.src?.name ?? '(inline)'),\n }))\n}\n\ninterface SerializeState {\n seen: WeakSet<object>\n count: number\n}\n\nfunction serializeNode(node: any, state: SerializeState): SerializedStateNode {\n if (!node || typeof node !== 'object') {\n return {\n id: '(unknown)',\n key: '(unknown)',\n type: 'atomic',\n states: {},\n on: [],\n always: [],\n entry: [],\n exit: [],\n invoke: [],\n }\n }\n\n if (state.count >= MAX_SERIALIZED_NODES) {\n return {\n id: node.id ?? '(truncated)',\n key: node.key ?? '(truncated)',\n type: node.type ?? 'atomic',\n states: {},\n on: [],\n always: [],\n entry: [],\n exit: [],\n invoke: [],\n }\n }\n\n if (state.seen.has(node)) {\n return {\n id: node.id ?? '(circular)',\n key: node.key ?? '(circular)',\n type: node.type ?? 'atomic',\n states: {},\n on: [],\n always: [],\n entry: [],\n exit: [],\n invoke: [],\n }\n }\n\n state.seen.add(node)\n state.count += 1\n\n const allTransitions: SerializedTransition[] = []\n if (node.transitions instanceof Map) {\n for (const [, tList] of node.transitions) {\n if (allTransitions.length >= MAX_TRANSITIONS_PER_NODE) break\n allTransitions.push(...serializeTransitionList(tList))\n if (allTransitions.length >= MAX_TRANSITIONS_PER_NODE) {\n allTransitions.length = MAX_TRANSITIONS_PER_NODE\n break\n }\n }\n }\n\n const always = Array.isArray(node.always) ? serializeTransitionList(node.always) : []\n const childEntries = Object.entries(node.states ?? {}).slice(0, MAX_CHILD_STATES)\n\n return {\n id: node.id,\n key: node.key,\n type: node.type,\n initial: node.initial?.target?.[0]?.key,\n states: Object.fromEntries(childEntries.map(([k, v]) => [k, serializeNode(v, state)])),\n on: allTransitions,\n always,\n entry: (node.entry ?? []).slice(0, MAX_ENTRY_EXIT_ACTIONS).map(serializeAction).filter(Boolean),\n exit: (node.exit ?? []).slice(0, MAX_ENTRY_EXIT_ACTIONS).map(serializeAction).filter(Boolean),\n invoke: serializeInvokes(node),\n sourceLocation: node.config?.__xstateDevtoolsSource ?? undefined,\n description: node.config?.description ?? undefined,\n }\n}\n\nexport function serializeMachine(\n machine: AnyStateMachine,\n sourceLocation?: string,\n): SerializedMachine {\n return {\n id: machine.id,\n root: serializeNode(machine.root, { seen: new WeakSet<object>(), count: 0 }),\n sourceLocation,\n }\n}\n","// Transport-agnostic XState inspection core.\n// Browser and server entrypoints supply their own transports.\nimport type { AnyActorRef } from 'xstate'\nimport type {\n ExtensionToPageMessage,\n PageToExtensionMessage,\n SerializedEvent,\n SerializedMachine,\n SerializedSnapshot,\n} from '../../extension/src/shared/types.js'\nimport {\n debugLog as baseDebugLog,\n infoLog as baseInfoLog,\n warnLog as baseWarnLog,\n} from './logging.js'\nimport { sanitize } from './sanitize.js'\nimport { serializeMachine } from './serialize.js'\n\nexport type Source = 'web' | 'srv'\n\nexport interface InspectorOptions {\n /**\n * Absolute filesystem root for the web app. When provided, web stack frames\n * like http://localhost:5173/app/... are remapped to <webSourceRoot>/app/...\n * so VS Code source links can open local files.\n */\n webSourceRoot?: string\n}\n\nexport interface Transport {\n /** Send a protocol message outbound (toward the panel). */\n send: (message: PageToExtensionMessage) => void\n /** Subscribe to inbound dispatch messages from the panel. Returns a teardown. */\n subscribe: (handler: (message: ExtensionToPageMessage) => void) => () => void\n}\n\ntype StateValue = string | { [key: string]: StateValue }\n\ninterface StateNodeLike {\n id: string\n key: string\n type: 'atomic' | 'compound' | 'parallel' | 'final' | 'history' | string\n parent?: StateNodeLike\n states?: Record<string, StateNodeLike>\n initial?: string | { target?: StateNodeLike[] }\n}\n\ninterface MachineLike {\n root: StateNodeLike\n getStateNodeById: (id: string) => StateNodeLike\n resolveState: (snapshot: {\n value: unknown\n context?: unknown\n status?: string\n output?: unknown\n error?: unknown\n historyValue?: unknown\n }) => unknown\n}\n\ninterface MutableActorRef extends AnyActorRef {\n logic?: MachineLike\n update?: (snapshot: unknown, event: { type: string; stateNodeId: string }) => void\n}\n\nfunction summarizeMessage(message: ExtensionToPageMessage | PageToExtensionMessage) {\n const summary: Record<string, unknown> = { type: message.type }\n if ('sessionId' in message) summary.sessionId = message.sessionId\n if ('stateNodeId' in message) summary.stateNodeId = message.stateNodeId\n if ('parentSessionId' in message && message.parentSessionId) {\n summary.parentSessionId = message.parentSessionId\n }\n if ('globalSeq' in message) summary.globalSeq = message.globalSeq\n if ('timestamp' in message) summary.timestamp = message.timestamp\n if (\n 'event' in message &&\n message.event &&\n typeof message.event === 'object' &&\n 'type' in message.event\n ) {\n summary.eventType = message.event.type\n }\n return summary\n}\n\nfunction summarizeInspectionEvent(event: any) {\n return {\n type: event?.type,\n sessionId: event?.actorRef?.sessionId,\n eventType:\n event?.type === '@xstate.event' && event?.event && typeof event.event === 'object'\n ? event.event.type\n : undefined,\n }\n}\n\nfunction debugLog(source: Source, message: string, details?: unknown) {\n baseDebugLog(`${source}:adapter`, message, details)\n}\n\nfunction infoLog(source: Source, message: string, details?: unknown) {\n baseInfoLog(`${source}:adapter`, message, details)\n}\n\nfunction warnLog(source: Source, message: string, details?: unknown) {\n baseWarnLog(`${source}:adapter`, message, details)\n}\n\nfunction isLibraryStackFrame(line: string): boolean {\n const normalized = line.replace(/\\\\/g, '/').toLowerCase()\n return (\n normalized.includes('/node_modules/xstate/') ||\n normalized.includes('/node_modules/@xstate/') ||\n normalized.includes('/@xstate-devtools/adapter/') ||\n normalized.includes('/packages/adapter/')\n )\n}\n\nfunction normalizeStackFrame(line: string): string {\n return line.trim().replace(/^at\\s+/, '')\n}\n\nfunction extractStackFrameLocation(frame: string): string {\n return frame.match(/\\((.*)\\)$/)?.[1] ?? frame\n}\n\nfunction isAnonymousOrEvalLocation(location: string): boolean {\n const normalized = location\n .trim()\n .toLowerCase()\n .replace(/^[./\\\\]+/, '')\n\n return (\n normalized === '<anonymous>' ||\n normalized === 'anonymous' ||\n normalized === '(anonymous)' ||\n normalized === 'eval' ||\n normalized === '<eval>' ||\n normalized === '[native code]'\n )\n}\n\nfunction hasFilesystemBackedPath(location: string): boolean {\n const trimmed = location.trim()\n if (!trimmed) return false\n\n const match = trimmed.match(/^(.*?)(?::\\d+)?(?::\\d+)?$/)\n const rawPath = (match?.[1] ?? trimmed).trim()\n if (!rawPath || isAnonymousOrEvalLocation(rawPath)) return false\n\n if (/^[a-zA-Z]:[\\\\/]/.test(rawPath)) return true\n if (rawPath.startsWith('/') || rawPath.startsWith('./') || rawPath.startsWith('../')) return true\n\n if (/^[a-zA-Z][a-zA-Z\\d+.-]*:\\/\\//.test(rawPath)) {\n try {\n const url = new URL(rawPath)\n if (url.protocol === 'file:') return true\n return url.pathname.startsWith('/@fs/')\n } catch {\n return false\n }\n }\n\n return false\n}\n\nfunction remapWebUrlLocationToFsPath(\n location: string,\n source: Source,\n options?: InspectorOptions,\n): string {\n if (source !== 'web' || !options?.webSourceRoot) return location\n\n const match = location.trim().match(/^(.*?)(?::(\\d+))?(?::(\\d+))?$/)\n if (!match) return location\n\n const [, rawPath, line, column] = match\n if (!rawPath || !/^https?:\\/\\//.test(rawPath)) return location\n\n try {\n const url = new URL(rawPath)\n const pathname = decodeURIComponent(url.pathname)\n if (!pathname.startsWith('/app/')) return location\n\n const root = options.webSourceRoot.replace(/\\/+$/, '')\n const filePath = `${root}${pathname}`\n const suffix = line ? `:${line}${column ? `:${column}` : ''}` : ''\n return `${filePath}${suffix}`\n } catch {\n return location\n }\n}\n\nexport function getSourceLocationFromStack(\n stack?: string,\n source: Source = 'web',\n options?: InspectorOptions,\n): string | undefined {\n const lines = stack?.split('\\n') ?? []\n\n for (let i = 0; i < lines.length; i += 1) {\n if (i <= 2) continue\n\n const line = lines[i]\n if (isLibraryStackFrame(line)) continue\n\n const normalizedFrame = normalizeStackFrame(line)\n const location = remapWebUrlLocationToFsPath(\n extractStackFrameLocation(normalizedFrame),\n source,\n options,\n )\n\n if (!hasFilesystemBackedPath(location)) continue\n\n const wrapped = normalizedFrame.match(/\\((.*)\\)$/)\n if (wrapped) {\n return normalizedFrame.replace(wrapped[1], location)\n }\n\n return location\n }\n\n return undefined\n}\n\nfunction getSourceLocation(source: Source, options?: InspectorOptions): string | undefined {\n try {\n const oldLimit = Error.stackTraceLimit\n Error.stackTraceLimit = 50\n const stack = new Error().stack\n Error.stackTraceLimit = oldLimit\n return getSourceLocationFromStack(stack, source, options)\n } catch {\n return undefined\n }\n}\n\nfunction serializeSnapshot(snapshot: any): SerializedSnapshot {\n return {\n value: snapshot?.value ?? null,\n context: sanitize(snapshot?.context),\n status: snapshot?.status ?? 'active',\n error: snapshot?.error ? sanitize(snapshot.error) : undefined,\n }\n}\n\nfunction safeSerializeSnapshot(actorRef: AnyActorRef): SerializedSnapshot {\n try {\n return serializeSnapshot(actorRef.getSnapshot())\n } catch {\n return { value: null, context: undefined, status: 'active' }\n }\n}\n\nfunction getActorDisplayName(actorRef: AnyActorRef): string | undefined {\n const actor = actorRef as {\n logic?: { id?: string; src?: unknown; name?: string } | undefined\n src?: unknown\n }\n\n const src = actor.src ?? actor.logic?.src\n if (typeof src === 'string' && src.length > 0) return src\n if (typeof actor.logic?.id === 'string' && actor.logic.id.length > 0) return actor.logic.id\n if (typeof actor.logic?.name === 'string' && actor.logic.name.length > 0) return actor.logic.name\n if (src && typeof src === 'object') {\n const namedSrc = src as { id?: string; name?: string }\n if (typeof namedSrc.id === 'string' && namedSrc.id.length > 0) return namedSrc.id\n if (typeof namedSrc.name === 'string' && namedSrc.name.length > 0) return namedSrc.name\n }\n return undefined\n}\n\nfunction getNodeInitialChild(node: StateNodeLike): StateNodeLike | null {\n if (!node.states) return null\n if (typeof node.initial === 'string') {\n return node.states[node.initial] ?? null\n }\n\n const target = Array.isArray(node.initial?.target) ? node.initial.target[0] : null\n return target ?? null\n}\n\nfunction encodeChildValue(child: StateNodeLike, childValue: StateValue): StateValue {\n if (child.type === 'atomic' || child.type === 'final' || child.type === 'history') {\n return child.key\n }\n\n return { [child.key]: childValue }\n}\n\nfunction getDefaultStateValue(node: StateNodeLike): StateValue {\n if (node.type === 'parallel') {\n const value: Record<string, StateValue> = {}\n for (const child of Object.values(node.states ?? {})) {\n value[child.key] = getDefaultSelectionValue(child)\n }\n return value\n }\n\n const initialChild = getNodeInitialChild(node)\n if (!initialChild) return {}\n return encodeChildValue(initialChild, getDefaultStateValue(initialChild))\n}\n\nfunction getDefaultSelectionValue(node: StateNodeLike): StateValue {\n if (node.type === 'atomic' || node.type === 'final' || node.type === 'history') {\n return node.key\n }\n\n return getDefaultStateValue(node)\n}\n\nfunction getExistingChildValue(value: unknown, childKey: string): StateValue | undefined {\n if (!value || typeof value !== 'object') return undefined\n return (value as Record<string, StateValue>)[childKey]\n}\n\nfunction getPathToRoot(target: StateNodeLike, root: StateNodeLike): StateNodeLike[] {\n const path: StateNodeLike[] = []\n let current: StateNodeLike | undefined = target\n\n while (current) {\n path.unshift(current)\n if (current.id === root.id) return path\n current = current.parent\n }\n\n throw new Error(`State node '${target.id}' is not part of machine '${root.id}'`)\n}\n\nfunction buildTargetStateValue(\n node: StateNodeLike,\n path: StateNodeLike[],\n currentValue: unknown,\n): StateValue {\n const [, ...restPath] = path\n\n if (restPath.length === 0) {\n if (node.type === 'parallel') {\n const next: Record<string, StateValue> = {}\n for (const child of Object.values(node.states ?? {})) {\n next[child.key] =\n getExistingChildValue(currentValue, child.key) ?? getDefaultSelectionValue(child)\n }\n return next\n }\n\n if (node.type === 'compound') {\n return getDefaultStateValue(node)\n }\n\n return node.key\n }\n\n const child = restPath[0]\n if (node.type === 'parallel') {\n const next: Record<string, StateValue> = {}\n for (const sibling of Object.values(node.states ?? {})) {\n if (sibling.key === child.key) {\n next[sibling.key] = buildTargetStateValue(\n sibling,\n restPath,\n getExistingChildValue(currentValue, sibling.key),\n )\n } else {\n next[sibling.key] =\n getExistingChildValue(currentValue, sibling.key) ?? getDefaultSelectionValue(sibling)\n }\n }\n return next\n }\n\n const childValue = buildTargetStateValue(\n child,\n restPath,\n getExistingChildValue(currentValue, child.key),\n )\n return encodeChildValue(child, childValue)\n}\n\nfunction setActiveState(actorRef: AnyActorRef, stateNodeId: string): void {\n const mutableActorRef = actorRef as MutableActorRef\n const machine = mutableActorRef.logic\n if (\n !machine?.getStateNodeById ||\n !machine.resolveState ||\n typeof mutableActorRef.update !== 'function'\n ) {\n throw new Error('Actor does not expose machine state mutation internals')\n }\n\n const currentSnapshot = actorRef.getSnapshot() as {\n value: unknown\n context?: unknown\n status?: string\n output?: unknown\n error?: unknown\n historyValue?: unknown\n }\n const targetNode = machine.getStateNodeById(stateNodeId)\n const path = getPathToRoot(targetNode, machine.root)\n const targetValue = buildTargetStateValue(machine.root, path, currentSnapshot?.value)\n\n const nextSnapshot = machine.resolveState({\n value: targetValue,\n context: currentSnapshot?.context,\n status: currentSnapshot?.status,\n output: currentSnapshot?.output,\n error: currentSnapshot?.error,\n historyValue: currentSnapshot?.historyValue,\n })\n\n mutableActorRef.update(nextSnapshot, {\n type: 'xstate.devtools.set-active-state',\n stateNodeId,\n })\n}\n\n// Cached on globalThis so HMR re-evaluating this module doesn't reset the\n// monotonic seq counter mid-session. The panel re-numbers messages on ingest\n// to merge multiple sources, but keeping a stable per-process seq still helps\n// when the panel reconnects to an already-running adapter.\nconst SEQ_KEY = '__xstate_devtools_global_seq__'\nfunction nextSeq(): number {\n const g = globalThis as Record<string, unknown>\n const cur = (g[SEQ_KEY] as number | undefined) ?? 0\n const next = cur + 1\n g[SEQ_KEY] = next\n return next\n}\n\nexport function createInspector(\n transport: Transport,\n source: Source,\n options: InspectorOptions = {},\n) {\n const actorRefs = new Map<string, AnyActorRef>()\n const actorMachines = new Map<string, SerializedMachine | null>()\n const prefix = `${source}:`\n const tag = (sessionId: string) => prefix + sessionId\n const tagOptional = (id: string | undefined) => (id ? prefix + id : undefined)\n const stripIfMine = (id: string): string | null =>\n id.startsWith(prefix) ? id.slice(prefix.length) : null\n\n function checkAndNotifyStop(actorRef: AnyActorRef) {\n let snap: any\n try {\n snap = actorRef.getSnapshot()\n } catch {\n return\n }\n if (snap?.status !== 'active') {\n const message: PageToExtensionMessage = {\n type: 'XSTATE_ACTOR_STOPPED',\n sessionId: tag(actorRef.sessionId),\n }\n debugLog(source, 'actor stopped; notifying transport', summarizeMessage(message))\n transport.send(message)\n actorRefs.delete(actorRef.sessionId)\n actorMachines.delete(actorRef.sessionId)\n }\n }\n\n const unsubscribe = transport.subscribe((message) => {\n debugLog(source, 'received message from transport', summarizeMessage(message))\n if (message.type === 'XSTATE_PANEL_CONNECTED') {\n // The devtools panel just connected (or reconnected). Re-broadcast every\n // currently-active actor so the panel is never blank because the MV3\n // service worker was killed between page load and panel open.\n infoLog(source, 'panel connected; resyncing actors', { actorCount: actorRefs.size })\n actorRefs.forEach((actorRef, sessionId) => {\n const machine = actorMachines.get(sessionId) ?? null\n const resyncMessage: PageToExtensionMessage = {\n type: 'XSTATE_ACTOR_REGISTERED',\n sessionId: tag(sessionId),\n parentSessionId: tagOptional((actorRef as any)._parent?.sessionId),\n displayName: getActorDisplayName(actorRef),\n machine,\n snapshot: safeSerializeSnapshot(actorRef),\n globalSeq: nextSeq(),\n timestamp: Date.now(),\n }\n debugLog(source, 'resyncing actor', summarizeMessage(resyncMessage))\n transport.send(resyncMessage)\n })\n return\n }\n if (message.type === 'XSTATE_DISPATCH') {\n const local = stripIfMine(message.sessionId)\n if (local === null) {\n debugLog(source, 'ignoring dispatch for different source', summarizeMessage(message))\n return // not for this transport source\n }\n const ref = actorRefs.get(local)\n if (ref) {\n try {\n debugLog(source, 'dispatching event to actor', {\n sessionId: local,\n eventType:\n message.event && typeof message.event === 'object' && 'type' in message.event\n ? message.event.type\n : undefined,\n })\n ref.send(message.event)\n } catch (e) {\n warnLog(source, 'dispatch error', { error: e, sessionId: local })\n }\n } else {\n warnLog(source, 'received dispatch for unknown actor', {\n sessionId: local,\n knownActors: actorRefs.size,\n })\n }\n return\n }\n if (message.type === 'XSTATE_SET_ACTIVE_STATE') {\n const local = stripIfMine(message.sessionId)\n if (local === null) {\n debugLog(\n source,\n 'ignoring state activation for different source',\n summarizeMessage(message),\n )\n return\n }\n\n const ref = actorRefs.get(local)\n if (!ref) {\n warnLog(source, 'received state activation for unknown actor', {\n sessionId: local,\n knownActors: actorRefs.size,\n })\n return\n }\n\n try {\n debugLog(source, 'setting active state on actor', summarizeMessage(message))\n setActiveState(ref, message.stateNodeId)\n const snapshotMessage: PageToExtensionMessage = {\n type: 'XSTATE_SNAPSHOT',\n sessionId: tag(local),\n snapshot: safeSerializeSnapshot(ref),\n timestamp: Date.now(),\n globalSeq: nextSeq(),\n }\n debugLog(\n source,\n 'sending snapshot after state activation',\n summarizeMessage(snapshotMessage),\n )\n transport.send(snapshotMessage)\n } catch (error) {\n warnLog(source, 'failed to set active state', {\n error,\n sessionId: local,\n stateNodeId: message.stateNodeId,\n })\n }\n }\n })\n\n // Notify the extension that the adapter is ready\n transport.send({ type: 'XSTATE_ADAPTER_READY' })\n\n infoLog(source, 'inspector created')\n\n const inspect = (inspectionEvent: any) => {\n debugLog(source, 'inspect callback invoked', summarizeInspectionEvent(inspectionEvent))\n if (inspectionEvent.type === '@xstate.actor') {\n const actorRef: AnyActorRef = inspectionEvent.actorRef\n const sessionId: string = actorRef.sessionId\n\n // Serialize machine and populate both Maps BEFORE subscribing so that\n // the Maps are fully consistent if complete()/error() ever fire\n // synchronously during subscribe() (e.g. an actor that starts in a\n // final state). Populating after subscribe introduced a race where\n // actorMachines.delete() in the callback was a no-op and the subsequent\n // actorMachines.set() would leak the entry permanently.\n const actorLogic = (actorRef as { logic?: unknown }).logic as any\n const machine = actorLogic?.root\n ? serializeMachine(\n actorLogic,\n actorLogic.config?.__xstateDevtoolsSource ?? getSourceLocation(source, options),\n )\n : null\n actorRefs.set(sessionId, actorRef)\n actorMachines.set(sessionId, machine)\n\n // Eagerly remove the actor from both maps when it stops so we don't\n // accumulate strong references to every short-lived actor indefinitely.\n // Without this, actors that stop silently (no further snapshot/event) are\n // only removed by checkAndNotifyStop — which never fires for them.\n const notifyStop = () => {\n if (actorRefs.has(sessionId)) {\n actorRefs.delete(sessionId)\n actorMachines.delete(sessionId)\n const stopMsg: PageToExtensionMessage = {\n type: 'XSTATE_ACTOR_STOPPED',\n sessionId: tag(sessionId),\n }\n debugLog(source, 'actor stopped; notifying transport', summarizeMessage(stopMsg))\n transport.send(stopMsg)\n }\n }\n try {\n actorRef.subscribe({ complete: notifyStop, error: notifyStop })\n } catch {\n // subscribe is best-effort; older actor implementations may not support it\n }\n\n const message: PageToExtensionMessage = {\n type: 'XSTATE_ACTOR_REGISTERED',\n sessionId: tag(sessionId),\n parentSessionId: tagOptional((actorRef as any)._parent?.sessionId),\n displayName: getActorDisplayName(actorRef),\n machine,\n snapshot: safeSerializeSnapshot(actorRef),\n globalSeq: nextSeq(),\n timestamp: Date.now(),\n }\n infoLog(source, 'registering actor with transport', {\n message: summarizeMessage(message),\n actorCount: actorRefs.size,\n hasMachine: machine !== null,\n })\n transport.send(message)\n } else if (inspectionEvent.type === '@xstate.snapshot') {\n const message: PageToExtensionMessage = {\n type: 'XSTATE_SNAPSHOT',\n sessionId: tag(inspectionEvent.actorRef.sessionId),\n snapshot: serializeSnapshot(inspectionEvent.snapshot),\n timestamp: Date.now(),\n globalSeq: nextSeq(),\n }\n debugLog(source, 'sending snapshot to transport', summarizeMessage(message))\n transport.send(message)\n checkAndNotifyStop(inspectionEvent.actorRef)\n } else if (inspectionEvent.type === '@xstate.event') {\n const message: PageToExtensionMessage = {\n type: 'XSTATE_EVENT',\n sessionId: tag(inspectionEvent.actorRef.sessionId),\n // sanitize() returns unknown; the inspected event keeps its { type, ... }\n // shape, so it is a SerializedEvent after deep-sanitizing.\n event: sanitize(inspectionEvent.event) as SerializedEvent,\n snapshotAfter: safeSerializeSnapshot(inspectionEvent.actorRef),\n timestamp: Date.now(),\n globalSeq: nextSeq(),\n }\n debugLog(source, 'sending event to transport', summarizeMessage(message))\n transport.send(message)\n checkAndNotifyStop(inspectionEvent.actorRef)\n } else {\n debugLog(\n source,\n 'ignoring unsupported inspection event',\n summarizeInspectionEvent(inspectionEvent),\n )\n }\n }\n\n function dispose() {\n infoLog(source, 'disposing inspector', { actorCount: actorRefs.size })\n unsubscribe()\n actorRefs.clear()\n actorMachines.clear()\n }\n\n return { inspect, dispose }\n}\n"],"mappings":"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","names":["debugLog","infoLog","warnLog","child","createTcpServer","summarizeMessage","debugLog","infoLog","warnLog"]}
1
+ {"version":3,"sources":["../src/server.ts","../src/serialize.ts","../src/sanitize.ts","../src/core.ts"],"sourcesContent":["// Server entrypoint — exposes a WebSocket bridge so the DevTools panel\n// can connect to actors running in Node.\nimport type { ExtensionToPageMessage, PageToExtensionMessage } from '@xstate-devtools/protocol'\nimport { createInspector, type Transport } from './core.js'\n\nexport interface ServerAdapterOptions {\n /** Port to listen on. Defaults to env XSTATE_DEVTOOLS_PORT or 9301. */\n port?: number\n /** Host to bind. Defaults to '127.0.0.1'. */\n host?: string\n /** Max events to buffer before the first panel connects. Default 200. */\n bufferSize?: number\n}\n\ninterface ClientLike {\n send(data: string): void\n on(event: string, listener: (...args: unknown[]) => void): void\n readyState: number\n}\n\nconst OPEN_STATE = 1\n\ntype ActorRegistered = Extract<PageToExtensionMessage, { type: 'XSTATE_ACTOR_REGISTERED' }>\n\ninterface LiveActor {\n /** The registration message, kept immutable so its snapshot stays the\n * registration-time one (the panel's time-travel floor). */\n reg: ActorRegistered\n /** Latest snapshot seen for this actor (the registration snapshot until updated). */\n snapshot: ActorRegistered['snapshot']\n}\n\ninterface CachedServer {\n clients: Set<ClientLike>\n dispatchHandlers: Set<(msg: ExtensionToPageMessage) => void>\n /** Currently-live actors (immutable registration + latest snapshot). */\n liveActors: Map<string, LiveActor>\n /** Pre-first-connection event/snapshot backlog, flushed once to the first panel. */\n recentEvents: string[]\n bufferSize: number\n /** Set once the first panel connects and drains the backlog. */\n activated: boolean\n close: () => void\n}\n\n/** Track live-actor state so it can be replayed to every connecting panel. */\nfunction trackLive(server: CachedServer, message: PageToExtensionMessage): void {\n switch (message.type) {\n case 'XSTATE_ACTOR_REGISTERED':\n server.liveActors.set(message.sessionId, { reg: message, snapshot: message.snapshot })\n break\n case 'XSTATE_SNAPSHOT': {\n const live = server.liveActors.get(message.sessionId)\n if (live) { live.snapshot = message.snapshot }\n break\n }\n case 'XSTATE_EVENT': {\n const live = server.liveActors.get(message.sessionId)\n if (live) { live.snapshot = message.snapshotAfter }\n break\n }\n case 'XSTATE_ACTOR_STOPPED':\n server.liveActors.delete(message.sessionId)\n break\n }\n}\n\n/**\n * Start a local WebSocket server that the DevTools panel can connect to.\n * Returns the inspector callback. Multiple panels can connect simultaneously.\n *\n * The WS server, connected clients, dispatch handlers, and the live-actor\n * registry are all stashed on globalThis keyed by port. This makes the function\n * idempotent across HMR re-evaluation: subsequent calls reuse the existing\n * server and only register new inspector hooks.\n *\n * Every connecting panel — including a reconnect after the editor/host restarts\n * — is replayed the current set of live actors (with their latest snapshots),\n * so actors registered at boot stay visible across reconnects (not just for\n * the first panel). The pre-connection event backlog, by contrast, is flushed\n * only to the very first panel; replaying it on every reconnect would re-flood\n * the log with stale events.\n */\nexport function createServerAdapter(options: ServerAdapterOptions = {}) {\n const port = options.port\n ?? (Number(process.env.XSTATE_DEVTOOLS_PORT) || 9301)\n const host = options.host ?? '127.0.0.1'\n const bufferSize = options.bufferSize ?? 200\n\n const key = `__xstate_devtools_server_${port}__`\n const cache = (globalThis as Record<string, unknown>)[key] as CachedServer | undefined\n\n let server: CachedServer\n if (cache) {\n server = cache\n // honour the most recent caller's buffer size if larger\n if (bufferSize > server.bufferSize) server.bufferSize = bufferSize\n } else {\n const clients = new Set<ClientLike>()\n const dispatchHandlers = new Set<(msg: ExtensionToPageMessage) => void>()\n const liveActors = new Map<string, LiveActor>()\n const recentEvents: string[] = []\n let wss: any = null\n let closed = false\n\n server = {\n clients, dispatchHandlers, liveActors, recentEvents, bufferSize,\n activated: false,\n close: () => {\n closed = true\n try { wss?.close() } catch { /* noop */ }\n clients.clear()\n dispatchHandlers.clear()\n liveActors.clear()\n recentEvents.length = 0\n delete (globalThis as Record<string, unknown>)[key]\n },\n }\n\n // Lazily import ws so this module is import-safe in environments that\n // never use the server entrypoint (or where ws isn't installed).\n void (async () => {\n try {\n const mod = await import('ws')\n const WSServer = (mod as any).WebSocketServer ?? (mod as any).Server\n if (closed) return\n wss = new WSServer({ port, host })\n wss.on('connection', (ws: ClientLike) => {\n // Replay current live actors to every connecting panel, so reconnects\n // see the current set. Send the immutable registration (carrying the\n // registration-time snapshot → correct time-travel floor), then a\n // snapshot update if the actor has advanced since.\n for (const { reg, snapshot } of server.liveActors.values()) {\n try { ws.send(JSON.stringify({ ...reg, __xstateDevtools: true })) } catch { /* ignore */ }\n if (snapshot !== reg.snapshot) {\n try {\n ws.send(JSON.stringify({\n type: 'XSTATE_SNAPSHOT', sessionId: reg.sessionId, snapshot,\n timestamp: reg.timestamp, globalSeq: reg.globalSeq, __xstateDevtools: true,\n }))\n } catch { /* ignore */ }\n }\n }\n // Tell the panel the authoritative live set so it can prune actors\n // from a previous session (reconnect/app-restart) without wiping the\n // ones we just replayed.\n try {\n ws.send(JSON.stringify({\n type: 'XSTATE_REPLAY_DONE',\n sessionIds: [...server.liveActors.keys()],\n __xstateDevtools: true,\n }))\n } catch { /* ignore */ }\n // Flush the pre-connection event backlog once, to the first panel only.\n if (!server.activated) {\n server.activated = true\n for (const payload of server.recentEvents) {\n try { ws.send(payload) } catch { /* ignore */ }\n }\n server.recentEvents.length = 0\n }\n server.clients.add(ws)\n ws.on('message', (raw: unknown) => {\n try {\n const text = typeof raw === 'string' ? raw : (raw as Buffer).toString('utf8')\n const msg = JSON.parse(text) as ExtensionToPageMessage\n for (const cb of server.dispatchHandlers) cb(msg)\n } catch {\n // ignore malformed messages\n }\n })\n ws.on('close', () => server.clients.delete(ws))\n ws.on('error', () => server.clients.delete(ws))\n })\n wss.on('error', (err: Error) => {\n console.warn('[xstate-devtools] WS server error:', err.message)\n })\n } catch (e) {\n console.warn(\n '[xstate-devtools] could not start server adapter — install `ws` to enable.',\n (e as Error).message,\n )\n }\n })()\n\n ;(globalThis as Record<string, unknown>)[key] = server\n }\n\n const transport: Transport = {\n send(message: PageToExtensionMessage) {\n // Maintain the live-actor registry so any panel that connects (or\n // reconnects) later can be replayed the current state.\n trackLive(server, message)\n const payload = JSON.stringify({ ...message, __xstateDevtools: true })\n // Buffer events only until the first panel connects; afterwards the log\n // streams live and the backlog is no longer replayed (avoids reconnect\n // re-flooding the log with stale events).\n if (!server.activated && (message.type === 'XSTATE_EVENT' || message.type === 'XSTATE_SNAPSHOT')) {\n server.recentEvents.push(payload)\n if (server.recentEvents.length > server.bufferSize) server.recentEvents.shift()\n }\n for (const ws of server.clients) {\n if (ws.readyState === OPEN_STATE) {\n try { ws.send(payload) } catch { /* ignore */ }\n }\n }\n },\n subscribe(handler) {\n server.dispatchHandlers.add(handler)\n return () => server.dispatchHandlers.delete(handler)\n },\n }\n\n const inspector = createInspector(transport, 'srv')\n return { ...inspector, close: server.close }\n}\n","// packages/adapter/src/serialize.ts\nimport type { AnyStateMachine } from 'xstate'\nimport type { SerializedMachine, SerializedStateNode, SerializedTransition, SerializedInvoke } from '@xstate-devtools/protocol'\n\nfunction serializeGuard(guard: unknown): string | undefined {\n if (!guard) return undefined\n if (typeof guard === 'string') return guard\n if (typeof guard === 'function') return (guard as Function).name || '(inline)'\n if (typeof guard === 'object' && guard !== null) {\n const g = guard as any\n return g.type ?? g.name ?? '(inline)'\n }\n return '(inline)'\n}\n\nfunction serializeAction(action: unknown): string {\n if (typeof action === 'string') return action\n if (typeof action === 'function') return (action as Function).name || '(anonymous)'\n if (typeof action === 'object' && action !== null) {\n const a = action as any\n return a.type ?? a.name ?? String(action)\n }\n return String(action)\n}\n\nfunction serializeTransitionList(transitions: any[]): SerializedTransition[] {\n return transitions.map((t: any) => ({\n eventType: t.eventType ?? '',\n targets: (t.target ?? []).map((n: any) => n?.id ?? String(n)).filter(Boolean),\n guard: serializeGuard(t.guard),\n actions: (t.actions ?? []).map(serializeAction).filter(Boolean),\n }))\n}\n\nfunction serializeInvokes(node: any): SerializedInvoke[] {\n return (node.invoke as any[]).map((inv: any) => ({\n id: inv.id ?? '(unknown)',\n src: typeof inv.src === 'string'\n ? inv.src\n : inv.src?.id ?? inv.src?.name ?? '(inline)',\n }))\n}\n\nfunction serializeNode(node: any): SerializedStateNode {\n const allTransitions: SerializedTransition[] = []\n if (node.transitions instanceof Map) {\n for (const [, tList] of node.transitions) {\n allTransitions.push(...serializeTransitionList(tList))\n }\n }\n\n const always = Array.isArray(node.always) ? serializeTransitionList(node.always) : []\n\n return {\n id: node.id,\n key: node.key,\n type: node.type,\n initial: node.initial?.target?.[0]?.key,\n states: Object.fromEntries(\n Object.entries(node.states ?? {}).map(([k, v]) => [k, serializeNode(v)])\n ),\n on: allTransitions,\n always,\n entry: (node.entry ?? []).map(serializeAction).filter(Boolean),\n exit: (node.exit ?? []).map(serializeAction).filter(Boolean),\n invoke: serializeInvokes(node),\n }\n}\n\nexport function serializeMachine(machine: AnyStateMachine, sourceLocation?: string): SerializedMachine {\n return {\n id: machine.id,\n root: serializeNode(machine.root),\n sourceLocation,\n }\n}\n","// packages/adapter/src/sanitize.ts\n\nconst MAX_DEPTH = 10\nconst MAX_STRING_LENGTH = 500\nconst MAX_ARRAY_LENGTH = 100\n// Hard ceiling on total nodes across the whole tree. The per-level caps above\n// still allow multiplicative blow-up (100^depth) on wide+deep or cross-linked\n// objects, which can produce a string too large for JSON.stringify to handle.\n// This bounds the output regardless of shape.\nconst MAX_NODES = 10000\n\ninterface Ctx {\n depth: number\n /** Shared mutable node counter — the global budget. */\n budget: { n: number }\n /** Objects/arrays seen on the current path + elsewhere, to break cycles and DAGs. */\n seen: WeakSet<object>\n}\n\nfunction sanitizeInner(value: unknown, ctx: Ctx): unknown {\n if (ctx.depth > MAX_DEPTH) return '[MaxDepth]'\n if (++ctx.budget.n > MAX_NODES) return '[Truncated]'\n if (value === null || value === undefined) return value\n if (typeof value === 'boolean' || typeof value === 'number') return value\n if (typeof value === 'string') {\n return value.length > MAX_STRING_LENGTH ? value.slice(0, MAX_STRING_LENGTH) + '…' : value\n }\n if (typeof value === 'function') return `[Function: ${value.name || '(anonymous)'}]`\n if (typeof value === 'symbol') return `[Symbol: ${value.description ?? ''}]`\n if (typeof value === 'bigint') return `[BigInt: ${value}]`\n if (value instanceof Error) return { __type: 'Error', name: value.name, message: value.message }\n if (value instanceof Date) return { __type: 'Date', iso: value.toISOString() }\n if (value instanceof RegExp) return { __type: 'RegExp', source: value.source, flags: value.flags }\n if (value instanceof Promise) return '[Promise]'\n if (value instanceof WeakMap || value instanceof WeakSet) return '[WeakCollection]'\n if (ArrayBuffer.isView(value)) return `[TypedArray: ${(value as any).constructor.name}]`\n // Detect DOM nodes (works in browser and is safe to check)\n if (typeof Node !== 'undefined' && value instanceof Node) {\n return `[DOMNode: ${(value as Element).tagName ?? value.nodeName}]`\n }\n\n // From here on we recurse into containers — guard against shared/circular refs.\n if (ctx.seen.has(value as object)) return '[Circular]'\n ctx.seen.add(value as object)\n const child = { ...ctx, depth: ctx.depth + 1 }\n\n if (value instanceof Map) {\n const entries: [unknown, unknown][] = []\n for (const [k, v] of value as Map<unknown, unknown>) {\n if (entries.length >= MAX_ARRAY_LENGTH) break\n entries.push([sanitizeInner(k, child), sanitizeInner(v, child)])\n }\n return { __type: 'Map', entries }\n }\n if (value instanceof Set) {\n const values: unknown[] = []\n for (const v of value as Set<unknown>) {\n if (values.length >= MAX_ARRAY_LENGTH) break\n values.push(sanitizeInner(v, child))\n }\n return { __type: 'Set', values }\n }\n if (Array.isArray(value)) {\n const sliced = value.slice(0, MAX_ARRAY_LENGTH)\n const result = sliced.map((v) => sanitizeInner(v, child))\n if (value.length > MAX_ARRAY_LENGTH) result.push(`[…${value.length - MAX_ARRAY_LENGTH} more]`)\n return result\n }\n if (typeof value === 'object') {\n const result: Record<string, unknown> = {}\n let count = 0\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (count++ >= MAX_ARRAY_LENGTH) { result['…'] = '[truncated]'; break }\n result[k] = sanitizeInner(v, child)\n }\n return result\n }\n return String(value)\n}\n\nexport function sanitize(value: unknown): unknown {\n return sanitizeInner(value, { depth: 0, budget: { n: 0 }, seen: new WeakSet() })\n}\n","// Transport-agnostic XState inspection core.\n// Browser and server entrypoints supply their own transports.\nimport type { AnyActorRef } from 'xstate'\nimport type {\n ExtensionToPageMessage, PageToExtensionMessage, SerializedSnapshot,\n} from '@xstate-devtools/protocol'\nimport { serializeMachine } from './serialize.js'\nimport { sanitize } from './sanitize.js'\n\nexport type Source = 'web' | 'srv'\n\nexport interface Transport {\n /** Send a protocol message outbound (toward the panel). */\n send: (message: PageToExtensionMessage) => void\n /** Subscribe to inbound dispatch messages from the panel. Returns a teardown. */\n subscribe: (handler: (message: ExtensionToPageMessage) => void) => () => void\n}\n\nfunction getSourceLocation(): string | undefined {\n try {\n const lines = new Error().stack?.split('\\n') ?? []\n const callerLine = lines.find(\n (l, i) => i > 3 && !l.includes('xstate') && !l.includes('adapter')\n )\n return callerLine?.trim().replace(/^at\\s+/, '')\n } catch {\n return undefined\n }\n}\n\nfunction serializeSnapshot(snapshot: any): SerializedSnapshot {\n return {\n value: sanitize(snapshot?.value ?? null) as SerializedSnapshot['value'],\n context: sanitize(snapshot?.context),\n status: snapshot?.status ?? 'active',\n error: snapshot?.error ? sanitize(snapshot.error) : undefined,\n }\n}\n\nfunction safeSerializeSnapshot(actorRef: AnyActorRef): SerializedSnapshot {\n try {\n return serializeSnapshot(actorRef.getSnapshot())\n } catch {\n return { value: null, context: undefined, status: 'active' }\n }\n}\n\n/**\n * Capture XState's persisted snapshot for an actor. Unlike the display snapshot,\n * this is meant to be restorable. We JSON round-trip it to guarantee it survives\n * the transport (and stays serializable); a throw here means the snapshot isn't\n * cleanly persistable, which we report as an error rather than silently dropping.\n */\nfunction safePersistedSnapshot(actorRef: AnyActorRef): { persisted?: unknown; error?: string } {\n const getPersisted = (actorRef as any).getPersistedSnapshot\n if (typeof getPersisted !== 'function') {\n return { error: 'Actor does not support getPersistedSnapshot.' }\n }\n try {\n const raw = getPersisted.call(actorRef)\n if (raw === undefined) return { error: 'No persisted snapshot available.' }\n return { persisted: JSON.parse(JSON.stringify(raw)) }\n } catch (e) {\n return { error: `Snapshot is not JSON-serializable: ${(e as Error).message}` }\n }\n}\n\n// Cached on globalThis so HMR re-evaluating this module doesn't reset the\n// monotonic seq counter mid-session. The panel re-numbers messages on ingest\n// to merge multiple sources, but keeping a stable per-process seq still helps\n// when the panel reconnects to an already-running adapter.\nconst SEQ_KEY = '__xstate_devtools_global_seq__'\nfunction nextSeq(): number {\n const g = globalThis as Record<string, unknown>\n const cur = (g[SEQ_KEY] as number | undefined) ?? 0\n const next = cur + 1\n g[SEQ_KEY] = next\n return next\n}\n\nexport function createInspector(transport: Transport, source: Source) {\n const actorRefs = new Map<string, AnyActorRef>()\n // Owner-registered restore handlers, keyed by local (un-prefixed) sessionId.\n // Populated by useRestorableInspectedMachine; an actor without a handler\n // simply can't be restored (live rewind is opt-in).\n const restoreHandlers = new Map<string, (persisted: unknown) => void>()\n const prefix = source + ':'\n const tag = (sessionId: string) => prefix + sessionId\n const tagOptional = (id: string | undefined) => (id ? prefix + id : undefined)\n const stripIfMine = (id: string): string | null =>\n id.startsWith(prefix) ? id.slice(prefix.length) : null\n\n function checkAndNotifyStop(actorRef: AnyActorRef) {\n let snap: any\n try { snap = actorRef.getSnapshot() } catch { return }\n if (snap?.status !== 'active') {\n transport.send({ type: 'XSTATE_ACTOR_STOPPED', sessionId: tag(actorRef.sessionId) })\n actorRefs.delete(actorRef.sessionId)\n }\n }\n\n const unsubscribe = transport.subscribe((message) => {\n if (message.type === 'XSTATE_DISPATCH') {\n const local = stripIfMine(message.sessionId)\n if (local === null) return // not for this transport source\n const ref = actorRefs.get(local)\n if (ref) {\n try { ref.send(message.event) } catch (e) {\n console.warn('[xstate-devtools] dispatch error:', e)\n }\n }\n } else if (message.type === 'XSTATE_REQUEST_PERSISTED') {\n const local = stripIfMine(message.sessionId)\n if (local === null) return // not for this transport source\n const ref = actorRefs.get(local)\n if (!ref) return\n const { persisted, error } = safePersistedSnapshot(ref)\n transport.send({\n type: 'XSTATE_PERSISTED_SNAPSHOT',\n sessionId: tag(local),\n persisted,\n error,\n timestamp: Date.now(),\n })\n } else if (message.type === 'XSTATE_RESTORE') {\n const local = stripIfMine(message.sessionId)\n if (local === null) return // not for this transport source\n const handler = restoreHandlers.get(local)\n if (handler) {\n try { handler(message.persisted) } catch (e) {\n console.warn('[xstate-devtools] restore error:', e)\n }\n }\n }\n })\n\n const inspect = (inspectionEvent: any) => {\n try {\n if (inspectionEvent.type === '@xstate.actor') {\n const actorRef: AnyActorRef = inspectionEvent.actorRef\n const sessionId: string = actorRef.sessionId\n actorRefs.set(sessionId, actorRef)\n\n // `logic` is internal and not on the public AnyActorRef type.\n const logic = (actorRef as any).logic\n const machine = logic?.root\n ? serializeMachine(logic, getSourceLocation())\n : null\n\n transport.send({\n type: 'XSTATE_ACTOR_REGISTERED',\n sessionId: tag(sessionId),\n parentSessionId: tagOptional((actorRef as any)._parent?.sessionId),\n // actorRef.id is the invoke `id` for invoked actors — lets the debugger\n // nest non-machine actors (promise/callback) under their state.\n actorId: (actorRef as any).id,\n machine,\n snapshot: safeSerializeSnapshot(actorRef),\n globalSeq: nextSeq(),\n timestamp: Date.now(),\n })\n } else if (inspectionEvent.type === '@xstate.snapshot') {\n transport.send({\n type: 'XSTATE_SNAPSHOT',\n sessionId: tag(inspectionEvent.actorRef.sessionId),\n snapshot: serializeSnapshot(inspectionEvent.snapshot),\n timestamp: Date.now(),\n globalSeq: nextSeq(),\n })\n checkAndNotifyStop(inspectionEvent.actorRef)\n } else if (inspectionEvent.type === '@xstate.event') {\n transport.send({\n type: 'XSTATE_EVENT',\n sessionId: tag(inspectionEvent.actorRef.sessionId),\n event: inspectionEvent.event,\n snapshotAfter: safeSerializeSnapshot(inspectionEvent.actorRef),\n timestamp: Date.now(),\n globalSeq: nextSeq(),\n })\n checkAndNotifyStop(inspectionEvent.actorRef)\n }\n } catch (e) {\n // Never let an inspection failure (e.g. an un-serializable snapshot too large\n // for JSON.stringify) propagate back into XState's synchronous inspection\n // callback — that would crash the host actor's start(). Drop the event.\n console.warn('[xstate-devtools] inspection failed, dropping event:', (e as Error).message)\n }\n }\n\n /**\n * Register an owner-side restore handler for an actor. Called by\n * useRestorableInspectedMachine. Returns an unregister function.\n */\n function registerRestore(sessionId: string, handler: (persisted: unknown) => void) {\n restoreHandlers.set(sessionId, handler)\n return () => {\n if (restoreHandlers.get(sessionId) === handler) restoreHandlers.delete(sessionId)\n }\n }\n\n function dispose() {\n unsubscribe()\n actorRefs.clear()\n restoreHandlers.clear()\n }\n\n return { inspect, dispose, registerRestore }\n}\n"],"mappings":"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","names":[]}
package/dist/server.d.cts CHANGED
@@ -3,27 +3,30 @@ interface ServerAdapterOptions {
3
3
  port?: number;
4
4
  /** Host to bind. Defaults to '127.0.0.1'. */
5
5
  host?: string;
6
- /** Max messages to buffer while no panel is connected. Default 200. */
6
+ /** Max events to buffer before the first panel connects. Default 200. */
7
7
  bufferSize?: number;
8
8
  }
9
9
  /**
10
10
  * Start a local WebSocket server that the DevTools panel can connect to.
11
11
  * Returns the inspector callback. Multiple panels can connect simultaneously.
12
12
  *
13
- * The WS server, connected clients, dispatch handlers, and pre-connection
14
- * buffer are all stashed on globalThis keyed by port. This makes the function
13
+ * The WS server, connected clients, dispatch handlers, and the live-actor
14
+ * registry are all stashed on globalThis keyed by port. This makes the function
15
15
  * idempotent across HMR re-evaluation: subsequent calls reuse the existing
16
16
  * server and only register new inspector hooks.
17
17
  *
18
- * Inspection events emitted before the first panel connects are buffered (up
19
- * to `bufferSize`, default 200) and flushed to the first connecting client so
20
- * actors registered at boot are visible.
18
+ * Every connecting panel including a reconnect after the editor/host restarts
19
+ * is replayed the current set of live actors (with their latest snapshots),
20
+ * so actors registered at boot stay visible across reconnects (not just for
21
+ * the first panel). The pre-connection event backlog, by contrast, is flushed
22
+ * only to the very first panel; replaying it on every reconnect would re-flood
23
+ * the log with stale events.
21
24
  */
22
25
  declare function createServerAdapter(options?: ServerAdapterOptions): {
23
26
  close: () => void;
24
- port: Promise<number>;
25
27
  inspect: (inspectionEvent: any) => void;
26
28
  dispose: () => void;
29
+ registerRestore: (sessionId: string, handler: (persisted: unknown) => void) => () => void;
27
30
  };
28
31
 
29
32
  export { type ServerAdapterOptions, createServerAdapter };
package/dist/server.d.ts CHANGED
@@ -3,27 +3,30 @@ interface ServerAdapterOptions {
3
3
  port?: number;
4
4
  /** Host to bind. Defaults to '127.0.0.1'. */
5
5
  host?: string;
6
- /** Max messages to buffer while no panel is connected. Default 200. */
6
+ /** Max events to buffer before the first panel connects. Default 200. */
7
7
  bufferSize?: number;
8
8
  }
9
9
  /**
10
10
  * Start a local WebSocket server that the DevTools panel can connect to.
11
11
  * Returns the inspector callback. Multiple panels can connect simultaneously.
12
12
  *
13
- * The WS server, connected clients, dispatch handlers, and pre-connection
14
- * buffer are all stashed on globalThis keyed by port. This makes the function
13
+ * The WS server, connected clients, dispatch handlers, and the live-actor
14
+ * registry are all stashed on globalThis keyed by port. This makes the function
15
15
  * idempotent across HMR re-evaluation: subsequent calls reuse the existing
16
16
  * server and only register new inspector hooks.
17
17
  *
18
- * Inspection events emitted before the first panel connects are buffered (up
19
- * to `bufferSize`, default 200) and flushed to the first connecting client so
20
- * actors registered at boot are visible.
18
+ * Every connecting panel including a reconnect after the editor/host restarts
19
+ * is replayed the current set of live actors (with their latest snapshots),
20
+ * so actors registered at boot stay visible across reconnects (not just for
21
+ * the first panel). The pre-connection event backlog, by contrast, is flushed
22
+ * only to the very first panel; replaying it on every reconnect would re-flood
23
+ * the log with stale events.
21
24
  */
22
25
  declare function createServerAdapter(options?: ServerAdapterOptions): {
23
26
  close: () => void;
24
- port: Promise<number>;
25
27
  inspect: (inspectionEvent: any) => void;
26
28
  dispose: () => void;
29
+ registerRestore: (sessionId: string, handler: (persisted: unknown) => void) => () => void;
27
30
  };
28
31
 
29
32
  export { type ServerAdapterOptions, createServerAdapter };
package/dist/server.js CHANGED
@@ -1,119 +1,67 @@
1
1
  import {
2
- createInspector,
3
- debugLog,
4
- infoLog,
5
- sanitize,
6
- warnLog
7
- } from "./chunk-3GM2SFT4.js";
2
+ createInspector
3
+ } from "./chunk-QLRTDBT5.js";
8
4
 
9
5
  // src/server.ts
10
- import { createServer as createTcpServer } from "net";
11
- function getAvailablePort(start) {
12
- return new Promise((resolve, reject) => {
13
- const probe = createTcpServer();
14
- probe.unref();
15
- probe.on("error", (err) => {
16
- if (err.code === "EADDRINUSE") {
17
- resolve(getAvailablePort(start + 1));
18
- } else {
19
- reject(err);
20
- }
21
- });
22
- probe.listen(start, "127.0.0.1", () => {
23
- const port = probe.address().port;
24
- probe.close(() => resolve(port));
25
- });
26
- });
27
- }
28
6
  var OPEN_STATE = 1;
29
- function summarizeMessage(message) {
30
- const summary = { type: message.type };
31
- if ("sessionId" in message) summary.sessionId = message.sessionId;
32
- if ("parentSessionId" in message && message.parentSessionId) {
33
- summary.parentSessionId = message.parentSessionId;
34
- }
35
- if ("globalSeq" in message) summary.globalSeq = message.globalSeq;
36
- if ("timestamp" in message) summary.timestamp = message.timestamp;
37
- if ("event" in message && message.event && typeof message.event === "object" && "type" in message.event) {
38
- summary.eventType = message.event.type;
39
- }
40
- return summary;
41
- }
42
- function debugLog2(message, details) {
43
- debugLog("server", message, details);
44
- }
45
- function infoLog2(message, details) {
46
- infoLog("server", message, details);
47
- }
48
- function warnLog2(message, details) {
49
- warnLog("server", message, details);
50
- }
51
- function stringifyOutgoingMessage(message) {
52
- const payload = { ...message, __xstateDevtools: true };
53
- try {
54
- return JSON.stringify(payload);
55
- } catch (error) {
56
- warnLog2("failed to stringify adapter message; retrying with sanitized payload", {
57
- error,
58
- message: summarizeMessage(message)
59
- });
60
- }
61
- try {
62
- return JSON.stringify(sanitize(payload));
63
- } catch (error) {
64
- warnLog2("dropping adapter message that could not be stringified", {
65
- error,
66
- message: summarizeMessage(message)
67
- });
68
- return null;
7
+ function trackLive(server, message) {
8
+ switch (message.type) {
9
+ case "XSTATE_ACTOR_REGISTERED":
10
+ server.liveActors.set(message.sessionId, { reg: message, snapshot: message.snapshot });
11
+ break;
12
+ case "XSTATE_SNAPSHOT": {
13
+ const live = server.liveActors.get(message.sessionId);
14
+ if (live) {
15
+ live.snapshot = message.snapshot;
16
+ }
17
+ break;
18
+ }
19
+ case "XSTATE_EVENT": {
20
+ const live = server.liveActors.get(message.sessionId);
21
+ if (live) {
22
+ live.snapshot = message.snapshotAfter;
23
+ }
24
+ break;
25
+ }
26
+ case "XSTATE_ACTOR_STOPPED":
27
+ server.liveActors.delete(message.sessionId);
28
+ break;
69
29
  }
70
30
  }
71
31
  function createServerAdapter(options = {}) {
72
32
  const port = options.port ?? (Number(process.env.XSTATE_DEVTOOLS_PORT) || 9301);
73
33
  const host = options.host ?? "127.0.0.1";
74
34
  const bufferSize = options.bufferSize ?? 200;
75
- infoLog2("createServerAdapter called", { host, port, bufferSize });
76
35
  const key = `__xstate_devtools_server_${port}__`;
77
36
  const cache = globalThis[key];
78
37
  let server;
79
38
  if (cache) {
80
39
  server = cache;
81
- infoLog2("reusing cached WebSocket server", {
82
- host,
83
- port,
84
- clientCount: server.clients.size,
85
- bufferedMessages: server.buffer.length
86
- });
87
40
  if (bufferSize > server.bufferSize) server.bufferSize = bufferSize;
88
41
  } else {
89
42
  const clients = /* @__PURE__ */ new Set();
90
43
  const dispatchHandlers = /* @__PURE__ */ new Set();
91
- const buffer = [];
44
+ const liveActors = /* @__PURE__ */ new Map();
45
+ const recentEvents = [];
92
46
  let wss = null;
93
47
  let closed = false;
94
- let portResolve;
95
- let portReject;
96
- const portPromise = new Promise((res, rej) => {
97
- portResolve = res;
98
- portReject = rej;
99
- });
100
48
  server = {
101
49
  clients,
102
50
  dispatchHandlers,
103
- buffer,
51
+ liveActors,
52
+ recentEvents,
104
53
  bufferSize,
105
54
  activated: false,
106
- port: portPromise,
107
55
  close: () => {
108
56
  closed = true;
109
- infoLog2("closing WebSocket server", { host, port, clientCount: clients.size });
110
57
  try {
111
58
  wss?.close();
112
59
  } catch {
113
60
  }
114
61
  clients.clear();
115
62
  dispatchHandlers.clear();
116
- buffer.length = 0;
63
+ liveActors.clear();
64
+ recentEvents.length = 0;
117
65
  delete globalThis[key];
118
66
  }
119
67
  };
@@ -122,112 +70,93 @@ function createServerAdapter(options = {}) {
122
70
  const mod = await import("ws");
123
71
  const WSServer = mod.WebSocketServer ?? mod.Server;
124
72
  if (closed) return;
125
- const actualPort = await getAvailablePort(port);
126
- if (closed) return;
127
- wss = new WSServer({ port: actualPort, host });
128
- process.env.XSTATE_DEVTOOLS_PORT = String(actualPort);
129
- portResolve(actualPort);
130
- infoLog2("WebSocket server listening", { host, port: actualPort });
73
+ wss = new WSServer({ port, host });
131
74
  wss.on("connection", (ws) => {
132
- infoLog2("panel connected to WebSocket server", {
133
- host,
134
- port,
135
- activated: server.activated,
136
- bufferedMessages: server.buffer.length
137
- });
75
+ for (const { reg, snapshot } of server.liveActors.values()) {
76
+ try {
77
+ ws.send(JSON.stringify({ ...reg, __xstateDevtools: true }));
78
+ } catch {
79
+ }
80
+ if (snapshot !== reg.snapshot) {
81
+ try {
82
+ ws.send(JSON.stringify({
83
+ type: "XSTATE_SNAPSHOT",
84
+ sessionId: reg.sessionId,
85
+ snapshot,
86
+ timestamp: reg.timestamp,
87
+ globalSeq: reg.globalSeq,
88
+ __xstateDevtools: true
89
+ }));
90
+ } catch {
91
+ }
92
+ }
93
+ }
94
+ try {
95
+ ws.send(JSON.stringify({
96
+ type: "XSTATE_REPLAY_DONE",
97
+ sessionIds: [...server.liveActors.keys()],
98
+ __xstateDevtools: true
99
+ }));
100
+ } catch {
101
+ }
138
102
  if (!server.activated) {
139
103
  server.activated = true;
140
- infoLog2("flushing bootstrap buffer to first panel", {
141
- host,
142
- port,
143
- bufferedMessages: server.buffer.length
144
- });
145
- for (const payload of server.buffer) {
104
+ for (const payload of server.recentEvents) {
146
105
  try {
147
106
  ws.send(payload);
148
107
  } catch {
149
108
  }
150
109
  }
151
- server.buffer.length = 0;
110
+ server.recentEvents.length = 0;
152
111
  }
153
112
  server.clients.add(ws);
154
113
  ws.on("message", (raw) => {
155
114
  try {
156
115
  const text = typeof raw === "string" ? raw : raw.toString("utf8");
157
116
  const msg = JSON.parse(text);
158
- debugLog2("received dispatch from panel", summarizeMessage(msg));
159
117
  for (const cb of server.dispatchHandlers) cb(msg);
160
- } catch (error) {
161
- warnLog2("failed to parse panel message", { error });
118
+ } catch {
162
119
  }
163
120
  });
164
- ws.on("close", () => {
165
- server.clients.delete(ws);
166
- infoLog2("panel disconnected from WebSocket server", {
167
- host,
168
- port,
169
- clientCount: server.clients.size
170
- });
171
- });
172
- ws.on("error", (error) => {
173
- server.clients.delete(ws);
174
- warnLog2("WebSocket client error", { error });
175
- });
121
+ ws.on("close", () => server.clients.delete(ws));
122
+ ws.on("error", () => server.clients.delete(ws));
176
123
  });
177
124
  wss.on("error", (err) => {
178
- warnLog2("WS server error", { host, port, message: err.message });
125
+ console.warn("[xstate-devtools] WS server error:", err.message);
179
126
  });
180
127
  } catch (e) {
181
- portReject(e);
182
- warnLog2("could not start server adapter \u2014 install `ws` to enable", {
183
- host,
184
- port,
185
- message: e.message
186
- });
128
+ console.warn(
129
+ "[xstate-devtools] could not start server adapter \u2014 install `ws` to enable.",
130
+ e.message
131
+ );
187
132
  }
188
133
  })();
189
134
  globalThis[key] = server;
190
135
  }
191
136
  const transport = {
192
137
  send(message) {
193
- const payload = stringifyOutgoingMessage(message);
194
- if (payload === null) return;
195
- if (!server.activated) {
196
- if (server.buffer.length >= server.bufferSize) server.buffer.shift();
197
- server.buffer.push(payload);
198
- debugLog2("buffered outgoing adapter message; no panel connected yet", {
199
- bufferedMessages: server.buffer.length,
200
- message: summarizeMessage(message)
201
- });
202
- return;
138
+ trackLive(server, message);
139
+ const payload = JSON.stringify({ ...message, __xstateDevtools: true });
140
+ if (!server.activated && (message.type === "XSTATE_EVENT" || message.type === "XSTATE_SNAPSHOT")) {
141
+ server.recentEvents.push(payload);
142
+ if (server.recentEvents.length > server.bufferSize) server.recentEvents.shift();
203
143
  }
204
- let sentCount = 0;
205
144
  for (const ws of server.clients) {
206
145
  if (ws.readyState === OPEN_STATE) {
207
146
  try {
208
147
  ws.send(payload);
209
- sentCount += 1;
210
148
  } catch {
211
149
  }
212
150
  }
213
151
  }
214
- debugLog2("sent adapter message to connected panels", {
215
- sentCount,
216
- clientCount: server.clients.size,
217
- message: summarizeMessage(message)
218
- });
219
152
  },
220
153
  subscribe(handler) {
221
154
  server.dispatchHandlers.add(handler);
222
- debugLog2("registered dispatch handler", { handlerCount: server.dispatchHandlers.size });
223
- return () => {
224
- server.dispatchHandlers.delete(handler);
225
- debugLog2("removed dispatch handler", { handlerCount: server.dispatchHandlers.size });
226
- };
155
+ return () => server.dispatchHandlers.delete(handler);
227
156
  }
228
157
  };
229
158
  const inspector = createInspector(transport, "srv");
230
- return { ...inspector, close: server.close, port: server.port };
159
+ return { ...inspector, close: server.close };
231
160
  }
232
161
  export {
233
162
  createServerAdapter