@vincent99/vlib 0.1.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 (92) hide show
  1. package/LICENSE +178 -0
  2. package/README.md +107 -0
  3. package/bin/vlib.js +10 -0
  4. package/dist/AdminForm.vue_vue_type_style_index_0_lang-xCk1ywLq.js +753 -0
  5. package/dist/auth/middleware.d.ts +18 -0
  6. package/dist/auth/middleware.d.ts.map +1 -0
  7. package/dist/auth/middleware.js +44 -0
  8. package/dist/auth/middleware.js.map +1 -0
  9. package/dist/auth/password.d.ts +10 -0
  10. package/dist/auth/password.d.ts.map +1 -0
  11. package/dist/auth/password.js +44 -0
  12. package/dist/auth/password.js.map +1 -0
  13. package/dist/cli.d.ts +3 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +104 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/components/AdminForm.vue.d.ts +7 -0
  18. package/dist/components/AdminTable.vue.d.ts +5 -0
  19. package/dist/components/AppLayout.vue.d.ts +36 -0
  20. package/dist/components/NavSidebar.vue.d.ts +11 -0
  21. package/dist/components/TableView.vue.d.ts +52 -0
  22. package/dist/components/index.d.ts +6 -0
  23. package/dist/components/index.js +8 -0
  24. package/dist/components/types.d.ts +25 -0
  25. package/dist/db/index.d.ts +12 -0
  26. package/dist/db/index.d.ts.map +1 -0
  27. package/dist/db/index.js +84 -0
  28. package/dist/db/index.js.map +1 -0
  29. package/dist/db/migrate.d.ts +2 -0
  30. package/dist/db/migrate.d.ts.map +1 -0
  31. package/dist/db/migrate.js +94 -0
  32. package/dist/db/migrate.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.js +11 -0
  35. package/dist/router/index.d.ts +33 -0
  36. package/dist/router/index.js +62 -0
  37. package/dist/server/api/admin.d.ts +3 -0
  38. package/dist/server/api/admin.d.ts.map +1 -0
  39. package/dist/server/api/admin.js +184 -0
  40. package/dist/server/api/admin.js.map +1 -0
  41. package/dist/server/api/auth.d.ts +3 -0
  42. package/dist/server/api/auth.d.ts.map +1 -0
  43. package/dist/server/api/auth.js +66 -0
  44. package/dist/server/api/auth.js.map +1 -0
  45. package/dist/server/index.d.ts +17 -0
  46. package/dist/server/index.d.ts.map +1 -0
  47. package/dist/server/index.js +47 -0
  48. package/dist/server/index.js.map +1 -0
  49. package/dist/types.d.ts +53 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +3 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/vlib.css +1 -0
  54. package/package.json +91 -0
  55. package/src/components/AdminForm.vue +491 -0
  56. package/src/components/AdminTable.vue +269 -0
  57. package/src/components/AppLayout.vue +280 -0
  58. package/src/components/NavSidebar.vue +176 -0
  59. package/src/components/TableView.vue +379 -0
  60. package/src/components/index.ts +13 -0
  61. package/src/components/types.ts +28 -0
  62. package/templates/.env.example +4 -0
  63. package/templates/.prettierignore +3 -0
  64. package/templates/.prettierrc +6 -0
  65. package/templates/Dockerfile.ejs +31 -0
  66. package/templates/docker-compose.prod.yml.ejs +22 -0
  67. package/templates/docker-compose.yml.ejs +22 -0
  68. package/templates/eslint.config.mjs +42 -0
  69. package/templates/index.html.ejs +13 -0
  70. package/templates/package.json.ejs +44 -0
  71. package/templates/postcss.config.js.ejs +6 -0
  72. package/templates/schemas/001-initial.sql +35 -0
  73. package/templates/scripts/migrate.ts +13 -0
  74. package/templates/server/index.ts +13 -0
  75. package/templates/src/App.vue +8 -0
  76. package/templates/src/main.ts +6 -0
  77. package/templates/src/router.ts +26 -0
  78. package/templates/src/routes/_layout.vue +58 -0
  79. package/templates/src/routes/admin/_layout.vue +8 -0
  80. package/templates/src/routes/admin/index.vue +88 -0
  81. package/templates/src/routes/admin/tables/[table]/[id].vue +20 -0
  82. package/templates/src/routes/admin/tables/[table]/index.vue +10 -0
  83. package/templates/src/routes/admin/tables/[table]/new.vue +10 -0
  84. package/templates/src/routes/index.vue +34 -0
  85. package/templates/src/routes/login.vue +128 -0
  86. package/templates/src/stores/auth.ts +58 -0
  87. package/templates/src/styles/main.scss +98 -0
  88. package/templates/src/styles/variables.scss +7 -0
  89. package/templates/tailwind.config.js.ejs +27 -0
  90. package/templates/tsconfig.json.ejs +26 -0
  91. package/templates/tsconfig.server.json.ejs +17 -0
  92. package/templates/vite.config.ts.ejs +36 -0
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <RouterView />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ // App.vue — top-level router outlet.
7
+ // Layout is handled by _layout.vue route wrappers.
8
+ </script>
@@ -0,0 +1,6 @@
1
+ import { createApp } from 'vue';
2
+ import App from './App.vue';
3
+ import router from './router';
4
+ import './styles/main.scss';
5
+
6
+ createApp(App).use(router).mount('#app');
@@ -0,0 +1,26 @@
1
+ import { createRouter, createWebHistory } from 'vue-router';
2
+ import { buildRoutes, createAuthGuard } from '@vincent99/vlib/router';
3
+ import { useAuthStore } from './stores/auth';
4
+
5
+ // Glob-import all route components
6
+ const pages = import.meta.glob('./routes/**/*.vue');
7
+
8
+ const router = createRouter({
9
+ history: createWebHistory(),
10
+ routes: buildRoutes(pages, {
11
+ publicPaths: ['/login'],
12
+ }),
13
+ scrollBehavior(_to, _from, savedPosition) {
14
+ return savedPosition ?? { top: 0 };
15
+ },
16
+ });
17
+
18
+ // Auth navigation guard
19
+ router.beforeEach(
20
+ createAuthGuard(() => {
21
+ const auth = useAuthStore();
22
+ return auth.isAuthenticated;
23
+ })
24
+ );
25
+
26
+ export default router;
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <AppLayout
3
+ :app-name="appName"
4
+ :user="auth.user.value"
5
+ :nav-items="navItems"
6
+ @logout="auth.logout()"
7
+ >
8
+ <RouterView />
9
+ </AppLayout>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed, onMounted } from 'vue';
14
+ import { AppLayout } from '@vincent99/vlib/components';
15
+ import type { NavItem } from '@vincent99/vlib/components';
16
+ import { useAuthStore } from '../stores/auth';
17
+
18
+ const appName = import.meta.env.VITE_APP_NAME || 'My App';
19
+ const auth = useAuthStore();
20
+
21
+ // Admin nav items loaded from the API at runtime
22
+ const adminTables = computed<NavItem[]>(() =>
23
+ tables.value.map((t) => ({
24
+ label: t.name,
25
+ to: `/admin/tables/${t.name}`,
26
+ }))
27
+ );
28
+
29
+ const navItems = computed<NavItem[]>(() => [
30
+ { label: 'Home', to: '/', icon: '🏠' },
31
+ {
32
+ label: 'Admin',
33
+ icon: '⚙️',
34
+ defaultOpen: true,
35
+ children: [
36
+ { label: 'Dashboard', to: '/admin', icon: '📊' },
37
+ ...adminTables.value,
38
+ ],
39
+ },
40
+ ]);
41
+
42
+ import { ref } from 'vue';
43
+ const tables = ref<{ name: string }[]>([]);
44
+
45
+ onMounted(async () => {
46
+ await auth.init();
47
+ try {
48
+ const res = await fetch('/api/admin/tables');
49
+ if (res.ok) {
50
+ const data: Array<{ name: string; isJoinTable: boolean }> =
51
+ await res.json();
52
+ tables.value = data.filter((t) => !t.isJoinTable);
53
+ }
54
+ } catch {
55
+ /* ignore */
56
+ }
57
+ });
58
+ </script>
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <RouterView />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ // Admin section layout — nested inside root _layout.vue.
7
+ // Currently a passthrough; add admin-specific UI (breadcrumbs, etc.) here as needed.
8
+ </script>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <div class="vl-page">
3
+ <h1 class="vl-page__title">Admin</h1>
4
+ <p class="vl-page__subtitle">Database tables</p>
5
+
6
+ <div v-if="loading" class="vl-admin-home__loading">Loading tables…</div>
7
+
8
+ <div v-else class="vl-admin-home__grid">
9
+ <RouterLink
10
+ v-for="table in tables"
11
+ :key="table.name"
12
+ :to="`/admin/tables/${table.name}`"
13
+ class="vl-admin-home__card"
14
+ >
15
+ <span class="vl-admin-home__card-name">{{ table.name }}</span>
16
+ <span class="vl-admin-home__card-arrow">→</span>
17
+ </RouterLink>
18
+ </div>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { ref, onMounted } from 'vue';
24
+
25
+ interface TableInfo {
26
+ name: string;
27
+ isJoinTable: boolean;
28
+ }
29
+
30
+ const tables = ref<TableInfo[]>([]);
31
+ const loading = ref(true);
32
+
33
+ onMounted(async () => {
34
+ try {
35
+ const res = await fetch('/api/admin/tables');
36
+ if (res.ok) {
37
+ const data: TableInfo[] = await res.json();
38
+ tables.value = data.filter((t) => !t.isJoinTable);
39
+ }
40
+ } finally {
41
+ loading.value = false;
42
+ }
43
+ });
44
+ </script>
45
+
46
+ <style lang="scss">
47
+ .vl-admin-home {
48
+ &__loading {
49
+ color: var(--color-text-secondary);
50
+ }
51
+
52
+ &__grid {
53
+ display: grid;
54
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
55
+ gap: var(--space-3);
56
+ margin-top: var(--space-4);
57
+ }
58
+
59
+ &__card {
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: space-between;
63
+ padding: var(--space-4);
64
+ background: white;
65
+ border: 1px solid var(--color-border);
66
+ border-radius: var(--radius-lg);
67
+ text-decoration: none;
68
+ color: var(--color-text);
69
+ font-weight: 500;
70
+ box-shadow: var(--shadow);
71
+ transition: all 0.15s;
72
+
73
+ &:hover {
74
+ border-color: var(--color-primary-light);
75
+ box-shadow: var(--shadow-lg);
76
+ transform: translateY(-1px);
77
+ }
78
+
79
+ &-name {
80
+ text-transform: capitalize;
81
+ }
82
+
83
+ &-arrow {
84
+ color: var(--color-primary);
85
+ }
86
+ }
87
+ }
88
+ </style>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <AdminForm :table-name="route.params.table as string" :row-ids="rowIds" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed } from 'vue';
7
+ import { useRoute } from 'vue-router';
8
+ import { AdminForm } from '@vincent99/vlib/components';
9
+
10
+ const route = useRoute();
11
+
12
+ // Support comma-separated IDs for multi-edit: /admin/tables/users/1,2,3
13
+ const rowIds = computed(() => {
14
+ const id = route.params.id as string;
15
+ return id.split(',').map((s) => {
16
+ const n = parseInt(s, 10);
17
+ return isNaN(n) ? s : n;
18
+ });
19
+ });
20
+ </script>
@@ -0,0 +1,10 @@
1
+ <template>
2
+ <AdminTable :table-name="route.params.table as string" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { useRoute } from 'vue-router';
7
+ import { AdminTable } from '@vincent99/vlib/components';
8
+
9
+ const route = useRoute();
10
+ </script>
@@ -0,0 +1,10 @@
1
+ <template>
2
+ <AdminForm :table-name="route.params.table as string" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { useRoute } from 'vue-router';
7
+ import { AdminForm } from '@vincent99/vlib/components';
8
+
9
+ const route = useRoute();
10
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div class="vl-page">
3
+ <h1 class="vl-page__title">Welcome</h1>
4
+ <p class="vl-page__subtitle">
5
+ You are logged in as
6
+ <strong>{{
7
+ auth.user.value?.displayName || auth.user.value?.username
8
+ }}</strong
9
+ >.
10
+ </p>
11
+ </div>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { useAuthStore } from '../stores/auth';
16
+
17
+ const auth = useAuthStore();
18
+ </script>
19
+
20
+ <style lang="scss">
21
+ .vl-page {
22
+ &__title {
23
+ font-size: 2rem;
24
+ font-weight: 700;
25
+ color: var(--color-text);
26
+ margin-bottom: var(--space-2);
27
+ }
28
+
29
+ &__subtitle {
30
+ color: var(--color-text-secondary);
31
+ font-size: 1rem;
32
+ }
33
+ }
34
+ </style>
@@ -0,0 +1,128 @@
1
+ <template>
2
+ <div class="vl-login-page">
3
+ <div class="vl-login-card">
4
+ <div class="vl-login-card__logo">
5
+ <span class="vl-login-card__app-name">{{ appName }}</span>
6
+ </div>
7
+ <h1 class="vl-login-card__title">Sign in</h1>
8
+
9
+ <div v-if="error" class="vl-alert vl-alert--error">{{ error }}</div>
10
+
11
+ <form class="vl-login-form" @submit.prevent="handleLogin">
12
+ <div class="vl-form__field">
13
+ <label for="username" class="vl-form__label">Username</label>
14
+ <input
15
+ id="username"
16
+ v-model="username"
17
+ type="text"
18
+ class="vl-form__input"
19
+ autocomplete="username"
20
+ required
21
+ :disabled="loading"
22
+ />
23
+ </div>
24
+
25
+ <div class="vl-form__field">
26
+ <label for="password" class="vl-form__label">Password</label>
27
+ <input
28
+ id="password"
29
+ v-model="password"
30
+ type="password"
31
+ class="vl-form__input"
32
+ autocomplete="current-password"
33
+ required
34
+ :disabled="loading"
35
+ />
36
+ </div>
37
+
38
+ <button
39
+ type="submit"
40
+ class="vl-btn vl-btn--primary vl-login-form__submit"
41
+ :disabled="loading"
42
+ >
43
+ {{ loading ? 'Signing in…' : 'Sign in' }}
44
+ </button>
45
+ </form>
46
+ </div>
47
+ </div>
48
+ </template>
49
+
50
+ <script setup lang="ts">
51
+ import { ref } from 'vue';
52
+ import { useRouter } from 'vue-router';
53
+ import { useAuthStore } from '../stores/auth';
54
+
55
+ const appName = import.meta.env.VITE_APP_NAME || 'My App';
56
+ const router = useRouter();
57
+ const auth = useAuthStore();
58
+
59
+ const username = ref('');
60
+ const password = ref('');
61
+ const loading = ref(false);
62
+ const error = ref<string | null>(null);
63
+
64
+ async function handleLogin() {
65
+ loading.value = true;
66
+ error.value = null;
67
+ const err = await auth.login(username.value, password.value);
68
+ if (err) {
69
+ error.value = err;
70
+ loading.value = false;
71
+ } else {
72
+ router.push('/');
73
+ }
74
+ }
75
+ </script>
76
+
77
+ <style lang="scss">
78
+ .vl-login-page {
79
+ min-height: 100vh;
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ background: var(--color-background);
84
+ padding: var(--space-4);
85
+ }
86
+
87
+ .vl-login-card {
88
+ background: white;
89
+ border-radius: var(--radius-lg);
90
+ box-shadow: var(--shadow-lg);
91
+ padding: var(--space-8);
92
+ width: 100%;
93
+ max-width: 400px;
94
+
95
+ &__logo {
96
+ text-align: center;
97
+ margin-bottom: var(--space-4);
98
+ }
99
+
100
+ &__app-name {
101
+ font-size: 1.5rem;
102
+ font-weight: 800;
103
+ color: var(--color-primary);
104
+ letter-spacing: -0.02em;
105
+ }
106
+
107
+ &__title {
108
+ font-size: 1.25rem;
109
+ font-weight: 700;
110
+ color: var(--color-text);
111
+ text-align: center;
112
+ margin-bottom: var(--space-6);
113
+ }
114
+ }
115
+
116
+ .vl-login-form {
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: var(--space-4);
120
+
121
+ &__submit {
122
+ width: 100%;
123
+ justify-content: center;
124
+ padding: var(--space-3);
125
+ font-size: 1rem;
126
+ }
127
+ }
128
+ </style>
@@ -0,0 +1,58 @@
1
+ import { ref, computed } from 'vue';
2
+ import { useRouter } from 'vue-router';
3
+
4
+ interface User {
5
+ id: number;
6
+ username: string;
7
+ displayName: string | null;
8
+ }
9
+
10
+ // Simple reactive auth store (no Pinia required)
11
+ const user = ref<User | null>(null);
12
+ const initialized = ref(false);
13
+
14
+ export function useAuthStore() {
15
+ const router = useRouter();
16
+
17
+ const isAuthenticated = computed(() => user.value !== null);
18
+
19
+ async function init() {
20
+ if (initialized.value) return;
21
+ try {
22
+ const res = await fetch('/api/auth/me');
23
+ if (res.ok) {
24
+ const data = await res.json();
25
+ user.value = data.user;
26
+ }
27
+ } catch {
28
+ /* not authenticated */
29
+ }
30
+ initialized.value = true;
31
+ }
32
+
33
+ async function login(
34
+ username: string,
35
+ password: string
36
+ ): Promise<string | null> {
37
+ const res = await fetch('/api/auth/login', {
38
+ method: 'POST',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ body: JSON.stringify({ username, password }),
41
+ });
42
+ if (!res.ok) {
43
+ const data = await res.json();
44
+ return data.error || 'Login failed';
45
+ }
46
+ const data = await res.json();
47
+ user.value = data.user;
48
+ return null;
49
+ }
50
+
51
+ async function logout() {
52
+ await fetch('/api/auth/logout', { method: 'POST' });
53
+ user.value = null;
54
+ router.push('/login');
55
+ }
56
+
57
+ return { user, isAuthenticated, initialized, init, login, logout };
58
+ }
@@ -0,0 +1,98 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ // ── CSS Custom Properties (design tokens) ────────────────────────────────────
6
+ :root {
7
+ // Colors — blues
8
+ --color-primary: #1e40af; // Blue 800
9
+ --color-primary-light: #3b82f6; // Blue 500
10
+ --color-primary-dark: #1e3a5f; // Deep navy
11
+ --color-accent: #60a5fa; // Blue 400
12
+
13
+ // Surface colors
14
+ --color-background: #f0f4f8;
15
+ --color-surface: #ffffff;
16
+ --color-sidebar: #1e3a5f;
17
+ --color-header: #1e40af;
18
+
19
+ // Text
20
+ --color-text: #1e293b; // Slate 800
21
+ --color-text-secondary: #64748b; // Slate 500
22
+ --color-border: #e2e8f0; // Slate 200
23
+
24
+ // Status
25
+ --color-danger: #ef4444;
26
+ --color-success: #22c55e;
27
+ --color-warning: #f59e0b;
28
+
29
+ // Layout
30
+ --header-height: 60px;
31
+ --sidebar-width: 240px;
32
+
33
+ // Spacing scale (multiples of 4px)
34
+ --space-1: 4px;
35
+ --space-2: 8px;
36
+ --space-3: 12px;
37
+ --space-4: 16px;
38
+ --space-5: 20px;
39
+ --space-6: 24px;
40
+ --space-8: 32px;
41
+ --space-10: 40px;
42
+ --space-12: 48px;
43
+
44
+ // Border radius
45
+ --radius: 6px;
46
+ --radius-lg: 10px;
47
+
48
+ // Shadows
49
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
50
+ --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
51
+ }
52
+
53
+ // ── Base reset / global styles ────────────────────────────────────────────────
54
+ *,
55
+ *::before,
56
+ *::after {
57
+ box-sizing: border-box;
58
+ }
59
+
60
+ html,
61
+ body,
62
+ #app {
63
+ margin: 0;
64
+ padding: 0;
65
+ height: 100%;
66
+ font-family:
67
+ system-ui,
68
+ -apple-system,
69
+ 'Segoe UI',
70
+ Roboto,
71
+ 'Helvetica Neue',
72
+ sans-serif;
73
+ color: var(--color-text);
74
+ background: var(--color-background);
75
+ -webkit-font-smoothing: antialiased;
76
+ }
77
+
78
+ h1,
79
+ h2,
80
+ h3,
81
+ h4,
82
+ h5,
83
+ h6 {
84
+ margin: 0;
85
+ }
86
+
87
+ p {
88
+ margin: 0;
89
+ }
90
+
91
+ a {
92
+ color: var(--color-primary);
93
+ }
94
+
95
+ // ── Responsive helpers ────────────────────────────────────────────────────────
96
+ // Small: ≤ 767px (portrait phone)
97
+ // Medium: 768–1023px (tablet)
98
+ // Large: ≥ 1024px (desktop)
@@ -0,0 +1,7 @@
1
+ // Design tokens — imported into every component via vite.config.ts preprocessorOptions
2
+
3
+ // ── Breakpoints ───────────────────────────────────────────────────────────────
4
+ // Used as Sass variables in media queries (CSS custom props can't be in @media)
5
+ $bp-small: 390px; // Small: portrait phone (≤ 767px)
6
+ $bp-medium: 768px; // Medium: tablet (768px – 1023px)
7
+ $bp-large: 1024px; // Large: desktop (≥ 1024px)
@@ -0,0 +1,27 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ './index.html',
5
+ './src/**/*.{vue,js,ts,jsx,tsx}',
6
+ './node_modules/@vincent99/vlib/src/components/**/*.vue',
7
+ ],
8
+ theme: {
9
+ extend: {
10
+ colors: {
11
+ primary: {
12
+ 50: '#eff6ff',
13
+ 100: '#dbeafe',
14
+ 200: '#bfdbfe',
15
+ 300: '#93c5fd',
16
+ 400: '#60a5fa',
17
+ 500: '#3b82f6',
18
+ 600: '#2563eb',
19
+ 700: '#1d4ed8',
20
+ 800: '#1e40af',
21
+ 900: '#1e3a5f',
22
+ },
23
+ },
24
+ },
25
+ },
26
+ plugins: [],
27
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "jsx": "preserve",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "resolveJsonModule": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "allowImportingTsExtensions": true,
14
+ "noEmit": true,
15
+ "paths": {
16
+ "@/*": ["./src/*"]
17
+ }
18
+ },
19
+ "include": [
20
+ "src/**/*.ts",
21
+ "src/**/*.d.ts",
22
+ "src/**/*.tsx",
23
+ "src/**/*.vue"
24
+ ],
25
+ "exclude": ["node_modules", "dist", "server", "scripts"]
26
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": ".",
9
+ "declaration": false,
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "resolveJsonModule": true
14
+ },
15
+ "include": ["server/**/*.ts", "scripts/**/*.ts"],
16
+ "exclude": ["node_modules", "dist", "src"]
17
+ }
@@ -0,0 +1,36 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+ import { resolve } from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [vue()],
7
+ resolve: {
8
+ alias: {
9
+ '@': resolve(__dirname, 'src'),
10
+ },
11
+ },
12
+ css: {
13
+ preprocessorOptions: {
14
+ scss: {
15
+ // Inject breakpoint variables into every SCSS file.
16
+ // Uses loadPaths so the Sass compiler can resolve the module.
17
+ loadPaths: [resolve(__dirname, 'src/styles')],
18
+ additionalData: `@use "variables" as *;`,
19
+ },
20
+ },
21
+ },
22
+ server: {
23
+ host: '0.0.0.0',
24
+ port: 5173,
25
+ proxy: {
26
+ '/api': {
27
+ target: 'http://localhost:3001',
28
+ changeOrigin: true,
29
+ },
30
+ },
31
+ },
32
+ build: {
33
+ outDir: 'dist/public',
34
+ emptyOutDir: true,
35
+ },
36
+ })