@tekyzinc/gsd-t 2.46.11 → 2.50.10
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/CHANGELOG.md +11 -0
- package/README.md +22 -2
- package/bin/debug-ledger.js +193 -0
- package/bin/gsd-t.js +259 -1
- package/commands/gsd-t-debug.md +26 -1
- package/commands/gsd-t-execute.md +31 -3
- package/commands/gsd-t-help.md +18 -2
- package/commands/gsd-t-integrate.md +16 -0
- package/commands/gsd-t-quick.md +18 -1
- package/commands/gsd-t-test-sync.md +5 -1
- package/commands/gsd-t-verify.md +6 -1
- package/commands/gsd-t-wave.md +26 -0
- package/docs/GSD-T-README.md +83 -1
- package/docs/architecture.md +9 -1
- package/docs/requirements.md +30 -0
- package/package.json +1 -1
- package/templates/CLAUDE-global.md +19 -2
- package/templates/stacks/_security.md +243 -0
- package/templates/stacks/desktop.ini +2 -0
- package/templates/stacks/docker.md +202 -0
- package/templates/stacks/firebase.md +166 -0
- package/templates/stacks/flutter.md +205 -0
- package/templates/stacks/github-actions.md +201 -0
- package/templates/stacks/graphql.md +216 -0
- package/templates/stacks/neo4j.md +218 -0
- package/templates/stacks/nextjs.md +184 -0
- package/templates/stacks/node-api.md +196 -0
- package/templates/stacks/playwright.md +528 -0
- package/templates/stacks/postgresql.md +225 -0
- package/templates/stacks/python.md +243 -0
- package/templates/stacks/react-native.md +216 -0
- package/templates/stacks/react.md +293 -0
- package/templates/stacks/redux.md +193 -0
- package/templates/stacks/rest-api.md +202 -0
- package/templates/stacks/supabase.md +188 -0
- package/templates/stacks/tailwind.md +169 -0
- package/templates/stacks/typescript.md +176 -0
- package/templates/stacks/vite.md +176 -0
- package/templates/stacks/vue.md +189 -0
- package/templates/stacks/zustand.md +203 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Supabase Standards
|
|
2
|
+
|
|
3
|
+
These rules are MANDATORY. Violations fail the task. No exceptions.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Row Level Security (RLS)
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MANDATORY:
|
|
11
|
+
├── Enable RLS on EVERY table — no exceptions
|
|
12
|
+
├── Default-deny: tables with RLS enabled and no policies block all access
|
|
13
|
+
├── Write policies per role: anon, authenticated, service_role
|
|
14
|
+
├── Use auth.uid() for user-scoped policies — NEVER trust client-sent user IDs
|
|
15
|
+
├── Test policies with different roles before deploying
|
|
16
|
+
└── Service role bypasses RLS — only use server-side, NEVER expose the service key
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**GOOD**
|
|
20
|
+
```sql
|
|
21
|
+
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
|
|
22
|
+
|
|
23
|
+
-- Users can read their own profile
|
|
24
|
+
CREATE POLICY "Users read own profile"
|
|
25
|
+
ON user_profiles FOR SELECT
|
|
26
|
+
USING (auth.uid() = user_id);
|
|
27
|
+
|
|
28
|
+
-- Users can update their own profile
|
|
29
|
+
CREATE POLICY "Users update own profile"
|
|
30
|
+
ON user_profiles FOR UPDATE
|
|
31
|
+
USING (auth.uid() = user_id)
|
|
32
|
+
WITH CHECK (auth.uid() = user_id);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Auth Patterns
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
MANDATORY:
|
|
41
|
+
├── Use Supabase Auth — don't build custom auth alongside it
|
|
42
|
+
├── Store user metadata in a separate profiles table — not in auth.users
|
|
43
|
+
├── Create profile on signup via database trigger or auth hook
|
|
44
|
+
├── Use auth.uid() in RLS policies — it's the trusted source of identity
|
|
45
|
+
├── Handle auth state changes with onAuthStateChange listener
|
|
46
|
+
└── NEVER store the service_role key in client code — it bypasses RLS
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**GOOD** — trigger to create profile:
|
|
50
|
+
```sql
|
|
51
|
+
CREATE OR REPLACE FUNCTION handle_new_user()
|
|
52
|
+
RETURNS TRIGGER AS $$
|
|
53
|
+
BEGIN
|
|
54
|
+
INSERT INTO user_profiles (user_id, email, display_name)
|
|
55
|
+
VALUES (NEW.id, NEW.email, COALESCE(NEW.raw_user_meta_data->>'display_name', ''));
|
|
56
|
+
RETURN NEW;
|
|
57
|
+
END;
|
|
58
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
59
|
+
|
|
60
|
+
CREATE TRIGGER on_auth_user_created
|
|
61
|
+
AFTER INSERT ON auth.users
|
|
62
|
+
FOR EACH ROW EXECUTE FUNCTION handle_new_user();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. Client Usage
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
MANDATORY:
|
|
71
|
+
├── Initialize client once — export from a shared module
|
|
72
|
+
├── Use typed client with generated types: supabase-js + supabase gen types
|
|
73
|
+
├── Handle errors from every Supabase call — check { data, error } response
|
|
74
|
+
├── Use .select() with specific columns — NEVER .select('*') in production
|
|
75
|
+
├── Use .single() when expecting one row — throws if 0 or 2+ rows
|
|
76
|
+
└── Regenerate types after every migration: npx supabase gen types typescript
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**GOOD**
|
|
80
|
+
```typescript
|
|
81
|
+
const { data, error } = await supabase
|
|
82
|
+
.from('user_profiles')
|
|
83
|
+
.select('id, email, display_name, role')
|
|
84
|
+
.eq('user_id', userId)
|
|
85
|
+
.single();
|
|
86
|
+
|
|
87
|
+
if (error) throw new DatabaseError('Failed to fetch profile', error);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 4. Edge Functions
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
MANDATORY:
|
|
96
|
+
├── Use for server-side logic that needs secrets or external API calls
|
|
97
|
+
├── Validate ALL inputs — edge functions are public endpoints
|
|
98
|
+
├── Use Zod or manual validation at the entry point
|
|
99
|
+
├── Set CORS headers explicitly
|
|
100
|
+
├── Handle errors with proper HTTP status codes
|
|
101
|
+
└── Keep functions focused — one concern per function
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 5. Realtime
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
WHEN USING REALTIME:
|
|
110
|
+
├── Enable realtime only on tables that need it — not all tables
|
|
111
|
+
├── Subscribe with filters to reduce message volume
|
|
112
|
+
├── Unsubscribe on component unmount — prevent memory leaks
|
|
113
|
+
├── Handle reconnection gracefully
|
|
114
|
+
└── RLS applies to realtime — users only receive rows they can SELECT
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**GOOD**
|
|
118
|
+
```typescript
|
|
119
|
+
const channel = supabase
|
|
120
|
+
.channel('user-orders')
|
|
121
|
+
.on('postgres_changes',
|
|
122
|
+
{ event: 'INSERT', schema: 'public', table: 'orders', filter: `user_id=eq.${userId}` },
|
|
123
|
+
(payload) => handleNewOrder(payload.new)
|
|
124
|
+
)
|
|
125
|
+
.subscribe();
|
|
126
|
+
|
|
127
|
+
// Cleanup
|
|
128
|
+
return () => { supabase.removeChannel(channel); };
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 6. Storage
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
MANDATORY:
|
|
137
|
+
├── Create separate buckets per use case (avatars, documents, uploads)
|
|
138
|
+
├── Set bucket-level access policies (public vs private)
|
|
139
|
+
├── Validate file type and size before upload — client AND server side
|
|
140
|
+
├── Use signed URLs for private file access — not public URLs
|
|
141
|
+
├── Set file size limits per bucket
|
|
142
|
+
└── NEVER store sensitive documents in public buckets
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 7. Migrations
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
MANDATORY:
|
|
151
|
+
├── Use supabase db diff or manual SQL files for migrations
|
|
152
|
+
├── One migration per change — don't combine unrelated changes
|
|
153
|
+
├── Test migrations locally: supabase db reset
|
|
154
|
+
├── Always regenerate types after migration: supabase gen types typescript
|
|
155
|
+
├── Include RLS policies in migration files — not applied manually
|
|
156
|
+
└── Seed data in a separate seed.sql file
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 8. Anti-Patterns
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
NEVER:
|
|
165
|
+
├── Expose service_role key to client — it bypasses RLS
|
|
166
|
+
├── Tables without RLS enabled
|
|
167
|
+
├── .select('*') in production queries
|
|
168
|
+
├── Trusting client-sent user IDs — use auth.uid()
|
|
169
|
+
├── Storing user data in auth.users metadata instead of a profiles table
|
|
170
|
+
├── Realtime on all tables — enable selectively
|
|
171
|
+
├── Ignoring { error } from Supabase calls
|
|
172
|
+
└── Manual SQL in Supabase dashboard instead of migration files
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Supabase Verification Checklist
|
|
178
|
+
|
|
179
|
+
- [ ] RLS enabled on every table with explicit policies
|
|
180
|
+
- [ ] auth.uid() used in policies — no client-sent user IDs trusted
|
|
181
|
+
- [ ] Service role key only used server-side
|
|
182
|
+
- [ ] Typed client with generated types (supabase gen types)
|
|
183
|
+
- [ ] Specific column selects — no .select('*')
|
|
184
|
+
- [ ] Error handling on every Supabase call
|
|
185
|
+
- [ ] Realtime subscriptions cleaned up on unmount
|
|
186
|
+
- [ ] Storage buckets have access policies and file limits
|
|
187
|
+
- [ ] Migrations in version control — not manual dashboard SQL
|
|
188
|
+
- [ ] Types regenerated after every migration
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Tailwind CSS Standards
|
|
2
|
+
|
|
3
|
+
These rules are MANDATORY. Violations fail the task. No exceptions.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Class-Only Styling
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MANDATORY:
|
|
11
|
+
├── Tailwind utility classes ONLY — no inline styles, no CSS modules, no styled-components
|
|
12
|
+
├── Exception: CSS variables for theme tokens (e.g., bg-[var(--brand-primary)])
|
|
13
|
+
├── Exception: Animations that require @keyframes — define in tailwind.config or globals.css
|
|
14
|
+
└── NEVER mix Tailwind with other CSS methodologies in the same project
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**BAD** — `<div style={{ padding: '16px', color: 'red' }}>`
|
|
18
|
+
|
|
19
|
+
**GOOD** — `<div className="p-4 text-red-500">`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. Responsive Design — Mobile First
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
MANDATORY:
|
|
27
|
+
├── Default styles target mobile — add breakpoints for larger screens
|
|
28
|
+
├── Breakpoint order: base → sm: → md: → lg: → xl: → 2xl:
|
|
29
|
+
├── NEVER use max-width breakpoints — always min-width (Tailwind default)
|
|
30
|
+
└── Test at each breakpoint — don't assume intermediate sizes work
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**BAD** — Desktop-first: `<div className="flex lg:flex max-lg:block">`
|
|
34
|
+
|
|
35
|
+
**GOOD** — Mobile-first: `<div className="block md:flex">`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 3. Component Extraction Over @apply
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
MANDATORY:
|
|
43
|
+
├── Extract repeated utility patterns into components — NOT @apply classes
|
|
44
|
+
├── @apply is allowed ONLY in global base styles (body, headings, links)
|
|
45
|
+
├── Use a cn() helper for conditional classes
|
|
46
|
+
└── NEVER create .btn, .card, etc. utility classes — make components instead
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**BAD** — `@apply` in CSS:
|
|
50
|
+
```css
|
|
51
|
+
.btn-primary { @apply px-4 py-2 bg-blue-500 text-white rounded; }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**GOOD** — React component:
|
|
55
|
+
```tsx
|
|
56
|
+
function Button({ children, variant = 'primary', className }: ButtonProps) {
|
|
57
|
+
return (
|
|
58
|
+
<button className={cn(
|
|
59
|
+
'px-4 py-2 rounded font-medium transition-colors',
|
|
60
|
+
variant === 'primary' && 'bg-blue-500 text-white hover:bg-blue-600',
|
|
61
|
+
variant === 'ghost' && 'bg-transparent hover:bg-gray-100',
|
|
62
|
+
className
|
|
63
|
+
)}>
|
|
64
|
+
{children}
|
|
65
|
+
</button>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 4. The cn() Helper
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
MANDATORY:
|
|
76
|
+
├── Use a cn() or clsx() utility for conditional and merged classes
|
|
77
|
+
├── For Tailwind merge conflicts, use tailwind-merge (twMerge)
|
|
78
|
+
└── Standard pattern: cn = (...classes) => twMerge(clsx(...classes))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
83
|
+
import { twMerge } from 'tailwind-merge';
|
|
84
|
+
|
|
85
|
+
export function cn(...inputs: ClassValue[]) {
|
|
86
|
+
return twMerge(clsx(inputs));
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 5. Color and Theme System
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
MANDATORY:
|
|
96
|
+
├── Define colors as CSS variables — not hardcoded Tailwind colors
|
|
97
|
+
├── Use semantic names (--color-primary, --color-surface) — not visual (--blue-500)
|
|
98
|
+
├── Support dark mode via CSS variables or Tailwind dark: prefix
|
|
99
|
+
├── NEVER use arbitrary color values ([#ff6b35]) — add to theme config
|
|
100
|
+
└── Opacity modifiers via Tailwind (text-primary/80) — not rgba
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**GOOD** — `tailwind.config.js`:
|
|
104
|
+
```js
|
|
105
|
+
theme: {
|
|
106
|
+
extend: {
|
|
107
|
+
colors: {
|
|
108
|
+
primary: 'var(--color-primary)',
|
|
109
|
+
surface: 'var(--color-surface)',
|
|
110
|
+
border: 'var(--color-border)',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 6. Spacing and Layout
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
MANDATORY:
|
|
122
|
+
├── Use Tailwind spacing scale (p-4, gap-3, m-2) — not arbitrary values
|
|
123
|
+
├── Use Flexbox (flex) or Grid (grid) for layout — not floats or absolute positioning
|
|
124
|
+
├── gap-* for spacing between flex/grid children — not margins on children
|
|
125
|
+
├── Consistent spacing: pick a scale (4px base) and stick to it
|
|
126
|
+
└── Arbitrary values ([17px]) only when matching a design spec exactly
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 7. Dark Mode
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
WHEN SUPPORTING DARK MODE:
|
|
135
|
+
├── Use class strategy (darkMode: 'class') for user-controlled toggling
|
|
136
|
+
├── Apply dark: variants alongside base styles — not in separate files
|
|
137
|
+
├── Test both modes — don't assume dark is just "invert colors"
|
|
138
|
+
└── Ensure sufficient contrast in both modes (WCAG AA: 4.5:1 for text)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**GOOD** — `<div className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">`
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 8. Anti-Patterns
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
NEVER:
|
|
149
|
+
├── Inline styles alongside Tailwind classes
|
|
150
|
+
├── @apply for component-level styles — make components instead
|
|
151
|
+
├── Arbitrary values when a Tailwind scale value exists (p-[16px] → p-4)
|
|
152
|
+
├── !important via ! prefix — fix specificity instead
|
|
153
|
+
├── Overly long class strings (15+ utilities) — extract a component
|
|
154
|
+
├── Hardcoded colors ([#hex]) — add to theme config
|
|
155
|
+
└── Margin on grid/flex children for spacing — use gap-* on parent
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Tailwind Verification Checklist
|
|
161
|
+
|
|
162
|
+
- [ ] No inline styles — Tailwind classes only
|
|
163
|
+
- [ ] Mobile-first responsive (base → sm → md → lg)
|
|
164
|
+
- [ ] Repeated patterns extracted to components — not @apply
|
|
165
|
+
- [ ] cn() helper used for conditional classes
|
|
166
|
+
- [ ] Colors defined as CSS variables / theme config — no hardcoded hex
|
|
167
|
+
- [ ] Spacing uses Tailwind scale — minimal arbitrary values
|
|
168
|
+
- [ ] Dark mode tested (if applicable)
|
|
169
|
+
- [ ] No class strings longer than ~15 utilities — component extracted
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# TypeScript Standards
|
|
2
|
+
|
|
3
|
+
These rules are MANDATORY. Violations fail the task. No exceptions.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Strict Mode
|
|
8
|
+
|
|
9
|
+
`tsconfig.json` MUST have `"strict": true`. Never disable strict flags to silence errors — fix the code.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
MANDATORY:
|
|
13
|
+
├── "strict": true in tsconfig.json
|
|
14
|
+
├── Never use `any` — use `unknown` for truly unknown types, then narrow
|
|
15
|
+
├── Never use the `object` type — use a specific interface or Record<K, V>
|
|
16
|
+
└── Never leave function params or return values untyped (no implicit any)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// BAD
|
|
21
|
+
function process(data: any) { return data.value; }
|
|
22
|
+
|
|
23
|
+
// GOOD — unknown forces narrowing before use
|
|
24
|
+
function process(data: unknown): string {
|
|
25
|
+
if (typeof data === 'object' && data !== null && 'value' in data) {
|
|
26
|
+
return String((data as { value: unknown }).value);
|
|
27
|
+
}
|
|
28
|
+
throw new Error('Unexpected data shape');
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Interface vs Type
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
USE INTERFACE: object shapes, extendable contracts, class implementations
|
|
38
|
+
USE TYPE: unions, intersections, utility types, mapped/conditional types
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// GOOD
|
|
43
|
+
interface User { id: string; name: string; email: string; }
|
|
44
|
+
interface AdminUser extends User { permissions: string[]; }
|
|
45
|
+
type UserRole = 'admin' | 'editor' | 'viewer';
|
|
46
|
+
type UserSummary = Pick<User, 'id' | 'name'>;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 3. Generic Components
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// GOOD — reusable generic table avoids per-type duplication
|
|
55
|
+
interface DataTableProps<T> {
|
|
56
|
+
data: T[];
|
|
57
|
+
columns: Array<{ key: keyof T; label: string }>;
|
|
58
|
+
onRowClick: (row: T) => void;
|
|
59
|
+
}
|
|
60
|
+
function DataTable<T>({ data, columns, onRowClick }: DataTableProps<T>) { /* ... */ }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 4. Zod Schema-Driven Validation
|
|
66
|
+
|
|
67
|
+
Zod schemas are the single source of truth for runtime validation AND TypeScript types. Never define a type separately when a Zod schema already defines the shape.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { z } from 'zod';
|
|
71
|
+
|
|
72
|
+
// GOOD — schema drives both validation and type
|
|
73
|
+
const UserSchema = z.object({
|
|
74
|
+
id: z.string().uuid(),
|
|
75
|
+
email: z.string().email(),
|
|
76
|
+
role: z.enum(['admin', 'editor', 'viewer']),
|
|
77
|
+
});
|
|
78
|
+
type User = z.infer<typeof UserSchema>; // derive — never duplicate
|
|
79
|
+
|
|
80
|
+
// BAD — separate type diverges from schema over time
|
|
81
|
+
type User = { id: string; email: string; role: string };
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Use Zod for: form validation, API response parsing, env variable validation, config files.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 5. Error Typing
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// BAD
|
|
92
|
+
try { /* ... */ } catch (e: any) { console.log(e.message); }
|
|
93
|
+
|
|
94
|
+
// GOOD — narrow unknown before use
|
|
95
|
+
try { /* ... */ } catch (error: unknown) {
|
|
96
|
+
if (error instanceof Error) console.error(error.message);
|
|
97
|
+
else console.error('Unknown error', error);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 6. Enums for Fixed Option Sets
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// GOOD — union for simple status flags
|
|
107
|
+
type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered';
|
|
108
|
+
|
|
109
|
+
// GOOD — enum when iteration or key mapping is needed
|
|
110
|
+
enum Direction { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT' }
|
|
111
|
+
|
|
112
|
+
// BAD — magic strings scattered through codebase (typos never caught)
|
|
113
|
+
if (status === 'pndng') { /* ... */ }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 7. Import Ordering
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// 1. React / framework
|
|
122
|
+
import React, { useState } from 'react';
|
|
123
|
+
// 2. Third-party libraries
|
|
124
|
+
import { z } from 'zod';
|
|
125
|
+
// 3. Shared / internal aliases
|
|
126
|
+
import { Button } from '@/components/ui/Button';
|
|
127
|
+
// 4. Local / relative
|
|
128
|
+
import { formatDate } from './utils';
|
|
129
|
+
import type { UserFilters } from './types';
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 8. Naming Conventions
|
|
135
|
+
|
|
136
|
+
| Item | Convention | Example |
|
|
137
|
+
|-----------------------|-------------------------|----------------------|
|
|
138
|
+
| React components | PascalCase | `UserList.tsx` |
|
|
139
|
+
| Hooks | camelCase + `use` | `useAuth.ts` |
|
|
140
|
+
| Services | camelCase + `Service` | `userService.ts` |
|
|
141
|
+
| Types / Interfaces | PascalCase | `User`, `UserFilters`|
|
|
142
|
+
| Constants | UPPER_SNAKE_CASE | `API_BASE_URL` |
|
|
143
|
+
| Non-component files | camelCase | `helpers.ts` |
|
|
144
|
+
| Folders | kebab-case | `user-profile/` |
|
|
145
|
+
| CSS classes | kebab-case | `.user-card` |
|
|
146
|
+
| Boolean props/state | `is`/`has`/`can` prefix | `isLoading` |
|
|
147
|
+
| Event handler fns | `handle` prefix | `handleSubmit` |
|
|
148
|
+
| Callback props | `on` prefix | `onSuccess` |
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// GOOD
|
|
152
|
+
const isLoading = true;
|
|
153
|
+
function handleSubmit(e: React.FormEvent) { /* ... */ }
|
|
154
|
+
<Form onSubmit={handleSubmit} />
|
|
155
|
+
|
|
156
|
+
// BAD
|
|
157
|
+
const loading = true;
|
|
158
|
+
function submitForm() { /* ... */ }
|
|
159
|
+
<Form submitHandler={submitForm} />
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Pre-Commit TypeScript Checklist
|
|
165
|
+
|
|
166
|
+
- [ ] `"strict": true` in `tsconfig.json`
|
|
167
|
+
- [ ] No `any` — `unknown` with narrowing used instead
|
|
168
|
+
- [ ] No bare `object` type — specific interfaces or `Record<K,V>` only
|
|
169
|
+
- [ ] Interfaces for object shapes; types for unions/utilities
|
|
170
|
+
- [ ] Zod schemas drive both validation and types via `z.infer`
|
|
171
|
+
- [ ] Caught errors narrowed before access (`instanceof Error` check)
|
|
172
|
+
- [ ] Enums or union types for all fixed option sets — no magic strings
|
|
173
|
+
- [ ] Imports ordered: React → third-party → shared → local
|
|
174
|
+
- [ ] Boolean props/state use `is`/`has`/`can` prefix
|
|
175
|
+
- [ ] Event handlers use `handle` prefix; callback props use `on` prefix
|
|
176
|
+
- [ ] All files, components, hooks, and services follow naming conventions
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Vite Standards
|
|
2
|
+
|
|
3
|
+
These rules are MANDATORY. Violations fail the task. No exceptions.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Environment Variables
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
MANDATORY:
|
|
11
|
+
├── Prefix ALL client-exposed env vars with VITE_ — Vite strips others
|
|
12
|
+
├── Access via import.meta.env.VITE_* — NEVER process.env (that's Node, not Vite)
|
|
13
|
+
├── Commit .env.development and .env.production — no secrets in either
|
|
14
|
+
├── Add .env.local to .gitignore — developer-specific overrides only
|
|
15
|
+
└── NEVER put API keys or secrets in VITE_ vars — they're bundled into client code
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**BAD**
|
|
19
|
+
```typescript
|
|
20
|
+
const url = process.env.API_URL; // undefined in browser
|
|
21
|
+
const key = import.meta.env.VITE_API_KEY; // secret exposed to client!
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**GOOD**
|
|
25
|
+
```typescript
|
|
26
|
+
// .env.development
|
|
27
|
+
// VITE_API_BASE_URL=http://localhost:4000/api
|
|
28
|
+
|
|
29
|
+
// src/lib/constants.ts
|
|
30
|
+
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:4000/api';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Config File
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
MANDATORY:
|
|
39
|
+
├── vite.config.ts (TypeScript) — not .js
|
|
40
|
+
├── Define resolve.alias for clean imports (@/ → src/)
|
|
41
|
+
├── Configure server.proxy for API calls in dev — avoid CORS issues
|
|
42
|
+
├── Set build.sourcemap to true for staging, false for production
|
|
43
|
+
└── Keep plugins list minimal — each plugin adds build time
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**GOOD**
|
|
47
|
+
```typescript
|
|
48
|
+
import { defineConfig } from 'vite';
|
|
49
|
+
import react from '@vitejs/plugin-react';
|
|
50
|
+
import path from 'path';
|
|
51
|
+
|
|
52
|
+
export default defineConfig({
|
|
53
|
+
plugins: [react()],
|
|
54
|
+
resolve: {
|
|
55
|
+
alias: { '@': path.resolve(__dirname, 'src') },
|
|
56
|
+
},
|
|
57
|
+
server: {
|
|
58
|
+
proxy: {
|
|
59
|
+
'/api': { target: 'http://localhost:4000', changeOrigin: true },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. Build Optimization
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
MANDATORY:
|
|
71
|
+
├── Code-split route-level components with React.lazy (or framework equivalent)
|
|
72
|
+
├── Configure manualChunks for large vendor libs (react, lodash, chart libs)
|
|
73
|
+
├── Check bundle size: npx vite-bundle-visualizer after build
|
|
74
|
+
├── Tree-shaking: use named imports — not default imports from barrel files
|
|
75
|
+
└── Set chunk size warning limit in config if needed
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**GOOD** — manual chunks:
|
|
79
|
+
```typescript
|
|
80
|
+
build: {
|
|
81
|
+
rollupOptions: {
|
|
82
|
+
output: {
|
|
83
|
+
manualChunks: {
|
|
84
|
+
vendor: ['react', 'react-dom'],
|
|
85
|
+
query: ['@tanstack/react-query'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 4. Path Aliases and Imports
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
MANDATORY:
|
|
98
|
+
├── Configure @/ alias in vite.config.ts AND tsconfig.json (both must match)
|
|
99
|
+
├── Use @/ for cross-feature imports — relative for same-directory
|
|
100
|
+
├── NEVER use deep relative paths (../../../shared/lib/helpers)
|
|
101
|
+
└── Barrel exports (index.ts) for feature public APIs — not for internal modules
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**tsconfig.json** must match:
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"compilerOptions": {
|
|
108
|
+
"baseUrl": ".",
|
|
109
|
+
"paths": { "@/*": ["src/*"] }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 5. Dev Server
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
MANDATORY:
|
|
120
|
+
├── Use server.proxy for backend API — NEVER hardcode localhost URLs in fetch calls
|
|
121
|
+
├── Enable server.open only if desired — don't force it
|
|
122
|
+
├── HMR should work — if it doesn't, check file naming (PascalCase components)
|
|
123
|
+
└── For HTTPS in dev: use @vitejs/plugin-basic-ssl — not self-signed certs
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 6. Testing Integration
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
MANDATORY:
|
|
132
|
+
├── Use Vitest (not Jest) — it shares Vite's config and transform pipeline
|
|
133
|
+
├── Configure test block in vite.config.ts or vitest.config.ts
|
|
134
|
+
├── Set environment: 'jsdom' for component tests
|
|
135
|
+
├── Use the same path aliases in tests — Vitest inherits from vite.config
|
|
136
|
+
└── Happy-dom is faster than jsdom — use if no compatibility issues
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**GOOD**
|
|
140
|
+
```typescript
|
|
141
|
+
// vite.config.ts
|
|
142
|
+
export default defineConfig({
|
|
143
|
+
test: {
|
|
144
|
+
environment: 'jsdom',
|
|
145
|
+
globals: true,
|
|
146
|
+
setupFiles: './src/test-setup.ts',
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 7. Anti-Patterns
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
NEVER:
|
|
157
|
+
├── process.env in client code (use import.meta.env)
|
|
158
|
+
├── Secrets in VITE_ env vars — they're in the bundle
|
|
159
|
+
├── Jest when Vitest is available — Vitest is faster and shares config
|
|
160
|
+
├── Deep relative imports (../../..) — use @/ alias
|
|
161
|
+
├── Importing entire libraries (import _ from 'lodash') — use named (import { debounce })
|
|
162
|
+
└── Disabling HMR to "fix" issues — find the root cause
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Vite Verification Checklist
|
|
168
|
+
|
|
169
|
+
- [ ] All client env vars prefixed with VITE_
|
|
170
|
+
- [ ] No secrets in VITE_ variables
|
|
171
|
+
- [ ] import.meta.env used — not process.env
|
|
172
|
+
- [ ] Path alias @/ configured in both vite.config.ts and tsconfig.json
|
|
173
|
+
- [ ] server.proxy configured for API calls
|
|
174
|
+
- [ ] Bundle analyzed — no oversized chunks
|
|
175
|
+
- [ ] Vitest configured (not Jest)
|
|
176
|
+
- [ ] .env.local in .gitignore
|