@websublime/vite-plugin-open-api-devtools 0.8.5 → 0.9.0-next.0

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.
Files changed (56) hide show
  1. package/dist/{ModelsPage-DxcKgz3y.js → ModelsPage-DoqfBilK.js} +5 -5
  2. package/dist/{ModelsPage-DxcKgz3y.js.map → ModelsPage-DoqfBilK.js.map} +1 -1
  3. package/dist/{RoutesPage-BCKS0kdj.js → RoutesPage-DmXBJ4J5.js} +4 -4
  4. package/dist/{RoutesPage-BCKS0kdj.js.map → RoutesPage-DmXBJ4J5.js.map} +1 -1
  5. package/dist/{SimulatorPage-BlbxLDdf.js → SimulatorPage-BmM-ExQg.js} +4 -4
  6. package/dist/{SimulatorPage-BlbxLDdf.js.map → SimulatorPage-BmM-ExQg.js.map} +1 -1
  7. package/dist/{TimelinePage-BhtcWy3o.js → TimelinePage-Dnh_47wz.js} +7 -7
  8. package/dist/{TimelinePage-BhtcWy3o.js.map → TimelinePage-Dnh_47wz.js.map} +1 -1
  9. package/dist/{check-BZ_jBwd4.js → check-BdvaZxRt.js} +2 -2
  10. package/dist/{check-BZ_jBwd4.js.map → check-BdvaZxRt.js.map} +1 -1
  11. package/dist/devtools.js +1 -1
  12. package/dist/devtools.umd.cjs +3 -3
  13. package/dist/devtools.umd.cjs.map +1 -1
  14. package/dist/{format-Dq-zmlAN.js → format-Cj8p3HJO.js} +2 -2
  15. package/dist/{format-Dq-zmlAN.js.map → format-Cj8p3HJO.js.map} +1 -1
  16. package/dist/main-HjHdsAps.js +619 -0
  17. package/dist/main-HjHdsAps.js.map +1 -0
  18. package/dist/spa/assets/{ModelsPage-Bd7YM0_p.js → ModelsPage-_etl-kz9.js} +2 -2
  19. package/dist/spa/assets/{ModelsPage-Bd7YM0_p.js.map → ModelsPage-_etl-kz9.js.map} +1 -1
  20. package/dist/spa/assets/{RoutesPage-YQcTov5t.js → RoutesPage-BN_Lo8uo.js} +2 -2
  21. package/dist/spa/assets/{RoutesPage-YQcTov5t.js.map → RoutesPage-BN_Lo8uo.js.map} +1 -1
  22. package/dist/spa/assets/{SimulatorPage-DPxecZGS.js → SimulatorPage-BF4VLn5k.js} +2 -2
  23. package/dist/spa/assets/{SimulatorPage-DPxecZGS.js.map → SimulatorPage-BF4VLn5k.js.map} +1 -1
  24. package/dist/spa/assets/TimelinePage-7OqKgItP.js +2 -0
  25. package/dist/spa/assets/{TimelinePage-CbWvtFc_.js.map → TimelinePage-7OqKgItP.js.map} +1 -1
  26. package/dist/spa/assets/check-9axZ93X-.js +2 -0
  27. package/dist/spa/assets/{check-DaD3RpI4.js.map → check-9axZ93X-.js.map} +1 -1
  28. package/dist/spa/assets/{format-er1_KlP8.js → format-CB-GiJ6N.js} +2 -2
  29. package/dist/spa/assets/{format-er1_KlP8.js.map → format-CB-GiJ6N.js.map} +1 -1
  30. package/dist/spa/assets/index-BQr16DJ3.js +3 -0
  31. package/dist/spa/assets/index-BQr16DJ3.js.map +1 -0
  32. package/dist/spa/assets/{registry-DjJcdO1T.js → registry-DAv66ZPp.js} +2 -2
  33. package/dist/spa/assets/{registry-DjJcdO1T.js.map → registry-DAv66ZPp.js.map} +1 -1
  34. package/dist/spa/assets/{trash-2-BhRxw6RN.js → trash-2-Cj6nHRK8.js} +2 -2
  35. package/dist/spa/assets/{trash-2-BhRxw6RN.js.map → trash-2-Cj6nHRK8.js.map} +1 -1
  36. package/dist/spa/assets/{triangle-alert-QXcMwGDR.js → triangle-alert-BXnev_tx.js} +2 -2
  37. package/dist/spa/assets/{triangle-alert-QXcMwGDR.js.map → triangle-alert-BXnev_tx.js.map} +1 -1
  38. package/dist/spa/assets/{vue-vendor-Bkktf9yg.js → vue-vendor-D62nux6V.js} +2 -2
  39. package/dist/spa/assets/{vue-vendor-Bkktf9yg.js.map → vue-vendor-D62nux6V.js.map} +1 -1
  40. package/dist/spa/assets/x-9zeMJ8Mo.js +2 -0
  41. package/dist/spa/assets/{x-DGt4DUCk.js.map → x-9zeMJ8Mo.js.map} +1 -1
  42. package/dist/spa/index.html +2 -2
  43. package/dist/{trash-2-D9Av3Lfp.js → trash-2-unSVE8Jc.js} +2 -2
  44. package/dist/{trash-2-D9Av3Lfp.js.map → trash-2-unSVE8Jc.js.map} +1 -1
  45. package/dist/{triangle-alert-6Spra6HA.js → triangle-alert-B1V6ucAg.js} +2 -2
  46. package/dist/{triangle-alert-6Spra6HA.js.map → triangle-alert-B1V6ucAg.js.map} +1 -1
  47. package/dist/{x-DI-fqJ3g.js → x-DWlhu3h-.js} +2 -2
  48. package/dist/{x-DI-fqJ3g.js.map → x-DWlhu3h-.js.map} +1 -1
  49. package/package.json +13 -12
  50. package/dist/main-DmyM_bX3.js +0 -537
  51. package/dist/main-DmyM_bX3.js.map +0 -1
  52. package/dist/spa/assets/TimelinePage-CbWvtFc_.js +0 -2
  53. package/dist/spa/assets/check-DaD3RpI4.js +0 -2
  54. package/dist/spa/assets/index-x38XzU3c.js +0 -3
  55. package/dist/spa/assets/index-x38XzU3c.js.map +0 -1
  56. package/dist/spa/assets/x-DGt4DUCk.js +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main-HjHdsAps.js","sources":["../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/shared/src/utils.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/defaultAttributes.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/Icon.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/createLucideIcon.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/icons/clock.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/icons/database.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/icons/route.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/icons/wifi-off.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/icons/wifi.js","../../../node_modules/.pnpm/lucide-vue-next@0.513.0_vue@3.5.27_typescript@5.9.3_/node_modules/lucide-vue-next/dist/esm/icons/zap.js","../src/stores/specs.ts","../src/composables/useWebSocket.ts","../src/router.ts","../src/App.vue","../src/composables/useTheme.ts","../src/main.ts"],"sourcesContent":["/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\n\nexport { mergeClasses, toCamelCase, toKebabCase, toPascalCase };\n//# sourceMappingURL=utils.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n \"stroke-width\": 2,\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { h } from 'vue';\nimport { mergeClasses, toKebabCase, toPascalCase } from './shared/src/utils.js';\nimport defaultAttributes from './defaultAttributes.js';\n\nconst Icon = ({ size, strokeWidth = 2, absoluteStrokeWidth, color, iconNode, name, class: classes, ...props }, { slots }) => {\n return h(\n \"svg\",\n {\n ...defaultAttributes,\n width: size || defaultAttributes.width,\n height: size || defaultAttributes.height,\n stroke: color || defaultAttributes.stroke,\n \"stroke-width\": absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,\n class: mergeClasses(\n \"lucide\",\n ...name ? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`] : [\"lucide-icon\"]\n ),\n ...props\n },\n [...iconNode.map((child) => h(...child)), ...slots.default ? [slots.default()] : []]\n );\n};\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { h } from 'vue';\nimport Icon from './Icon.js';\n\nconst createLucideIcon = (iconName, iconNode) => (props, { slots }) => h(\n Icon,\n {\n ...props,\n iconNode,\n name: iconName\n },\n slots\n);\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Clock = createLucideIcon(\"clock\", [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\", key: \"1mglay\" }],\n [\"polyline\", { points: \"12 6 12 12 16 14\", key: \"68esgv\" }]\n]);\n\nexport { Clock as default };\n//# sourceMappingURL=clock.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Database = createLucideIcon(\"database\", [\n [\"ellipse\", { cx: \"12\", cy: \"5\", rx: \"9\", ry: \"3\", key: \"msslwz\" }],\n [\"path\", { d: \"M3 5V19A9 3 0 0 0 21 19V5\", key: \"1wlel7\" }],\n [\"path\", { d: \"M3 12A9 3 0 0 0 21 12\", key: \"mv7ke4\" }]\n]);\n\nexport { Database as default };\n//# sourceMappingURL=database.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Route = createLucideIcon(\"route\", [\n [\"circle\", { cx: \"6\", cy: \"19\", r: \"3\", key: \"1kj8tv\" }],\n [\"path\", { d: \"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15\", key: \"1d8sl\" }],\n [\"circle\", { cx: \"18\", cy: \"5\", r: \"3\", key: \"gq8acd\" }]\n]);\n\nexport { Route as default };\n//# sourceMappingURL=route.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst WifiOff = createLucideIcon(\"wifi-off\", [\n [\"path\", { d: \"M12 20h.01\", key: \"zekei9\" }],\n [\"path\", { d: \"M8.5 16.429a5 5 0 0 1 7 0\", key: \"1bycff\" }],\n [\"path\", { d: \"M5 12.859a10 10 0 0 1 5.17-2.69\", key: \"1dl1wf\" }],\n [\"path\", { d: \"M19 12.859a10 10 0 0 0-2.007-1.523\", key: \"4k23kn\" }],\n [\"path\", { d: \"M2 8.82a15 15 0 0 1 4.177-2.643\", key: \"1grhjp\" }],\n [\"path\", { d: \"M22 8.82a15 15 0 0 0-11.288-3.764\", key: \"z3jwby\" }],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n]);\n\nexport { WifiOff as default };\n//# sourceMappingURL=wifi-off.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Wifi = createLucideIcon(\"wifi\", [\n [\"path\", { d: \"M12 20h.01\", key: \"zekei9\" }],\n [\"path\", { d: \"M2 8.82a15 15 0 0 1 20 0\", key: \"dnpr2z\" }],\n [\"path\", { d: \"M5 12.859a10 10 0 0 1 14 0\", key: \"1x1e6c\" }],\n [\"path\", { d: \"M8.5 16.429a5 5 0 0 1 7 0\", key: \"1bycff\" }]\n]);\n\nexport { Wifi as default };\n//# sourceMappingURL=wifi.js.map\n","/**\n * @license lucide-vue-next v0.513.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst Zap = createLucideIcon(\"zap\", [\n [\n \"path\",\n {\n d: \"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\",\n key: \"1xq2db\"\n }\n ]\n]);\n\nexport { Zap as default };\n//# sourceMappingURL=zap.js.map\n","/**\n * Specs Store\n *\n * What: Pinia store for managing OpenAPI spec metadata\n * How: Stores spec info received via WebSocket, provides filtering by active spec\n * Why: Enables multi-spec awareness across the DevTools UI (colors, labels, filtering)\n *\n * @module stores/specs\n */\n\nimport { defineStore } from 'pinia';\nimport { computed, ref } from 'vue';\n\n/**\n * OpenAPI spec metadata\n *\n * Canonical source of truth: packages/core/src/websocket/protocol.ts (SpecInfo)\n * This interface is duplicated here to keep devtools-client decoupled from core.\n * If the core SpecInfo changes, this must be updated to match.\n */\nexport interface SpecInfo {\n /** Unique spec identifier */\n id: string;\n /** Human-readable title from the OpenAPI info object */\n title: string;\n /** Spec version from the OpenAPI info object */\n version: string;\n /** Proxy path prefix for this spec's endpoints */\n proxyPath: string;\n /** Assigned color for UI differentiation (hex) */\n color: string;\n /** Number of endpoints in this spec */\n endpointCount: number;\n /** Number of schemas in this spec */\n schemaCount: number;\n}\n\n/** Default fallback color when a spec has no assigned color (slate-400) */\nconst DEFAULT_SPEC_COLOR = '#94a3b8';\n\n/**\n * Specs store for multi-spec metadata management\n *\n * Provides:\n * - Spec list storage and lookup\n * - Active spec filtering (null = show all)\n * - Color retrieval with fallback\n * - Computed helpers for spec IDs, map, and filter state\n */\nexport const useSpecsStore = defineStore('specs', () => {\n // ==========================================================================\n // State\n // ==========================================================================\n\n /** All registered specs from the server */\n const specs = ref<SpecInfo[]>([]);\n\n /** Currently active spec filter (null = all specs visible) */\n const activeSpecFilter = ref<string | null>(null);\n\n // ==========================================================================\n // Getters / Computed\n // ==========================================================================\n\n /**\n * Map of spec ID to SpecInfo for O(1) lookup\n */\n const specMap = computed(() => new Map(specs.value.map((s) => [s.id, s])));\n\n /**\n * Ordered list of spec IDs\n */\n const specIds = computed(() => specs.value.map((s) => s.id));\n\n /**\n * The currently filtered spec, or null if no filter is active\n */\n const activeSpec = computed(() =>\n activeSpecFilter.value ? (specMap.value.get(activeSpecFilter.value) ?? null) : null,\n );\n\n /**\n * Whether a spec filter is currently active\n */\n const isFiltered = computed(() => activeSpecFilter.value !== null);\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Replace the full specs list (typically from a WebSocket connected event)\n */\n function setSpecs(newSpecs: SpecInfo[]): void {\n specs.value = newSpecs;\n // Clear stale filter if the active spec no longer exists\n if (activeSpecFilter.value && !newSpecs.some((s) => s.id === activeSpecFilter.value)) {\n activeSpecFilter.value = null;\n }\n }\n\n /**\n * Set the active spec filter by ID, or null to show all\n */\n function setFilter(specId: string | null): void {\n activeSpecFilter.value =\n specId !== null && !specs.value.some((s) => s.id === specId) ? null : specId;\n }\n\n /**\n * Toggle the active spec filter: if the given spec is already active, clear;\n * otherwise set it as the active filter\n */\n function toggleFilter(specId: string): void {\n if (activeSpecFilter.value === specId) {\n activeSpecFilter.value = null;\n } else {\n setFilter(specId);\n }\n }\n\n /**\n * Get the assigned color for a spec, with fallback to default slate color\n */\n function getColor(specId: string): string {\n return specMap.value.get(specId)?.color ?? DEFAULT_SPEC_COLOR;\n }\n\n // ==========================================================================\n // Return\n // ==========================================================================\n\n return {\n // State\n specs,\n activeSpecFilter,\n\n // Getters\n specMap,\n specIds,\n activeSpec,\n isFiltered,\n\n // Actions\n setSpecs,\n setFilter,\n toggleFilter,\n getColor,\n };\n});\n","/**\n * useWebSocket Composable\n *\n * What: Provides reactive WebSocket connection management for the DevTools SPA\n * How: Manages WebSocket lifecycle with auto-reconnect and event subscription\n * Why: Enables real-time communication between DevTools and the mock server\n *\n * @module composables/useWebSocket\n */\n\nimport type { ComputedRef } from 'vue';\nimport { computed, getCurrentInstance, onMounted, ref } from 'vue';\n\nimport type { SpecInfo } from '../stores/specs';\nimport { useSpecsStore } from '../stores/specs';\n\n/**\n * Server event types that can be received from the server\n * These match the ServerEvent types defined in @websublime/vite-plugin-open-api-core\n */\nexport type ServerEventType =\n | 'connected'\n | 'request'\n | 'response'\n | 'timeline:cleared'\n | 'store:updated'\n | 'handler:reloaded'\n | 'handlers:updated'\n | 'seed:reloaded'\n | 'seeds:updated'\n | 'simulation:active'\n | 'simulation:added'\n | 'simulation:removed'\n | 'simulations:cleared'\n | 'registry'\n | 'timeline'\n | 'store'\n | 'store:set'\n | 'store:cleared'\n | 'simulation:set'\n | 'simulation:cleared'\n | 'reseeded'\n | 'error';\n\n/**\n * Server event structure\n */\nexport interface ServerEvent<T = unknown> {\n type: ServerEventType;\n data: T;\n}\n\n/**\n * Connected event data\n *\n * In multi-spec mode, the server sends `specs` alongside `serverVersion`.\n * The `specs` field is optional for backward compatibility with single-spec servers.\n */\nexport interface ConnectedEventData {\n serverVersion: string;\n /** Spec metadata array (multi-spec mode only) */\n specs?: SpecInfo[];\n}\n\n/**\n * Client command types that can be sent to the server\n */\nexport type ClientCommandType =\n | 'get:specs'\n | 'get:registry'\n | 'get:timeline'\n | 'get:store'\n | 'set:store'\n | 'clear:store'\n | 'set:simulation'\n | 'clear:simulation'\n | 'clear:timeline'\n | 'reseed';\n\n/**\n * Client command structure\n *\n * In multi-spec mode, commands include `specId` in their data to target\n * a specific spec instance. Some commands (get:specs, get:registry, get:timeline,\n * clear:timeline) are global and do not require specId.\n */\nexport interface ClientCommand<T = unknown> {\n type: ClientCommandType;\n data?: T;\n}\n\n/**\n * Simulation configuration for set:simulation command\n *\n * Mirrors SimulationConfig from @websublime/vite-plugin-open-api-core.\n * Kept decoupled to avoid cross-package imports.\n */\nexport interface SimulationConfig {\n path: string;\n method?: string;\n status?: number;\n delay?: number;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\n/**\n * WebSocket connection state\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';\n\n/**\n * Event handler function type\n */\nexport type EventHandler<T = unknown> = (data: T) => void;\n\n/**\n * Configuration options for useWebSocket\n */\nexport interface UseWebSocketOptions {\n /**\n * WebSocket URL path (default: '/_ws')\n */\n path?: string;\n\n /**\n * Reconnection delay in milliseconds (default: 2000)\n */\n reconnectDelay?: number;\n\n /**\n * Maximum reconnection attempts (default: Infinity)\n */\n maxReconnectAttempts?: number;\n\n /**\n * Whether to auto-connect on mount (default: true)\n */\n autoConnect?: boolean;\n}\n\n/**\n * Return type for useWebSocket composable\n *\n * Provides explicit typing for the composable return value as required by CLAUDE.md\n */\nexport interface UseWebSocketReturn {\n /** Current connection state */\n connectionState: ComputedRef<ConnectionState>;\n /** Whether the WebSocket is connected */\n connected: ComputedRef<boolean>;\n /** Whether the WebSocket is attempting to reconnect */\n isReconnecting: ComputedRef<boolean>;\n /** Server version received on connection */\n serverVersion: ComputedRef<string | null>;\n /** Number of reconnection attempts made */\n reconnectAttempts: ComputedRef<number>;\n /** Connect to the WebSocket server */\n connect: () => void;\n /** Disconnect from the WebSocket server */\n disconnect: () => void;\n /** Send a command to the server */\n send: <T = unknown>(command: ClientCommand<T>) => boolean;\n /** Subscribe to a server event */\n on: <T = unknown>(event: ServerEventType | '*', handler: EventHandler<T>) => () => void;\n /** Unsubscribe from a server event */\n off: <T = unknown>(event: ServerEventType | '*', handler: EventHandler<T>) => void;\n /**\n * Subscribe to an event and automatically unsubscribe after the first invocation.\n * This is a standard one-shot subscription.\n */\n once: <T = unknown>(event: ServerEventType | '*', handler: EventHandler<T>) => () => void;\n /**\n * Subscribe to an event and unsubscribe when the handler returns true.\n * Useful for conditional one-time events where you need to wait for specific data.\n */\n onUntil: <T = unknown>(\n event: ServerEventType | '*',\n handler: (data: T) => boolean | undefined,\n ) => () => void;\n /** Reset the composable state (useful for testing) */\n resetState: () => void;\n\n // =========================================================================\n // Multi-spec command wrappers\n // =========================================================================\n\n /** Request the list of available specs */\n getSpecs: () => boolean;\n /** Request the route registry, optionally scoped to a spec */\n getRegistry: (specId?: string) => boolean;\n /** Request timeline entries, optionally scoped to a spec */\n getTimeline: (specId?: string, limit?: number) => boolean;\n /** Clear timeline entries, optionally scoped to a spec */\n clearTimeline: (specId?: string) => boolean;\n /** Request store data for a specific schema in a spec */\n getStore: (specId: string, schema: string) => boolean;\n /** Set store data for a specific schema in a spec */\n setStore: (specId: string, schema: string, items: unknown[]) => boolean;\n /** Clear store data for a specific schema in a spec */\n clearStore: (specId: string, schema: string) => boolean;\n /** Set a simulation configuration for a spec */\n setSimulation: (specId: string, config: SimulationConfig) => boolean;\n /** Clear a simulation for a specific path in a spec */\n clearSimulation: (specId: string, path: string) => boolean;\n /** Re-seed mock data for a spec */\n reseed: (specId: string) => boolean;\n}\n\n/**\n * Default options\n */\nconst DEFAULT_OPTIONS: Required<UseWebSocketOptions> = {\n path: '/_ws',\n reconnectDelay: 2000,\n maxReconnectAttempts: Number.POSITIVE_INFINITY,\n autoConnect: true,\n};\n\n/**\n * Singleton state for WebSocket - shared across all component instances\n */\nconst connectionState = ref<ConnectionState>('disconnected');\nconst serverVersion = ref<string | null>(null);\nconst reconnectAttempts = ref(0);\n\n/**\n * Store the WebSocket instance and configuration\n */\nlet ws: WebSocket | null = null;\nlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\nlet currentOptions: Required<UseWebSocketOptions> = { ...DEFAULT_OPTIONS };\n\n/**\n * Flag to track if options have been initialized\n * Options are only set on the first call to useWebSocket or when disconnected\n */\nlet optionsInitialized = false;\n\n/**\n * Event handlers map - stores handlers for each event type\n */\nconst eventHandlers = new Map<string, Set<EventHandler>>();\n\n/**\n * Check if we're running in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof WebSocket !== 'undefined';\n}\n\n/**\n * Check if we're inside a Vue component context\n */\nfunction hasComponentContext(): boolean {\n return getCurrentInstance() !== null;\n}\n\n/**\n * Build the WebSocket URL based on current location\n */\nfunction buildWebSocketUrl(path: string): string {\n if (!isBrowser()) {\n return `ws://localhost${path}`;\n }\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n return `${protocol}//${window.location.host}${path}`;\n}\n\n/**\n * Clear the reconnect timer if active\n */\nfunction clearReconnectTimer(): void {\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n}\n\n/**\n * Dispatch an event to all registered handlers\n */\nfunction dispatchEvent(type: string, data: unknown): void {\n const handlers = eventHandlers.get(type);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`[DevTools WebSocket] Error in event handler for '${type}':`, error);\n }\n }\n }\n\n // Also dispatch to wildcard handlers\n const wildcardHandlers = eventHandlers.get('*');\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) {\n try {\n handler({ type, data });\n } catch (error) {\n console.error('[DevTools WebSocket] Error in wildcard event handler:', error);\n }\n }\n }\n}\n\n/**\n * Handle WebSocket open event\n */\nfunction handleOpen(): void {\n connectionState.value = 'connected';\n reconnectAttempts.value = 0;\n clearReconnectTimer();\n\n if (import.meta.env.DEV) {\n console.log('[DevTools WebSocket] Connected');\n }\n}\n\n/**\n * Request initial data for each known spec after connection.\n *\n * Sends `get:registry` and `get:timeline` for every spec so the UI is\n * immediately populated without user interaction.\n */\nfunction requestInitialData(specs: SpecInfo[]): void {\n for (const spec of specs) {\n send({ type: 'get:registry', data: { specId: spec.id } });\n send({ type: 'get:timeline', data: { specId: spec.id } });\n }\n}\n\n/**\n * Handle WebSocket message event\n */\nfunction handleMessage(event: MessageEvent): void {\n try {\n const message = JSON.parse(event.data) as ServerEvent;\n\n // Handle connected event specially to extract server version and specs\n if (message.type === 'connected') {\n const connectedData = message.data as ConnectedEventData;\n serverVersion.value = connectedData.serverVersion;\n\n // Populate specs store if specs are present (multi-spec mode)\n if (connectedData.specs && Array.isArray(connectedData.specs)) {\n // Lazy access to Pinia store — only called when a message arrives,\n // so Pinia is guaranteed to be installed by then.\n const specsStore = useSpecsStore();\n specsStore.setSpecs(connectedData.specs);\n\n // Request initial data per-spec so the UI is immediately populated\n requestInitialData(connectedData.specs);\n }\n\n if (import.meta.env.DEV) {\n const specCount = connectedData.specs?.length ?? 0;\n console.log(\n `[DevTools WebSocket] Server version: ${connectedData.serverVersion}, specs: ${specCount}`,\n );\n }\n }\n\n // Dispatch to registered handlers\n dispatchEvent(message.type, message.data);\n } catch (error) {\n console.error('[DevTools WebSocket] Failed to parse message:', error);\n }\n}\n\n/**\n * Handle WebSocket close event\n */\nfunction handleClose(): void {\n const wasConnected = connectionState.value === 'connected';\n connectionState.value = 'disconnected';\n ws = null;\n\n if (wasConnected && import.meta.env.DEV) {\n console.log('[DevTools WebSocket] Disconnected');\n }\n\n // Attempt to reconnect if within limits\n if (reconnectAttempts.value < currentOptions.maxReconnectAttempts) {\n connectionState.value = 'reconnecting';\n reconnectAttempts.value++;\n\n if (import.meta.env.DEV) {\n console.log(\n `[DevTools WebSocket] Reconnecting in ${currentOptions.reconnectDelay}ms (attempt ${reconnectAttempts.value})`,\n );\n }\n\n reconnectTimer = setTimeout(() => {\n connect();\n }, currentOptions.reconnectDelay);\n } else if (import.meta.env.DEV) {\n console.warn('[DevTools WebSocket] Max reconnection attempts reached');\n }\n}\n\n/**\n * Handle WebSocket error event\n */\nfunction handleError(event: Event): void {\n console.error('[DevTools WebSocket] Error:', event);\n}\n\n/**\n * Connect to the WebSocket server\n */\nfunction connect(): void {\n if (!isBrowser()) {\n if (import.meta.env.DEV) {\n console.warn('[DevTools WebSocket] Cannot connect outside browser environment');\n }\n return;\n }\n\n // Don't connect if already connected or connecting\n // Check connectionState first to prevent race condition when connect() is called rapidly\n if (\n connectionState.value === 'connecting' ||\n connectionState.value === 'connected' ||\n (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN))\n ) {\n return;\n }\n\n // Clean up existing connection without resetting reconnect state\n cleanupConnection();\n\n connectionState.value = 'connecting';\n const url = buildWebSocketUrl(currentOptions.path);\n\n try {\n ws = new WebSocket(url);\n ws.onopen = handleOpen;\n ws.onmessage = handleMessage;\n ws.onclose = handleClose;\n ws.onerror = handleError;\n } catch (error) {\n console.error('[DevTools WebSocket] Failed to create WebSocket:', error);\n connectionState.value = 'disconnected';\n }\n}\n\n/**\n * Clean up WebSocket connection without resetting reconnect state.\n * Used internally when reconnecting.\n */\nfunction cleanupConnection(): void {\n if (ws) {\n // Remove event handlers to prevent close handler from triggering reconnect\n ws.onopen = null;\n ws.onmessage = null;\n ws.onclose = null;\n ws.onerror = null;\n\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n ws.close();\n }\n\n ws = null;\n }\n}\n\n/**\n * Disconnect from the WebSocket server\n */\nfunction disconnect(): void {\n clearReconnectTimer();\n cleanupConnection();\n\n connectionState.value = 'disconnected';\n reconnectAttempts.value = 0;\n}\n\n/**\n * Send a command to the server\n *\n * @param command - The command to send\n * @returns true if the command was sent, false otherwise\n */\nfunction send<T = unknown>(command: ClientCommand<T>): boolean {\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n if (import.meta.env.DEV) {\n console.warn('[DevTools WebSocket] Cannot send command - not connected');\n }\n return false;\n }\n\n try {\n ws.send(JSON.stringify(command));\n return true;\n } catch (error) {\n console.error('[DevTools WebSocket] Failed to send command:', error);\n return false;\n }\n}\n\n// =============================================================================\n// Multi-spec command wrappers\n// =============================================================================\n\n/**\n * Request the list of available specs (global command, no specId)\n */\nfunction getSpecs(): boolean {\n return send({ type: 'get:specs' });\n}\n\n/**\n * Request the route registry, optionally scoped to a single spec\n */\nfunction getRegistry(specId?: string): boolean {\n return send({ type: 'get:registry', data: specId ? { specId } : undefined });\n}\n\n/**\n * Request timeline entries, optionally scoped to a single spec\n */\nfunction getTimeline(specId?: string, limit?: number): boolean {\n const data: { specId?: string; limit?: number } = {};\n if (specId) data.specId = specId;\n if (limit !== undefined) data.limit = limit;\n return send({\n type: 'get:timeline',\n data: Object.keys(data).length > 0 ? data : undefined,\n });\n}\n\n/**\n * Clear timeline entries, optionally scoped to a single spec\n */\nfunction clearTimeline(specId?: string): boolean {\n return send({ type: 'clear:timeline', data: specId ? { specId } : undefined });\n}\n\n/**\n * Request store data for a specific schema within a spec (spec-scoped)\n */\nfunction getStore(specId: string, schema: string): boolean {\n return send({ type: 'get:store', data: { specId, schema } });\n}\n\n/**\n * Set store data for a specific schema within a spec (spec-scoped)\n */\nfunction setStore(specId: string, schema: string, items: unknown[]): boolean {\n return send({ type: 'set:store', data: { specId, schema, items } });\n}\n\n/**\n * Clear store data for a specific schema within a spec (spec-scoped)\n */\nfunction clearStore(specId: string, schema: string): boolean {\n return send({ type: 'clear:store', data: { specId, schema } });\n}\n\n/**\n * Set a simulation configuration for a spec (spec-scoped)\n */\nfunction setSimulation(specId: string, config: SimulationConfig): boolean {\n return send({ type: 'set:simulation', data: { specId, ...config } });\n}\n\n/**\n * Clear a simulation for a specific path in a spec (spec-scoped)\n */\nfunction clearSimulation(specId: string, path: string): boolean {\n return send({ type: 'clear:simulation', data: { specId, path } });\n}\n\n/**\n * Re-seed mock data for a spec (spec-scoped)\n */\nfunction reseed(specId: string): boolean {\n return send({ type: 'reseed', data: { specId } });\n}\n\n// =============================================================================\n// Event subscription\n// =============================================================================\n\n/**\n * Subscribe to a server event\n *\n * @param event - The event type to subscribe to (or '*' for all events)\n * @param handler - The handler function to call when the event is received\n * @returns An unsubscribe function\n */\nfunction on<T = unknown>(event: ServerEventType | '*', handler: EventHandler<T>): () => void {\n if (!eventHandlers.has(event)) {\n eventHandlers.set(event, new Set());\n }\n\n const handlers = eventHandlers.get(event);\n if (handlers) {\n handlers.add(handler as EventHandler);\n }\n\n // Return unsubscribe function\n return () => {\n const currentHandlers = eventHandlers.get(event);\n if (currentHandlers) {\n currentHandlers.delete(handler as EventHandler);\n if (currentHandlers.size === 0) {\n eventHandlers.delete(event);\n }\n }\n };\n}\n\n/**\n * Unsubscribe from a server event\n *\n * @param event - The event type to unsubscribe from\n * @param handler - The handler function to remove\n */\nfunction off<T = unknown>(event: ServerEventType | '*', handler: EventHandler<T>): void {\n const handlers = eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n eventHandlers.delete(event);\n }\n }\n}\n\n/**\n * Subscribe to an event and automatically unsubscribe after the first invocation.\n * This is a standard one-shot subscription - the handler is called exactly once.\n *\n * @param event - The event type to subscribe to\n * @param handler - The handler function to call once\n * @returns An unsubscribe function (can be used to cancel before the event fires)\n *\n * @example\n * ```typescript\n * // Wait for the first 'connected' event\n * once('connected', (data) => {\n * console.log('Connected with version:', data.serverVersion);\n * });\n * ```\n */\nfunction once<T = unknown>(event: ServerEventType | '*', handler: EventHandler<T>): () => void {\n const wrappedHandler: EventHandler<T> = (data) => {\n off(event, wrappedHandler);\n handler(data);\n };\n\n return on(event, wrappedHandler);\n}\n\n/**\n * Subscribe to an event and unsubscribe when the handler returns true.\n * Useful for conditional one-time events where you need to wait for specific data.\n *\n * @param event - The event type to subscribe to\n * @param handler - The handler function that returns true to unsubscribe\n * @returns An unsubscribe function\n *\n * @example\n * ```typescript\n * // Wait until we receive a response with status 200\n * onUntil('response', (data) => {\n * if (data.status === 200) {\n * console.log('Success response received');\n * return true; // Unsubscribe\n * }\n * return false; // Keep listening\n * });\n * ```\n */\nfunction onUntil<T = unknown>(\n event: ServerEventType | '*',\n handler: (data: T) => boolean | undefined,\n): () => void {\n const wrappedHandler: EventHandler<T> = (data) => {\n const result = handler(data);\n if (result === true) {\n off(event, wrappedHandler);\n }\n };\n\n return on(event, wrappedHandler);\n}\n\n/**\n * Reset the composable state (useful for testing)\n */\nfunction resetState(): void {\n disconnect();\n serverVersion.value = null;\n eventHandlers.clear();\n currentOptions = { ...DEFAULT_OPTIONS };\n optionsInitialized = false;\n}\n\n/**\n * useWebSocket composable\n *\n * Provides WebSocket connection management functionality including:\n * - Reactive connection state\n * - Auto-reconnect with configurable delay\n * - Event subscription system\n * - Command sending\n * - Multi-spec command wrappers\n *\n * @param options - Configuration options\n * @returns WebSocket management utilities\n */\nexport function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketReturn {\n // Only merge options when not connected and not initialized, or when disconnected\n // This prevents race conditions when multiple components pass different options\n if (!optionsInitialized || connectionState.value === 'disconnected') {\n currentOptions = { ...DEFAULT_OPTIONS, ...options };\n optionsInitialized = true;\n }\n\n /**\n * Computed property indicating if the WebSocket is connected\n */\n const connected = computed(() => connectionState.value === 'connected');\n\n /**\n * Computed property indicating if the WebSocket is attempting to reconnect\n */\n const isReconnecting = computed(() => connectionState.value === 'reconnecting');\n\n // Note: We don't disconnect on component unmount because this is singleton state\n // and other components may still be using the connection.\n // The connection will be cleaned up when the page unloads.\n\n // Only register lifecycle hooks when inside a Vue component context\n if (hasComponentContext()) {\n onMounted(() => {\n if (currentOptions.autoConnect) {\n connect();\n }\n });\n }\n\n return {\n /**\n * Current connection state\n */\n connectionState: computed(() => connectionState.value),\n\n /**\n * Whether the WebSocket is connected\n */\n connected,\n\n /**\n * Whether the WebSocket is attempting to reconnect\n */\n isReconnecting,\n\n /**\n * Server version received on connection\n */\n serverVersion: computed(() => serverVersion.value),\n\n /**\n * Number of reconnection attempts made\n */\n reconnectAttempts: computed(() => reconnectAttempts.value),\n\n /**\n * Connect to the WebSocket server\n */\n connect,\n\n /**\n * Disconnect from the WebSocket server\n */\n disconnect,\n\n /**\n * Send a command to the server\n */\n send,\n\n /**\n * Subscribe to a server event\n */\n on,\n\n /**\n * Unsubscribe from a server event\n */\n off,\n\n /**\n * Subscribe to an event once (one-shot, auto-unsubscribes after first call)\n */\n once,\n\n /**\n * Subscribe to an event until handler returns true\n */\n onUntil,\n\n /**\n * Reset the composable state (useful for testing)\n */\n resetState,\n\n // =========================================================================\n // Multi-spec command wrappers\n // =========================================================================\n\n getSpecs,\n getRegistry,\n getTimeline,\n clearTimeline,\n getStore,\n setStore,\n clearStore,\n setSimulation,\n clearSimulation,\n reseed,\n };\n}\n","/**\n * Vue Router Configuration\n *\n * What: Defines routing configuration for the DevTools SPA\n * How: Creates routes for each main page/tab in the application\n * Why: Enables navigation between different DevTools features\n */\n\nimport { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router';\n\n/**\n * Route definitions for the DevTools SPA\n *\n * Each route corresponds to a tab in the DevTools interface:\n * - Routes: Endpoint listing and details\n * - Timeline: Request/response log with real-time updates\n * - Models: Store data editor for viewing/modifying mock data\n * - Simulator: Error simulation controls\n */\nconst routes: RouteRecordRaw[] = [\n {\n path: '/',\n redirect: '/routes',\n },\n {\n path: '/routes',\n name: 'routes',\n component: () => import('@/pages/RoutesPage.vue'),\n meta: {\n title: 'Routes',\n icon: 'route',\n },\n },\n {\n path: '/timeline',\n name: 'timeline',\n component: () => import('@/pages/TimelinePage.vue'),\n meta: {\n title: 'Timeline',\n icon: 'clock',\n },\n },\n {\n path: '/models',\n name: 'models',\n component: () => import('@/pages/ModelsPage.vue'),\n meta: {\n title: 'Models',\n icon: 'database',\n },\n },\n {\n path: '/simulator',\n name: 'simulator',\n component: () => import('@/pages/SimulatorPage.vue'),\n meta: {\n title: 'Simulator',\n icon: 'zap',\n },\n },\n // Catch-all route for undefined paths - redirects to routes page\n {\n path: '/:pathMatch(.*)*',\n name: 'not-found',\n redirect: '/routes',\n },\n];\n\n/**\n * Create the router instance\n *\n * Uses hash history mode for compatibility when embedded in iframes\n * or served from arbitrary paths in the dev server\n */\nconst router = createRouter({\n history: createWebHashHistory(),\n routes,\n});\n\nexport { routes };\nexport default router;\n","<!--\n App.vue - Main Application Component\n\n What: Root component for the DevTools SPA\n How: Provides the main layout with header, tab navigation, and router view\n Why: Acts as the entry point for the Vue application and defines the overall structure\n-->\n\n<script setup lang=\"ts\">\nimport { Clock, Database, Route, Wifi, WifiOff, Zap } from 'lucide-vue-next';\nimport { computed } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport { useWebSocket } from '@/composables/useWebSocket';\nimport { routes } from '@/router';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Navigation tabs derived from routes\nconst tabs = computed(() =>\n routes\n .filter((r) => r.name && r.meta?.title)\n .map((r) => ({\n name: r.name as string,\n path: r.path,\n title: r.meta?.title as string,\n icon: r.meta?.icon as string,\n })),\n);\n\n// Current active tab\nconst activeTab = computed(() => route.name as string);\n\n// Navigate to a tab\nfunction navigateTo(path: string): void {\n router.push(path);\n}\n\n// Icon component map\nconst iconMap: Record<string, typeof Route> = {\n route: Route,\n clock: Clock,\n database: Database,\n zap: Zap,\n};\n\n// Get icon with fallback for unknown icon names\nfunction getIcon(iconName: string): typeof Route {\n const icon = iconMap[iconName];\n if (!icon && import.meta.env.DEV) {\n console.warn(`[DevTools] Unknown icon name: \"${iconName}\". Using fallback.`);\n }\n return icon ?? Route;\n}\n\n// Connection status from WebSocket composable\nconst { connected: isConnected } = useWebSocket();\n</script>\n\n<template>\n <div class=\"app\">\n <!-- Header with branding and connection status -->\n <header class=\"app-header\">\n <div class=\"app-header__brand\">\n <Zap class=\"app-header__logo\" :size=\"20\" />\n <span class=\"app-header__title\">OpenAPI DevTools</span>\n </div>\n\n <!-- Tab Navigation -->\n <nav class=\"app-nav\" role=\"tablist\" aria-label=\"DevTools navigation\">\n <button\n v-for=\"tab in tabs\"\n :key=\"tab.name\"\n role=\"tab\"\n :aria-selected=\"activeTab === tab.name\"\n :aria-current=\"activeTab === tab.name ? 'true' : undefined\"\n :aria-controls=\"`panel-${tab.name}`\"\n :tabindex=\"0\"\n :class=\"[\n 'app-nav__tab',\n { 'app-nav__tab--active': activeTab === tab.name },\n ]\"\n @click=\"navigateTo(tab.path)\"\n >\n <component\n :is=\"getIcon(tab.icon)\"\n :size=\"16\"\n class=\"app-nav__icon\"\n />\n <span class=\"app-nav__label\">{{ tab.title }}</span>\n </button>\n </nav>\n\n <!-- Connection Status -->\n <div class=\"app-header__status\">\n <div class=\"connection-status\">\n <span\n :class=\"[\n 'connection-status__dot',\n isConnected\n ? 'connection-status__dot--connected'\n : 'connection-status__dot--disconnected',\n ]\"\n />\n <span class=\"connection-status__text\">\n {{ isConnected ? 'Connected' : 'Disconnected' }}\n </span>\n <component\n :is=\"isConnected ? Wifi : WifiOff\"\n :size=\"14\"\n class=\"connection-status__icon\"\n />\n </div>\n </div>\n </header>\n\n <!-- Main Content Area -->\n <main class=\"app-main\">\n <router-view v-slot=\"{ Component }\">\n <transition name=\"fade\" mode=\"out-in\">\n <component :is=\"Component\" />\n </transition>\n </router-view>\n </main>\n </div>\n</template>\n\n<style scoped>\n.app {\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--devtools-bg);\n}\n\n/* Header styles */\n.app-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: var(--devtools-header-height);\n padding: 0 var(--devtools-space-md);\n background-color: var(--devtools-surface);\n border-bottom: 1px solid var(--devtools-border);\n position: sticky;\n top: 0;\n z-index: 100;\n}\n\n.app-header__brand {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-sm);\n}\n\n.app-header__logo {\n color: var(--devtools-primary);\n}\n\n.app-header__title {\n font-weight: var(--font-weight-6);\n font-size: var(--font-size-1);\n color: var(--devtools-text);\n}\n\n.app-header__status {\n display: flex;\n align-items: center;\n}\n\n/* Navigation styles */\n.app-nav {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-xs);\n height: 100%;\n}\n\n.app-nav__tab {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-xs);\n height: 100%;\n padding: 0 var(--devtools-space-md);\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n color: var(--devtools-text-muted);\n font-family: var(--devtools-font-sans);\n font-size: var(--font-size-0);\n font-weight: var(--font-weight-5);\n cursor: pointer;\n transition: all var(--devtools-transition-fast);\n}\n\n.app-nav__tab:hover {\n color: var(--devtools-text);\n background-color: var(--devtools-surface-elevated);\n}\n\n.app-nav__tab:focus {\n outline: none;\n}\n\n.app-nav__tab:focus-visible {\n outline: 2px solid var(--devtools-primary);\n outline-offset: -2px;\n background-color: var(--devtools-surface-elevated);\n}\n\n/* High contrast mode support */\n@media (forced-colors: active) {\n .app-nav__tab:focus-visible {\n outline: 3px solid CanvasText;\n outline-offset: 2px;\n }\n}\n\n.app-nav__tab--active {\n color: var(--devtools-primary);\n border-bottom-color: var(--devtools-primary);\n}\n\n.app-nav__tab--active:focus-visible {\n outline-color: var(--devtools-primary-hover);\n}\n\n.app-nav__icon {\n flex-shrink: 0;\n}\n\n.app-nav__label {\n white-space: nowrap;\n}\n\n/* Connection status styles */\n.connection-status {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-xs);\n font-size: var(--font-size-0);\n color: var(--devtools-text-muted);\n}\n\n.connection-status__icon {\n opacity: 0.7;\n}\n\n/* Main content styles */\n.app-main {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n</style>\n\n<style>\n/* Page transition - must be unscoped to apply to router-view children */\n.fade-enter-active,\n.fade-leave-active {\n transition: opacity var(--devtools-transition-normal);\n}\n\n.fade-enter-from,\n.fade-leave-to {\n opacity: 0;\n}\n</style>\n","/**\n * useTheme Composable\n *\n * What: Provides reactive theme management for the DevTools SPA\n * How: Uses CSS class on document root and localStorage for persistence\n * Why: Allows users to switch between dark and light mode, respecting system preference\n */\n\nimport { computed, getCurrentInstance, onMounted, onUnmounted, ref, watch } from 'vue';\n\n/**\n * Theme mode type\n */\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Valid theme modes for type guard validation\n */\nconst VALID_THEME_MODES: readonly ThemeMode[] = ['light', 'dark', 'system'];\n\n/**\n * Type guard to check if a value is a valid ThemeMode\n */\nfunction isThemeMode(value: unknown): value is ThemeMode {\n return typeof value === 'string' && VALID_THEME_MODES.includes(value as ThemeMode);\n}\n\n/**\n * Storage key for persisting theme preference\n */\nconst STORAGE_KEY = 'openapi-devtools-theme';\n\n/**\n * Singleton state for theme - shared across all component instances\n */\nconst themeMode = ref<ThemeMode>('system');\nconst systemPrefersDark = ref(false);\n\n/**\n * Track the current mediaQuery and handler for cleanup\n */\nlet currentMediaQuery: MediaQueryList | null = null;\nlet currentMediaHandler: ((e: MediaQueryListEvent) => void) | null = null;\n\n/**\n * Check if we're running in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if we're inside a Vue component context\n * This allows the composable to work both inside and outside components\n */\nfunction hasComponentContext(): boolean {\n return getCurrentInstance() !== null;\n}\n\n/**\n * useTheme composable\n *\n * Provides theme management functionality including:\n * - Reactive theme state\n * - System preference detection\n * - Theme toggling\n * - LocalStorage persistence\n *\n * @returns Theme management utilities\n */\nexport function useTheme() {\n /**\n * Computed property for the effective theme (resolved from system if needed)\n */\n const effectiveTheme = computed<'light' | 'dark'>(() => {\n if (themeMode.value === 'system') {\n return systemPrefersDark.value ? 'dark' : 'light';\n }\n return themeMode.value;\n });\n\n /**\n * Computed property indicating if dark mode is active\n */\n const isDark = computed(() => effectiveTheme.value === 'dark');\n\n /**\n * Apply the current theme to the document\n */\n function applyTheme(): void {\n if (!isBrowser()) return;\n\n const root = document.documentElement;\n\n if (effectiveTheme.value === 'dark') {\n root.classList.add('dark');\n root.classList.remove('light');\n } else {\n root.classList.add('light');\n root.classList.remove('dark');\n }\n }\n\n /**\n * Set the theme mode\n */\n function setTheme(mode: ThemeMode): void {\n if (!isThemeMode(mode)) {\n console.warn(`[DevTools] Invalid theme mode: ${mode}`);\n return;\n }\n\n themeMode.value = mode;\n\n if (isBrowser()) {\n try {\n localStorage.setItem(STORAGE_KEY, mode);\n } catch {\n // Storage unavailable (private browsing, quota exceeded, etc.)\n console.warn('[DevTools] Unable to persist theme preference');\n }\n }\n }\n\n /**\n * Toggle between light and dark mode\n * If currently in system mode, switch to the opposite of system preference\n */\n function toggleTheme(): void {\n if (themeMode.value === 'system') {\n // Switch to explicit mode opposite of system preference\n setTheme(systemPrefersDark.value ? 'light' : 'dark');\n } else {\n // Toggle between light and dark\n setTheme(themeMode.value === 'dark' ? 'light' : 'dark');\n }\n }\n\n /**\n * Reset to system preference\n */\n function resetToSystem(): void {\n setTheme('system');\n }\n\n /**\n * Clean up the media query listener\n */\n function cleanupMediaQuery(): void {\n if (currentMediaQuery && currentMediaHandler) {\n currentMediaQuery.removeEventListener('change', currentMediaHandler);\n currentMediaQuery = null;\n currentMediaHandler = null;\n }\n }\n\n /**\n * Initialize theme from storage and system preference\n */\n function initialize(): void {\n if (!isBrowser()) return;\n\n // Clean up any existing media query listener before adding a new one\n cleanupMediaQuery();\n\n // Check system preference\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n systemPrefersDark.value = mediaQuery.matches;\n\n // Create and store the handler for cleanup\n const handler = (e: MediaQueryListEvent): void => {\n systemPrefersDark.value = e.matches;\n };\n\n // Listen for system preference changes\n mediaQuery.addEventListener('change', handler);\n\n // Store references for cleanup\n currentMediaQuery = mediaQuery;\n currentMediaHandler = handler;\n\n // Load saved preference from localStorage\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (isThemeMode(saved)) {\n themeMode.value = saved;\n }\n } catch {\n // Storage unavailable (private browsing, etc.)\n }\n\n // Apply initial theme\n applyTheme();\n }\n\n /**\n * Reset theme state to defaults (useful for testing)\n * This resets the singleton state and cleans up listeners\n */\n function resetState(): void {\n cleanupMediaQuery();\n themeMode.value = 'system';\n systemPrefersDark.value = false;\n\n if (isBrowser()) {\n const root = document.documentElement;\n root.classList.remove('dark', 'light');\n\n try {\n localStorage.removeItem(STORAGE_KEY);\n } catch {\n // Storage unavailable\n }\n }\n }\n\n // Watch for theme changes and apply them\n watch([themeMode, systemPrefersDark], () => {\n applyTheme();\n });\n\n // Only register lifecycle hooks when inside a Vue component context\n // This allows the composable to be used in tests without Vue warnings\n if (hasComponentContext()) {\n // Initialize on mount\n onMounted(() => {\n initialize();\n });\n\n // Clean up on unmount\n onUnmounted(() => {\n cleanupMediaQuery();\n });\n }\n\n return {\n /**\n * Current theme mode setting ('light', 'dark', or 'system')\n */\n themeMode: computed(() => themeMode.value),\n\n /**\n * The effective theme after resolving 'system' mode\n */\n effectiveTheme,\n\n /**\n * Whether dark mode is currently active\n */\n isDark,\n\n /**\n * Whether the system prefers dark mode\n */\n systemPrefersDark: computed(() => systemPrefersDark.value),\n\n /**\n * Set the theme mode\n */\n setTheme,\n\n /**\n * Toggle between light and dark mode\n */\n toggleTheme,\n\n /**\n * Reset to system preference\n */\n resetToSystem,\n\n /**\n * Manually initialize theme (useful for SSR hydration)\n */\n initialize,\n\n /**\n * Reset theme state to defaults (useful for testing)\n */\n resetState,\n };\n}\n","/**\n * Main Entry Point for DevTools SPA\n *\n * What: Initializes and mounts the Vue application\n * How: Creates Vue app instance with Pinia and Vue Router\n * Why: Required entry point for the DevTools SPA\n */\n\nimport { createPinia } from 'pinia';\nimport type { App } from 'vue';\nimport { createApp } from 'vue';\n\nimport AppComponent from '@/App.vue';\nimport router from '@/router';\nimport { initializeStores } from '@/stores';\n\n// Import global styles\nimport '@/assets/main.css';\n\n/**\n * Module-scoped guard to ensure bootstrap only runs once\n */\nlet isBootstrapped = false;\nlet appInstance: App | null = null;\n\n/**\n * Create and configure the Vue application\n * This function is idempotent - calling it multiple times has no effect\n * after the first successful call.\n *\n * @returns The Vue app instance, or null if already bootstrapped\n */\nfunction bootstrap(): App | null {\n // Guard against multiple bootstrap calls\n if (isBootstrapped) {\n if (import.meta.env.DEV) {\n console.warn('[OpenAPI DevTools] Application already initialized, skipping bootstrap');\n }\n return appInstance;\n }\n\n // Create Vue app instance\n const app = createApp(AppComponent);\n\n // Create Pinia store instance\n const pinia = createPinia();\n\n // Install plugins\n app.use(pinia);\n app.use(router);\n\n // Initialize stores after Pinia is installed\n initializeStores();\n\n // Mount the app to the DOM\n app.mount('#app');\n\n // Mark as bootstrapped and store the instance\n isBootstrapped = true;\n appInstance = app;\n\n // Log startup info in development\n if (import.meta.env.DEV) {\n console.log('[OpenAPI DevTools] Application initialized');\n }\n\n return app;\n}\n\n// Only auto-bootstrap when running as standalone app (index.html entry)\n// When imported as a library, consumers should call bootstrap() manually\nif (typeof window !== 'undefined' && document.getElementById('app')) {\n bootstrap();\n}\n\n/**\n * Export the bootstrap function for library consumers\n * who may want to manually initialize the DevTools\n */\nexport { bootstrap };\n\nexport type {\n ClientCommand,\n ClientCommandType,\n ConnectedEventData,\n ConnectionState,\n EventHandler,\n ServerEvent,\n ServerEventType,\n ThemeMode,\n UseWebSocketOptions,\n UseWebSocketReturn,\n} from '@/composables';\n// Re-export composables for library consumers\nexport { useTheme, useWebSocket } from '@/composables';\n"],"names":["toKebabCase","string","toCamelCase","match","p1","p2","toPascalCase","camelCase","mergeClasses","classes","className","index","array","defaultAttributes","Icon","size","strokeWidth","absoluteStrokeWidth","color","iconNode","name","props","slots","h","child","createLucideIcon","iconName","Clock","Database","Route","WifiOff","Wifi","Zap","DEFAULT_SPEC_COLOR","useSpecsStore","defineStore","specs","ref","activeSpecFilter","specMap","computed","s","specIds","activeSpec","isFiltered","setSpecs","newSpecs","setFilter","specId","toggleFilter","getColor","DEFAULT_OPTIONS","connectionState","serverVersion","reconnectAttempts","ws","reconnectTimer","currentOptions","optionsInitialized","eventHandlers","isBrowser","hasComponentContext","getCurrentInstance","buildWebSocketUrl","path","clearReconnectTimer","dispatchEvent","type","data","handlers","handler","error","wildcardHandlers","handleOpen","requestInitialData","spec","send","handleMessage","event","message","connectedData","handleClose","connect","handleError","cleanupConnection","url","disconnect","command","getSpecs","getRegistry","getTimeline","limit","clearTimeline","getStore","schema","setStore","items","clearStore","setSimulation","config","clearSimulation","reseed","on","currentHandlers","off","once","wrappedHandler","onUntil","resetState","useWebSocket","options","connected","isReconnecting","onMounted","routes","router","createRouter","createWebHashHistory","route","useRoute","useRouter","tabs","r","activeTab","navigateTo","iconMap","getIcon","isConnected","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","_cache","_hoisted_4","_Fragment","_renderList","tab","_normalizeClass","$event","_createBlock","_resolveDynamicComponent","_hoisted_6","_toDisplayString","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_component_router_view","_withCtx","Component","_Transition","VALID_THEME_MODES","isThemeMode","value","STORAGE_KEY","themeMode","systemPrefersDark","currentMediaQuery","currentMediaHandler","useTheme","effectiveTheme","isDark","applyTheme","root","setTheme","mode","toggleTheme","resetToSystem","cleanupMediaQuery","initialize","mediaQuery","e","saved","watch","onUnmounted","isBootstrapped","appInstance","bootstrap","app","createApp","AppComponent","pinia","createPinia"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9]}
