@react-spa-scaffold/mcp 2.2.0 → 2.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.
Files changed (138) hide show
  1. package/dist/constants.d.ts +4 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +4 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/features/definitions/api.d.ts.map +1 -1
  6. package/dist/features/definitions/api.js +2 -1
  7. package/dist/features/definitions/api.js.map +1 -1
  8. package/dist/features/definitions/database.d.ts +3 -0
  9. package/dist/features/definitions/database.d.ts.map +1 -0
  10. package/dist/features/definitions/database.js +45 -0
  11. package/dist/features/definitions/database.js.map +1 -0
  12. package/dist/features/definitions/deployment.d.ts +3 -0
  13. package/dist/features/definitions/deployment.d.ts.map +1 -0
  14. package/dist/features/definitions/deployment.js +14 -0
  15. package/dist/features/definitions/deployment.js.map +1 -0
  16. package/dist/features/definitions/electron.d.ts +3 -0
  17. package/dist/features/definitions/electron.d.ts.map +1 -0
  18. package/dist/features/definitions/electron.js +23 -0
  19. package/dist/features/definitions/electron.js.map +1 -0
  20. package/dist/features/definitions/index.d.ts +3 -0
  21. package/dist/features/definitions/index.d.ts.map +1 -1
  22. package/dist/features/definitions/index.js +3 -0
  23. package/dist/features/definitions/index.js.map +1 -1
  24. package/dist/features/registry.d.ts.map +1 -1
  25. package/dist/features/registry.js +4 -1
  26. package/dist/features/registry.js.map +1 -1
  27. package/dist/features/types.d.ts +1 -0
  28. package/dist/features/types.d.ts.map +1 -1
  29. package/dist/features/types.test.js +5 -2
  30. package/dist/features/types.test.js.map +1 -1
  31. package/dist/resources/docs.d.ts.map +1 -1
  32. package/dist/resources/docs.js +5 -0
  33. package/dist/resources/docs.js.map +1 -1
  34. package/dist/tools/add-features.js +1 -1
  35. package/dist/tools/add-features.js.map +1 -1
  36. package/dist/tools/get-features.test.js +7 -0
  37. package/dist/tools/get-features.test.js.map +1 -1
  38. package/dist/tools/get-scaffold.d.ts +1 -0
  39. package/dist/tools/get-scaffold.d.ts.map +1 -1
  40. package/dist/tools/get-scaffold.js +4 -1
  41. package/dist/tools/get-scaffold.js.map +1 -1
  42. package/dist/tools/get-scaffold.test.js +50 -0
  43. package/dist/tools/get-scaffold.test.js.map +1 -1
  44. package/dist/utils/docs.d.ts.map +1 -1
  45. package/dist/utils/docs.js +2 -0
  46. package/dist/utils/docs.js.map +1 -1
  47. package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
  48. package/dist/utils/scaffold/claude-md/index.js +4 -1
  49. package/dist/utils/scaffold/claude-md/index.js.map +1 -1
  50. package/dist/utils/scaffold/claude-md/sections.d.ts +3 -0
  51. package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
  52. package/dist/utils/scaffold/claude-md/sections.js +174 -2
  53. package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
  54. package/dist/utils/scaffold/compute.d.ts.map +1 -1
  55. package/dist/utils/scaffold/compute.js +4 -2
  56. package/dist/utils/scaffold/compute.js.map +1 -1
  57. package/dist/utils/scaffold/generators.d.ts +7 -2
  58. package/dist/utils/scaffold/generators.d.ts.map +1 -1
  59. package/dist/utils/scaffold/generators.js +100 -22
  60. package/dist/utils/scaffold/generators.js.map +1 -1
  61. package/package.json +1 -1
  62. package/templates/.env.example +40 -12
  63. package/templates/.github/workflows/ci.yml +49 -2
  64. package/templates/.github/workflows/deploy.yml +46 -0
  65. package/templates/CLAUDE.md +180 -1
  66. package/templates/docs/AUTHENTICATION.md +325 -0
  67. package/templates/docs/DEPLOYMENT.md +296 -0
  68. package/templates/docs/E2E_TESTING.md +81 -4
  69. package/templates/docs/SUPABASE_INTEGRATION.md +310 -0
  70. package/templates/docs/TESTING.md +195 -77
  71. package/templates/e2e/auth/auth.setup.ts +60 -0
  72. package/templates/e2e/fixtures/index.ts +11 -0
  73. package/templates/e2e/tests/profile.auth.spec.ts +103 -0
  74. package/templates/e2e/tests/profile.spec.ts +64 -0
  75. package/templates/e2e/tests/register-form.spec.ts +38 -0
  76. package/templates/forge.config.js +53 -0
  77. package/templates/gitignore +5 -0
  78. package/templates/package.json +13 -1
  79. package/templates/playwright.config.ts +33 -3
  80. package/templates/src/App.tsx +32 -19
  81. package/templates/src/components/layout/Header.test.tsx +17 -1
  82. package/templates/src/components/layout/Header.tsx +11 -0
  83. package/templates/src/components/shared/AccountButton/AccountButton.test.tsx +3 -3
  84. package/templates/src/components/shared/ProfileSync/ProfileSync.test.tsx +44 -0
  85. package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +104 -0
  86. package/templates/src/components/shared/ProfileSync/index.ts +1 -0
  87. package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.test.tsx +3 -3
  88. package/templates/src/components/shared/index.ts +1 -0
  89. package/templates/src/contexts/performanceContext.tsx +3 -3
  90. package/templates/src/contexts/queryContext.tsx +9 -8
  91. package/templates/src/contexts/supabaseContext.test.tsx +59 -0
  92. package/templates/src/contexts/supabaseContext.tsx +87 -0
  93. package/templates/src/hooks/index.ts +17 -0
  94. package/templates/src/hooks/supabase/index.ts +12 -0
  95. package/templates/src/hooks/supabase/useProfiles.test.tsx +207 -0
  96. package/templates/src/hooks/supabase/useProfiles.ts +213 -0
  97. package/templates/src/hooks/supabase/useSupabaseQuery.test.tsx +150 -0
  98. package/templates/src/hooks/supabase/useSupabaseQuery.ts +91 -0
  99. package/templates/src/lib/api.test.ts +30 -38
  100. package/templates/src/lib/api.ts +1 -7
  101. package/templates/src/lib/config.ts +54 -4
  102. package/templates/src/lib/env.ts +36 -14
  103. package/templates/src/lib/index.ts +4 -2
  104. package/templates/src/lib/routes.ts +1 -0
  105. package/templates/src/lib/sentry.ts +13 -10
  106. package/templates/src/lib/supabase/client.ts +58 -0
  107. package/templates/src/lib/supabase/index.ts +5 -0
  108. package/templates/src/main.ts +227 -0
  109. package/templates/src/main.tsx +32 -42
  110. package/templates/src/mocks/constants.ts +31 -0
  111. package/templates/src/mocks/fixtures/index.ts +3 -1
  112. package/templates/src/mocks/fixtures/profiles.ts +55 -0
  113. package/templates/src/mocks/fixtures/users.ts +91 -0
  114. package/templates/src/mocks/handlers/index.ts +2 -1
  115. package/templates/src/mocks/handlers/supabase.ts +64 -0
  116. package/templates/src/mocks/handlers/todos.ts +1 -1
  117. package/templates/src/mocks/index.ts +6 -0
  118. package/templates/src/pages/Profile.test.tsx +263 -0
  119. package/templates/src/pages/Profile.tsx +171 -0
  120. package/templates/src/pages/index.ts +1 -0
  121. package/templates/src/preload.ts +26 -0
  122. package/templates/src/stores/preferencesStore.ts +2 -1
  123. package/templates/src/test/clerkMock.tsx +49 -9
  124. package/templates/src/test/fetchMock.ts +58 -0
  125. package/templates/src/test/index.ts +49 -3
  126. package/templates/src/test/mocks.ts +128 -1
  127. package/templates/src/test/providers.tsx +7 -4
  128. package/templates/src/test/supabaseMock.ts +112 -0
  129. package/templates/src/test-setup.ts +26 -0
  130. package/templates/src/types/database.ts +46 -0
  131. package/templates/src/types/global.d.ts +28 -0
  132. package/templates/src/types/index.ts +1 -0
  133. package/templates/src/types/supabase.ts +167 -0
  134. package/templates/src/vite-env.d.ts +6 -0
  135. package/templates/supabase/migrations/20260104000000_create_profiles_table.sql +67 -0
  136. package/templates/vite.main.config.mjs +20 -0
  137. package/templates/vite.preload.config.mjs +17 -0
  138. package/templates/vite.renderer.config.mjs +52 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Supabase mocks for testing.
