@odvi/create-dtt-framework 0.1.3 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +16 -13
- package/dist/commands/create.js.map +1 -1
- package/package.json +3 -2
- package/template/.env.example +106 -0
- package/template/components.json +22 -0
- package/template/docs/framework/01-overview.md +289 -0
- package/template/docs/framework/02-techstack.md +503 -0
- package/template/docs/framework/api-layer.md +681 -0
- package/template/docs/framework/clerk-authentication.md +649 -0
- package/template/docs/framework/cli-installation.md +564 -0
- package/template/docs/framework/deployment/ci-cd.md +907 -0
- package/template/docs/framework/deployment/digitalocean.md +991 -0
- package/template/docs/framework/deployment/domain-setup.md +972 -0
- package/template/docs/framework/deployment/environment-variables.md +862 -0
- package/template/docs/framework/deployment/monitoring.md +927 -0
- package/template/docs/framework/deployment/production-checklist.md +649 -0
- package/template/docs/framework/deployment/vercel.md +791 -0
- package/template/docs/framework/environment-variables.md +646 -0
- package/template/docs/framework/health-check-system.md +583 -0
- package/template/docs/framework/implementation.md +559 -0
- package/template/docs/framework/snowflake-integration.md +594 -0
- package/template/docs/framework/state-management.md +615 -0
- package/template/docs/framework/supabase-integration.md +582 -0
- package/template/docs/framework/testing-guide.md +544 -0
- package/template/docs/framework/what-did-i-miss.md +526 -0
- package/template/drizzle.config.ts +11 -0
- package/template/next.config.js +21 -0
- package/template/postcss.config.js +5 -0
- package/template/prettier.config.js +4 -0
- package/template/public/favicon.ico +0 -0
- package/template/src/app/(auth)/layout.tsx +4 -0
- package/template/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +10 -0
- package/template/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx +10 -0
- package/template/src/app/(dashboard)/dashboard/page.tsx +8 -0
- package/template/src/app/(dashboard)/health/page.tsx +16 -0
- package/template/src/app/(dashboard)/layout.tsx +17 -0
- package/template/src/app/api/[[...route]]/route.ts +11 -0
- package/template/src/app/api/debug-files/route.ts +33 -0
- package/template/src/app/api/webhooks/clerk/route.ts +112 -0
- package/template/src/app/layout.tsx +28 -0
- package/template/src/app/page.tsx +12 -0
- package/template/src/app/providers.tsx +20 -0
- package/template/src/components/layouts/navbar.tsx +14 -0
- package/template/src/components/shared/loading-spinner.tsx +6 -0
- package/template/src/components/ui/badge.tsx +46 -0
- package/template/src/components/ui/button.tsx +62 -0
- package/template/src/components/ui/card.tsx +92 -0
- package/template/src/components/ui/collapsible.tsx +33 -0
- package/template/src/components/ui/scroll-area.tsx +58 -0
- package/template/src/components/ui/sheet.tsx +139 -0
- package/template/src/config/__tests__/env.test.ts +164 -0
- package/template/src/config/__tests__/site.test.ts +46 -0
- package/template/src/config/env.ts +36 -0
- package/template/src/config/site.ts +10 -0
- package/template/src/env.js +44 -0
- package/template/src/features/__tests__/health-check-config.test.ts +142 -0
- package/template/src/features/__tests__/health-check-types.test.ts +201 -0
- package/template/src/features/documentation/components/doc-sidebar.tsx +109 -0
- package/template/src/features/documentation/components/doc-viewer.tsx +70 -0
- package/template/src/features/documentation/index.tsx +92 -0
- package/template/src/features/documentation/utils/doc-loader.ts +177 -0
- package/template/src/features/health-check/components/health-dashboard.tsx +374 -0
- package/template/src/features/health-check/config.ts +71 -0
- package/template/src/features/health-check/index.ts +4 -0
- package/template/src/features/health-check/stores/health-store.ts +14 -0
- package/template/src/features/health-check/types.ts +18 -0
- package/template/src/hooks/__tests__/use-debounce.test.tsx +28 -0
- package/template/src/hooks/queries/use-health-checks.ts +16 -0
- package/template/src/hooks/utils/use-debounce.ts +20 -0
- package/template/src/lib/__tests__/utils.test.ts +52 -0
- package/template/src/lib/__tests__/validators.test.ts +114 -0
- package/template/src/lib/nextbank/client.ts +67 -0
- package/template/src/lib/snowflake/client.ts +102 -0
- package/template/src/lib/supabase/admin.ts +7 -0
- package/template/src/lib/supabase/client.ts +7 -0
- package/template/src/lib/supabase/server.ts +23 -0
- package/template/src/lib/utils.ts +6 -0
- package/template/src/lib/validators.ts +9 -0
- package/template/src/middleware.ts +22 -0
- package/template/src/server/api/index.ts +22 -0
- package/template/src/server/api/middleware/auth.ts +19 -0
- package/template/src/server/api/middleware/logger.ts +4 -0
- package/template/src/server/api/routes/health/clerk.ts +214 -0
- package/template/src/server/api/routes/health/database.ts +141 -0
- package/template/src/server/api/routes/health/edge-functions.ts +107 -0
- package/template/src/server/api/routes/health/framework.ts +48 -0
- package/template/src/server/api/routes/health/index.ts +102 -0
- package/template/src/server/api/routes/health/nextbank.ts +46 -0
- package/template/src/server/api/routes/health/snowflake.ts +83 -0
- package/template/src/server/api/routes/health/storage.ts +177 -0
- package/template/src/server/api/routes/users.ts +79 -0
- package/template/src/server/db/index.ts +17 -0
- package/template/src/server/db/queries/users.ts +8 -0
- package/template/src/server/db/schema/__tests__/health-checks.test.ts +31 -0
- package/template/src/server/db/schema/__tests__/users.test.ts +46 -0
- package/template/src/server/db/schema/health-checks.ts +11 -0
- package/template/src/server/db/schema/index.ts +2 -0
- package/template/src/server/db/schema/users.ts +16 -0
- package/template/src/server/db/schema.ts +1 -0
- package/template/src/stores/__tests__/ui-store.test.ts +87 -0
- package/template/src/stores/ui-store.ts +14 -0
- package/template/src/styles/globals.css +129 -0
- package/template/src/test/mocks/clerk.ts +35 -0
- package/template/src/test/mocks/snowflake.ts +28 -0
- package/template/src/test/mocks/supabase.ts +37 -0
- package/template/src/test/setup.ts +69 -0
- package/template/src/test/utils/test-helpers.ts +158 -0
- package/template/src/types/index.ts +14 -0
- package/template/tsconfig.json +43 -0
- package/template/vitest.config.ts +44 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock Supabase client
|
|
4
|
+
export const mockSupabaseClient = {
|
|
5
|
+
from: vi.fn(() => mockSupabaseClient),
|
|
6
|
+
select: vi.fn(() => mockSupabaseClient),
|
|
7
|
+
insert: vi.fn(() => mockSupabaseClient),
|
|
8
|
+
update: vi.fn(() => mockSupabaseClient),
|
|
9
|
+
delete: vi.fn(() => mockSupabaseClient),
|
|
10
|
+
eq: vi.fn(() => mockSupabaseClient),
|
|
11
|
+
order: vi.fn(() => mockSupabaseClient),
|
|
12
|
+
limit: vi.fn(() => mockSupabaseClient),
|
|
13
|
+
single: vi.fn(),
|
|
14
|
+
maybeSingle: vi.fn(),
|
|
15
|
+
rpc: vi.fn(),
|
|
16
|
+
auth: {
|
|
17
|
+
getUser: vi.fn(),
|
|
18
|
+
signInWithPassword: vi.fn(),
|
|
19
|
+
signOut: vi.fn(),
|
|
20
|
+
onAuthStateChange: vi.fn(),
|
|
21
|
+
},
|
|
22
|
+
storage: {
|
|
23
|
+
from: vi.fn(() => ({
|
|
24
|
+
upload: vi.fn(),
|
|
25
|
+
download: vi.fn(),
|
|
26
|
+
remove: vi.fn(),
|
|
27
|
+
list: vi.fn(),
|
|
28
|
+
getPublicUrl: vi.fn(),
|
|
29
|
+
})),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
vi.mock('@supabase/supabase-js', () => ({
|
|
34
|
+
createClient: vi.fn(() => mockSupabaseClient),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
export default mockSupabaseClient;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import { cleanup } from '@testing-library/react';
|
|
3
|
+
import { afterEach, vi } from 'vitest';
|
|
4
|
+
|
|
5
|
+
// Cleanup after each test
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
cleanup();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Mock window.matchMedia
|
|
11
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
12
|
+
writable: true,
|
|
13
|
+
value: vi.fn().mockImplementation((query) => ({
|
|
14
|
+
matches: false,
|
|
15
|
+
media: query,
|
|
16
|
+
onchange: null,
|
|
17
|
+
addListener: vi.fn(),
|
|
18
|
+
removeListener: vi.fn(),
|
|
19
|
+
addEventListener: vi.fn(),
|
|
20
|
+
removeEventListener: vi.fn(),
|
|
21
|
+
dispatchEvent: vi.fn(),
|
|
22
|
+
})),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Mock IntersectionObserver
|
|
26
|
+
global.IntersectionObserver = class IntersectionObserver {
|
|
27
|
+
constructor() {}
|
|
28
|
+
disconnect() {}
|
|
29
|
+
observe() {}
|
|
30
|
+
takeRecords() {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
unobserve() {}
|
|
34
|
+
} as any;
|
|
35
|
+
|
|
36
|
+
// Mock ResizeObserver
|
|
37
|
+
global.ResizeObserver = class ResizeObserver {
|
|
38
|
+
constructor() {}
|
|
39
|
+
disconnect() {}
|
|
40
|
+
observe() {}
|
|
41
|
+
unobserve() {}
|
|
42
|
+
} as any;
|
|
43
|
+
|
|
44
|
+
// Mock requestAnimationFrame
|
|
45
|
+
global.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
46
|
+
return setTimeout(callback, 0) as unknown as number;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
global.cancelAnimationFrame = (id: number) => {
|
|
50
|
+
clearTimeout(id);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Suppress console errors in tests (optional, can be removed for debugging)
|
|
54
|
+
const originalError = console.error;
|
|
55
|
+
beforeAll(() => {
|
|
56
|
+
console.error = (...args: any[]) => {
|
|
57
|
+
if (
|
|
58
|
+
typeof args[0] === 'string' &&
|
|
59
|
+
args[0].includes('Warning: ReactDOM.render')
|
|
60
|
+
) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
originalError.call(console, ...args);
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterAll(() => {
|
|
68
|
+
console.error = originalError;
|
|
69
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { RenderOptions } from '@testing-library/react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import type { ReactElement } from 'react';
|
|
4
|
+
import { vi, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Custom render function that includes providers if needed
|
|
8
|
+
*/
|
|
9
|
+
export function renderWithProviders(
|
|
10
|
+
ui: ReactElement,
|
|
11
|
+
options?: Omit<RenderOptions, 'wrapper'>
|
|
12
|
+
) {
|
|
13
|
+
return render(ui, options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a mock function that returns a resolved value
|
|
18
|
+
*/
|
|
19
|
+
export function mockResolved<T>(value: T) {
|
|
20
|
+
return vi.fn().mockResolvedValue(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a mock function that returns a rejected value
|
|
25
|
+
*/
|
|
26
|
+
export function mockRejected(error: Error | string) {
|
|
27
|
+
return vi.fn().mockRejectedValue(error instanceof Error ? error : new Error(error));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Wait for a specified amount of time
|
|
32
|
+
*/
|
|
33
|
+
export function wait(ms: number): Promise<void> {
|
|
34
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a mock user object
|
|
39
|
+
*/
|
|
40
|
+
export function createMockUser(overrides: Partial<any> = {}) {
|
|
41
|
+
return {
|
|
42
|
+
id: 'user-123',
|
|
43
|
+
email: 'test@example.com',
|
|
44
|
+
firstName: 'Test',
|
|
45
|
+
lastName: 'User',
|
|
46
|
+
imageUrl: 'https://example.com/avatar.jpg',
|
|
47
|
+
clerkOrgId: 'org-123',
|
|
48
|
+
createdAt: new Date(),
|
|
49
|
+
updatedAt: new Date(),
|
|
50
|
+
...overrides,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a mock health check result
|
|
56
|
+
*/
|
|
57
|
+
export function createMockHealthCheck(overrides: Partial<any> = {}) {
|
|
58
|
+
return {
|
|
59
|
+
status: 'healthy',
|
|
60
|
+
responseTimeMs: 100,
|
|
61
|
+
message: 'Service is healthy',
|
|
62
|
+
...overrides,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a mock service health object
|
|
68
|
+
*/
|
|
69
|
+
export function createMockServiceHealth(overrides: Partial<any> = {}) {
|
|
70
|
+
return {
|
|
71
|
+
name: 'Test Service',
|
|
72
|
+
icon: 'test-icon',
|
|
73
|
+
status: 'healthy',
|
|
74
|
+
responseTimeMs: 100,
|
|
75
|
+
checks: [
|
|
76
|
+
{
|
|
77
|
+
name: 'Test Check',
|
|
78
|
+
endpoint: '/test/endpoint',
|
|
79
|
+
status: 'healthy',
|
|
80
|
+
responseTimeMs: 50,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
...overrides,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Mock console methods to suppress warnings in tests
|
|
89
|
+
*/
|
|
90
|
+
export function suppressConsoleWarnings() {
|
|
91
|
+
const originalWarn = console.warn;
|
|
92
|
+
const originalError = console.error;
|
|
93
|
+
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
console.warn = vi.fn();
|
|
96
|
+
console.error = vi.fn();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
console.warn = originalWarn;
|
|
101
|
+
console.error = originalError;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create a mock fetch response
|
|
107
|
+
*/
|
|
108
|
+
export function createMockResponse(
|
|
109
|
+
data: any,
|
|
110
|
+
status: number = 200,
|
|
111
|
+
ok: boolean = true
|
|
112
|
+
) {
|
|
113
|
+
return {
|
|
114
|
+
ok,
|
|
115
|
+
status,
|
|
116
|
+
json: vi.fn().mockResolvedValue(data),
|
|
117
|
+
text: vi.fn().mockResolvedValue(JSON.stringify(data)),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Mock localStorage
|
|
123
|
+
*/
|
|
124
|
+
export function mockLocalStorage() {
|
|
125
|
+
const store: Record<string, string> = {};
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
getItem: (key: string) => store[key] ?? null,
|
|
129
|
+
setItem: (key: string, value: string) => {
|
|
130
|
+
store[key] = value;
|
|
131
|
+
},
|
|
132
|
+
removeItem: (key: string) => {
|
|
133
|
+
delete store[key];
|
|
134
|
+
},
|
|
135
|
+
clear: () => {
|
|
136
|
+
Object.keys(store).forEach((key) => delete store[key]);
|
|
137
|
+
},
|
|
138
|
+
get length() {
|
|
139
|
+
return Object.keys(store).length;
|
|
140
|
+
},
|
|
141
|
+
key: (index: number) => Object.keys(store)[index] ?? null,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Create a mock router
|
|
147
|
+
*/
|
|
148
|
+
export function createMockRouter() {
|
|
149
|
+
return {
|
|
150
|
+
push: vi.fn(),
|
|
151
|
+
replace: vi.fn(),
|
|
152
|
+
prefetch: vi.fn(),
|
|
153
|
+
back: vi.fn(),
|
|
154
|
+
pathname: '/',
|
|
155
|
+
query: {},
|
|
156
|
+
asPath: '/',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Global type definitions placeholder
|
|
2
|
+
|
|
3
|
+
export interface User {
|
|
4
|
+
id: string
|
|
5
|
+
email: string
|
|
6
|
+
firstName?: string
|
|
7
|
+
lastName?: string
|
|
8
|
+
imageUrl?: string
|
|
9
|
+
clerkOrgId?: string
|
|
10
|
+
createdAt: Date
|
|
11
|
+
updatedAt: Date
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type HealthStatus = 'healthy' | 'unhealthy' | 'error' | 'pending' | 'unconfigured'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
/* Base Options: */
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"skipLibCheck": true,
|
|
6
|
+
"target": "es2022",
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"moduleDetection": "force",
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"verbatimModuleSyntax": true,
|
|
12
|
+
|
|
13
|
+
/* Strictness */
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUncheckedIndexedAccess": true,
|
|
16
|
+
"checkJs": true,
|
|
17
|
+
|
|
18
|
+
/* Bundled projects */
|
|
19
|
+
"lib": ["dom", "dom.iterable", "ES2022"],
|
|
20
|
+
"noEmit": true,
|
|
21
|
+
"module": "ESNext",
|
|
22
|
+
"moduleResolution": "Bundler",
|
|
23
|
+
"jsx": "preserve",
|
|
24
|
+
"plugins": [{ "name": "next" }],
|
|
25
|
+
"incremental": true,
|
|
26
|
+
|
|
27
|
+
/* Path Aliases */
|
|
28
|
+
"baseUrl": ".",
|
|
29
|
+
"paths": {
|
|
30
|
+
"~/*": ["./src/*"],
|
|
31
|
+
"@/*": ["./src/*"]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"include": [
|
|
35
|
+
"next-env.d.ts",
|
|
36
|
+
"**/*.ts",
|
|
37
|
+
"**/*.tsx",
|
|
38
|
+
"**/*.cjs",
|
|
39
|
+
"**/*.js",
|
|
40
|
+
".next/types/**/*.ts"
|
|
41
|
+
],
|
|
42
|
+
"exclude": ["node_modules", "generated", "cli", "src/test", "**/*.test.ts", "**/*.test.tsx", "vitest.config.ts", "drizzle.config.ts", "supabase"]
|
|
43
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
test: {
|
|
8
|
+
globals: true,
|
|
9
|
+
environment: 'jsdom',
|
|
10
|
+
setupFiles: ['./src/test/setup.ts'],
|
|
11
|
+
include: ['**/__tests__/**/*.{test,spec}.{ts,tsx}', '**/*.{test,spec}.{ts,tsx}'],
|
|
12
|
+
exclude: ['node_modules', 'dist', '.next', 'coverage'],
|
|
13
|
+
coverage: {
|
|
14
|
+
provider: 'v8',
|
|
15
|
+
reporter: ['text', 'json', 'html', 'lcov'],
|
|
16
|
+
exclude: [
|
|
17
|
+
'node_modules/',
|
|
18
|
+
'.next/',
|
|
19
|
+
'dist/',
|
|
20
|
+
'coverage/',
|
|
21
|
+
'**/*.config.{js,ts}',
|
|
22
|
+
'**/types/**',
|
|
23
|
+
'**/test/**',
|
|
24
|
+
'**/__tests__/**',
|
|
25
|
+
'**/*.d.ts',
|
|
26
|
+
'**/mockData.ts',
|
|
27
|
+
'src/middleware.ts',
|
|
28
|
+
'src/env.js',
|
|
29
|
+
],
|
|
30
|
+
all: true,
|
|
31
|
+
lines: 80,
|
|
32
|
+
functions: 80,
|
|
33
|
+
branches: 80,
|
|
34
|
+
statements: 80,
|
|
35
|
+
},
|
|
36
|
+
testTimeout: 10000,
|
|
37
|
+
hookTimeout: 10000,
|
|
38
|
+
},
|
|
39
|
+
resolve: {
|
|
40
|
+
alias: {
|
|
41
|
+
'@': path.resolve(__dirname, './src'),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|