@@ -1,4 +1,4 @@
1
- import{E as te,c as D,r as C,H as se,d as q,e as m,y as M,i as $,f as a,u as r,F as N,j as A,n as B,t as S,s as f,D as H,I as ae,J as ne,o as oe,k as le,z as Y,G as re,K as ie,L as ce}from"./vue-vendor-Bkktf9yg.js";import{c as W,D as K,_ as G,u as de}from"./index-x38XzU3c.js";import{C as ue}from"./check-DaD3RpI4.js";import{X as fe}from"./x-DGt4DUCk.js";import{T as me}from"./trash-2-BhRxw6RN.js";const ve=W("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);const he=W("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);const ye=W("save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]);function L(t){try{return structuredClone(se(t))}catch{return JSON.parse(JSON.stringify(t))}}const _e=te("models",()=>{const t=C([]),e=C(null),i=C([]),c=C([]),u=C(!1),l=C(null),_=D(()=>e.value?t.value.find(n=>n.name===e.value)??null:null),g=C(!1),d=D(()=>g.value),v=D(()=>t.value.length),p=D(()=>t.value.reduce((n,y)=>n+y.count,0));async function k(){u.value=!0,l.value=null;try{const n=await fetch("/_api/store");if(!n.ok)throw new Error(`Failed to fetch schemas: ${n.statusText}`);const y=await n.json();t.value=y.schemas??[]}catch(n){l.value=n instanceof Error?n.message:"Failed to fetch schemas",console.error("[ModelsStore] Error fetching schemas:",n)}finally{u.value=!1}}async function F(n){e.value!==n&&(e.value=n,await T(n))}async function T(n){u.value=!0,l.value=null;try{const y=await fetch(`/_api/store/${encodeURIComponent(n)}`);if(!y.ok)throw new Error(`Failed to fetch schema data: ${y.statusText}`);const x=await y.json(),j=x.items??[];i.value=L(j),c.value=L(j),g.value=!1;const X=t.value.findIndex(ee=>ee.name===n);X!==-1&&(t.value[X].count=x.count)}catch(y){l.value=y instanceof Error?y.message:"Failed to fetch schema data",console.error("[ModelsStore] Error fetching schema data:",y)}finally{u.value=!1}}function R(n){if(!Array.isArray(n)){l.value="Invalid data: Expected an array of items",console.error("[ModelsStore] updateItems received non-array value:",typeof n);return}i.value=n,l.value=null,g.value=!0}async function V(){if(!e.value)return l.value="No schema selected",!1;u.value=!0,l.value=null;try{const n=await fetch(`/_api/store/${encodeURIComponent(e.value)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i.value)});if(!n.ok){const j=await n.json().catch(()=>({}));throw new Error(j.error||`Failed to save items: ${n.statusText}`)}const y=await n.json();c.value=L(i.value),g.value=!1;const x=t.value.findIndex(j=>j.name===e.value);return x!==-1&&(t.value[x].count=y.created??i.value.length),!0}catch(n){return l.value=n instanceof Error?n.message:"Failed to save items",console.error("[ModelsStore] Error saving items:",n),!1}finally{u.value=!1}}async function O(){if(!e.value)return l.value="No schema selected",!1;u.value=!0,l.value=null;try{const n=await fetch(`/_api/store/${encodeURIComponent(e.value)}`,{method:"DELETE"});if(!n.ok)throw new Error(`Failed to clear schema: ${n.statusText}`);i.value=[],c.value=[],g.value=!1;const y=t.value.findIndex(x=>x.name===e.value);return y!==-1&&(t.value[y].count=0),!0}catch(n){return l.value=n instanceof Error?n.message:"Failed to clear schema",console.error("[ModelsStore] Error clearing schema:",n),!1}finally{u.value=!1}}function h(){i.value=L(c.value),g.value=!1}async function o(){e.value?await T(e.value):await k()}function s(){t.value=[],e.value=null,i.value=[],c.value=[],u.value=!1,l.value=null,g.value=!1}function b(n){const y=t.value.findIndex(x=>x.name===n.schema);y!==-1&&(t.value[y].count=n.count),e.value===n.schema&&(d.value?console.warn(`[ModelsStore] Skipping auto-refresh for schema "${n.schema}" - unsaved changes exist`):T(n.schema))}function E(n){n.success&&(k(),e.value&&(d.value?console.warn(`[ModelsStore] Skipping auto-refresh after reseed for schema "${e.value}" - unsaved changes exist`):T(e.value)))}return{schemas:t,selectedSchema:e,currentItems:i,loading:u,error:l,currentSchema:_,isDirty:d,schemaCount:v,totalItems:p,fetchSchemas:k,selectSchemaByName:F,fetchSchemaData:T,updateItems:R,saveItems:V,clearSchema:O,discardChanges:h,refresh:o,reset:s,handleStoreUpdate:b,handleReseedComplete:E}});function P(t,e){return t.length<=e?t:`${t.slice(0,e)}…`}function pe(t){return t==null?"--":typeof t=="string"?P(t,50):typeof t=="number"||typeof t=="boolean"?String(t):Array.isArray(t)||typeof t=="object"?P(JSON.stringify(t),50):String(t)}function Q(t,e){if(!(typeof t!="object"||t===null))return t[e]}function ge(t,e,i){const c=Q(t,e);return typeof c=="string"||typeof c=="number"?c:i}function be(t,e){if(t.length===0)return[];const i=new Set;for(const u of t)if(typeof u=="object"&&u!==null)for(const l of Object.keys(u))i.add(l);if(i.size===0)return[];const c=Array.from(i);return c.includes(e)?[e,...c.filter(u=>u!==e)]:c}const Se={class:"data-table"},ke={key:0,class:"data-table__empty"},Ce={key:1,class:"data-table__container"},we={class:"data-table__table"},xe=["onClick"],Ie={class:"data-table__row-num"},$e={key:2,class:"data-table__footer"},Te={class:"data-table__info"},Ee={class:"data-table__info"},je=q({__name:"DataTable",props:{items:{},idField:{},selectedIndex:{default:-1}},emits:["select"],setup(t,{emit:e}){const i=t,c=e,u=D(()=>be(i.items,i.idField));function l(g){c("select",g)}function _(g,d){return ge(g,i.idField,d)}return(g,d)=>(f(),m("div",Se,[t.items.length===0?(f(),m("div",ke,[$(r(K),{size:32,class:"data-table__empty-icon"}),d[0]||(d[0]=a("span",null,"No items",-1))])):(f(),m("div",Ce,[a("table",we,[a("thead",null,[a("tr",null,[d[1]||(d[1]=a("th",{class:"data-table__row-num"},"#",-1)),(f(!0),m(N,null,A(u.value,v=>(f(),m("th",{key:v,class:B(["data-table__header",{"data-table__header--id":v===t.idField}])},S(v),3))),128))])]),a("tbody",null,[(f(!0),m(N,null,A(t.items,(v,p)=>(f(),m("tr",{key:_(v,p),class:B(["data-table__row",{"data-table__row--selected":p===t.selectedIndex}]),onClick:k=>l(p)},[a("td",Ie,S(p+1),1),(f(!0),m(N,null,A(u.value,k=>(f(),m("td",{key:k,class:B(["data-table__cell",{"data-table__cell--id":k===t.idField}])},S(r(pe)(r(Q)(v,k))),3))),128))],10,xe))),128))])])])),t.items.length>0?(f(),m("div",$e,[a("span",Te,S(t.items.length)+" items",1),a("span",Ee,S(u.value.length)+" columns",1)])):M("",!0)]))}}),De=G(je,[["__scopeId","data-v-dcdce8f7"]]),Me={class:"json-editor"},Ne={class:"json-editor__container"},Je=["value","readonly","placeholder"],Fe={class:"json-editor__status"},Re={class:"json-editor__status-left"},Ve={key:0,class:"json-editor__valid"},Oe={key:1,class:"json-editor__error"},Ae={class:"json-editor__status-right"},ze={class:"json-editor__info"},Le=q({__name:"JsonEditor",props:{modelValue:{},readonly:{type:Boolean,default:!1},placeholder:{default:"Enter JSON data..."},minHeight:{default:200}},emits:["update:modelValue"],setup(t,{expose:e,emit:i}){const c=t,u=i,l=C(""),_=C(null),g=D(()=>_.value===null),d=D(()=>l.value.split(`
1
+ import{d as te,c as D,r as C,H as se,e as q,f as m,z as M,j as $,i as a,u as r,F as N,k as A,n as B,t as S,v as f,E as H,I as ae,J as ne,o as oe,l as le,A as Y,G as re,K as ie,L as ce}from"./vue-vendor-D62nux6V.js";import{c as W,D as K,_ as G,u as de}from"./index-BQr16DJ3.js";import{C as ue}from"./check-9axZ93X-.js";import{X as fe}from"./x-9zeMJ8Mo.js";import{T as me}from"./trash-2-Cj6nHRK8.js";const ve=W("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);const he=W("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);const ye=W("save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]);function L(t){try{return structuredClone(se(t))}catch{return JSON.parse(JSON.stringify(t))}}const _e=te("models",()=>{const t=C([]),e=C(null),i=C([]),c=C([]),u=C(!1),l=C(null),_=D(()=>e.value?t.value.find(n=>n.name===e.value)??null:null),g=C(!1),d=D(()=>g.value),v=D(()=>t.value.length),p=D(()=>t.value.reduce((n,y)=>n+y.count,0));async function k(){u.value=!0,l.value=null;try{const n=await fetch("/_api/store");if(!n.ok)throw new Error(`Failed to fetch schemas: ${n.statusText}`);const y=await n.json();t.value=y.schemas??[]}catch(n){l.value=n instanceof Error?n.message:"Failed to fetch schemas",console.error("[ModelsStore] Error fetching schemas:",n)}finally{u.value=!1}}async function F(n){e.value!==n&&(e.value=n,await T(n))}async function T(n){u.value=!0,l.value=null;try{const y=await fetch(`/_api/store/${encodeURIComponent(n)}`);if(!y.ok)throw new Error(`Failed to fetch schema data: ${y.statusText}`);const x=await y.json(),j=x.items??[];i.value=L(j),c.value=L(j),g.value=!1;const X=t.value.findIndex(ee=>ee.name===n);X!==-1&&(t.value[X].count=x.count)}catch(y){l.value=y instanceof Error?y.message:"Failed to fetch schema data",console.error("[ModelsStore] Error fetching schema data:",y)}finally{u.value=!1}}function R(n){if(!Array.isArray(n)){l.value="Invalid data: Expected an array of items",console.error("[ModelsStore] updateItems received non-array value:",typeof n);return}i.value=n,l.value=null,g.value=!0}async function V(){if(!e.value)return l.value="No schema selected",!1;u.value=!0,l.value=null;try{const n=await fetch(`/_api/store/${encodeURIComponent(e.value)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i.value)});if(!n.ok){const j=await n.json().catch(()=>({}));throw new Error(j.error||`Failed to save items: ${n.statusText}`)}const y=await n.json();c.value=L(i.value),g.value=!1;const x=t.value.findIndex(j=>j.name===e.value);return x!==-1&&(t.value[x].count=y.created??i.value.length),!0}catch(n){return l.value=n instanceof Error?n.message:"Failed to save items",console.error("[ModelsStore] Error saving items:",n),!1}finally{u.value=!1}}async function O(){if(!e.value)return l.value="No schema selected",!1;u.value=!0,l.value=null;try{const n=await fetch(`/_api/store/${encodeURIComponent(e.value)}`,{method:"DELETE"});if(!n.ok)throw new Error(`Failed to clear schema: ${n.statusText}`);i.value=[],c.value=[],g.value=!1;const y=t.value.findIndex(x=>x.name===e.value);return y!==-1&&(t.value[y].count=0),!0}catch(n){return l.value=n instanceof Error?n.message:"Failed to clear schema",console.error("[ModelsStore] Error clearing schema:",n),!1}finally{u.value=!1}}function h(){i.value=L(c.value),g.value=!1}async function o(){e.value?await T(e.value):await k()}function s(){t.value=[],e.value=null,i.value=[],c.value=[],u.value=!1,l.value=null,g.value=!1}function b(n){const y=t.value.findIndex(x=>x.name===n.schema);y!==-1&&(t.value[y].count=n.count),e.value===n.schema&&(d.value?console.warn(`[ModelsStore] Skipping auto-refresh for schema "${n.schema}" - unsaved changes exist`):T(n.schema))}function E(n){n.success&&(k(),e.value&&(d.value?console.warn(`[ModelsStore] Skipping auto-refresh after reseed for schema "${e.value}" - unsaved changes exist`):T(e.value)))}return{schemas:t,selectedSchema:e,currentItems:i,loading:u,error:l,currentSchema:_,isDirty:d,schemaCount:v,totalItems:p,fetchSchemas:k,selectSchemaByName:F,fetchSchemaData:T,updateItems:R,saveItems:V,clearSchema:O,discardChanges:h,refresh:o,reset:s,handleStoreUpdate:b,handleReseedComplete:E}});function P(t,e){return t.length<=e?t:`${t.slice(0,e)}…`}function pe(t){return t==null?"--":typeof t=="string"?P(t,50):typeof t=="number"||typeof t=="boolean"?String(t):Array.isArray(t)||typeof t=="object"?P(JSON.stringify(t),50):String(t)}function Q(t,e){if(!(typeof t!="object"||t===null))return t[e]}function ge(t,e,i){const c=Q(t,e);return typeof c=="string"||typeof c=="number"?c:i}function be(t,e){if(t.length===0)return[];const i=new Set;for(const u of t)if(typeof u=="object"&&u!==null)for(const l of Object.keys(u))i.add(l);if(i.size===0)return[];const c=Array.from(i);return c.includes(e)?[e,...c.filter(u=>u!==e)]:c}const Se={class:"data-table"},ke={key:0,class:"data-table__empty"},Ce={key:1,class:"data-table__container"},we={class:"data-table__table"},xe=["onClick"],Ie={class:"data-table__row-num"},$e={key:2,class:"data-table__footer"},Te={class:"data-table__info"},Ee={class:"data-table__info"},je=q({__name:"DataTable",props:{items:{},idField:{},selectedIndex:{default:-1}},emits:["select"],setup(t,{emit:e}){const i=t,c=e,u=D(()=>be(i.items,i.idField));function l(g){c("select",g)}function _(g,d){return ge(g,i.idField,d)}return(g,d)=>(f(),m("div",Se,[t.items.length===0?(f(),m("div",ke,[$(r(K),{size:32,class:"data-table__empty-icon"}),d[0]||(d[0]=a("span",null,"No items",-1))])):(f(),m("div",Ce,[a("table",we,[a("thead",null,[a("tr",null,[d[1]||(d[1]=a("th",{class:"data-table__row-num"},"#",-1)),(f(!0),m(N,null,A(u.value,v=>(f(),m("th",{key:v,class:B(["data-table__header",{"data-table__header--id":v===t.idField}])},S(v),3))),128))])]),a("tbody",null,[(f(!0),m(N,null,A(t.items,(v,p)=>(f(),m("tr",{key:_(v,p),class:B(["data-table__row",{"data-table__row--selected":p===t.selectedIndex}]),onClick:k=>l(p)},[a("td",Ie,S(p+1),1),(f(!0),m(N,null,A(u.value,k=>(f(),m("td",{key:k,class:B(["data-table__cell",{"data-table__cell--id":k===t.idField}])},S(r(pe)(r(Q)(v,k))),3))),128))],10,xe))),128))])])])),t.items.length>0?(f(),m("div",$e,[a("span",Te,S(t.items.length)+" items",1),a("span",Ee,S(u.value.length)+" columns",1)])):M("",!0)]))}}),De=G(je,[["__scopeId","data-v-dcdce8f7"]]),Me={class:"json-editor"},Ne={class:"json-editor__container"},Je=["value","readonly","placeholder"],Fe={class:"json-editor__status"},Re={class:"json-editor__status-left"},Ve={key:0,class:"json-editor__valid"},Oe={key:1,class:"json-editor__error"},Ae={class:"json-editor__status-right"},ze={class:"json-editor__info"},Le=q({__name:"JsonEditor",props:{modelValue:{},readonly:{type:Boolean,default:!1},placeholder:{default:"Enter JSON data..."},minHeight:{default:200}},emits:["update:modelValue"],setup(t,{expose:e,emit:i}){const c=t,u=i,l=C(""),_=C(null),g=D(()=>_.value===null),d=D(()=>l.value.split(`
2
2
  `).length),v=D(()=>Array.from({length:d.value},(h,o)=>o+1)),p=C(null);function k(){try{l.value=JSON.stringify(c.modelValue,null,2),_.value=null}catch{l.value="",_.value="Invalid initial value"}}k(),H(()=>c.modelValue,()=>{try{const h=JSON.parse(l.value);JSON.stringify(h)!==JSON.stringify(c.modelValue)&&k()}catch{k()}});function F(h){const o=h.target;l.value=o.value,T()}function T(){if(l.value.trim()===""){_.value=null,u("update:modelValue",[]);return}try{const h=JSON.parse(l.value);_.value=null,u("update:modelValue",h)}catch(h){if(h instanceof Error){const o=h.message.match(/position (\d+)/);if(o){const s=Number.parseInt(o[1],10),b=l.value.substring(0,s).split(`
3
3
  `),E=b.length,n=b[b.length-1].length+1;_.value=`Line ${E}, Column ${n}: ${h.message}`}else _.value=h.message}else _.value="Invalid JSON"}}function R(){try{const h=JSON.parse(l.value);l.value=JSON.stringify(h,null,2),_.value=null,u("update:modelValue",h)}catch{}}function V(h){if(h.key==="Tab"){h.preventDefault();const o=h.target,s=o.selectionStart,b=o.selectionEnd,E=`${l.value.substring(0,s)} ${l.value.substring(b)}`;l.value=E,setTimeout(()=>{o.selectionStart=o.selectionEnd=s+2},0),T()}}function O(h){const o=h.target;p.value&&(p.value.scrollTop=o.scrollTop)}return e({formatJson:R,isValid:g}),(h,o)=>(f(),m("div",Me,[a("div",Ne,[a("div",{ref_key:"linesRef",ref:p,class:"json-editor__lines","aria-hidden":"true"},[(f(!0),m(N,null,A(v.value,s=>(f(),m("div",{key:s,class:"json-editor__line-number"},S(s),1))),128))],512),a("textarea",{value:l.value,readonly:t.readonly,placeholder:t.placeholder,style:ae({"--json-editor-min-height":`${t.minHeight}px`}),class:"json-editor__textarea",spellcheck:"false",onInput:F,onKeydown:V,onScroll:O},null,44,Je)]),a("div",Fe,[a("div",Re,[g.value?(f(),m("div",Ve,[$(r(ue),{size:14}),o[0]||(o[0]=a("span",null,"Valid JSON",-1))])):_.value?(f(),m("div",Oe,[$(r(ve),{size:14}),a("span",null,S(_.value),1)])):M("",!0)]),a("div",Ae,[a("span",ze,S(d.value)+" lines",1)])])]))}}),Ue=G(Le,[["__scopeId","data-v-a953e764"]]),z=C([]),I=ne({visible:!1,title:"",message:"",confirmText:"Confirm",cancelText:"Cancel",onConfirm:null,onCancel:null}),J=new Map;let w=null,Be=0;function Pe(){return`toast-${Date.now()}-${Be++}`}function U(t,e,i=3e3){const c=Pe(),u={id:c,type:t,message:e,duration:i,timestamp:Date.now()};if(z.value.push(u),i>0){const l=setTimeout(()=>{Z(c),J.delete(c)},i);J.set(c,l)}return c}function Z(t){const e=J.get(t);e!==void 0&&(clearTimeout(e),J.delete(t));const i=z.value.findIndex(c=>c.id===t);i!==-1&&z.value.splice(i,1)}function He(){function t(d,v){return U("success",d,v)}function e(d,v){return U("error",d,v)}function i(d,v){return U("info",d,v)}function c(d,v){return U("warning",d,v)}function u(d,v){return new Promise(p=>{w&&(w(!1),w=null),w=p,I.visible=!0,I.title=v?.title||"Confirm",I.message=d,I.confirmText=v?.confirmText||"Confirm",I.cancelText=v?.cancelText||"Cancel",I.onConfirm=()=>{I.visible=!1,w&&(w(!0),w=null)},I.onCancel=()=>{I.visible=!1,w&&(w(!1),w=null)}})}function l(){I.visible=!1,w&&(w(!1),w=null)}function _(d){Z(d)}function g(){for(const d of J.values())clearTimeout(d);J.clear(),z.value=[]}return{toasts:z,confirmDialog:I,success:t,error:e,info:i,warning:c,dismiss:_,clearAll:g,confirm:u,closeConfirm:l}}const Ke={class:"models-page"},qe={class:"models-sidebar"},We={class:"models-sidebar__header"},Ge={class:"models-sidebar__badge"},Xe={key:0,class:"models-sidebar__loading"},Ye={key:1,class:"models-sidebar__list"},Qe=["onClick"],Ze={class:"models-sidebar__name"},et={class:"models-sidebar__count"},tt={class:"models-sidebar__footer"},st={class:"models-sidebar__stat"},at={class:"font-mono"},nt={class:"models-content"},ot={class:"models-toolbar"},lt={class:"models-toolbar__title"},rt={class:"font-mono"},it={class:"text-muted"},ct={key:0,class:"models-toolbar__badge models-toolbar__badge--warning"},dt={class:"models-toolbar__actions"},ut=["disabled"],ft=["disabled"],mt={key:0,class:"models-error"},vt={class:"models-panels"},ht={class:"models-panel"},yt={class:"models-panel"},_t={key:1,class:"models-loading-overlay"},pt={key:1,class:"empty-state"},gt={key:0,class:"models-error"},bt={class:"modal__body"},St={class:"modal__footer"},kt=q({__name:"ModelsPage",setup(t){const e=_e(),{send:i,on:c,connected:u}=de(),{success:l,error:_,confirm:g}=He(),d=C(null),v=C(!1),p=C(-1);oe(async()=>{try{await e.fetchSchemas(),e.schemas.length>0&&!e.selectedSchema&&await e.selectSchemaByName(e.schemas[0].name)}catch(o){e.error||(e.error=o instanceof Error?o.message:"Failed to load schemas")}}),c("store:updated",o=>{const s=o;if(typeof o!="object"||o===null||typeof s.schema!="string"||typeof s.action!="string"||typeof s.count!="number"){console.warn("[ModelsPage] Invalid store:updated payload:",o);return}e.handleStoreUpdate(o),p.value=-1}),c("reseeded",o=>{const s=o;if(typeof o!="object"||o===null||typeof s.success!="boolean"||!Array.isArray(s.schemas)){console.warn("[ModelsPage] Invalid reseeded payload:",o);return}e.handleReseedComplete(o),p.value=-1}),H(()=>e.selectedSchema,async()=>{p.value=-1,d.value&&e.currentItems.length>0&&(await ce(),d.value?.formatJson())}),H(()=>e.currentItems,(o,s)=>{const b=p.value;if(b<0)return;if(b>=o.length){p.value=-1;return}const E=e.currentSchema?.idField??"id",n=s?.[b],y=o[b];if(n!==y){if(typeof n=="object"&&n!==null&&typeof y=="object"&&y!==null){const x=n[E],j=y[E];if(x!==void 0&&x===j)return}p.value=-1}});async function k(o){e.isDirty&&!await g("You have unsaved changes. Are you sure you want to switch schemas?",{title:"Unsaved Changes",confirmText:"Switch Schema",cancelText:"Cancel"})||await e.selectSchemaByName(o)}async function F(){if(!d.value?.isValid){_("Cannot save invalid JSON. Please fix the errors first.");return}if(await e.saveItems())l("Items saved successfully");else{const s=e.error||"Failed to save items";_(s)}}async function T(){if(!e.isDirty)return;await g("Discard all changes and revert to saved data?",{title:"Discard Changes",confirmText:"Discard",cancelText:"Cancel"})&&(e.discardChanges(),l("Changes discarded"))}async function R(){v.value=!1;try{if(await e.clearSchema())l("Schema cleared successfully");else{const s=e.error||"Failed to clear schema";_(s)}}catch(o){const s=o instanceof Error?o.message:"Failed to clear schema";_(s)}}async function V(){if(!u.value){_("WebSocket not connected. Cannot trigger reseed.");return}await g("This will regenerate all seed data and replace existing items. Continue?",{title:"Reseed All Schemas",confirmText:"Reseed",cancelText:"Cancel"})&&(i({type:"reseed"}),l("Reseed command sent"))}function O(o){e.updateItems(o)}function h(o){p.value=p.value===o?-1:o}return(o,s)=>(f(),m("div",Ke,[a("aside",qe,[a("div",We,[$(r(K),{size:18}),s[4]||(s[4]=a("span",null,"Schemas",-1)),a("span",Ge,S(r(e).schemaCount),1)]),r(e).loading&&r(e).schemas.length===0?(f(),m("div",Xe,[...s[5]||(s[5]=[a("div",{class:"spinner"},null,-1),a("span",null,"Loading...",-1)])])):(f(),m("div",Ye,[(f(!0),m(N,null,A(r(e).schemas,b=>(f(),m("button",{key:b.name,class:B(["models-sidebar__item",{"models-sidebar__item--active":r(e).selectedSchema===b.name}]),onClick:E=>k(b.name)},[a("span",Ze,S(b.name),1),a("span",et,S(b.count),1)],10,Qe))),128))])),a("div",tt,[a("div",st,[s[6]||(s[6]=a("span",{class:"text-muted"},"Total Items:",-1)),a("span",at,S(r(e).totalItems),1)])])]),a("main",nt,[r(e).selectedSchema?(f(),m(N,{key:0},[a("div",ot,[a("div",lt,[a("span",rt,S(r(e).selectedSchema),1),a("span",it,"("+S(r(e).currentItems.length)+" items)",1),r(e).isDirty?(f(),m("span",ct," Unsaved ")):M("",!0)]),a("div",dt,[r(e).isDirty?(f(),m("button",{key:0,class:"btn btn--ghost",title:"Discard changes",onClick:T},[$(r(fe),{size:16}),s[7]||(s[7]=a("span",null,"Discard",-1))])):M("",!0),a("button",{disabled:!r(e).isDirty||r(e).loading,class:"btn btn--primary",title:"Save changes",onClick:F},[$(r(ye),{size:16}),s[8]||(s[8]=a("span",null,"Save",-1))],8,ut),a("button",{class:"btn btn--danger",title:"Clear all items",onClick:s[0]||(s[0]=b=>v.value=!0)},[$(r(me),{size:16}),s[9]||(s[9]=a("span",null,"Clear",-1))]),a("button",{disabled:!r(u),class:"btn btn--secondary",title:"Reseed all schemas with generated data",onClick:V},[$(r(he),{size:16}),s[10]||(s[10]=a("span",null,"Reseed All",-1))],8,ft)])]),r(e).error?(f(),m("div",mt,[a("span",null,"⚠️ "+S(r(e).error),1)])):M("",!0),a("div",vt,[a("div",ht,[$(Ue,{ref_key:"jsonEditorRef",ref:d,"model-value":r(e).currentItems,readonly:r(e).loading,"min-height":400,"onUpdate:modelValue":O},null,8,["model-value","readonly"])]),a("div",yt,[$(De,{items:r(e).currentItems,"id-field":r(e).currentSchema?.idField??"id","selected-index":p.value,onSelect:h},null,8,["items","id-field","selected-index"])])]),r(e).loading?(f(),m("div",_t,[...s[11]||(s[11]=[a("div",{class:"spinner"},null,-1),a("span",null,"Loading...",-1)])])):M("",!0)],64)):(f(),m("div",pt,[r(e).error?(f(),m("div",gt,[a("span",null,"⚠️ "+S(r(e).error),1)])):(f(),m(N,{key:1},[$(r(K),{size:48,class:"empty-state__icon"}),s[12]||(s[12]=a("h3",{class:"empty-state__title"},"Select a schema",-1)),s[13]||(s[13]=a("p",{class:"empty-state__description"}," Choose a schema from the sidebar to view and edit its data. ",-1))],64))]))]),(f(),le(ie,{to:"body"},[v.value?(f(),m("div",{key:0,class:"modal-overlay",onClick:s[3]||(s[3]=b=>v.value=!1)},[a("div",{class:"modal",onClick:s[2]||(s[2]=re(()=>{},["stop"]))},[s[17]||(s[17]=a("div",{class:"modal__header"},[a("h3",null,"Clear Schema Data")],-1)),a("div",bt,[a("p",null,[s[14]||(s[14]=Y(" Are you sure you want to clear all items for ",-1)),a("strong",null,S(r(e).selectedSchema),1),s[15]||(s[15]=Y("? ",-1))]),s[16]||(s[16]=a("p",{class:"text-muted"},"This action cannot be undone.",-1))]),a("div",St,[a("button",{class:"btn btn--ghost",onClick:s[1]||(s[1]=b=>v.value=!1)}," Cancel "),a("button",{class:"btn btn--danger",onClick:R}," Clear Schema ")])])])):M("",!0)]))]))}}),Tt=G(kt,[["__scopeId","data-v-62a953d5"]]);export{Tt as default};
4
- //# sourceMappingURL=ModelsPage-Bd7YM0_p.js.map
4
+ //# sourceMappingURL=ModelsPage-_etl-kz9.js.map