3
+ *
4
+ * Provides mock implementations of the Supabase context and client,
5
+ * with state controls for testing different scenarios.
6
+ */
7
+
8
+ import type { ReactNode } from 'react';
9
+ import { vi } from 'vitest';
10
+
11
+ // Re-export fixtures for test convenience
12
+ export { createProfile, createProfiles, mockProfiles } from '@/mocks/fixtures/profiles';
13
+ export type { Profile } from '@/types/database';
14
+
15
+ // =============================================================================
16
+ // Mock State
17
+ // =============================================================================
18
+
19
+ interface SupabaseMockState {
20
+ data: unknown[];
21
+ error: { message: string; code: string } | null;
22
+ }
23
+
24
+ const defaultState: SupabaseMockState = {
25
+ data: [],
26
+ error: null,
27
+ };
28
+
29
+ let mockState: SupabaseMockState = { ...defaultState };
30
+
31
+ // =============================================================================
32
+ // State Controls
33
+ // =============================================================================
34
+
35
+ /** Set mock data to be returned by Supabase queries */
36
+ export function setMockSupabaseData(data: unknown[]) {
37
+ mockState.data = data;
38
+ }
39
+
40
+ /** Set a mock error to be returned by Supabase queries */
41
+ export function setMockSupabaseError(error: { message: string; code: string } | null) {
42
+ mockState.error = error;
43
+ }
44
+
45
+ /** Reset all Supabase mocks to default state */
46
+ export function resetSupabaseMocks() {
47
+ mockState = { ...defaultState };
48
+ }
49
+
50
+ // =============================================================================
51
+ // Mock Query Builder
52
+ // =============================================================================
53
+
54
+ function createMockQueryBuilder() {
55
+ const resolveQuery = () => {
56
+ if (mockState.error) {
57
+ return { data: null, error: mockState.error };
58
+ }
59
+ return { data: mockState.data, error: null };
60
+ };
61
+
62
+ const resolveSingle = () => {
63
+ if (mockState.error) {
64
+ return { data: null, error: mockState.error };
65
+ }
66
+ return { data: mockState.data[0] ?? null, error: null };
67
+ };
68
+
69
+ return {
70
+ select: vi.fn().mockReturnThis(),
71
+ insert: vi.fn().mockReturnThis(),
72
+ update: vi.fn().mockReturnThis(),
73
+ delete: vi.fn().mockReturnThis(),
74
+ upsert: vi.fn().mockReturnThis(),
75
+ eq: vi.fn().mockReturnThis(),
76
+ neq: vi.fn().mockReturnThis(),
77
+ single: vi.fn().mockImplementation(() => Promise.resolve(resolveSingle())),
78
+ maybeSingle: vi.fn().mockImplementation(() => Promise.resolve(resolveSingle())),
79
+ then: (resolve: (value: { data: unknown[] | null; error: unknown }) => void) => {
80
+ resolve(resolveQuery());
81
+ return { catch: () => {} };
82
+ },
83
+ };
84
+ }
85
+
86
+ // =============================================================================
87
+ // Mock Supabase Client
88
+ // =============================================================================
89
+
90
+ /** Create a mock Supabase client for testing */
91
+ export function createMockSupabaseClient() {
92
+ return {
93
+ from: vi.fn().mockReturnValue(createMockQueryBuilder()),
94
+ };
95
+ }
96
+
97
+ // Singleton client instance for useSupabase hook (maintains referential equality)
98
+ const mockClientInstance = createMockSupabaseClient();
99
+
100
+ // =============================================================================
101
+ // Mock Context (for vi.mock)
102
+ // =============================================================================
103
+
104
+ /** Mock SupabaseProvider - passes through children */
105
+ export function SupabaseProvider({ children }: { children: ReactNode }) {
106
+ return children;
107
+ }
108
+
109
+ /** Mock useSupabase hook - returns stable client instance */
110
+ export function useSupabase() {
111
+ return mockClientInstance;
112
+ }
@@ -1,8 +1,30 @@
1
1
  import '@testing-library/jest-dom/vitest';
