@websublime/vite-plugin-open-api-server 0.23.1 → 0.23.3

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.
package/dist/index.d.ts CHANGED
@@ -26,8 +26,8 @@ import { App } from 'vue';
26
26
  * spec: './openapi/petstore.yaml',
27
27
  * port: 4000,
28
28
  * proxyPath: '/api',
29
- * handlersDir: './mocks/handlers',
30
- * seedsDir: './mocks/seeds',
29
+ * handlersDir: './mocks/handlers', // optional
30
+ * seedsDir: './mocks/seeds', // optional
31
31
  * }),
32
32
  * ],
33
33
  * });
@@ -72,8 +72,9 @@ interface OpenApiServerOptions {
72
72
  * and handler functions as values. Use `defineHandlers()` for
73
73
  * type-safe handler definitions.
74
74
  *
75
+ * When not provided, no handler crawling is performed.
76
+ *
75
77
  * @example './mocks/handlers'
76
- * @default './mocks/handlers'
77
78
  */
78
79
  handlersDir?: string;
79
80
  /**
@@ -83,8 +84,9 @@ interface OpenApiServerOptions {
83
84
  * and seed functions as values. Use `defineSeeds()` for
84
85
  * type-safe seed definitions.
85
86
  *
87
+ * When not provided, no seed crawling is performed.
88
+ *
86
89
  * @example './mocks/seeds'
87
- * @default './mocks/seeds'
88
90
  */
89
91
  seedsDir?: string;
90
92
  /**
@@ -162,8 +164,8 @@ interface ResolvedOptions {
162
164
  spec: string;
163
165
  port: number;
164
166
  proxyPath: string;
165
- handlersDir: string;
166
- seedsDir: string;
167
+ handlersDir: string | null;
168
+ seedsDir: string | null;
167
169
  enabled: boolean;
168
170
  idFields: Record<string, string>;
169
171
  timelineLimit: number;
package/dist/index.js CHANGED
@@ -419,8 +419,8 @@ function resolveOptions(options) {
419
419
  spec: options.spec,
420
420
  port: options.port ?? 4e3,
421
421
  proxyPath: options.proxyPath ?? "/api",
422
- handlersDir: options.handlersDir ?? "./mocks/handlers",
423
- seedsDir: options.seedsDir ?? "./mocks/seeds",
422
+ handlersDir: options.handlersDir?.trim() || null,
423
+ seedsDir: options.seedsDir?.trim() || null,
424
424
  enabled: options.enabled ?? true,
425
425
  idFields: options.idFields ?? {},
426
426
  timelineLimit: options.timelineLimit ?? 500,
@@ -435,6 +435,26 @@ function resolveOptions(options) {
435
435
  // src/plugin.ts
436
436
  var VIRTUAL_DEVTOOLS_TAB_ID = "virtual:open-api-devtools-tab";
437
437
  var RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID = `\0${VIRTUAL_DEVTOOLS_TAB_ID}`;
438
+ function resolveDevtoolsSpaDir(logger) {
439
+ const pluginDir = dirname(fileURLToPath(import.meta.url));
440
+ const spaDir = join(pluginDir, "devtools-spa");
441
+ if (existsSync(spaDir)) {
442
+ return spaDir;
443
+ }
444
+ logger?.warn?.(
445
+ "[vite-plugin-open-api-server] DevTools SPA not found at",
446
+ spaDir,
447
+ '- serving placeholder. Run "pnpm build" to include the SPA.'
448
+ );
449
+ return void 0;
450
+ }
451
+ function buildSeedDataMap(server) {
452
+ const seedDataMap = /* @__PURE__ */ new Map();
453
+ for (const schema of server.store.getSchemas()) {
454
+ seedDataMap.set(schema, server.store.list(schema));
455
+ }
456
+ return seedDataMap;
457
+ }
438
458
  function openApiServer(options) {
439
459
  const resolvedOptions = resolveOptions(options);
440
460
  let server = null;
@@ -523,22 +543,9 @@ try {
523
543
  return;
524
544
  }
525
545
  try {
526
- const handlersResult = await loadHandlers(resolvedOptions.handlersDir, viteServer, cwd);
527
- const seedsResult = await loadSeeds(resolvedOptions.seedsDir, viteServer, cwd);
528
- let devtoolsSpaDir;
529
- if (resolvedOptions.devtools) {
530
- const pluginDir = dirname(fileURLToPath(import.meta.url));
531
- const spaDir = join(pluginDir, "devtools-spa");
532
- if (existsSync(spaDir)) {
533
- devtoolsSpaDir = spaDir;
534
- } else {
535
- resolvedOptions.logger?.warn?.(
536
- "[vite-plugin-open-api-server] DevTools SPA not found at",
537
- spaDir,
538
- '- serving placeholder. Run "pnpm build" to include the SPA.'
539
- );
540
- }
541
- }
546
+ const handlersResult = resolvedOptions.handlersDir ? await loadHandlers(resolvedOptions.handlersDir, viteServer, cwd) : { handlers: /* @__PURE__ */ new Map(), fileCount: 0, files: [] };
547
+ const seedsResult = resolvedOptions.seedsDir ? await loadSeeds(resolvedOptions.seedsDir, viteServer, cwd) : { seeds: /* @__PURE__ */ new Map(), fileCount: 0, files: [] };
548
+ const devtoolsSpaDir = resolvedOptions.devtools ? resolveDevtoolsSpaDir(resolvedOptions.logger) : void 0;
542
549
  server = await createOpenApiServer({
543
550
  spec: resolvedOptions.spec,
544
551
  port: resolvedOptions.port,
@@ -555,6 +562,7 @@ try {
555
562
  });
556
563
  if (seedsResult.seeds.size > 0) {
557
564
  await executeSeeds(seedsResult.seeds, server.store, server.document);
565
+ server.updateSeeds(buildSeedDataMap(server));
558
566
  }
559
567
  await server.start();
560
568
  configureProxy(viteServer, resolvedOptions.proxyPath, resolvedOptions.port);
@@ -637,15 +645,15 @@ try {
637
645
  const debouncedHandlerReload = debounce(reloadHandlers, 100);
638
646
  const debouncedSeedReload = debounce(reloadSeeds, 100);
639
647
  fileWatcher = await createFileWatcher({
640
- handlersDir: resolvedOptions.handlersDir,
641
- seedsDir: resolvedOptions.seedsDir,
648
+ handlersDir: resolvedOptions.handlersDir ?? void 0,
649
+ seedsDir: resolvedOptions.seedsDir ?? void 0,
642
650
  cwd,
643
651
  onHandlerChange: debouncedHandlerReload,
644
652
  onSeedChange: debouncedSeedReload
645
653
  });
646
654
  }
647
655
  async function reloadHandlers() {
648
- if (!server || !vite) return;
656
+ if (!server || !vite || !resolvedOptions.handlersDir) return;
649
657
  try {
650
658
  const handlersResult = await loadHandlers(resolvedOptions.handlersDir, vite, cwd);
651
659
  server.updateHandlers(handlersResult.handlers);
@@ -659,7 +667,7 @@ try {
659
667
  }
660
668
  }
661
669
  async function reloadSeeds() {
662
- if (!server || !vite) return;
670
+ if (!server || !vite || !resolvedOptions.seedsDir) return;
663
671
  try {
664
672
  const seedsResult = await loadSeeds(resolvedOptions.seedsDir, vite, cwd);
665
673
  if (seedsResult.seeds.size > 0) {
@@ -668,10 +676,7 @@ try {
668
676
  } else {
669
677
  server.store.clearAll();
670
678
  }
671
- server.wsHub.broadcast({
672
- type: "seeds:updated",
673
- data: { count: seedsResult.seeds.size }
674
- });
679
+ server.updateSeeds(buildSeedDataMap(server));
675
680
  printReloadNotification("seeds", seedsResult.seeds.size, resolvedOptions);
676
681
  } catch (error) {
677
682
  printError("Failed to reload seeds", error, resolvedOptions);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/banner.ts","../src/utils.ts","../src/handlers.ts","../src/hot-reload.ts","../src/seeds.ts","../src/types.ts","../src/plugin.ts","../src/devtools.ts"],"names":["path","fg","require","vite"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Startup Banner\n *\n * What: Prints a colorful startup banner with server information\n * How: Uses picocolors for ANSI color output\n * Why: Provides clear feedback about server status and configuration\n *\n * @module banner\n */\n\nimport type { EndpointRegistry } from '@websublime/vite-plugin-open-api-core';\nimport pc from 'picocolors';\nimport type { ResolvedOptions } from './types.js';\n\n/**\n * Banner configuration\n */\nexport interface BannerInfo {\n /** Server port */\n port: number;\n /** Proxy path in Vite */\n proxyPath: string;\n /** OpenAPI spec title */\n title: string;\n /** OpenAPI spec version */\n version: string;\n /** Number of endpoints */\n endpointCount: number;\n /** Number of loaded handlers */\n handlerCount: number;\n /** Number of loaded seeds */\n seedCount: number;\n /** DevTools enabled */\n devtools: boolean;\n}\n\n/**\n * Print the startup banner\n *\n * @param info - Banner information\n * @param options - Resolved plugin options\n */\nexport function printBanner(info: BannerInfo, options: ResolvedOptions): void {\n if (options.silent) {\n return;\n }\n\n const logger = options.logger ?? console;\n const log = (msg: string) => logger.info(msg);\n\n // Box drawing characters\n const BOX = {\n topLeft: '╭',\n topRight: '╮',\n bottomLeft: '╰',\n bottomRight: '╯',\n horizontal: '─',\n vertical: '│',\n };\n\n const width = 56;\n const horizontalLine = BOX.horizontal.repeat(width - 2);\n\n log('');\n log(pc.cyan(`${BOX.topLeft}${horizontalLine}${BOX.topRight}`));\n log(\n pc.cyan(BOX.vertical) + centerText('🚀 OpenAPI Mock Server', width - 2) + pc.cyan(BOX.vertical),\n );\n log(pc.cyan(`${BOX.bottomLeft}${horizontalLine}${BOX.bottomRight}`));\n log('');\n\n // API Info\n log(\n ` ${pc.bold(pc.white('API:'))} ${pc.green(info.title)} ${pc.dim(`v${info.version}`)}`,\n );\n log(` ${pc.bold(pc.white('Server:'))} ${pc.cyan(`http://localhost:${info.port}`)}`);\n log(\n ` ${pc.bold(pc.white('Proxy:'))} ${pc.yellow(info.proxyPath)} ${pc.dim('→')} ${pc.dim(`localhost:${info.port}`)}`,\n );\n log('');\n\n // Stats\n const stats = [\n { label: 'Endpoints', value: info.endpointCount, color: pc.blue },\n { label: 'Handlers', value: info.handlerCount, color: pc.green },\n { label: 'Seeds', value: info.seedCount, color: pc.magenta },\n ];\n\n const statsLine = stats\n .map((s) => `${pc.dim(`${s.label}:`)} ${s.color(String(s.value))}`)\n .join(pc.dim(' │ '));\n log(` ${statsLine}`);\n log('');\n\n // DevTools\n if (info.devtools) {\n log(\n ` ${pc.bold(pc.white('DevTools:'))} ${pc.cyan(`http://localhost:${info.port}/_devtools`)}`,\n );\n log(` ${pc.bold(pc.white('API Info:'))} ${pc.cyan(`http://localhost:${info.port}/_api`)}`);\n log('');\n }\n\n // Footer\n log(pc.dim(' Press Ctrl+C to stop the server'));\n log('');\n}\n\n/**\n * Center text within a given width\n *\n * @param text - Text to center\n * @param width - Total width\n * @returns Centered text with padding\n */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes require control characters\nconst ANSI_ESCAPE_REGEX = /\\x1b\\[[0-9;]*m/g;\n\nfunction centerText(text: string, width: number): string {\n // Account for ANSI codes - get visible length\n const visibleLength = text.replace(ANSI_ESCAPE_REGEX, '').length;\n const padding = Math.max(0, width - visibleLength);\n const leftPad = Math.floor(padding / 2);\n const rightPad = padding - leftPad;\n return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);\n}\n\n/**\n * Extract banner info from server registry and document\n *\n * @param registry - Endpoint registry\n * @param document - OpenAPI document\n * @param handlerCount - Number of loaded handlers\n * @param seedCount - Number of loaded seed schemas\n * @param options - Resolved options\n * @returns Banner info\n */\nexport function extractBannerInfo(\n registry: EndpointRegistry,\n document: { info: { title: string; version: string } },\n handlerCount: number,\n seedCount: number,\n options: ResolvedOptions,\n): BannerInfo {\n return {\n port: options.port,\n proxyPath: options.proxyPath,\n title: document.info.title,\n version: document.info.version,\n endpointCount: registry.endpoints.size,\n handlerCount,\n seedCount,\n devtools: options.devtools,\n };\n}\n\n/**\n * Print a hot reload notification\n *\n * @param type - Type of reload ('handlers' | 'seeds')\n * @param count - Number of reloaded items\n * @param options - Resolved options\n */\nexport function printReloadNotification(\n type: 'handlers' | 'seeds',\n count: number,\n options: ResolvedOptions,\n): void {\n if (options.silent) {\n return;\n }\n\n const logger = options.logger ?? console;\n const icon = type === 'handlers' ? '🔄' : '🌱';\n const label = type === 'handlers' ? 'Handlers' : 'Seeds';\n const color = type === 'handlers' ? pc.green : pc.magenta;\n\n logger.info(` ${icon} ${color(label)} reloaded: ${pc.bold(String(count))} ${type}`);\n}\n\n/**\n * Print an error message\n *\n * @param message - Error message\n * @param error - Optional error object\n * @param options - Resolved options\n */\nexport function printError(message: string, error: unknown, options: ResolvedOptions): void {\n const logger = options.logger ?? console;\n logger.error(`${pc.red('✖')} ${pc.bold(pc.red('Error:'))} ${message}`);\n if (error instanceof Error) {\n logger.error(pc.dim(` ${error.message}`));\n }\n}\n\n/**\n * Print a warning message\n *\n * @param message - Warning message\n * @param options - Resolved options\n */\nexport function printWarning(message: string, options: ResolvedOptions): void {\n if (options.silent) {\n return;\n }\n\n const logger = options.logger ?? console;\n logger.warn(`${pc.yellow('⚠')} ${pc.yellow('Warning:')} ${message}`);\n}\n","/**\n * Filesystem Utilities\n *\n * What: Shared utility functions for filesystem operations\n * How: Uses Node.js fs/promises module with error handling\n * Why: Provides reusable utilities for handler and seed loading\n *\n * @module utils\n */\n\n/**\n * Check if a directory exists\n *\n * @param dirPath - Path to check\n * @returns Promise resolving to true if directory exists\n */\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const fs = await import('node:fs/promises');\n const stats = await fs.stat(dirPath);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n","/**\n * Handler Loading\n *\n * What: Loads handler files from a directory using glob patterns\n * How: Uses Vite's ssrLoadModule to transform and load TypeScript files\n * Why: Enables users to define custom handlers for operationIds in TypeScript\n *\n * @module handlers\n */\n\nimport path from 'node:path';\nimport type { HandlerDefinition, HandlerFn, Logger } from '@websublime/vite-plugin-open-api-core';\nimport fg from 'fast-glob';\nimport type { ViteDevServer } from 'vite';\nimport { directoryExists } from './utils.js';\n\n/**\n * Result of loading handlers\n */\nexport interface LoadHandlersResult {\n /** Map of operationId to handler function */\n handlers: Map<string, HandlerFn>;\n /** Number of handler files loaded */\n fileCount: number;\n /** List of loaded file paths (relative) */\n files: string[];\n}\n\n/**\n * Load handlers from a directory\n *\n * Searches for handler files matching the pattern `*.handlers.{ts,js,mjs}`\n * in the specified directory. Each file should export an object with\n * operationId keys and handler functions as values.\n *\n * Uses Vite's ssrLoadModule to transform TypeScript files on-the-fly.\n *\n * @example\n * ```typescript\n * // mocks/handlers/pets.handlers.ts\n * import { defineHandlers } from '@websublime/vite-plugin-open-api-core';\n *\n * export default defineHandlers({\n * getPetById: async ({ req, store }) => {\n * const pet = store.get('Pet', req.params.petId);\n * return pet ?? { status: 404, data: { message: 'Pet not found' } };\n * },\n * });\n * ```\n *\n * @param handlersDir - Directory to search for handler files\n * @param viteServer - Vite dev server instance for ssrLoadModule\n * @param cwd - Current working directory (defaults to process.cwd())\n * @param logger - Optional logger for warnings/errors\n * @returns Promise resolving to loaded handlers\n */\nexport async function loadHandlers(\n handlersDir: string,\n viteServer: ViteDevServer,\n cwd: string = process.cwd(),\n logger: Logger = console,\n): Promise<LoadHandlersResult> {\n const handlers = new Map<string, HandlerFn>();\n const absoluteDir = path.resolve(cwd, handlersDir);\n\n // Check if directory exists\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return {\n handlers,\n fileCount: 0,\n files: [],\n };\n }\n\n // Find handler files (supports TypeScript via Vite's transform)\n const pattern = '**/*.handlers.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: false,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n // Load each file using Vite's ssrLoadModule\n for (const file of files) {\n const absolutePath = path.join(absoluteDir, file);\n const fileHandlers = await loadHandlerFile(absolutePath, viteServer, logger);\n\n // Merge handlers\n for (const [operationId, handler] of Object.entries(fileHandlers)) {\n if (handlers.has(operationId)) {\n logger.warn(\n `[vite-plugin-open-api-server] Duplicate handler for operationId \"${operationId}\" in ${file}. Using last definition.`,\n );\n }\n handlers.set(operationId, handler);\n }\n }\n\n return {\n handlers,\n fileCount: files.length,\n files,\n };\n}\n\n/**\n * Load a single handler file using Vite's ssrLoadModule\n *\n * @param filePath - Absolute path to the handler file\n * @param viteServer - Vite dev server instance\n * @param logger - Logger for warnings/errors\n * @returns Promise resolving to handler definition object\n */\nasync function loadHandlerFile(\n filePath: string,\n viteServer: ViteDevServer,\n logger: Logger,\n): Promise<HandlerDefinition> {\n try {\n // Invalidate module cache for hot reload\n const moduleNode = viteServer.moduleGraph.getModuleById(filePath);\n if (moduleNode) {\n viteServer.moduleGraph.invalidateModule(moduleNode);\n }\n\n // Use Vite's ssrLoadModule to transform and load the file\n // This handles TypeScript, ESM, and other transforms automatically\n const module = await viteServer.ssrLoadModule(filePath);\n\n // Support both default export and named export\n const handlers = module.default ?? module.handlers ?? module;\n\n // Validate handlers object\n if (!handlers || typeof handlers !== 'object') {\n logger.warn(\n `[vite-plugin-open-api-server] Invalid handler file ${filePath}: expected object export`,\n );\n return {};\n }\n\n // Filter to only handler functions\n const validHandlers: HandlerDefinition = {};\n for (const [key, value] of Object.entries(handlers)) {\n if (typeof value === 'function') {\n validHandlers[key] = value as HandlerFn;\n }\n }\n\n return validHandlers;\n } catch (error) {\n logger.error(\n `[vite-plugin-open-api-server] Failed to load handler file ${filePath}:`,\n error instanceof Error ? error.message : error,\n );\n return {};\n }\n}\n\n/**\n * Get list of handler files in a directory\n *\n * Useful for file watching setup.\n *\n * @param handlersDir - Directory to search\n * @param cwd - Current working directory\n * @returns Promise resolving to list of absolute file paths\n */\nexport async function getHandlerFiles(\n handlersDir: string,\n cwd: string = process.cwd(),\n): Promise<string[]> {\n const absoluteDir = path.resolve(cwd, handlersDir);\n\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return [];\n }\n\n const pattern = '**/*.handlers.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: true,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n return files;\n}\n","/**\n * Hot Reload\n *\n * What: File watcher for hot reloading handlers and seeds\n * How: Uses chokidar to watch for file changes\n * Why: Enables rapid development iteration without server restart\n *\n * @module hot-reload\n *\n * TODO: Full implementation in Task 3.3\n * This module provides placeholder/basic functionality for Task 3.1\n */\n\nimport path from 'node:path';\nimport type { Logger } from '@websublime/vite-plugin-open-api-core';\nimport type { FSWatcher } from 'chokidar';\n\n/**\n * File watcher configuration\n */\nexport interface FileWatcherOptions {\n /** Directory containing handler files */\n handlersDir?: string;\n /** Directory containing seed files */\n seedsDir?: string;\n /** Callback when a handler file changes */\n onHandlerChange?: (filePath: string) => Promise<void> | void;\n /** Callback when a seed file changes */\n onSeedChange?: (filePath: string) => Promise<void> | void;\n /** Current working directory */\n cwd?: string;\n /** Logger for error messages */\n logger?: Logger;\n}\n\n/**\n * File watcher instance\n */\nexport interface FileWatcher {\n /** Close the watcher and release resources */\n close(): Promise<void>;\n /** Check if watcher is active */\n readonly isWatching: boolean;\n /** Promise that resolves when all watchers are ready */\n readonly ready: Promise<void>;\n}\n\n/**\n * Create a file watcher for handlers and seeds\n *\n * Watches for changes to handler and seed files and invokes\n * callbacks when changes are detected. Supports add, change,\n * and unlink events.\n *\n * @example\n * ```typescript\n * const watcher = await createFileWatcher({\n * handlersDir: './mocks/handlers',\n * seedsDir: './mocks/seeds',\n * onHandlerChange: async (file) => {\n * console.log('Handler changed:', file);\n * const handlers = await loadHandlers('./mocks/handlers');\n * server.updateHandlers(handlers.handlers);\n * },\n * onSeedChange: async (file) => {\n * console.log('Seed changed:', file);\n * const seeds = await loadSeeds('./mocks/seeds');\n * // Re-execute seeds...\n * },\n * });\n *\n * // Later, clean up\n * await watcher.close();\n * ```\n *\n * @param options - Watcher configuration\n * @returns Promise resolving to file watcher instance\n */\nexport async function createFileWatcher(options: FileWatcherOptions): Promise<FileWatcher> {\n const {\n handlersDir,\n seedsDir,\n onHandlerChange,\n onSeedChange,\n cwd = process.cwd(),\n logger = console,\n } = options;\n\n // Dynamic import chokidar to avoid bundling issues\n const { watch } = await import('chokidar');\n\n const watchers: FSWatcher[] = [];\n const readyPromises: Promise<void>[] = [];\n let isWatching = true;\n\n // Handler file patterns\n const handlerPattern = '**/*.handlers.{ts,js,mjs}';\n const seedPattern = '**/*.seeds.{ts,js,mjs}';\n\n /**\n * Wrapper to safely invoke async callbacks and log errors\n * Prevents unhandled promise rejections from file watcher events\n */\n const safeInvoke = (\n callback: (filePath: string) => Promise<void> | void,\n filePath: string,\n context: string,\n ): void => {\n Promise.resolve()\n .then(() => callback(filePath))\n .catch((error) => {\n logger.error(\n `[vite-plugin-open-api-server] ${context} callback error for ${filePath}:`,\n error,\n );\n });\n };\n\n // Watch handlers directory\n if (handlersDir && onHandlerChange) {\n const absoluteHandlersDir = path.resolve(cwd, handlersDir);\n const handlerWatcher = watch(handlerPattern, {\n cwd: absoluteHandlersDir,\n ignoreInitial: true,\n ignored: ['**/node_modules/**', '**/dist/**'],\n persistent: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n handlerWatcher.on('add', (file) => {\n const absolutePath = path.join(absoluteHandlersDir, file);\n safeInvoke(onHandlerChange, absolutePath, 'Handler add');\n });\n\n handlerWatcher.on('change', (file) => {\n const absolutePath = path.join(absoluteHandlersDir, file);\n safeInvoke(onHandlerChange, absolutePath, 'Handler change');\n });\n\n handlerWatcher.on('unlink', (file) => {\n const absolutePath = path.join(absoluteHandlersDir, file);\n safeInvoke(onHandlerChange, absolutePath, 'Handler unlink');\n });\n\n handlerWatcher.on('error', (error) => {\n logger.error('[vite-plugin-open-api-server] Handler watcher error:', error);\n });\n\n // Track ready promise for this watcher\n readyPromises.push(\n new Promise<void>((resolve) => {\n handlerWatcher.on('ready', () => resolve());\n }),\n );\n\n watchers.push(handlerWatcher);\n }\n\n // Watch seeds directory\n if (seedsDir && onSeedChange) {\n const absoluteSeedsDir = path.resolve(cwd, seedsDir);\n const seedWatcher = watch(seedPattern, {\n cwd: absoluteSeedsDir,\n ignoreInitial: true,\n ignored: ['**/node_modules/**', '**/dist/**'],\n persistent: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n seedWatcher.on('add', (file) => {\n const absolutePath = path.join(absoluteSeedsDir, file);\n safeInvoke(onSeedChange, absolutePath, 'Seed add');\n });\n\n seedWatcher.on('change', (file) => {\n const absolutePath = path.join(absoluteSeedsDir, file);\n safeInvoke(onSeedChange, absolutePath, 'Seed change');\n });\n\n seedWatcher.on('unlink', (file) => {\n const absolutePath = path.join(absoluteSeedsDir, file);\n safeInvoke(onSeedChange, absolutePath, 'Seed unlink');\n });\n\n seedWatcher.on('error', (error) => {\n logger.error('[vite-plugin-open-api-server] Seed watcher error:', error);\n });\n\n // Track ready promise for this watcher\n readyPromises.push(\n new Promise<void>((resolve) => {\n seedWatcher.on('ready', () => resolve());\n }),\n );\n\n watchers.push(seedWatcher);\n }\n\n // Create combined ready promise\n const readyPromise = Promise.all(readyPromises).then(() => {});\n\n return {\n async close(): Promise<void> {\n isWatching = false;\n await Promise.all(watchers.map((w) => w.close()));\n },\n get isWatching(): boolean {\n return isWatching;\n },\n get ready(): Promise<void> {\n return readyPromise;\n },\n };\n}\n\n/**\n * Debounce a function with async execution guard\n *\n * Useful for preventing multiple rapid reloads when\n * multiple files change at once (e.g., during save all).\n *\n * This implementation prevents overlapping async executions:\n * - If the function is already running, the call is queued\n * - When the running function completes, it executes with the latest args\n * - Multiple calls during execution are coalesced into one\n *\n * @param fn - Function to debounce (can be sync or async)\n * @param delay - Debounce delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n delay: number,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let isRunning = false;\n let pendingArgs: Parameters<T> | null = null;\n\n const execute = async (...args: Parameters<T>): Promise<void> => {\n if (isRunning) {\n // Queue the latest args for execution after current run completes\n pendingArgs = args;\n return;\n }\n\n isRunning = true;\n try {\n // Wrap in try-catch to handle sync throws, then await for async rejections\n // This prevents both sync errors and async rejections from propagating\n try {\n await fn(...args);\n } catch {\n // Silently catch errors - the caller is responsible for error handling\n // This prevents unhandled rejections from breaking the debounce chain\n }\n } finally {\n isRunning = false;\n\n // If there were calls during execution, run with the latest args\n if (pendingArgs !== null) {\n const nextArgs = pendingArgs;\n pendingArgs = null;\n // Use setTimeout to avoid deep recursion\n setTimeout(() => execute(...nextArgs), 0);\n }\n }\n };\n\n return (...args: Parameters<T>): void => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n timeoutId = null;\n execute(...args);\n }, delay);\n };\n}\n","/**\n * Seed Loading\n *\n * What: Loads seed files from a directory using glob patterns\n * How: Uses Vite's ssrLoadModule to transform and load TypeScript files\n * Why: Enables users to define seed data for schemas in TypeScript\n *\n * @module seeds\n */\n\nimport path from 'node:path';\nimport type { AnySeedFn, Logger, SeedDefinition } from '@websublime/vite-plugin-open-api-core';\nimport fg from 'fast-glob';\nimport type { ViteDevServer } from 'vite';\nimport { directoryExists } from './utils.js';\n\n/**\n * Result of loading seeds\n */\nexport interface LoadSeedsResult {\n /** Map of schema name to seed function */\n seeds: Map<string, AnySeedFn>;\n /** Number of seed files loaded */\n fileCount: number;\n /** List of loaded file paths (relative) */\n files: string[];\n}\n\n/**\n * Load seeds from a directory\n *\n * Searches for seed files matching the pattern `*.seeds.{ts,js,mjs}`\n * in the specified directory. Each file should export an object with\n * schema name keys and seed functions as values.\n *\n * Uses Vite's ssrLoadModule to transform TypeScript files on-the-fly.\n *\n * @example\n * ```typescript\n * // mocks/seeds/pets.seeds.ts\n * import { defineSeeds } from '@websublime/vite-plugin-open-api-core';\n *\n * export default defineSeeds({\n * Pet: ({ seed, faker }) => {\n * return seed.count(10, () => ({\n * id: faker.number.int({ min: 1, max: 1000 }),\n * name: faker.animal.dog(),\n * status: faker.helpers.arrayElement(['available', 'pending', 'sold']),\n * }));\n * },\n * });\n * ```\n *\n * @param seedsDir - Directory to search for seed files\n * @param viteServer - Vite dev server instance for ssrLoadModule\n * @param cwd - Current working directory (defaults to process.cwd())\n * @param logger - Optional logger for warnings/errors\n * @returns Promise resolving to loaded seeds\n */\nexport async function loadSeeds(\n seedsDir: string,\n viteServer: ViteDevServer,\n cwd: string = process.cwd(),\n logger: Logger = console,\n): Promise<LoadSeedsResult> {\n const seeds = new Map<string, AnySeedFn>();\n const absoluteDir = path.resolve(cwd, seedsDir);\n\n // Check if directory exists\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return {\n seeds,\n fileCount: 0,\n files: [],\n };\n }\n\n // Find seed files (supports TypeScript via Vite's transform)\n const pattern = '**/*.seeds.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: false,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n // Load each file using Vite's ssrLoadModule\n for (const file of files) {\n const absolutePath = path.join(absoluteDir, file);\n const fileSeeds = await loadSeedFile(absolutePath, viteServer, logger);\n\n // Merge seeds\n for (const [schemaName, seedFn] of Object.entries(fileSeeds)) {\n if (seeds.has(schemaName)) {\n logger.warn(\n `[vite-plugin-open-api-server] Duplicate seed for schema \"${schemaName}\" in ${file}. Using last definition.`,\n );\n }\n seeds.set(schemaName, seedFn);\n }\n }\n\n return {\n seeds,\n fileCount: files.length,\n files,\n };\n}\n\n/**\n * Load a single seed file using Vite's ssrLoadModule\n *\n * @param filePath - Absolute path to the seed file\n * @param viteServer - Vite dev server instance\n * @param logger - Logger for warnings/errors\n * @returns Promise resolving to seed definition object\n */\nasync function loadSeedFile(\n filePath: string,\n viteServer: ViteDevServer,\n logger: Logger,\n): Promise<SeedDefinition> {\n try {\n // Invalidate module cache for hot reload\n const moduleNode = viteServer.moduleGraph.getModuleById(filePath);\n if (moduleNode) {\n viteServer.moduleGraph.invalidateModule(moduleNode);\n }\n\n // Use Vite's ssrLoadModule to transform and load the file\n // This handles TypeScript, ESM, and other transforms automatically\n const module = await viteServer.ssrLoadModule(filePath);\n\n // Support both default export and named export\n const seeds = module.default ?? module.seeds ?? module;\n\n // Validate seeds object\n if (!seeds || typeof seeds !== 'object') {\n logger.warn(\n `[vite-plugin-open-api-server] Invalid seed file ${filePath}: expected object export`,\n );\n return {};\n }\n\n // Filter to only seed functions\n const validSeeds: SeedDefinition = {};\n for (const [key, value] of Object.entries(seeds)) {\n if (typeof value === 'function') {\n validSeeds[key] = value as AnySeedFn;\n }\n }\n\n return validSeeds;\n } catch (error) {\n logger.error(\n `[vite-plugin-open-api-server] Failed to load seed file ${filePath}:`,\n error instanceof Error ? error.message : error,\n );\n return {};\n }\n}\n\n/**\n * Get list of seed files in a directory\n *\n * Useful for file watching setup.\n *\n * @param seedsDir - Directory to search\n * @param cwd - Current working directory\n * @returns Promise resolving to list of absolute file paths\n */\nexport async function getSeedFiles(\n seedsDir: string,\n cwd: string = process.cwd(),\n): Promise<string[]> {\n const absoluteDir = path.resolve(cwd, seedsDir);\n\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return [];\n }\n\n const pattern = '**/*.seeds.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: true,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n return files;\n}\n","/**\n * Vite Plugin Types\n *\n * What: Type definitions for the OpenAPI server Vite plugin\n * How: Defines configuration options and internal types\n * Why: Provides type safety and documentation for plugin consumers\n *\n * @module types\n */\n\nimport type { Logger } from '@websublime/vite-plugin-open-api-core';\n\n/**\n * Plugin configuration options\n *\n * @example\n * ```typescript\n * import { openApiServer } from '@websublime/vite-plugin-open-api-server';\n *\n * export default defineConfig({\n * plugins: [\n * openApiServer({\n * spec: './openapi/petstore.yaml',\n * port: 4000,\n * proxyPath: '/api',\n * handlersDir: './mocks/handlers',\n * seedsDir: './mocks/seeds',\n * }),\n * ],\n * });\n * ```\n */\nexport interface OpenApiServerOptions {\n /**\n * Path to OpenAPI spec file (required)\n *\n * Supports:\n * - Local file paths (relative or absolute)\n * - URLs (http:// or https://)\n * - YAML or JSON format\n *\n * @example './openapi/petstore.yaml'\n * @example 'https://petstore3.swagger.io/api/v3/openapi.json'\n */\n spec: string;\n\n /**\n * Server port for the mock server\n *\n * The mock server runs on this port, and Vite proxies\n * requests from `proxyPath` to this server.\n *\n * @default 4000\n */\n port?: number;\n\n /**\n * Base path for request proxy\n *\n * All requests to this path in your Vite dev server\n * will be proxied to the mock server.\n *\n * @example '/api/v3'\n * @default '/api'\n */\n proxyPath?: string;\n\n /**\n * Directory containing handler files\n *\n * Handler files should export an object with operationId keys\n * and handler functions as values. Use `defineHandlers()` for\n * type-safe handler definitions.\n *\n * @example './mocks/handlers'\n * @default './mocks/handlers'\n */\n handlersDir?: string;\n\n /**\n * Directory containing seed files\n *\n * Seed files should export an object with schema name keys\n * and seed functions as values. Use `defineSeeds()` for\n * type-safe seed definitions.\n *\n * @example './mocks/seeds'\n * @default './mocks/seeds'\n */\n seedsDir?: string;\n\n /**\n * Enable/disable the plugin\n *\n * When false, the plugin does nothing and Vite runs normally.\n * Useful for conditional enabling based on environment.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * ID field configuration per schema\n *\n * Maps schema names to the field used as the primary identifier.\n * Used by the store for CRUD operations.\n *\n * @example { User: 'username', Order: 'orderId' }\n * @default {} (uses 'id' for all schemas)\n */\n idFields?: Record<string, string>;\n\n /**\n * Maximum timeline events to keep\n *\n * The timeline stores request/response history for DevTools.\n * Older events are removed when this limit is exceeded.\n *\n * @default 500\n */\n timelineLimit?: number;\n\n /**\n * Enable DevTools integration\n *\n * When true, mounts the DevTools SPA at `/_devtools`\n * and enables Vue DevTools custom tab.\n *\n * @default true\n */\n devtools?: boolean;\n\n /**\n * Enable CORS on the mock server\n *\n * @default true\n */\n cors?: boolean;\n\n /**\n * CORS origin configuration\n *\n * @default '*'\n */\n corsOrigin?: string | string[];\n\n /**\n * Custom logger instance\n *\n * Defaults to a Vite-integrated logger that respects\n * Vite's logging level settings.\n */\n logger?: Logger;\n\n /**\n * Suppress startup banner\n *\n * When true, the plugin won't print the startup banner\n * showing server details and loaded handlers/seeds.\n *\n * @default false\n */\n silent?: boolean;\n}\n\n/**\n * Resolved plugin options with defaults applied\n *\n * @internal\n */\nexport interface ResolvedOptions {\n spec: string;\n port: number;\n proxyPath: string;\n handlersDir: string;\n seedsDir: string;\n enabled: boolean;\n idFields: Record<string, string>;\n timelineLimit: number;\n devtools: boolean;\n cors: boolean;\n corsOrigin: string | string[];\n silent: boolean;\n logger?: Logger;\n}\n\n/**\n * Resolve options with defaults\n *\n * @param options - User-provided options\n * @returns Resolved options with all defaults applied\n */\nexport function resolveOptions(options: OpenApiServerOptions): ResolvedOptions {\n // Validate required spec option\n if (!options.spec || typeof options.spec !== 'string' || options.spec.trim() === '') {\n throw new Error(\n 'spec is required and must be a non-empty string (path or URL to OpenAPI spec)',\n );\n }\n\n return {\n spec: options.spec,\n port: options.port ?? 4000,\n proxyPath: options.proxyPath ?? '/api',\n handlersDir: options.handlersDir ?? './mocks/handlers',\n seedsDir: options.seedsDir ?? './mocks/seeds',\n enabled: options.enabled ?? true,\n idFields: options.idFields ?? {},\n timelineLimit: options.timelineLimit ?? 500,\n devtools: options.devtools ?? true,\n cors: options.cors ?? true,\n corsOrigin: options.corsOrigin ?? '*',\n silent: options.silent ?? false,\n logger: options.logger,\n };\n}\n","/**\n * Vite Plugin Implementation\n *\n * What: Main Vite plugin for OpenAPI mock server\n * How: Uses configureServer hook to start mock server and configure proxy\n * Why: Integrates OpenAPI mock server seamlessly into Vite dev workflow\n *\n * @module plugin\n */\n\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n createOpenApiServer,\n executeSeeds,\n type OpenApiServer,\n} from '@websublime/vite-plugin-open-api-core';\nimport type { Plugin, ViteDevServer } from 'vite';\nimport { extractBannerInfo, printBanner, printError, printReloadNotification } from './banner.js';\nimport { loadHandlers } from './handlers.js';\nimport { createFileWatcher, debounce, type FileWatcher } from './hot-reload.js';\nimport { loadSeeds } from './seeds.js';\nimport { type OpenApiServerOptions, resolveOptions } from './types.js';\n\n/**\n * Create the OpenAPI Server Vite plugin\n *\n * This plugin starts a mock server based on an OpenAPI specification\n * and configures Vite to proxy API requests to it.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import vue from '@vitejs/plugin-vue';\n * import { openApiServer } from '@websublime/vite-plugin-open-api-server';\n *\n * export default defineConfig({\n * plugins: [\n * vue(),\n * openApiServer({\n * spec: './openapi/petstore.yaml',\n * port: 4000,\n * proxyPath: '/api',\n * handlersDir: './mocks/handlers',\n * seedsDir: './mocks/seeds',\n * }),\n * ],\n * });\n * ```\n *\n * @param options - Plugin configuration options\n * @returns Vite plugin\n */\n/**\n * Virtual module ID for the DevTools tab registration script.\n *\n * This script is served as a Vite module (not inline HTML) so that bare\n * import specifiers like `@vue/devtools-api` are resolved through Vite's\n * module pipeline. Inline `<script type=\"module\">` blocks in HTML are\n * executed directly by the browser, which cannot resolve bare specifiers.\n */\nconst VIRTUAL_DEVTOOLS_TAB_ID = 'virtual:open-api-devtools-tab';\nconst RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID = `\\0${VIRTUAL_DEVTOOLS_TAB_ID}`;\n\nexport function openApiServer(options: OpenApiServerOptions): Plugin {\n const resolvedOptions = resolveOptions(options);\n\n // Server instance (created in configureServer)\n let server: OpenApiServer | null = null;\n\n // Vite dev server reference (needed for ssrLoadModule)\n let vite: ViteDevServer | null = null;\n\n // File watcher for hot reload\n let fileWatcher: FileWatcher | null = null;\n\n // Current working directory (set in configureServer)\n let cwd: string = process.cwd();\n\n return {\n name: 'vite-plugin-open-api-server',\n\n // Only active in dev mode\n apply: 'serve',\n\n /**\n * Ensure @vue/devtools-api is available for the DevTools tab module\n *\n * The virtual module imports from '@vue/devtools-api', which Vite needs\n * to pre-bundle so it can be resolved at runtime in the browser, even\n * though the host app may not have it as a direct dependency.\n */\n config() {\n if (!resolvedOptions.devtools || !resolvedOptions.enabled) {\n return;\n }\n\n // Only add to optimizeDeps if the package is actually resolvable\n // to avoid Vite warnings when the consumer hasn't installed it\n const require = createRequire(import.meta.url);\n try {\n require.resolve('@vue/devtools-api');\n } catch {\n return;\n }\n\n return {\n optimizeDeps: {\n include: ['@vue/devtools-api'],\n },\n };\n },\n\n /**\n * Resolve the virtual module for DevTools tab registration\n */\n resolveId(id: string) {\n if (id === VIRTUAL_DEVTOOLS_TAB_ID) {\n return RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID;\n }\n },\n\n /**\n * Load the virtual module that registers the custom Vue DevTools tab\n *\n * This code is served as a proper Vite module, allowing bare import\n * specifiers to be resolved through Vite's dependency pre-bundling.\n */\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID) {\n return `\nimport { addCustomTab } from '@vue/devtools-api';\n\ntry {\n // Route through Vite's proxy so it works in all environments\n const iframeSrc = window.location.origin + '/_devtools/';\n\n addCustomTab({\n name: 'vite-plugin-open-api-server',\n title: 'OpenAPI Server',\n icon: 'https://api.iconify.design/carbon:api-1.svg?width=24&height=24&color=%2394a3b8',\n view: {\n type: 'iframe',\n src: iframeSrc,\n },\n category: 'app',\n });\n} catch (e) {\n // @vue/devtools-api not available - expected when the package is not installed\n}\n`;\n }\n },\n\n /**\n * Configure the Vite dev server\n *\n * This hook is called when the dev server is created.\n * We use it to:\n * 1. Start the OpenAPI mock server\n * 2. Configure the Vite proxy to forward API requests\n * 3. Set up file watching for hot reload\n */\n async configureServer(viteServer: ViteDevServer): Promise<void> {\n vite = viteServer;\n cwd = viteServer.config.root;\n\n // Check if plugin is disabled\n if (!resolvedOptions.enabled) {\n return;\n }\n\n try {\n // Load handlers from handlers directory (using Vite's ssrLoadModule for TS support)\n const handlersResult = await loadHandlers(resolvedOptions.handlersDir, viteServer, cwd);\n\n // Load seeds from seeds directory (using Vite's ssrLoadModule for TS support)\n const seedsResult = await loadSeeds(resolvedOptions.seedsDir, viteServer, cwd);\n\n // Resolve DevTools SPA directory (shipped inside this package's dist/)\n let devtoolsSpaDir: string | undefined;\n if (resolvedOptions.devtools) {\n const pluginDir = dirname(fileURLToPath(import.meta.url));\n const spaDir = join(pluginDir, 'devtools-spa');\n if (existsSync(spaDir)) {\n devtoolsSpaDir = spaDir;\n } else {\n resolvedOptions.logger?.warn?.(\n '[vite-plugin-open-api-server] DevTools SPA not found at',\n spaDir,\n '- serving placeholder. Run \"pnpm build\" to include the SPA.',\n );\n }\n }\n\n // Create the OpenAPI mock server\n server = await createOpenApiServer({\n spec: resolvedOptions.spec,\n port: resolvedOptions.port,\n idFields: resolvedOptions.idFields,\n handlers: handlersResult.handlers,\n // Seeds are populated via executeSeeds, not directly\n seeds: new Map(),\n timelineLimit: resolvedOptions.timelineLimit,\n devtools: resolvedOptions.devtools,\n devtoolsSpaDir,\n cors: resolvedOptions.cors,\n corsOrigin: resolvedOptions.corsOrigin,\n logger: resolvedOptions.logger,\n });\n\n // Execute seeds to populate the store\n if (seedsResult.seeds.size > 0) {\n await executeSeeds(seedsResult.seeds, server.store, server.document);\n }\n\n // Start the mock server\n await server.start();\n\n // Configure Vite proxy\n configureProxy(viteServer, resolvedOptions.proxyPath, resolvedOptions.port);\n\n // Print startup banner\n const bannerInfo = extractBannerInfo(\n server.registry,\n {\n info: {\n title: server.document.info?.title ?? 'OpenAPI Server',\n version: server.document.info?.version ?? '1.0.0',\n },\n },\n handlersResult.handlers.size,\n seedsResult.seeds.size,\n resolvedOptions,\n );\n printBanner(bannerInfo, resolvedOptions);\n\n // Set up file watching for hot reload\n await setupFileWatching();\n } catch (error) {\n printError('Failed to start OpenAPI mock server', error, resolvedOptions);\n throw error;\n }\n },\n\n /**\n * Clean up when Vite server closes\n */\n async closeBundle(): Promise<void> {\n await cleanup();\n },\n\n /**\n * Inject Vue DevTools custom tab registration script\n *\n * When devtools is enabled, this injects a script tag that loads the\n * virtual module for custom tab registration. Using a virtual module\n * (instead of an inline script) ensures that bare import specifiers\n * like `@vue/devtools-api` are resolved through Vite's module pipeline.\n */\n transformIndexHtml() {\n if (!resolvedOptions.devtools || !resolvedOptions.enabled) {\n return;\n }\n\n return [\n {\n tag: 'script',\n attrs: { type: 'module', src: `/@id/${VIRTUAL_DEVTOOLS_TAB_ID}` },\n injectTo: 'head' as const,\n },\n ];\n },\n };\n\n /**\n * Configure Vite proxy for API requests\n *\n * @param vite - Vite dev server\n * @param proxyPath - Base path to proxy\n * @param port - Mock server port\n */\n function configureProxy(vite: ViteDevServer, proxyPath: string, port: number): void {\n // Ensure server config exists (create mutable copy if needed)\n const serverConfig = vite.config.server ?? {};\n const proxyConfig = serverConfig.proxy ?? {};\n\n // Escape special regex characters in proxy path and pre-compile regex\n const escapedPath = proxyPath.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pathPrefixRegex = new RegExp(`^${escapedPath}`);\n\n // Add proxy configuration for API requests\n proxyConfig[proxyPath] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n // Remove the proxy path prefix when forwarding\n rewrite: (path: string) => path.replace(pathPrefixRegex, ''),\n };\n\n // Proxy internal routes so they work through Vite's dev server port\n proxyConfig['/_devtools'] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n };\n\n proxyConfig['/_api'] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n };\n\n proxyConfig['/_ws'] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n ws: true,\n };\n\n // Update the proxy config (Vite's proxy is mutable)\n if (vite.config.server) {\n vite.config.server.proxy = proxyConfig;\n }\n }\n\n /**\n * Set up file watching for hot reload\n */\n async function setupFileWatching(): Promise<void> {\n if (!server || !vite) return;\n\n // Debounce reload functions to prevent rapid-fire reloads\n const debouncedHandlerReload = debounce(reloadHandlers, 100);\n const debouncedSeedReload = debounce(reloadSeeds, 100);\n\n fileWatcher = await createFileWatcher({\n handlersDir: resolvedOptions.handlersDir,\n seedsDir: resolvedOptions.seedsDir,\n cwd,\n onHandlerChange: debouncedHandlerReload,\n onSeedChange: debouncedSeedReload,\n });\n }\n\n /**\n * Reload handlers when files change\n */\n async function reloadHandlers(): Promise<void> {\n if (!server || !vite) return;\n\n try {\n const handlersResult = await loadHandlers(resolvedOptions.handlersDir, vite, cwd);\n server.updateHandlers(handlersResult.handlers);\n\n // Notify via WebSocket\n server.wsHub.broadcast({\n type: 'handlers:updated',\n data: { count: handlersResult.handlers.size },\n });\n\n printReloadNotification('handlers', handlersResult.handlers.size, resolvedOptions);\n } catch (error) {\n printError('Failed to reload handlers', error, resolvedOptions);\n }\n }\n\n /**\n * Reload seeds when files change\n *\n * Note: This operation is not fully atomic - there's a brief window between\n * clearing the store and repopulating it where requests may see empty data.\n * For development tooling, this tradeoff is acceptable.\n */\n async function reloadSeeds(): Promise<void> {\n if (!server || !vite) return;\n\n try {\n // Load seeds first (before clearing) to minimize the window where store is empty\n const seedsResult = await loadSeeds(resolvedOptions.seedsDir, vite, cwd);\n\n // Only clear and repopulate if we successfully loaded seeds\n // This prevents clearing the store on load errors\n if (seedsResult.seeds.size > 0) {\n // Clear and immediately repopulate - minimizes empty window\n server.store.clearAll();\n await executeSeeds(seedsResult.seeds, server.store, server.document);\n } else {\n // User removed all seed files - clear the store\n server.store.clearAll();\n }\n\n // Notify via WebSocket\n server.wsHub.broadcast({\n type: 'seeds:updated',\n data: { count: seedsResult.seeds.size },\n });\n\n printReloadNotification('seeds', seedsResult.seeds.size, resolvedOptions);\n } catch (error) {\n printError('Failed to reload seeds', error, resolvedOptions);\n }\n }\n\n /**\n * Clean up resources\n */\n async function cleanup(): Promise<void> {\n // Close file watcher\n if (fileWatcher) {\n await fileWatcher.close();\n fileWatcher = null;\n }\n\n // Stop mock server\n if (server) {\n await server.stop();\n server = null;\n }\n\n vite = null;\n }\n}\n","/**\n * Vue DevTools Integration\n *\n * What: Provides a function to register OpenAPI Server in Vue DevTools\n * How: Uses @vue/devtools-api to add a custom tab with iframe to DevTools SPA\n * Why: Enables debugging and inspection of the mock API server within Vue DevTools\n *\n * @module devtools\n */\n\nimport type { App } from 'vue';\n\n/**\n * Options for registering the DevTools plugin\n */\nexport interface RegisterDevToolsOptions {\n /**\n * The port where the OpenAPI server is running\n * @default 3000\n */\n port?: number;\n\n /**\n * The host where the OpenAPI server is running\n * @default 'localhost' (or derived from window.location.hostname if in browser)\n */\n host?: string;\n\n /**\n * The protocol to use for the DevTools URL\n * @default 'http' (or derived from window.location.protocol if in browser)\n */\n protocol?: 'http' | 'https';\n\n /**\n * Enable or disable DevTools registration\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Custom label for the DevTools tab\n * @default 'OpenAPI Server'\n */\n label?: string;\n}\n\n/**\n * Register OpenAPI Server in Vue DevTools\n *\n * This function should be called in your Vue application's main entry point\n * to add a custom tab to Vue DevTools. The tab contains an iframe that loads\n * the OpenAPI Server DevTools SPA.\n *\n * @example\n * ```typescript\n * // In your main.ts or main.js\n * import { createApp } from 'vue';\n * import { registerDevTools } from '@websublime/vite-plugin-open-api-server';\n * import App from './App.vue';\n *\n * const app = createApp(App);\n *\n * // Register OpenAPI Server DevTools\n * if (import.meta.env.DEV) {\n * await registerDevTools(app, { port: 3000 });\n * }\n *\n * app.mount('#app');\n * ```\n *\n * @param app - Vue application instance\n * @param options - Configuration options\n */\nexport async function registerDevTools(\n app: App,\n options: RegisterDevToolsOptions = {},\n): Promise<void> {\n const { enabled = true, label = 'OpenAPI Server', port, host, protocol } = options;\n\n // Validate app parameter\n if (!app || typeof app !== 'object') {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for invalid app instance\n console.warn('[OpenAPI DevTools] Invalid Vue app instance provided');\n return;\n }\n\n // Only register if enabled\n if (!enabled) {\n return;\n }\n\n // Check if running in browser environment\n if (typeof window === 'undefined') {\n return;\n }\n\n // Lazy import to avoid SSR issues\n try {\n const { addCustomTab } = await import('@vue/devtools-api');\n\n // Get the DevTools URL\n const devtoolsUrl = getDevToolsUrl(port, host, protocol);\n\n // Add custom tab with iframe to Vue DevTools\n addCustomTab({\n name: 'vite-plugin-open-api-server',\n title: label,\n icon: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%234f46e5%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z%22/%3E%3Cpolyline points=%223.29 7 12 12 20.71 7%22/%3E%3Cline x1=%2212%22 y1=%2222%22 x2=%2212%22 y2=%2212%22/%3E%3C/svg%3E',\n view: {\n type: 'iframe',\n src: devtoolsUrl,\n },\n category: 'app',\n });\n } catch (error) {\n // Log warning but don't crash the app\n // DevTools integration is optional, so the app should continue working\n // biome-ignore lint/suspicious/noConsole: Intentional warning for registration failure\n console.warn('[OpenAPI DevTools] Failed to register with Vue DevTools:', error);\n }\n}\n\n/**\n * Get the DevTools URL for the given configuration\n *\n * When running in a browser, protocol and host are automatically derived from\n * window.location if not explicitly provided.\n *\n * @param port - Server port (default: 3000)\n * @param host - Server host (default: 'localhost' or window.location.hostname)\n * @param protocol - Protocol to use (default: 'http' or window.location.protocol)\n * @returns DevTools SPA URL\n */\nexport function getDevToolsUrl(port = 3000, host?: string, protocol?: 'http' | 'https'): string {\n // Derive defaults from browser environment if available\n const actualProtocol =\n protocol ||\n (typeof window !== 'undefined'\n ? (window.location.protocol.replace(':', '') as 'http' | 'https')\n : 'http');\n const actualHost =\n host || (typeof window !== 'undefined' ? window.location.hostname : 'localhost');\n\n return `${actualProtocol}://${actualHost}:${port}/_devtools/`;\n}\n"]}
1
+ {"version":3,"sources":["../src/banner.ts","../src/utils.ts","../src/handlers.ts","../src/hot-reload.ts","../src/seeds.ts","../src/types.ts","../src/plugin.ts","../src/devtools.ts"],"names":["path","fg","require","vite"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Startup Banner\n *\n * What: Prints a colorful startup banner with server information\n * How: Uses picocolors for ANSI color output\n * Why: Provides clear feedback about server status and configuration\n *\n * @module banner\n */\n\nimport type { EndpointRegistry } from '@websublime/vite-plugin-open-api-core';\nimport pc from 'picocolors';\nimport type { ResolvedOptions } from './types.js';\n\n/**\n * Banner configuration\n */\nexport interface BannerInfo {\n /** Server port */\n port: number;\n /** Proxy path in Vite */\n proxyPath: string;\n /** OpenAPI spec title */\n title: string;\n /** OpenAPI spec version */\n version: string;\n /** Number of endpoints */\n endpointCount: number;\n /** Number of loaded handlers */\n handlerCount: number;\n /** Number of loaded seeds */\n seedCount: number;\n /** DevTools enabled */\n devtools: boolean;\n}\n\n/**\n * Print the startup banner\n *\n * @param info - Banner information\n * @param options - Resolved plugin options\n */\nexport function printBanner(info: BannerInfo, options: ResolvedOptions): void {\n if (options.silent) {\n return;\n }\n\n const logger = options.logger ?? console;\n const log = (msg: string) => logger.info(msg);\n\n // Box drawing characters\n const BOX = {\n topLeft: '╭',\n topRight: '╮',\n bottomLeft: '╰',\n bottomRight: '╯',\n horizontal: '─',\n vertical: '│',\n };\n\n const width = 56;\n const horizontalLine = BOX.horizontal.repeat(width - 2);\n\n log('');\n log(pc.cyan(`${BOX.topLeft}${horizontalLine}${BOX.topRight}`));\n log(\n pc.cyan(BOX.vertical) + centerText('🚀 OpenAPI Mock Server', width - 2) + pc.cyan(BOX.vertical),\n );\n log(pc.cyan(`${BOX.bottomLeft}${horizontalLine}${BOX.bottomRight}`));\n log('');\n\n // API Info\n log(\n ` ${pc.bold(pc.white('API:'))} ${pc.green(info.title)} ${pc.dim(`v${info.version}`)}`,\n );\n log(` ${pc.bold(pc.white('Server:'))} ${pc.cyan(`http://localhost:${info.port}`)}`);\n log(\n ` ${pc.bold(pc.white('Proxy:'))} ${pc.yellow(info.proxyPath)} ${pc.dim('→')} ${pc.dim(`localhost:${info.port}`)}`,\n );\n log('');\n\n // Stats\n const stats = [\n { label: 'Endpoints', value: info.endpointCount, color: pc.blue },\n { label: 'Handlers', value: info.handlerCount, color: pc.green },\n { label: 'Seeds', value: info.seedCount, color: pc.magenta },\n ];\n\n const statsLine = stats\n .map((s) => `${pc.dim(`${s.label}:`)} ${s.color(String(s.value))}`)\n .join(pc.dim(' │ '));\n log(` ${statsLine}`);\n log('');\n\n // DevTools\n if (info.devtools) {\n log(\n ` ${pc.bold(pc.white('DevTools:'))} ${pc.cyan(`http://localhost:${info.port}/_devtools`)}`,\n );\n log(` ${pc.bold(pc.white('API Info:'))} ${pc.cyan(`http://localhost:${info.port}/_api`)}`);\n log('');\n }\n\n // Footer\n log(pc.dim(' Press Ctrl+C to stop the server'));\n log('');\n}\n\n/**\n * Center text within a given width\n *\n * @param text - Text to center\n * @param width - Total width\n * @returns Centered text with padding\n */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes require control characters\nconst ANSI_ESCAPE_REGEX = /\\x1b\\[[0-9;]*m/g;\n\nfunction centerText(text: string, width: number): string {\n // Account for ANSI codes - get visible length\n const visibleLength = text.replace(ANSI_ESCAPE_REGEX, '').length;\n const padding = Math.max(0, width - visibleLength);\n const leftPad = Math.floor(padding / 2);\n const rightPad = padding - leftPad;\n return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);\n}\n\n/**\n * Extract banner info from server registry and document\n *\n * @param registry - Endpoint registry\n * @param document - OpenAPI document\n * @param handlerCount - Number of loaded handlers\n * @param seedCount - Number of loaded seed schemas\n * @param options - Resolved options\n * @returns Banner info\n */\nexport function extractBannerInfo(\n registry: EndpointRegistry,\n document: { info: { title: string; version: string } },\n handlerCount: number,\n seedCount: number,\n options: ResolvedOptions,\n): BannerInfo {\n return {\n port: options.port,\n proxyPath: options.proxyPath,\n title: document.info.title,\n version: document.info.version,\n endpointCount: registry.endpoints.size,\n handlerCount,\n seedCount,\n devtools: options.devtools,\n };\n}\n\n/**\n * Print a hot reload notification\n *\n * @param type - Type of reload ('handlers' | 'seeds')\n * @param count - Number of reloaded items\n * @param options - Resolved options\n */\nexport function printReloadNotification(\n type: 'handlers' | 'seeds',\n count: number,\n options: ResolvedOptions,\n): void {\n if (options.silent) {\n return;\n }\n\n const logger = options.logger ?? console;\n const icon = type === 'handlers' ? '🔄' : '🌱';\n const label = type === 'handlers' ? 'Handlers' : 'Seeds';\n const color = type === 'handlers' ? pc.green : pc.magenta;\n\n logger.info(` ${icon} ${color(label)} reloaded: ${pc.bold(String(count))} ${type}`);\n}\n\n/**\n * Print an error message\n *\n * @param message - Error message\n * @param error - Optional error object\n * @param options - Resolved options\n */\nexport function printError(message: string, error: unknown, options: ResolvedOptions): void {\n const logger = options.logger ?? console;\n logger.error(`${pc.red('✖')} ${pc.bold(pc.red('Error:'))} ${message}`);\n if (error instanceof Error) {\n logger.error(pc.dim(` ${error.message}`));\n }\n}\n\n/**\n * Print a warning message\n *\n * @param message - Warning message\n * @param options - Resolved options\n */\nexport function printWarning(message: string, options: ResolvedOptions): void {\n if (options.silent) {\n return;\n }\n\n const logger = options.logger ?? console;\n logger.warn(`${pc.yellow('⚠')} ${pc.yellow('Warning:')} ${message}`);\n}\n","/**\n * Filesystem Utilities\n *\n * What: Shared utility functions for filesystem operations\n * How: Uses Node.js fs/promises module with error handling\n * Why: Provides reusable utilities for handler and seed loading\n *\n * @module utils\n */\n\n/**\n * Check if a directory exists\n *\n * @param dirPath - Path to check\n * @returns Promise resolving to true if directory exists\n */\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const fs = await import('node:fs/promises');\n const stats = await fs.stat(dirPath);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n","/**\n * Handler Loading\n *\n * What: Loads handler files from a directory using glob patterns\n * How: Uses Vite's ssrLoadModule to transform and load TypeScript files\n * Why: Enables users to define custom handlers for operationIds in TypeScript\n *\n * @module handlers\n */\n\nimport path from 'node:path';\nimport type { HandlerDefinition, HandlerFn, Logger } from '@websublime/vite-plugin-open-api-core';\nimport fg from 'fast-glob';\nimport type { ViteDevServer } from 'vite';\nimport { directoryExists } from './utils.js';\n\n/**\n * Result of loading handlers\n */\nexport interface LoadHandlersResult {\n /** Map of operationId to handler function */\n handlers: Map<string, HandlerFn>;\n /** Number of handler files loaded */\n fileCount: number;\n /** List of loaded file paths (relative) */\n files: string[];\n}\n\n/**\n * Load handlers from a directory\n *\n * Searches for handler files matching the pattern `*.handlers.{ts,js,mjs}`\n * in the specified directory. Each file should export an object with\n * operationId keys and handler functions as values.\n *\n * Uses Vite's ssrLoadModule to transform TypeScript files on-the-fly.\n *\n * @example\n * ```typescript\n * // mocks/handlers/pets.handlers.ts\n * import { defineHandlers } from '@websublime/vite-plugin-open-api-core';\n *\n * export default defineHandlers({\n * getPetById: async ({ req, store }) => {\n * const pet = store.get('Pet', req.params.petId);\n * return pet ?? { status: 404, data: { message: 'Pet not found' } };\n * },\n * });\n * ```\n *\n * @param handlersDir - Directory to search for handler files\n * @param viteServer - Vite dev server instance for ssrLoadModule\n * @param cwd - Current working directory (defaults to process.cwd())\n * @param logger - Optional logger for warnings/errors\n * @returns Promise resolving to loaded handlers\n */\nexport async function loadHandlers(\n handlersDir: string,\n viteServer: ViteDevServer,\n cwd: string = process.cwd(),\n logger: Logger = console,\n): Promise<LoadHandlersResult> {\n const handlers = new Map<string, HandlerFn>();\n const absoluteDir = path.resolve(cwd, handlersDir);\n\n // Check if directory exists\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return {\n handlers,\n fileCount: 0,\n files: [],\n };\n }\n\n // Find handler files (supports TypeScript via Vite's transform)\n const pattern = '**/*.handlers.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: false,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n // Load each file using Vite's ssrLoadModule\n for (const file of files) {\n const absolutePath = path.join(absoluteDir, file);\n const fileHandlers = await loadHandlerFile(absolutePath, viteServer, logger);\n\n // Merge handlers\n for (const [operationId, handler] of Object.entries(fileHandlers)) {\n if (handlers.has(operationId)) {\n logger.warn(\n `[vite-plugin-open-api-server] Duplicate handler for operationId \"${operationId}\" in ${file}. Using last definition.`,\n );\n }\n handlers.set(operationId, handler);\n }\n }\n\n return {\n handlers,\n fileCount: files.length,\n files,\n };\n}\n\n/**\n * Load a single handler file using Vite's ssrLoadModule\n *\n * @param filePath - Absolute path to the handler file\n * @param viteServer - Vite dev server instance\n * @param logger - Logger for warnings/errors\n * @returns Promise resolving to handler definition object\n */\nasync function loadHandlerFile(\n filePath: string,\n viteServer: ViteDevServer,\n logger: Logger,\n): Promise<HandlerDefinition> {\n try {\n // Invalidate module cache for hot reload\n const moduleNode = viteServer.moduleGraph.getModuleById(filePath);\n if (moduleNode) {\n viteServer.moduleGraph.invalidateModule(moduleNode);\n }\n\n // Use Vite's ssrLoadModule to transform and load the file\n // This handles TypeScript, ESM, and other transforms automatically\n const module = await viteServer.ssrLoadModule(filePath);\n\n // Support both default export and named export\n const handlers = module.default ?? module.handlers ?? module;\n\n // Validate handlers object\n if (!handlers || typeof handlers !== 'object') {\n logger.warn(\n `[vite-plugin-open-api-server] Invalid handler file ${filePath}: expected object export`,\n );\n return {};\n }\n\n // Filter to only handler functions\n const validHandlers: HandlerDefinition = {};\n for (const [key, value] of Object.entries(handlers)) {\n if (typeof value === 'function') {\n validHandlers[key] = value as HandlerFn;\n }\n }\n\n return validHandlers;\n } catch (error) {\n logger.error(\n `[vite-plugin-open-api-server] Failed to load handler file ${filePath}:`,\n error instanceof Error ? error.message : error,\n );\n return {};\n }\n}\n\n/**\n * Get list of handler files in a directory\n *\n * Useful for file watching setup.\n *\n * @param handlersDir - Directory to search\n * @param cwd - Current working directory\n * @returns Promise resolving to list of absolute file paths\n */\nexport async function getHandlerFiles(\n handlersDir: string,\n cwd: string = process.cwd(),\n): Promise<string[]> {\n const absoluteDir = path.resolve(cwd, handlersDir);\n\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return [];\n }\n\n const pattern = '**/*.handlers.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: true,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n return files;\n}\n","/**\n * Hot Reload\n *\n * What: File watcher for hot reloading handlers and seeds\n * How: Uses chokidar to watch for file changes\n * Why: Enables rapid development iteration without server restart\n *\n * @module hot-reload\n *\n * TODO: Full implementation in Task 3.3\n * This module provides placeholder/basic functionality for Task 3.1\n */\n\nimport path from 'node:path';\nimport type { Logger } from '@websublime/vite-plugin-open-api-core';\nimport type { FSWatcher } from 'chokidar';\n\n/**\n * File watcher configuration\n */\nexport interface FileWatcherOptions {\n /** Directory containing handler files */\n handlersDir?: string;\n /** Directory containing seed files */\n seedsDir?: string;\n /** Callback when a handler file changes */\n onHandlerChange?: (filePath: string) => Promise<void> | void;\n /** Callback when a seed file changes */\n onSeedChange?: (filePath: string) => Promise<void> | void;\n /** Current working directory */\n cwd?: string;\n /** Logger for error messages */\n logger?: Logger;\n}\n\n/**\n * File watcher instance\n */\nexport interface FileWatcher {\n /** Close the watcher and release resources */\n close(): Promise<void>;\n /** Check if watcher is active */\n readonly isWatching: boolean;\n /** Promise that resolves when all watchers are ready */\n readonly ready: Promise<void>;\n}\n\n/**\n * Create a file watcher for handlers and seeds\n *\n * Watches for changes to handler and seed files and invokes\n * callbacks when changes are detected. Supports add, change,\n * and unlink events.\n *\n * @example\n * ```typescript\n * const watcher = await createFileWatcher({\n * handlersDir: './mocks/handlers',\n * seedsDir: './mocks/seeds',\n * onHandlerChange: async (file) => {\n * console.log('Handler changed:', file);\n * const handlers = await loadHandlers('./mocks/handlers');\n * server.updateHandlers(handlers.handlers);\n * },\n * onSeedChange: async (file) => {\n * console.log('Seed changed:', file);\n * const seeds = await loadSeeds('./mocks/seeds');\n * // Re-execute seeds...\n * },\n * });\n *\n * // Later, clean up\n * await watcher.close();\n * ```\n *\n * @param options - Watcher configuration\n * @returns Promise resolving to file watcher instance\n */\nexport async function createFileWatcher(options: FileWatcherOptions): Promise<FileWatcher> {\n const {\n handlersDir,\n seedsDir,\n onHandlerChange,\n onSeedChange,\n cwd = process.cwd(),\n logger = console,\n } = options;\n\n // Dynamic import chokidar to avoid bundling issues\n const { watch } = await import('chokidar');\n\n const watchers: FSWatcher[] = [];\n const readyPromises: Promise<void>[] = [];\n let isWatching = true;\n\n // Handler file patterns\n const handlerPattern = '**/*.handlers.{ts,js,mjs}';\n const seedPattern = '**/*.seeds.{ts,js,mjs}';\n\n /**\n * Wrapper to safely invoke async callbacks and log errors\n * Prevents unhandled promise rejections from file watcher events\n */\n const safeInvoke = (\n callback: (filePath: string) => Promise<void> | void,\n filePath: string,\n context: string,\n ): void => {\n Promise.resolve()\n .then(() => callback(filePath))\n .catch((error) => {\n logger.error(\n `[vite-plugin-open-api-server] ${context} callback error for ${filePath}:`,\n error,\n );\n });\n };\n\n // Watch handlers directory\n if (handlersDir && onHandlerChange) {\n const absoluteHandlersDir = path.resolve(cwd, handlersDir);\n const handlerWatcher = watch(handlerPattern, {\n cwd: absoluteHandlersDir,\n ignoreInitial: true,\n ignored: ['**/node_modules/**', '**/dist/**'],\n persistent: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n handlerWatcher.on('add', (file) => {\n const absolutePath = path.join(absoluteHandlersDir, file);\n safeInvoke(onHandlerChange, absolutePath, 'Handler add');\n });\n\n handlerWatcher.on('change', (file) => {\n const absolutePath = path.join(absoluteHandlersDir, file);\n safeInvoke(onHandlerChange, absolutePath, 'Handler change');\n });\n\n handlerWatcher.on('unlink', (file) => {\n const absolutePath = path.join(absoluteHandlersDir, file);\n safeInvoke(onHandlerChange, absolutePath, 'Handler unlink');\n });\n\n handlerWatcher.on('error', (error) => {\n logger.error('[vite-plugin-open-api-server] Handler watcher error:', error);\n });\n\n // Track ready promise for this watcher\n readyPromises.push(\n new Promise<void>((resolve) => {\n handlerWatcher.on('ready', () => resolve());\n }),\n );\n\n watchers.push(handlerWatcher);\n }\n\n // Watch seeds directory\n if (seedsDir && onSeedChange) {\n const absoluteSeedsDir = path.resolve(cwd, seedsDir);\n const seedWatcher = watch(seedPattern, {\n cwd: absoluteSeedsDir,\n ignoreInitial: true,\n ignored: ['**/node_modules/**', '**/dist/**'],\n persistent: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n seedWatcher.on('add', (file) => {\n const absolutePath = path.join(absoluteSeedsDir, file);\n safeInvoke(onSeedChange, absolutePath, 'Seed add');\n });\n\n seedWatcher.on('change', (file) => {\n const absolutePath = path.join(absoluteSeedsDir, file);\n safeInvoke(onSeedChange, absolutePath, 'Seed change');\n });\n\n seedWatcher.on('unlink', (file) => {\n const absolutePath = path.join(absoluteSeedsDir, file);\n safeInvoke(onSeedChange, absolutePath, 'Seed unlink');\n });\n\n seedWatcher.on('error', (error) => {\n logger.error('[vite-plugin-open-api-server] Seed watcher error:', error);\n });\n\n // Track ready promise for this watcher\n readyPromises.push(\n new Promise<void>((resolve) => {\n seedWatcher.on('ready', () => resolve());\n }),\n );\n\n watchers.push(seedWatcher);\n }\n\n // Create combined ready promise\n const readyPromise = Promise.all(readyPromises).then(() => {});\n\n return {\n async close(): Promise<void> {\n isWatching = false;\n await Promise.all(watchers.map((w) => w.close()));\n },\n get isWatching(): boolean {\n return isWatching;\n },\n get ready(): Promise<void> {\n return readyPromise;\n },\n };\n}\n\n/**\n * Debounce a function with async execution guard\n *\n * Useful for preventing multiple rapid reloads when\n * multiple files change at once (e.g., during save all).\n *\n * This implementation prevents overlapping async executions:\n * - If the function is already running, the call is queued\n * - When the running function completes, it executes with the latest args\n * - Multiple calls during execution are coalesced into one\n *\n * @param fn - Function to debounce (can be sync or async)\n * @param delay - Debounce delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n delay: number,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let isRunning = false;\n let pendingArgs: Parameters<T> | null = null;\n\n const execute = async (...args: Parameters<T>): Promise<void> => {\n if (isRunning) {\n // Queue the latest args for execution after current run completes\n pendingArgs = args;\n return;\n }\n\n isRunning = true;\n try {\n // Wrap in try-catch to handle sync throws, then await for async rejections\n // This prevents both sync errors and async rejections from propagating\n try {\n await fn(...args);\n } catch {\n // Silently catch errors - the caller is responsible for error handling\n // This prevents unhandled rejections from breaking the debounce chain\n }\n } finally {\n isRunning = false;\n\n // If there were calls during execution, run with the latest args\n if (pendingArgs !== null) {\n const nextArgs = pendingArgs;\n pendingArgs = null;\n // Use setTimeout to avoid deep recursion\n setTimeout(() => execute(...nextArgs), 0);\n }\n }\n };\n\n return (...args: Parameters<T>): void => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n timeoutId = null;\n execute(...args);\n }, delay);\n };\n}\n","/**\n * Seed Loading\n *\n * What: Loads seed files from a directory using glob patterns\n * How: Uses Vite's ssrLoadModule to transform and load TypeScript files\n * Why: Enables users to define seed data for schemas in TypeScript\n *\n * @module seeds\n */\n\nimport path from 'node:path';\nimport type { AnySeedFn, Logger, SeedDefinition } from '@websublime/vite-plugin-open-api-core';\nimport fg from 'fast-glob';\nimport type { ViteDevServer } from 'vite';\nimport { directoryExists } from './utils.js';\n\n/**\n * Result of loading seeds\n */\nexport interface LoadSeedsResult {\n /** Map of schema name to seed function */\n seeds: Map<string, AnySeedFn>;\n /** Number of seed files loaded */\n fileCount: number;\n /** List of loaded file paths (relative) */\n files: string[];\n}\n\n/**\n * Load seeds from a directory\n *\n * Searches for seed files matching the pattern `*.seeds.{ts,js,mjs}`\n * in the specified directory. Each file should export an object with\n * schema name keys and seed functions as values.\n *\n * Uses Vite's ssrLoadModule to transform TypeScript files on-the-fly.\n *\n * @example\n * ```typescript\n * // mocks/seeds/pets.seeds.ts\n * import { defineSeeds } from '@websublime/vite-plugin-open-api-core';\n *\n * export default defineSeeds({\n * Pet: ({ seed, faker }) => {\n * return seed.count(10, () => ({\n * id: faker.number.int({ min: 1, max: 1000 }),\n * name: faker.animal.dog(),\n * status: faker.helpers.arrayElement(['available', 'pending', 'sold']),\n * }));\n * },\n * });\n * ```\n *\n * @param seedsDir - Directory to search for seed files\n * @param viteServer - Vite dev server instance for ssrLoadModule\n * @param cwd - Current working directory (defaults to process.cwd())\n * @param logger - Optional logger for warnings/errors\n * @returns Promise resolving to loaded seeds\n */\nexport async function loadSeeds(\n seedsDir: string,\n viteServer: ViteDevServer,\n cwd: string = process.cwd(),\n logger: Logger = console,\n): Promise<LoadSeedsResult> {\n const seeds = new Map<string, AnySeedFn>();\n const absoluteDir = path.resolve(cwd, seedsDir);\n\n // Check if directory exists\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return {\n seeds,\n fileCount: 0,\n files: [],\n };\n }\n\n // Find seed files (supports TypeScript via Vite's transform)\n const pattern = '**/*.seeds.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: false,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n // Load each file using Vite's ssrLoadModule\n for (const file of files) {\n const absolutePath = path.join(absoluteDir, file);\n const fileSeeds = await loadSeedFile(absolutePath, viteServer, logger);\n\n // Merge seeds\n for (const [schemaName, seedFn] of Object.entries(fileSeeds)) {\n if (seeds.has(schemaName)) {\n logger.warn(\n `[vite-plugin-open-api-server] Duplicate seed for schema \"${schemaName}\" in ${file}. Using last definition.`,\n );\n }\n seeds.set(schemaName, seedFn);\n }\n }\n\n return {\n seeds,\n fileCount: files.length,\n files,\n };\n}\n\n/**\n * Load a single seed file using Vite's ssrLoadModule\n *\n * @param filePath - Absolute path to the seed file\n * @param viteServer - Vite dev server instance\n * @param logger - Logger for warnings/errors\n * @returns Promise resolving to seed definition object\n */\nasync function loadSeedFile(\n filePath: string,\n viteServer: ViteDevServer,\n logger: Logger,\n): Promise<SeedDefinition> {\n try {\n // Invalidate module cache for hot reload\n const moduleNode = viteServer.moduleGraph.getModuleById(filePath);\n if (moduleNode) {\n viteServer.moduleGraph.invalidateModule(moduleNode);\n }\n\n // Use Vite's ssrLoadModule to transform and load the file\n // This handles TypeScript, ESM, and other transforms automatically\n const module = await viteServer.ssrLoadModule(filePath);\n\n // Support both default export and named export\n const seeds = module.default ?? module.seeds ?? module;\n\n // Validate seeds object\n if (!seeds || typeof seeds !== 'object') {\n logger.warn(\n `[vite-plugin-open-api-server] Invalid seed file ${filePath}: expected object export`,\n );\n return {};\n }\n\n // Filter to only seed functions\n const validSeeds: SeedDefinition = {};\n for (const [key, value] of Object.entries(seeds)) {\n if (typeof value === 'function') {\n validSeeds[key] = value as AnySeedFn;\n }\n }\n\n return validSeeds;\n } catch (error) {\n logger.error(\n `[vite-plugin-open-api-server] Failed to load seed file ${filePath}:`,\n error instanceof Error ? error.message : error,\n );\n return {};\n }\n}\n\n/**\n * Get list of seed files in a directory\n *\n * Useful for file watching setup.\n *\n * @param seedsDir - Directory to search\n * @param cwd - Current working directory\n * @returns Promise resolving to list of absolute file paths\n */\nexport async function getSeedFiles(\n seedsDir: string,\n cwd: string = process.cwd(),\n): Promise<string[]> {\n const absoluteDir = path.resolve(cwd, seedsDir);\n\n const dirExists = await directoryExists(absoluteDir);\n if (!dirExists) {\n return [];\n }\n\n const pattern = '**/*.seeds.{ts,js,mjs}';\n const files = await fg(pattern, {\n cwd: absoluteDir,\n absolute: true,\n onlyFiles: true,\n ignore: ['node_modules/**', 'dist/**'],\n });\n\n return files;\n}\n","/**\n * Vite Plugin Types\n *\n * What: Type definitions for the OpenAPI server Vite plugin\n * How: Defines configuration options and internal types\n * Why: Provides type safety and documentation for plugin consumers\n *\n * @module types\n */\n\nimport type { Logger } from '@websublime/vite-plugin-open-api-core';\n\n/**\n * Plugin configuration options\n *\n * @example\n * ```typescript\n * import { openApiServer } from '@websublime/vite-plugin-open-api-server';\n *\n * export default defineConfig({\n * plugins: [\n * openApiServer({\n * spec: './openapi/petstore.yaml',\n * port: 4000,\n * proxyPath: '/api',\n * handlersDir: './mocks/handlers', // optional\n * seedsDir: './mocks/seeds', // optional\n * }),\n * ],\n * });\n * ```\n */\nexport interface OpenApiServerOptions {\n /**\n * Path to OpenAPI spec file (required)\n *\n * Supports:\n * - Local file paths (relative or absolute)\n * - URLs (http:// or https://)\n * - YAML or JSON format\n *\n * @example './openapi/petstore.yaml'\n * @example 'https://petstore3.swagger.io/api/v3/openapi.json'\n */\n spec: string;\n\n /**\n * Server port for the mock server\n *\n * The mock server runs on this port, and Vite proxies\n * requests from `proxyPath` to this server.\n *\n * @default 4000\n */\n port?: number;\n\n /**\n * Base path for request proxy\n *\n * All requests to this path in your Vite dev server\n * will be proxied to the mock server.\n *\n * @example '/api/v3'\n * @default '/api'\n */\n proxyPath?: string;\n\n /**\n * Directory containing handler files\n *\n * Handler files should export an object with operationId keys\n * and handler functions as values. Use `defineHandlers()` for\n * type-safe handler definitions.\n *\n * When not provided, no handler crawling is performed.\n *\n * @example './mocks/handlers'\n */\n handlersDir?: string;\n\n /**\n * Directory containing seed files\n *\n * Seed files should export an object with schema name keys\n * and seed functions as values. Use `defineSeeds()` for\n * type-safe seed definitions.\n *\n * When not provided, no seed crawling is performed.\n *\n * @example './mocks/seeds'\n */\n seedsDir?: string;\n\n /**\n * Enable/disable the plugin\n *\n * When false, the plugin does nothing and Vite runs normally.\n * Useful for conditional enabling based on environment.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * ID field configuration per schema\n *\n * Maps schema names to the field used as the primary identifier.\n * Used by the store for CRUD operations.\n *\n * @example { User: 'username', Order: 'orderId' }\n * @default {} (uses 'id' for all schemas)\n */\n idFields?: Record<string, string>;\n\n /**\n * Maximum timeline events to keep\n *\n * The timeline stores request/response history for DevTools.\n * Older events are removed when this limit is exceeded.\n *\n * @default 500\n */\n timelineLimit?: number;\n\n /**\n * Enable DevTools integration\n *\n * When true, mounts the DevTools SPA at `/_devtools`\n * and enables Vue DevTools custom tab.\n *\n * @default true\n */\n devtools?: boolean;\n\n /**\n * Enable CORS on the mock server\n *\n * @default true\n */\n cors?: boolean;\n\n /**\n * CORS origin configuration\n *\n * @default '*'\n */\n corsOrigin?: string | string[];\n\n /**\n * Custom logger instance\n *\n * Defaults to a Vite-integrated logger that respects\n * Vite's logging level settings.\n */\n logger?: Logger;\n\n /**\n * Suppress startup banner\n *\n * When true, the plugin won't print the startup banner\n * showing server details and loaded handlers/seeds.\n *\n * @default false\n */\n silent?: boolean;\n}\n\n/**\n * Resolved plugin options with defaults applied\n *\n * @internal\n */\nexport interface ResolvedOptions {\n spec: string;\n port: number;\n proxyPath: string;\n handlersDir: string | null;\n seedsDir: string | null;\n enabled: boolean;\n idFields: Record<string, string>;\n timelineLimit: number;\n devtools: boolean;\n cors: boolean;\n corsOrigin: string | string[];\n silent: boolean;\n logger?: Logger;\n}\n\n/**\n * Resolve options with defaults\n *\n * @param options - User-provided options\n * @returns Resolved options with all defaults applied\n */\nexport function resolveOptions(options: OpenApiServerOptions): ResolvedOptions {\n // Validate required spec option\n if (!options.spec || typeof options.spec !== 'string' || options.spec.trim() === '') {\n throw new Error(\n 'spec is required and must be a non-empty string (path or URL to OpenAPI spec)',\n );\n }\n\n return {\n spec: options.spec,\n port: options.port ?? 4000,\n proxyPath: options.proxyPath ?? '/api',\n handlersDir: options.handlersDir?.trim() || null,\n seedsDir: options.seedsDir?.trim() || null,\n enabled: options.enabled ?? true,\n idFields: options.idFields ?? {},\n timelineLimit: options.timelineLimit ?? 500,\n devtools: options.devtools ?? true,\n cors: options.cors ?? true,\n corsOrigin: options.corsOrigin ?? '*',\n silent: options.silent ?? false,\n logger: options.logger,\n };\n}\n","/**\n * Vite Plugin Implementation\n *\n * What: Main Vite plugin for OpenAPI mock server\n * How: Uses configureServer hook to start mock server and configure proxy\n * Why: Integrates OpenAPI mock server seamlessly into Vite dev workflow\n *\n * @module plugin\n */\n\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n createOpenApiServer,\n executeSeeds,\n type Logger,\n type OpenApiServer,\n} from '@websublime/vite-plugin-open-api-core';\nimport type { Plugin, ViteDevServer } from 'vite';\nimport { extractBannerInfo, printBanner, printError, printReloadNotification } from './banner.js';\nimport { loadHandlers } from './handlers.js';\nimport { createFileWatcher, debounce, type FileWatcher } from './hot-reload.js';\nimport { loadSeeds } from './seeds.js';\nimport { type OpenApiServerOptions, resolveOptions } from './types.js';\n\n/**\n * Create the OpenAPI Server Vite plugin\n *\n * This plugin starts a mock server based on an OpenAPI specification\n * and configures Vite to proxy API requests to it.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import vue from '@vitejs/plugin-vue';\n * import { openApiServer } from '@websublime/vite-plugin-open-api-server';\n *\n * export default defineConfig({\n * plugins: [\n * vue(),\n * openApiServer({\n * spec: './openapi/petstore.yaml',\n * port: 4000,\n * proxyPath: '/api',\n * handlersDir: './mocks/handlers',\n * seedsDir: './mocks/seeds',\n * }),\n * ],\n * });\n * ```\n *\n * @param options - Plugin configuration options\n * @returns Vite plugin\n */\n/**\n * Virtual module ID for the DevTools tab registration script.\n *\n * This script is served as a Vite module (not inline HTML) so that bare\n * import specifiers like `@vue/devtools-api` are resolved through Vite's\n * module pipeline. Inline `<script type=\"module\">` blocks in HTML are\n * executed directly by the browser, which cannot resolve bare specifiers.\n */\nconst VIRTUAL_DEVTOOLS_TAB_ID = 'virtual:open-api-devtools-tab';\nconst RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID = `\\0${VIRTUAL_DEVTOOLS_TAB_ID}`;\n\n/**\n * Resolve the DevTools SPA directory path\n *\n * @returns Absolute path to the SPA directory, or undefined if not found\n */\nfunction resolveDevtoolsSpaDir(logger?: Logger): string | undefined {\n const pluginDir = dirname(fileURLToPath(import.meta.url));\n const spaDir = join(pluginDir, 'devtools-spa');\n if (existsSync(spaDir)) {\n return spaDir;\n }\n logger?.warn?.(\n '[vite-plugin-open-api-server] DevTools SPA not found at',\n spaDir,\n '- serving placeholder. Run \"pnpm build\" to include the SPA.',\n );\n return undefined;\n}\n\n/**\n * Build a seed data map from the store for the route builder\n *\n * The route builder needs a Map<string, unknown[]> to know which schemas\n * have seed data. This reads the current store contents and builds that map.\n */\nfunction buildSeedDataMap(server: OpenApiServer): Map<string, unknown[]> {\n const seedDataMap = new Map<string, unknown[]>();\n for (const schema of server.store.getSchemas()) {\n seedDataMap.set(schema, server.store.list(schema));\n }\n return seedDataMap;\n}\n\nexport function openApiServer(options: OpenApiServerOptions): Plugin {\n const resolvedOptions = resolveOptions(options);\n\n // Server instance (created in configureServer)\n let server: OpenApiServer | null = null;\n\n // Vite dev server reference (needed for ssrLoadModule)\n let vite: ViteDevServer | null = null;\n\n // File watcher for hot reload\n let fileWatcher: FileWatcher | null = null;\n\n // Current working directory (set in configureServer)\n let cwd: string = process.cwd();\n\n return {\n name: 'vite-plugin-open-api-server',\n\n // Only active in dev mode\n apply: 'serve',\n\n /**\n * Ensure @vue/devtools-api is available for the DevTools tab module\n *\n * The virtual module imports from '@vue/devtools-api', which Vite needs\n * to pre-bundle so it can be resolved at runtime in the browser, even\n * though the host app may not have it as a direct dependency.\n */\n config() {\n if (!resolvedOptions.devtools || !resolvedOptions.enabled) {\n return;\n }\n\n // Only add to optimizeDeps if the package is actually resolvable\n // to avoid Vite warnings when the consumer hasn't installed it\n const require = createRequire(import.meta.url);\n try {\n require.resolve('@vue/devtools-api');\n } catch {\n return;\n }\n\n return {\n optimizeDeps: {\n include: ['@vue/devtools-api'],\n },\n };\n },\n\n /**\n * Resolve the virtual module for DevTools tab registration\n */\n resolveId(id: string) {\n if (id === VIRTUAL_DEVTOOLS_TAB_ID) {\n return RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID;\n }\n },\n\n /**\n * Load the virtual module that registers the custom Vue DevTools tab\n *\n * This code is served as a proper Vite module, allowing bare import\n * specifiers to be resolved through Vite's dependency pre-bundling.\n */\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_DEVTOOLS_TAB_ID) {\n return `\nimport { addCustomTab } from '@vue/devtools-api';\n\ntry {\n // Route through Vite's proxy so it works in all environments\n const iframeSrc = window.location.origin + '/_devtools/';\n\n addCustomTab({\n name: 'vite-plugin-open-api-server',\n title: 'OpenAPI Server',\n icon: 'https://api.iconify.design/carbon:api-1.svg?width=24&height=24&color=%2394a3b8',\n view: {\n type: 'iframe',\n src: iframeSrc,\n },\n category: 'app',\n });\n} catch (e) {\n // @vue/devtools-api not available - expected when the package is not installed\n}\n`;\n }\n },\n\n /**\n * Configure the Vite dev server\n *\n * This hook is called when the dev server is created.\n * We use it to:\n * 1. Start the OpenAPI mock server\n * 2. Configure the Vite proxy to forward API requests\n * 3. Set up file watching for hot reload\n */\n async configureServer(viteServer: ViteDevServer): Promise<void> {\n vite = viteServer;\n cwd = viteServer.config.root;\n\n // Check if plugin is disabled\n if (!resolvedOptions.enabled) {\n return;\n }\n\n try {\n // Load handlers from handlers directory (using Vite's ssrLoadModule for TS support)\n const handlersResult = resolvedOptions.handlersDir\n ? await loadHandlers(resolvedOptions.handlersDir, viteServer, cwd)\n : { handlers: new Map(), fileCount: 0, files: [] };\n\n // Load seeds from seeds directory (using Vite's ssrLoadModule for TS support)\n const seedsResult = resolvedOptions.seedsDir\n ? await loadSeeds(resolvedOptions.seedsDir, viteServer, cwd)\n : { seeds: new Map(), fileCount: 0, files: [] };\n\n // Resolve DevTools SPA directory (shipped inside this package's dist/)\n const devtoolsSpaDir = resolvedOptions.devtools\n ? resolveDevtoolsSpaDir(resolvedOptions.logger)\n : undefined;\n\n // Create the OpenAPI mock server\n server = await createOpenApiServer({\n spec: resolvedOptions.spec,\n port: resolvedOptions.port,\n idFields: resolvedOptions.idFields,\n handlers: handlersResult.handlers,\n // Seeds are populated via executeSeeds, not directly\n seeds: new Map(),\n timelineLimit: resolvedOptions.timelineLimit,\n devtools: resolvedOptions.devtools,\n devtoolsSpaDir,\n cors: resolvedOptions.cors,\n corsOrigin: resolvedOptions.corsOrigin,\n logger: resolvedOptions.logger,\n });\n\n // Execute seeds to populate the store, then sync the route builder's seed map\n if (seedsResult.seeds.size > 0) {\n await executeSeeds(seedsResult.seeds, server.store, server.document);\n\n server.updateSeeds(buildSeedDataMap(server));\n }\n\n // Start the mock server\n await server.start();\n\n // Configure Vite proxy\n configureProxy(viteServer, resolvedOptions.proxyPath, resolvedOptions.port);\n\n // Print startup banner\n const bannerInfo = extractBannerInfo(\n server.registry,\n {\n info: {\n title: server.document.info?.title ?? 'OpenAPI Server',\n version: server.document.info?.version ?? '1.0.0',\n },\n },\n handlersResult.handlers.size,\n seedsResult.seeds.size,\n resolvedOptions,\n );\n printBanner(bannerInfo, resolvedOptions);\n\n // Set up file watching for hot reload\n await setupFileWatching();\n } catch (error) {\n printError('Failed to start OpenAPI mock server', error, resolvedOptions);\n throw error;\n }\n },\n\n /**\n * Clean up when Vite server closes\n */\n async closeBundle(): Promise<void> {\n await cleanup();\n },\n\n /**\n * Inject Vue DevTools custom tab registration script\n *\n * When devtools is enabled, this injects a script tag that loads the\n * virtual module for custom tab registration. Using a virtual module\n * (instead of an inline script) ensures that bare import specifiers\n * like `@vue/devtools-api` are resolved through Vite's module pipeline.\n */\n transformIndexHtml() {\n if (!resolvedOptions.devtools || !resolvedOptions.enabled) {\n return;\n }\n\n return [\n {\n tag: 'script',\n attrs: { type: 'module', src: `/@id/${VIRTUAL_DEVTOOLS_TAB_ID}` },\n injectTo: 'head' as const,\n },\n ];\n },\n };\n\n /**\n * Configure Vite proxy for API requests\n *\n * @param vite - Vite dev server\n * @param proxyPath - Base path to proxy\n * @param port - Mock server port\n */\n function configureProxy(vite: ViteDevServer, proxyPath: string, port: number): void {\n // Ensure server config exists (create mutable copy if needed)\n const serverConfig = vite.config.server ?? {};\n const proxyConfig = serverConfig.proxy ?? {};\n\n // Escape special regex characters in proxy path and pre-compile regex\n const escapedPath = proxyPath.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pathPrefixRegex = new RegExp(`^${escapedPath}`);\n\n // Add proxy configuration for API requests\n proxyConfig[proxyPath] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n // Remove the proxy path prefix when forwarding\n rewrite: (path: string) => path.replace(pathPrefixRegex, ''),\n };\n\n // Proxy internal routes so they work through Vite's dev server port\n proxyConfig['/_devtools'] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n };\n\n proxyConfig['/_api'] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n };\n\n proxyConfig['/_ws'] = {\n target: `http://localhost:${port}`,\n changeOrigin: true,\n ws: true,\n };\n\n // Update the proxy config (Vite's proxy is mutable)\n if (vite.config.server) {\n vite.config.server.proxy = proxyConfig;\n }\n }\n\n /**\n * Set up file watching for hot reload\n */\n async function setupFileWatching(): Promise<void> {\n if (!server || !vite) return;\n\n // Debounce reload functions to prevent rapid-fire reloads\n const debouncedHandlerReload = debounce(reloadHandlers, 100);\n const debouncedSeedReload = debounce(reloadSeeds, 100);\n\n fileWatcher = await createFileWatcher({\n handlersDir: resolvedOptions.handlersDir ?? undefined,\n seedsDir: resolvedOptions.seedsDir ?? undefined,\n cwd,\n onHandlerChange: debouncedHandlerReload,\n onSeedChange: debouncedSeedReload,\n });\n }\n\n /**\n * Reload handlers when files change\n */\n async function reloadHandlers(): Promise<void> {\n if (!server || !vite || !resolvedOptions.handlersDir) return;\n\n try {\n const handlersResult = await loadHandlers(resolvedOptions.handlersDir, vite, cwd);\n server.updateHandlers(handlersResult.handlers);\n\n // Notify via WebSocket\n server.wsHub.broadcast({\n type: 'handlers:updated',\n data: { count: handlersResult.handlers.size },\n });\n\n printReloadNotification('handlers', handlersResult.handlers.size, resolvedOptions);\n } catch (error) {\n printError('Failed to reload handlers', error, resolvedOptions);\n }\n }\n\n /**\n * Reload seeds when files change\n *\n * Note: This operation is not fully atomic - there's a brief window between\n * clearing the store and repopulating it where requests may see empty data.\n * For development tooling, this tradeoff is acceptable.\n */\n async function reloadSeeds(): Promise<void> {\n if (!server || !vite || !resolvedOptions.seedsDir) return;\n\n try {\n // Load seeds first (before clearing) to minimize the window where store is empty\n const seedsResult = await loadSeeds(resolvedOptions.seedsDir, vite, cwd);\n\n // Only clear and repopulate if we successfully loaded seeds\n // This prevents clearing the store on load errors\n if (seedsResult.seeds.size > 0) {\n // Clear and immediately repopulate - minimizes empty window\n server.store.clearAll();\n await executeSeeds(seedsResult.seeds, server.store, server.document);\n } else {\n // User removed all seed files - clear the store\n server.store.clearAll();\n }\n\n // Sync the route builder's seed map with the new store data\n // (updateSeeds mutates the map in-place so route closures see the updates)\n server.updateSeeds(buildSeedDataMap(server));\n\n printReloadNotification('seeds', seedsResult.seeds.size, resolvedOptions);\n } catch (error) {\n printError('Failed to reload seeds', error, resolvedOptions);\n }\n }\n\n /**\n * Clean up resources\n */\n async function cleanup(): Promise<void> {\n // Close file watcher\n if (fileWatcher) {\n await fileWatcher.close();\n fileWatcher = null;\n }\n\n // Stop mock server\n if (server) {\n await server.stop();\n server = null;\n }\n\n vite = null;\n }\n}\n","/**\n * Vue DevTools Integration\n *\n * What: Provides a function to register OpenAPI Server in Vue DevTools\n * How: Uses @vue/devtools-api to add a custom tab with iframe to DevTools SPA\n * Why: Enables debugging and inspection of the mock API server within Vue DevTools\n *\n * @module devtools\n */\n\nimport type { App } from 'vue';\n\n/**\n * Options for registering the DevTools plugin\n */\nexport interface RegisterDevToolsOptions {\n /**\n * The port where the OpenAPI server is running\n * @default 3000\n */\n port?: number;\n\n /**\n * The host where the OpenAPI server is running\n * @default 'localhost' (or derived from window.location.hostname if in browser)\n */\n host?: string;\n\n /**\n * The protocol to use for the DevTools URL\n * @default 'http' (or derived from window.location.protocol if in browser)\n */\n protocol?: 'http' | 'https';\n\n /**\n * Enable or disable DevTools registration\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Custom label for the DevTools tab\n * @default 'OpenAPI Server'\n */\n label?: string;\n}\n\n/**\n * Register OpenAPI Server in Vue DevTools\n *\n * This function should be called in your Vue application's main entry point\n * to add a custom tab to Vue DevTools. The tab contains an iframe that loads\n * the OpenAPI Server DevTools SPA.\n *\n * @example\n * ```typescript\n * // In your main.ts or main.js\n * import { createApp } from 'vue';\n * import { registerDevTools } from '@websublime/vite-plugin-open-api-server';\n * import App from './App.vue';\n *\n * const app = createApp(App);\n *\n * // Register OpenAPI Server DevTools\n * if (import.meta.env.DEV) {\n * await registerDevTools(app, { port: 3000 });\n * }\n *\n * app.mount('#app');\n * ```\n *\n * @param app - Vue application instance\n * @param options - Configuration options\n */\nexport async function registerDevTools(\n app: App,\n options: RegisterDevToolsOptions = {},\n): Promise<void> {\n const { enabled = true, label = 'OpenAPI Server', port, host, protocol } = options;\n\n // Validate app parameter\n if (!app || typeof app !== 'object') {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for invalid app instance\n console.warn('[OpenAPI DevTools] Invalid Vue app instance provided');\n return;\n }\n\n // Only register if enabled\n if (!enabled) {\n return;\n }\n\n // Check if running in browser environment\n if (typeof window === 'undefined') {\n return;\n }\n\n // Lazy import to avoid SSR issues\n try {\n const { addCustomTab } = await import('@vue/devtools-api');\n\n // Get the DevTools URL\n const devtoolsUrl = getDevToolsUrl(port, host, protocol);\n\n // Add custom tab with iframe to Vue DevTools\n addCustomTab({\n name: 'vite-plugin-open-api-server',\n title: label,\n icon: 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%234f46e5%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z%22/%3E%3Cpolyline points=%223.29 7 12 12 20.71 7%22/%3E%3Cline x1=%2212%22 y1=%2222%22 x2=%2212%22 y2=%2212%22/%3E%3C/svg%3E',\n view: {\n type: 'iframe',\n src: devtoolsUrl,\n },\n category: 'app',\n });\n } catch (error) {\n // Log warning but don't crash the app\n // DevTools integration is optional, so the app should continue working\n // biome-ignore lint/suspicious/noConsole: Intentional warning for registration failure\n console.warn('[OpenAPI DevTools] Failed to register with Vue DevTools:', error);\n }\n}\n\n/**\n * Get the DevTools URL for the given configuration\n *\n * When running in a browser, protocol and host are automatically derived from\n * window.location if not explicitly provided.\n *\n * @param port - Server port (default: 3000)\n * @param host - Server host (default: 'localhost' or window.location.hostname)\n * @param protocol - Protocol to use (default: 'http' or window.location.protocol)\n * @returns DevTools SPA URL\n */\nexport function getDevToolsUrl(port = 3000, host?: string, protocol?: 'http' | 'https'): string {\n // Derive defaults from browser environment if available\n const actualProtocol =\n protocol ||\n (typeof window !== 'undefined'\n ? (window.location.protocol.replace(':', '') as 'http' | 'https')\n : 'http');\n const actualHost =\n host || (typeof window !== 'undefined' ? window.location.hostname : 'localhost');\n\n return `${actualProtocol}://${actualHost}:${port}/_devtools/`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websublime/vite-plugin-open-api-server",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
4
4
  "description": "Vite plugin for OpenAPI mock server with DevTools integration",
5
5
  "keywords": [
6
6
  "vite",
@@ -23,22 +23,22 @@
23
23
  "directory": "packages/server"
24
24
  },
25
25
  "dependencies": {
26
- "picocolors": "^1.1.1",
27
26
  "@vue/devtools-api": "^8.0.6",
28
27
  "chokidar": "^5.0.0",
28
+ "picocolors": "^1.1.1",
29
29
  "fast-glob": "^3.3.3",
30
- "@websublime/vite-plugin-open-api-core": "0.13.1"
30
+ "@websublime/vite-plugin-open-api-core": "0.13.3"
31
31
  },
32
32
  "devDependencies": {
33
- "vite": "^7.0.0",
34
33
  "vue": "^3.5.27",
35
34
  "typescript": "^5.9.0",
36
35
  "tsup": "^8.5.0",
36
+ "vite": "^7.0.0",
37
37
  "@websublime/vite-plugin-open-api-devtools": "0.8.5"
38
38
  },
39
39
  "peerDependencies": {
40
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
41
- "vue": "^3.0.0"
40
+ "vue": "^3.0.0",
41
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
42
42
  },
43
43
  "engines": {
44
44
  "node": "^20.19.0 || >=22.12.0"
@@ -46,17 +46,17 @@
46
46
  "private": false,
47
47
  "type": "module",
48
48
  "types": "./dist/index.d.ts",
49
- "module": "./dist/index.js",
50
49
  "exports": {
51
50
  ".": {
52
51
  "import": "./dist/index.js",
53
52
  "types": "./dist/index.d.ts"
54
53
  }
55
54
  },
55
+ "module": "./dist/index.js",
56
56
  "scripts": {
57
- "build": "tsup && pnpm run copy:devtools-spa",
58
57
  "dev": "tsup --watch",
59
58
  "copy:devtools-spa": "node scripts/copy-devtools-spa.mjs",
59
+ "build": "tsup && pnpm run copy:devtools-spa",
60
60
  "typecheck": "tsc --noEmit"
61
61
  }
62
62
  }