@websublime/vite-plugin-open-api-devtools 0.4.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WebSublime
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # @websublime/vite-plugin-open-api-devtools
2
+
3
+ Vue-based DevTools SPA for debugging and inspecting the OpenAPI mock server.
4
+
5
+ ## Features
6
+
7
+ - **Routes Page** - Browse all available API endpoints from your OpenAPI spec
8
+ - **Timeline Page** - Monitor request/response traffic in real-time
9
+ - **Models Page** - View and edit in-memory store data
10
+ - **Simulator Page** - Simulate errors and slow responses for testing
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm add @websublime/vite-plugin-open-api-devtools
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ The DevTools SPA is typically served automatically by the Vite plugin. However, you can also use it standalone for development:
21
+
22
+ ### Development
23
+
24
+ ```bash
25
+ # Start the dev server
26
+ pnpm dev
27
+
28
+ # Build for production
29
+ pnpm build
30
+
31
+ # Type check
32
+ pnpm typecheck
33
+ ```
34
+
35
+ ### Standalone Integration
36
+
37
+ ```typescript
38
+ import { bootstrap } from '@websublime/vite-plugin-open-api-devtools';
39
+ import '@websublime/vite-plugin-open-api-devtools/style.css';
40
+
41
+ // Mount the DevTools SPA
42
+ bootstrap();
43
+ ```
44
+
45
+ ## Architecture
46
+
47
+ ```text
48
+ devtools-client/
49
+ ├── src/
50
+ │ ├── App.vue # Main layout with tabs
51
+ │ ├── main.ts # Entry point
52
+ │ ├── router.ts # Vue Router setup
53
+ │ │
54
+ │ ├── pages/
55
+ │ │ ├── RoutesPage.vue # Endpoint listing
56
+ │ │ ├── TimelinePage.vue # Request/response log
57
+ │ │ ├── ModelsPage.vue # Store data editor
58
+ │ │ └── SimulatorPage.vue# Error simulation
59
+ │ │
60
+ │ ├── components/ # Reusable UI components
61
+ │ │
62
+ │ ├── composables/
63
+ │ │ ├── useWebSocket.ts # WebSocket connection (TODO)
64
+ │ │ └── useTheme.ts # Dark/light mode
65
+ │ │
66
+ │ ├── stores/ # Pinia stores
67
+ │ │
68
+ │ └── assets/
69
+ │ └── main.css # Global styles with OpenProps
70
+ ```
71
+
72
+ ## Tech Stack
73
+
74
+ - **Vue 3** - Composition API with `<script setup>`
75
+ - **Pinia** - State management
76
+ - **Vue Router** - Navigation with hash history (for iframe compatibility)
77
+ - **OpenProps** - CSS design tokens
78
+ - **Lucide Vue Next** - Icon library
79
+ - **TypeScript** - Type safety
80
+
81
+ ## Theming
82
+
83
+ The DevTools support both light and dark modes, automatically respecting system preferences. The theme can also be toggled programmatically:
84
+
85
+ ```typescript
86
+ import { useTheme } from '@websublime/vite-plugin-open-api-devtools';
87
+
88
+ const { isDark, toggleTheme, setTheme } = useTheme();
89
+
90
+ // Toggle between light and dark
91
+ toggleTheme();
92
+
93
+ // Set explicit theme
94
+ setTheme('dark');
95
+
96
+ // Use system preference
97
+ setTheme('system');
98
+ ```
99
+
100
+ ## CSS Custom Properties
101
+
102
+ The DevTools expose CSS custom properties for customization:
103
+
104
+ ```css
105
+ :root {
106
+ --devtools-primary: var(--blue-6);
107
+ --devtools-bg: var(--gray-0);
108
+ --devtools-surface: var(--gray-1);
109
+ --devtools-text: var(--gray-9);
110
+ /* ... and more */
111
+ }
112
+ ```
113
+
114
+ ## API Reference
115
+
116
+ ### `bootstrap()`
117
+
118
+ Creates and mounts the Vue application. This function is idempotent - calling it multiple times has no effect after the first successful call.
119
+
120
+ ```typescript
121
+ import { bootstrap } from '@websublime/vite-plugin-open-api-devtools';
122
+ import '@websublime/vite-plugin-open-api-devtools/style.css';
123
+
124
+ const app = bootstrap();
125
+ ```
126
+
127
+ **Returns:** `App | null` - The Vue app instance on first call, or `null` if already bootstrapped.
128
+
129
+ **Notes:**
130
+ - Auto-bootstraps when `#app` element exists in DOM
131
+ - Logs warning in development if called multiple times
132
+
133
+ ---
134
+
135
+ ### `useTheme()`
136
+
137
+ Composable for theme management. Must be called within a Vue component's `setup()` function.
138
+
139
+ ```typescript
140
+ import { useTheme } from '@websublime/vite-plugin-open-api-devtools';
141
+
142
+ const { isDark, toggleTheme, setTheme, resetToSystem } = useTheme();
143
+ ```
144
+
145
+ **Returns:**
146
+
147
+ | Property | Type | Description |
148
+ |----------|------|-------------|
149
+ | `themeMode` | `ComputedRef<ThemeMode>` | Current theme setting (`'light'`, `'dark'`, or `'system'`) |
150
+ | `effectiveTheme` | `ComputedRef<'light' \| 'dark'>` | Resolved theme after applying system preference |
151
+ | `isDark` | `ComputedRef<boolean>` | Whether dark mode is currently active |
152
+ | `systemPrefersDark` | `ComputedRef<boolean>` | Whether system prefers dark mode |
153
+ | `setTheme` | `(mode: ThemeMode) => void` | Set theme to `'light'`, `'dark'`, or `'system'` |
154
+ | `toggleTheme` | `() => void` | Toggle between light and dark mode |
155
+ | `resetToSystem` | `() => void` | Reset to follow system preference |
156
+ | `initialize` | `() => void` | Manually initialize (for SSR hydration) |
157
+
158
+ **Type:**
159
+
160
+ ```typescript
161
+ type ThemeMode = 'light' | 'dark' | 'system';
162
+ ```
163
+
164
+ **Persistence:** Theme preference is persisted to `localStorage` under the key `openapi-devtools-theme`.
165
+
166
+ ## Requirements
167
+
168
+ - **Node.js**: ^20.19.0 || >=22.12.0
169
+ - **pnpm**: 9.x
170
+
171
+ ## Related Packages
172
+
173
+ | Package | Description |
174
+ |---------|-------------|
175
+ | `@websublime/vite-plugin-open-api-server` | Vite plugin (main package) |
176
+ | `@websublime/vite-plugin-open-api-core` | Core server, store, router, generator |
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,93 @@
1
+ import { defineComponent as b, ref as i, createElementBlock as a, openBlock as o, createElementVNode as e, createVNode as l, unref as c, Fragment as _, renderList as u, normalizeClass as f, toDisplayString as d } from "vue";
2
+ import { c as g, D as p, _ as k } from "./main-AUiFaD93.js";
3
+ const C = g("refresh-cw", [
4
+ ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
5
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
6
+ ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
7
+ ["path", { d: "M8 16H3v5", key: "1cv678" }]
8
+ ]), S = { class: "models-page" }, M = { class: "models-sidebar" }, w = { class: "models-sidebar__header" }, D = { class: "models-sidebar__list" }, R = ["onClick"], x = { class: "models-sidebar__name" }, z = { class: "models-sidebar__count" }, L = { class: "models-content" }, P = { class: "models-toolbar" }, B = { class: "models-toolbar__title" }, F = { class: "font-mono" }, N = { class: "text-muted" }, E = { class: "models-toolbar__actions" }, H = { class: "models-data" }, I = { class: "models-data__json font-mono" }, O = {
9
+ key: 1,
10
+ class: "empty-state"
11
+ }, V = /* @__PURE__ */ b({
12
+ __name: "ModelsPage",
13
+ setup(j) {
14
+ const n = i("Pet"), h = i([
15
+ { name: "Pet", count: 5 },
16
+ { name: "Category", count: 3 },
17
+ { name: "Order", count: 2 },
18
+ { name: "User", count: 1 }
19
+ ]), m = i([
20
+ { id: 1, name: "Fluffy", category: { id: 1, name: "Dogs" }, status: "available" },
21
+ { id: 2, name: "Whiskers", category: { id: 2, name: "Cats" }, status: "pending" },
22
+ { id: 3, name: "Goldie", category: { id: 3, name: "Fish" }, status: "available" },
23
+ { id: 4, name: "Tweety", category: { id: 4, name: "Birds" }, status: "sold" },
24
+ { id: 5, name: "Hoppy", category: { id: 5, name: "Rabbits" }, status: "available" }
25
+ ]);
26
+ function v(r) {
27
+ n.value = r;
28
+ }
29
+ function y() {
30
+ console.log("Reseeding schema:", n.value);
31
+ }
32
+ return (r, s) => (o(), a("div", S, [
33
+ e("aside", M, [
34
+ e("div", w, [
35
+ l(c(p), { size: 18 }),
36
+ s[0] || (s[0] = e("span", null, "Schemas", -1))
37
+ ]),
38
+ e("div", D, [
39
+ (o(!0), a(_, null, u(h.value, (t) => (o(), a("button", {
40
+ key: t.name,
41
+ class: f([
42
+ "models-sidebar__item",
43
+ { "models-sidebar__item--active": n.value === t.name }
44
+ ]),
45
+ onClick: (q) => v(t.name)
46
+ }, [
47
+ e("span", x, d(t.name), 1),
48
+ e("span", z, d(t.count), 1)
49
+ ], 10, R))), 128))
50
+ ])
51
+ ]),
52
+ e("main", L, [
53
+ n.value ? (o(), a(_, { key: 0 }, [
54
+ e("div", P, [
55
+ e("div", B, [
56
+ e("span", F, d(n.value), 1),
57
+ e("span", N, "(" + d(m.value.length) + " items)", 1)
58
+ ]),
59
+ e("div", E, [
60
+ e("button", {
61
+ class: "btn btn--secondary",
62
+ title: "Reseed with generated data",
63
+ onClick: y
64
+ }, [
65
+ l(c(C), { size: 16 }),
66
+ s[1] || (s[1] = e("span", null, "Reseed", -1))
67
+ ])
68
+ ])
69
+ ]),
70
+ e("div", H, [
71
+ (o(!0), a(_, null, u(m.value, (t) => (o(), a("div", {
72
+ key: t.id,
73
+ class: "models-data__item card"
74
+ }, [
75
+ e("pre", I, d(JSON.stringify(t, null, 2)), 1)
76
+ ]))), 128))
77
+ ])
78
+ ], 64)) : (o(), a("div", O, [
79
+ l(c(p), {
80
+ size: 48,
81
+ class: "empty-state__icon"
82
+ }),
83
+ s[2] || (s[2] = e("h3", { class: "empty-state__title" }, "Select a schema", -1)),
84
+ s[3] || (s[3] = e("p", { class: "empty-state__description" }, " Choose a schema from the sidebar to view and edit its data. ", -1))
85
+ ]))
86
+ ])
87
+ ]));
88
+ }
89
+ }), T = /* @__PURE__ */ k(V, [["__scopeId", "data-v-6373fe56"]]);
90
+ export {
91
+ T as default
92
+ };
93
+ //# sourceMappingURL=ModelsPage-D-qLEz14.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelsPage-D-qLEz14.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/icons/refresh-cw.js","../src/pages/ModelsPage.vue"],"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\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst RefreshCw = createLucideIcon(\"refresh-cw\", [\n [\"path\", { d: \"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\", key: \"v9h5vc\" }],\n [\"path\", { d: \"M21 3v5h-5\", key: \"1q7to0\" }],\n [\"path\", { d: \"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\", key: \"3uifl3\" }],\n [\"path\", { d: \"M8 16H3v5\", key: \"1cv678\" }]\n]);\n\nexport { RefreshCw as default };\n//# sourceMappingURL=refresh-cw.js.map\n","<!--\n ModelsPage.vue - Store Data Editor Page\n\n What: Displays and allows editing of in-memory store data organized by schema\n How: Will fetch store data from models store and display in an editable JSON view\n Why: Allows developers to inspect and modify mock data during development\n-->\n\n<script setup lang=\"ts\">\nimport { Database, RefreshCw } from 'lucide-vue-next';\nimport { ref } from 'vue';\n\n// TODO: Will be replaced with data from models store\nconst selectedSchema = ref<string | null>('Pet');\n\n// Placeholder schemas for UI development\nconst schemas = ref([\n { name: 'Pet', count: 5 },\n { name: 'Category', count: 3 },\n { name: 'Order', count: 2 },\n { name: 'User', count: 1 },\n]);\n\n// Placeholder data for selected schema\nconst schemaData = ref([\n { id: 1, name: 'Fluffy', category: { id: 1, name: 'Dogs' }, status: 'available' },\n { id: 2, name: 'Whiskers', category: { id: 2, name: 'Cats' }, status: 'pending' },\n { id: 3, name: 'Goldie', category: { id: 3, name: 'Fish' }, status: 'available' },\n { id: 4, name: 'Tweety', category: { id: 4, name: 'Birds' }, status: 'sold' },\n { id: 5, name: 'Hoppy', category: { id: 5, name: 'Rabbits' }, status: 'available' },\n]);\n\n/**\n * Select a schema to view its data\n */\nfunction selectSchema(schemaName: string): void {\n selectedSchema.value = schemaName;\n}\n\n/**\n * Reseed the selected schema with fresh generated data\n */\nfunction reseedSchema(): void {\n // TODO: Will trigger reseed via WebSocket command\n console.log('Reseeding schema:', selectedSchema.value);\n}\n</script>\n\n<template>\n <div class=\"models-page\">\n <!-- Schema Sidebar -->\n <aside class=\"models-sidebar\">\n <div class=\"models-sidebar__header\">\n <Database :size=\"18\" />\n <span>Schemas</span>\n </div>\n <div class=\"models-sidebar__list\">\n <button\n v-for=\"schema in schemas\"\n :key=\"schema.name\"\n :class=\"[\n 'models-sidebar__item',\n { 'models-sidebar__item--active': selectedSchema === schema.name },\n ]\"\n @click=\"selectSchema(schema.name)\"\n >\n <span class=\"models-sidebar__name\">{{ schema.name }}</span>\n <span class=\"models-sidebar__count\">{{ schema.count }}</span>\n </button>\n </div>\n </aside>\n\n <!-- Data Panel -->\n <main class=\"models-content\">\n <template v-if=\"selectedSchema\">\n <!-- Toolbar -->\n <div class=\"models-toolbar\">\n <div class=\"models-toolbar__title\">\n <span class=\"font-mono\">{{ selectedSchema }}</span>\n <span class=\"text-muted\">({{ schemaData.length }} items)</span>\n </div>\n <div class=\"models-toolbar__actions\">\n <button\n class=\"btn btn--secondary\"\n title=\"Reseed with generated data\"\n @click=\"reseedSchema\"\n >\n <RefreshCw :size=\"16\" />\n <span>Reseed</span>\n </button>\n </div>\n </div>\n\n <!-- Data List -->\n <div class=\"models-data\">\n <div\n v-for=\"item in schemaData\"\n :key=\"item.id\"\n class=\"models-data__item card\"\n >\n <pre class=\"models-data__json font-mono\">{{ JSON.stringify(item, null, 2) }}</pre>\n </div>\n </div>\n </template>\n\n <!-- Empty State -->\n <div v-else class=\"empty-state\">\n <Database :size=\"48\" class=\"empty-state__icon\" />\n <h3 class=\"empty-state__title\">Select a schema</h3>\n <p class=\"empty-state__description\">\n Choose a schema from the sidebar to view and edit its data.\n </p>\n </div>\n </main>\n </div>\n</template>\n\n<style scoped>\n.models-page {\n display: grid;\n grid-template-columns: 240px 1fr;\n height: 100%;\n overflow: hidden;\n}\n\n/* Sidebar */\n.models-sidebar {\n display: flex;\n flex-direction: column;\n background-color: var(--devtools-surface);\n border-right: 1px solid var(--devtools-border);\n overflow: hidden;\n}\n\n.models-sidebar__header {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-sm);\n padding: var(--devtools-space-md);\n font-weight: var(--font-weight-6);\n font-size: var(--font-size-1);\n border-bottom: 1px solid var(--devtools-border);\n}\n\n.models-sidebar__list {\n flex: 1;\n overflow-y: auto;\n padding: var(--devtools-space-xs);\n}\n\n.models-sidebar__item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: var(--devtools-space-sm) var(--devtools-space-md);\n background: none;\n border: none;\n border-radius: var(--devtools-radius-sm);\n color: var(--devtools-text);\n font-family: var(--devtools-font-sans);\n font-size: var(--font-size-1);\n cursor: pointer;\n transition: background-color var(--devtools-transition-fast);\n}\n\n.models-sidebar__item:hover {\n background-color: var(--devtools-surface-elevated);\n}\n\n.models-sidebar__item--active {\n background-color: color-mix(in srgb, var(--devtools-primary) 15%, transparent);\n color: var(--devtools-primary);\n}\n\n.models-sidebar__name {\n font-family: var(--devtools-font-mono);\n}\n\n.models-sidebar__count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 20px;\n padding: 0 var(--devtools-space-xs);\n background-color: var(--devtools-surface-elevated);\n border-radius: var(--devtools-radius-sm);\n font-size: var(--font-size-0);\n color: var(--devtools-text-muted);\n}\n\n/* Content Area */\n.models-content {\n display: flex;\n flex-direction: column;\n padding: var(--devtools-space-md);\n overflow: hidden;\n}\n\n/* Toolbar */\n.models-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--devtools-space-md);\n margin-bottom: var(--devtools-space-md);\n}\n\n.models-toolbar__title {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-sm);\n font-size: var(--font-size-2);\n font-weight: var(--font-weight-6);\n}\n\n.models-toolbar__actions {\n display: flex;\n align-items: center;\n gap: var(--devtools-space-sm);\n}\n\n/* Data Display */\n.models-data {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: var(--devtools-space-sm);\n}\n\n.models-data__item {\n padding: var(--devtools-space-sm);\n}\n\n.models-data__json {\n margin: 0;\n padding: var(--devtools-space-sm);\n background-color: var(--devtools-bg);\n border-radius: var(--devtools-radius-sm);\n font-size: var(--font-size-0);\n line-height: var(--font-lineheight-3);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-all;\n}\n</style>\n"],"names":["RefreshCw","createLucideIcon","selectedSchema","ref","schemas","schemaData","selectSchema","schemaName","reseedSchema","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","Database","_cache","_hoisted_4","_Fragment","_renderList","schema","_normalizeClass","$event","_hoisted_6","_toDisplayString","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","item","_hoisted_15","_hoisted_16"],"mappings":"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","x_google_ignoreList":[0]}