2
2
  import { afterAll, afterEach, beforeAll, vi } from 'vitest';
3
3
 
4
+ // =============================================================================
5
+ // Environment Variable Mock (must be mocked before any imports that use env.ts)
6
+ // =============================================================================
7
+ vi.mock('@/lib/env', () => ({
8
+ env: {
9
+ VITE_APP_NAME: 'My App',
10
+ VITE_APP_URL: 'http://localhost:5173',
11
+ VITE_API_URL: 'https://jsonplaceholder.typicode.com',
12
+ VITE_SENTRY_DSN: 'https://test@sentry.io/123',
13
+ VITE_SENTRY_ENABLED: false,
14
+ VITE_CLERK_PUBLISHABLE_KEY: 'pk_test_mock',
15
+ VITE_SUPABASE_DATABASE_URL: 'https://test.supabase.co',
16
+ VITE_SUPABASE_ANON_KEY: 'test-anon-key',
17
+ VITE_PERF_TEST: false,
18
+ MODE: 'test',
19
+ DEV: false,
20
+ PROD: false,
21
+ },
22
+ validateEnv: vi.fn(),
23
+ }));
24
+
4
25
  import { server } from '@/mocks/node';
5
26
  import { resetClerkMocks } from '@/test/clerkMock';
27
+ import { resetSupabaseMocks } from '@/test/supabaseMock';
6
28
 
7
29
  // =============================================================================
8
30
  // Module Mocks
@@ -16,6 +38,9 @@ vi.mock('@clerk/themes', () => ({
16
38
  shadcn: { baseTheme: 'shadcn' },
17
39
  }));
18
40
 
41
+ // Mock Supabase context with test client
42
+ vi.mock('@/contexts/supabaseContext', async () => import('@/test/supabaseMock'));
43
+
19
44
  // =============================================================================
20
45
  // MSW Server Setup
21
46
  // =============================================================================
@@ -31,6 +56,7 @@ beforeAll(() => {
31
56
  afterEach(() => {
32
57
  server.resetHandlers();
33
58
  resetClerkMocks();
59
+ resetSupabaseMocks();
34
60
  });
35
61
 
36
62
  // Close MSW server after all tests complete
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Database type aliases and re-exports.
3
+ *
4
+ * This file provides a database-agnostic public API for type imports.
5
+ * The underlying types are auto-generated in supabase.ts via `npm run db:types`.
6
+ *
7
+ * Usage:
8
+ * import type { Profile, ProfileInsert, ProfileUpdate } from '@/types/database';
9
+ *
10
+ * When adding new tables:
11
+ * 1. Run `npm run db:types` to regenerate supabase.ts
12
+ * 2. Add convenience aliases here for your new tables
13
+ */
14
+
15
+ // Re-export everything from the auto-generated Supabase types
16
+ export * from './supabase';
17
+
18
+ // Re-import for creating aliases
19
+ import type { Database, Tables, TablesInsert, TablesUpdate } from './supabase';
20
+
21
+ // =============================================================================
22
+ // Generic Table Types
23
+ // =============================================================================
24
+
25
+ /**
26
+ * Table names available in the database schema.
27
+ */
28
+ export type TableName = keyof Database['public']['Tables'];
29
+
30
+ /**
31
+ * Row type for a given table.
32
+ */
33
+ export type TableRowType<T extends TableName> = Database['public']['Tables'][T]['Row'];
34
+
35
+ // =============================================================================
36
+ // Profile Types
37
+ // =============================================================================
38
+
39
+ /** Profile row type (read operations) */
40
+ export type Profile = Tables<'profiles'>;
41
+
42
+ /** Profile insert type (create operations) */
43
+ export type ProfileInsert = TablesInsert<'profiles'>;
44
+
45
+ /** Profile update type (update operations) */
46
+ export type ProfileUpdate = TablesUpdate<'profiles'>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Global type declarations for Electron integration.
3
+ * These types are automatically available in the renderer process
4
+ * when running inside Electron.
5
+ */
6
+
7
+ interface ElectronWindowAPI {
8
+ setAlwaysOnTop: (enable: boolean) => Promise<boolean>;
9
+ setContentProtection: (enable: boolean) => Promise<boolean>;
10
+ getState: () => Promise<{
11
+ alwaysOnTop: boolean;
12
+ contentProtection: boolean;
13
+ }>;
14
+ }
15
+
16
+ interface ElectronAPI {
17
+ window: ElectronWindowAPI;
18
+ platform: NodeJS.Platform;
19
+ isElectron: true;
20
+ }
21
+
22
+ declare global {
23
+ interface Window {
24
+ electronAPI?: ElectronAPI;
25
+ }
26
+ }
27
+
28
+ export {};
@@ -1,2 +1,3 @@
1
1
  export * from './preferences';
2
2
  export * from './api';
3
+ export * from './database';
@@ -0,0 +1,167 @@
1
+ export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[];
2
+
3
+ export type Database = {
4
+ // Allows to automatically instantiate createClient with right options
5
+ // instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
6
+ __InternalSupabase: {
7
+ PostgrestVersion: '14.1';
8
+ };
9
+ public: {
10
+ Tables: {
11
+ profiles: {
12
+ Row: {
13
+ avatar_url: string | null;
14
+ created_at: string | null;
15
+ email: string;
16
+ full_name: string | null;
17
+ id: string;
18
+ updated_at: string | null;
19
+ };
20
+ Insert: {
21
+ avatar_url?: string | null;
22
+ created_at?: string | null;
23
+ email: string;
24
+ full_name?: string | null;
25
+ id: string;
26
+ updated_at?: string | null;
27
+ };
28
+ Update: {
29
+ avatar_url?: string | null;
30
+ created_at?: string | null;
31
+ email?: string;
32
+ full_name?: string | null;
33
+ id?: string;
34
+ updated_at?: string | null;
35
+ };
36
+ Relationships: [];
37
+ };
38
+ };
39
+ Views: {
40
+ [_ in never]: never;
41
+ };
42
+ Functions: {
43
+ [_ in never]: never;
44
+ };
45
+ Enums: {
46
+ [_ in never]: never;
47
+ };
48
+ CompositeTypes: {
49
+ [_ in never]: never;
50
+ };
51
+ };
52
+ };
53
+
54
+ type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>;
55
+
56
+ type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, 'public'>];
57
+
58
+ export type Tables<
59
+ DefaultSchemaTableNameOrOptions extends
60
+ | keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
61
+ | { schema: keyof DatabaseWithoutInternals },
62
+ TableName extends DefaultSchemaTableNameOrOptions extends {
63
+ schema: keyof DatabaseWithoutInternals;
64
+ }
65
+ ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
66
+ DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])
67
+ : never = never,
68
+ > = DefaultSchemaTableNameOrOptions extends {
69
+ schema: keyof DatabaseWithoutInternals;
70
+ }
71
+ ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
72
+ DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])[TableName] extends {
73
+ Row: infer R;
74
+ }
75
+ ? R
76
+ : never
77
+ : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
78
+ ? (DefaultSchema['Tables'] & DefaultSchema['Views'])[DefaultSchemaTableNameOrOptions] extends {
79
+ Row: infer R;
80
+ }
81
+ ? R
82
+ : never
83
+ : never;
84
+
85
+ export type TablesInsert<
86
+ DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables'] | { schema: keyof DatabaseWithoutInternals },
87
+ TableName extends DefaultSchemaTableNameOrOptions extends {
88
+ schema: keyof DatabaseWithoutInternals;
89
+ }
90
+ ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables']
91
+ : never = never,
92
+ > = DefaultSchemaTableNameOrOptions extends {
93
+ schema: keyof DatabaseWithoutInternals;
94
+ }
95
+ ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
96
+ Insert: infer I;
97
+ }
98
+ ? I
99
+ : never
100
+ : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
101
+ ? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
102
+ Insert: infer I;
103
+ }
104
+ ? I
105
+ : never
106
+ : never;
107
+
108
+ export type TablesUpdate<
109
+ DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables'] | { schema: keyof DatabaseWithoutInternals },
110
+ TableName extends DefaultSchemaTableNameOrOptions extends {
111
+ schema: keyof DatabaseWithoutInternals;
112
+ }
113
+ ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables']
114
+ : never = never,
115
+ > = DefaultSchemaTableNameOrOptions extends {
116
+ schema: keyof DatabaseWithoutInternals;
117
+ }
118
+ ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
119
+ Update: infer U;
120
+ }
121
+ ? U
122
+ : never
123
+ : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
124
+ ? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
125
+ Update: infer U;
126
+ }
127
+ ? U
128
+ : never
129
+ : never;
130
+
131
+ export type Enums<
132
+ DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums'] | { schema: keyof DatabaseWithoutInternals },
133
+ EnumName extends DefaultSchemaEnumNameOrOptions extends {
134
+ schema: keyof DatabaseWithoutInternals;
135
+ }
136
+ ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums']
137
+ : never = never,
138
+ > = DefaultSchemaEnumNameOrOptions extends {
139
+ schema: keyof DatabaseWithoutInternals;
140
+ }
141
+ ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'][EnumName]
142
+ : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums']
143
+ ? DefaultSchema['Enums'][DefaultSchemaEnumNameOrOptions]
144
+ : never;
145
+
146
+ export type CompositeTypes<
147
+ PublicCompositeTypeNameOrOptions extends
148
+ | keyof DefaultSchema['CompositeTypes']
149
+ | { schema: keyof DatabaseWithoutInternals },
150
+ CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
151
+ schema: keyof DatabaseWithoutInternals;
152
+ }
153
+ ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes']
154
+ : never = never,
155
+ > = PublicCompositeTypeNameOrOptions extends {
156
+ schema: keyof DatabaseWithoutInternals;
157
+ }
158
+ ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName]
159
+ : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema['CompositeTypes']
160
+ ? DefaultSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions]
161
+ : never;
162
+
163
+ export const Constants = {
164
+ public: {
165
+ Enums: {},
166
+ },
167
+ } as const;
@@ -3,6 +3,12 @@
3
3
  interface ImportMetaEnv {
4
4
  readonly VITE_APP_NAME: string;
5
5
  readonly VITE_APP_URL: string;
6
+ readonly VITE_API_URL?: string;
7
+ readonly VITE_SENTRY_DSN?: string;
8
+ readonly VITE_CLERK_PUBLISHABLE_KEY?: string;
9
+ readonly MODE: 'development' | 'production' | 'test';
10
+ readonly DEV: boolean;
11
+ readonly PROD: boolean;
6
12
  }
7
13
 
8
14
  interface ImportMeta {
@@ -0,0 +1,67 @@
1
+ -- ============================================================
2
+ -- PROFILES TABLE (linked to Clerk user_id)
3
+ -- ============================================================
4
+
5
+ -- Create the table
6
+ CREATE TABLE IF NOT EXISTS profiles (
7
+ id TEXT PRIMARY KEY, -- Clerk user_id (from auth.uid())
8
+ email TEXT NOT NULL,
9
+ full_name TEXT,
10
+ avatar_url TEXT,
11
+ created_at TIMESTAMPTZ DEFAULT NOW(),
12
+ updated_at TIMESTAMPTZ DEFAULT NOW()
13
+ );
14
+
15
+ -- Enable Row Level Security
16
+ ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
17
+
18
+ -- ============================================================
19
+ -- RLS POLICIES
20
+ -- Using auth.jwt()->>'sub' for Clerk compatibility (string IDs)
21
+ -- ============================================================
22
+
23
+ -- Users can view their own profile
24
+ -- NOTE: Using auth.jwt()->>'sub' instead of auth.uid() because
25
+ -- Clerk user IDs are strings (e.g., user_xxx), not UUIDs
26
+ CREATE POLICY "Users can view own profile"
27
+ ON profiles FOR SELECT TO authenticated
28
+ USING (id = (select auth.jwt()->>'sub'));
29
+
30
+ -- Users can insert their own profile (first login)
31
+ CREATE POLICY "Users can insert own profile"
32
+ ON profiles FOR INSERT TO authenticated
33
+ WITH CHECK (id = (select auth.jwt()->>'sub'));
34
+
35
+ -- Users can update their own profile
36
+ CREATE POLICY "Users can update own profile"
37
+ ON profiles FOR UPDATE TO authenticated
38
+ USING (id = (select auth.jwt()->>'sub'))
39
+ WITH CHECK (id = (select auth.jwt()->>'sub'));
40
+
41
+ -- Users can delete their own profile
42
+ CREATE POLICY "Users can delete own profile"
43
+ ON profiles FOR DELETE TO authenticated
44
+ USING (id = (select auth.jwt()->>'sub'));
45
+
46
+ -- ============================================================
47
+ -- AUTO-UPDATE TIMESTAMP TRIGGER
48
+ -- ============================================================
49
+
50
+ -- Function to update updated_at (with secure search_path)
51
+ CREATE OR REPLACE FUNCTION public.update_updated_at()
52
+ RETURNS TRIGGER
53
+ LANGUAGE plpgsql
54
+ SECURITY DEFINER
55
+ SET search_path = ''
56
+ AS $$
57
+ BEGIN
58
+ NEW.updated_at = NOW();
59
+ RETURN NEW;
60
+ END;
61
+ $$;
62
+
63
+ -- Trigger for profiles
64
+ CREATE TRIGGER profiles_updated_at
65
+ BEFORE UPDATE ON profiles
66
+ FOR EACH ROW
67
+ EXECUTE FUNCTION update_updated_at();
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ export default defineConfig({
4
+ build: {
5
+ outDir: '.vite/build',
6
+ lib: {
7
+ entry: 'src/main.ts',
8
+ formats: ['es'],
9
+ fileName: () => 'main.js',
10
+ },
11
+ rollupOptions: {
12
+ external: ['electron'],
13
+ },
14
+ minify: false,
15
+ sourcemap: true,
16
+ },
17
+ resolve: {
18
+ conditions: ['node'],
19
+ },
20
+ });
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ export default defineConfig({
4
+ build: {
5
+ outDir: '.vite/build',
6
+ lib: {
7
+ entry: 'src/preload.ts',
8
+ formats: ['cjs'],
9
+ fileName: () => 'preload.js',
10
+ },
11
+ rollupOptions: {
12
+ external: ['electron'],
13
+ },
14
+ minify: false,
15
+ sourcemap: true,
16
+ },
17
+ });
@@ -0,0 +1,52 @@
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ import { lingui } from '@lingui/vite-plugin';
6
+ import tailwindcss from '@tailwindcss/vite';
7
+ import react from '@vitejs/plugin-react';
8
+ import { defineConfig } from 'vite';
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+
12
+ // Support both flat layout (config at root) and packages/electron layout
13
+ // Flat: __dirname is project root, packages: __dirname is packages/electron
14
+ const isPackagesLayout = existsSync(path.join(__dirname, '..', '..', 'package.json')) &&
15
+ __dirname.includes('packages/electron');
16
+ const rootDir = isPackagesLayout ? path.resolve(__dirname, '../..') : __dirname;
17
+
18
+ export default defineConfig({
19
+ root: rootDir,
20
+ plugins: [
21
+ react({
22
+ babel: {
23
+ plugins: ['@lingui/babel-plugin-lingui-macro'],
24
+ },
25
+ }),
26
+ lingui(),
27
+ tailwindcss(),
28
+ ],
29
+ resolve: {
30
+ alias: {
31
+ '@': path.resolve(rootDir, './src'),
32
+ },
33
+ },
34
+ base: './', // Critical: Use relative paths for file:// protocol
35
+ build: {
36
+ outDir: path.resolve(__dirname, '.vite/renderer/main_window'),
37
+ emptyOutDir: true,
38
+ rollupOptions: {
39
+ input: path.resolve(rootDir, 'index.html'),
40
+ },
41
+ },
42
+ // Dev server configuration for HMR
43
+ server: {
44
+ port: 5173,
45
+ strictPort: true,
46
+ },
47
+ // Dependency optimization settings
48
+ optimizeDeps: {
49
+ // Include problematic ESM/CJS packages
50
+ include: ['cookie'],
51
+ },
52
+ });