validate-implementation
Access: /template validate-implementation or /t validate-implementation
Validates your MakerKit implementation against best practices, ensuring proper setup and configuration.
What It Does
The validate-implementation command helps you:
- Verify correct MakerKit patterns usage
- Check database schema consistency
- Validate authentication setup
- Ensure proper TypeScript types
- Verify environment configuration
- Check for common implementation mistakes
- Generate validation report
Usage
bash
/template validate-implementation "Description"
# or
/t validate-implementation "Description"When prompted, specify:
- Validation depth (quick or comprehensive)
- Areas to focus on
- Strict mode (fail on warnings)
Prerequisites
- A MakerKit project implementation
- All dependencies installed
- Commercial MakerKit license from MakerKit
What Gets Validated
1. Project Structure
typescript
// Validate MakerKit project structure
async function validateProjectStructure() {
const requiredPaths = [
// Monorepo structure
'apps/web',
'packages/ui',
'packages/shared',
'packages/billing',
'packages/auth',
'packages/supabase',
// Configuration files
'apps/web/next.config.js',
'apps/web/tailwind.config.js',
'turbo.json',
'pnpm-workspace.yaml',
// Environment files
'.env.example',
'.env.local',
];
const issues = [];
for (const path of requiredPaths) {
if (!await pathExists(path)) {
issues.push({
severity: path.includes('.env.example') ? 'high' : 'medium',
path,
message: `Required path missing: ${path}`,
fix: `Ensure MakerKit is properly initialized`,
});
}
}
// Check for incorrect structures
const antiPatterns = [
{ path: 'src', message: 'Using src directory instead of app directory' },
{ path: 'pages', message: 'Using pages directory instead of app router' },
{ path: '.env', message: '.env file should not be committed' },
];
for (const { path, message } of antiPatterns) {
if (await pathExists(path)) {
issues.push({
severity: 'medium',
path,
message,
fix: 'Follow MakerKit conventions',
});
}
}
return issues;
}2. Database Schema
typescript
// Validate database schema
async function validateDatabaseSchema() {
const client = getSupabaseServerClient({ admin: true });
const issues = [];
// Required tables
const requiredTables = [
'users',
'accounts',
'accounts_account_members',
'subscriptions',
'invitations',
];
const { data: tables } = await client
.from('information_schema.tables')
.select('table_name')
.eq('table_schema', 'public');
const existingTables = tables?.map(t => t.table_name) || [];
for (const table of requiredTables) {
if (!existingTables.includes(table)) {
issues.push({
severity: 'critical',
table,
message: `Required table missing: ${table}`,
fix: 'Run database migrations',
});
}
}
// Validate RLS
for (const table of existingTables) {
const { data: rlsEnabled } = await client
.from('pg_tables')
.select('rowsecurity')
.eq('schemaname', 'public')
.eq('tablename', table)
.single();
if (!rlsEnabled?.rowsecurity) {
issues.push({
severity: 'critical',
table,
message: `RLS not enabled on table: ${table}`,
fix: `ALTER TABLE public.${table} ENABLE ROW LEVEL SECURITY;`,
});
}
}
// Check for proper indexes
const indexChecks = [
{ table: 'users', column: 'email' },
{ table: 'accounts', column: 'slug' },
{ table: 'accounts_account_members', columns: ['account_id', 'user_id'] },
];
for (const check of indexChecks) {
const hasIndex = await checkIndexExists(check);
if (!hasIndex) {
issues.push({
severity: 'medium',
table: check.table,
message: `Missing index on ${check.column || check.columns.join(', ')}`,
fix: 'Create index for better performance',
});
}
}
return issues;
}3. Authentication Setup
typescript
// Validate auth configuration
async function validateAuthentication() {
const issues = [];
// Check Supabase configuration
if (!process.env.NEXT_PUBLIC_SUPABASE_URL) {
issues.push({
severity: 'critical',
config: 'NEXT_PUBLIC_SUPABASE_URL',
message: 'Supabase URL not configured',
});
}
if (!process.env.SUPABASE_SERVICE_ROLE_KEY) {
issues.push({
severity: 'critical',
config: 'SUPABASE_SERVICE_ROLE_KEY',
message: 'Service role key not configured',
});
}
// Check auth providers
const { data: providers } = await supabase
.from('auth.providers')
.select('*')
.eq('enabled', true);
if (!providers || providers.length === 0) {
issues.push({
severity: 'high',
message: 'No auth providers enabled',
fix: 'Enable at least email authentication',
});
}
// Check auth hooks implementation
const authFiles = [
'packages/auth/src/hooks/use-user.ts',
'packages/auth/src/hooks/use-sign-out.ts',
'packages/auth/src/components/auth-provider.tsx',
];
for (const file of authFiles) {
if (!await pathExists(file)) {
issues.push({
severity: 'high',
file,
message: 'Required auth file missing',
});
}
}
// Check middleware
const middlewarePath = 'apps/web/middleware.ts';
if (await pathExists(middlewarePath)) {
const content = await fs.readFile(middlewarePath, 'utf-8');
if (!content.includes('createMiddlewareClient')) {
issues.push({
severity: 'high',
file: middlewarePath,
message: 'Middleware not using Supabase auth',
fix: 'Implement proper auth middleware',
});
}
} else {
issues.push({
severity: 'high',
message: 'Auth middleware missing',
fix: 'Create middleware.ts for route protection',
});
}
return issues;
}4. TypeScript Configuration
typescript
// Validate TypeScript setup
async function validateTypeScript() {
const issues = [];
// Check tsconfig
const tsconfigPath = 'tsconfig.json';
if (!await pathExists(tsconfigPath)) {
issues.push({
severity: 'critical',
message: 'tsconfig.json missing',
});
return issues;
}
const tsconfig = JSON.parse(await fs.readFile(tsconfigPath, 'utf-8'));
// Required compiler options
const requiredOptions = {
strict: true,
skipLibCheck: true,
esModuleInterop: true,
moduleResolution: 'node',
};
for (const [option, value] of Object.entries(requiredOptions)) {
if (tsconfig.compilerOptions?.[option] !== value) {
issues.push({
severity: 'medium',
option,
message: `TypeScript option ${option} should be ${value}`,
});
}
}
// Check for type errors
try {
const { stdout, stderr } = await execAsync('pnpm typecheck');
if (stderr) {
const errors = stderr.split('\n').filter(line => line.includes('error'));
issues.push({
severity: 'high',
message: `TypeScript errors found: ${errors.length}`,
errors: errors.slice(0, 10),
fix: 'Fix type errors before deployment',
});
}
} catch (error) {
issues.push({
severity: 'high',
message: 'TypeScript compilation failed',
error: error.message,
});
}
// Check for generated types
const typesPath = 'packages/supabase/src/types/database.types.ts';
if (!await pathExists(typesPath)) {
issues.push({
severity: 'high',
message: 'Database types not generated',
fix: 'Run: pnpm db:typegen',
});
} else {
// Check if types are up to date
const typesMtime = (await fs.stat(typesPath)).mtime;
const migrationFiles = await glob('apps/web/supabase/migrations/*.sql');
for (const migration of migrationFiles) {
const migrationMtime = (await fs.stat(migration)).mtime;
if (migrationMtime > typesMtime) {
issues.push({
severity: 'medium',
message: 'Database types may be outdated',
fix: 'Regenerate types: pnpm db:typegen',
});
break;
}
}
}
return issues;
}5. MakerKit Patterns
typescript
// Validate MakerKit pattern usage
async function validateMakerKitPatterns() {
const issues = [];
// Check Server Actions usage
const serverActionFiles = await glob('apps/web/**/*actions*.ts');
for (const file of serverActionFiles) {
const content = await fs.readFile(file, 'utf-8');
// Check for enhanceAction usage
if (!content.includes('enhanceAction')) {
issues.push({
severity: 'medium',
file,
pattern: 'Server Actions',
message: 'Not using enhanceAction wrapper',
fix: 'Wrap server actions with enhanceAction for auth and validation',
});
}
// Check for proper error handling
if (!content.includes('try') || !content.includes('catch')) {
issues.push({
severity: 'low',
file,
pattern: 'Error Handling',
message: 'Missing error handling in server actions',
});
}
}
// Check data fetching patterns
const pageFiles = await glob('apps/web/app/**/page.tsx');
for (const file of pageFiles) {
const content = await fs.readFile(file, 'utf-8');
// Check for client-side data fetching in server components
if (content.includes('useEffect') && content.includes('fetch')) {
issues.push({
severity: 'high',
file,
pattern: 'Data Fetching',
message: 'Using client-side fetch in server component',
fix: 'Fetch data at the server component level',
});
}
}
// Check UI component usage
const componentFiles = await glob('apps/web/**/*.tsx');
for (const file of componentFiles) {
const content = await fs.readFile(file, 'utf-8');
// Check for custom implementations of MakerKit components
if (content.includes('className="button"') && !content.includes('@kit/ui')) {
issues.push({
severity: 'low',
file,
pattern: 'UI Components',
message: 'Not using MakerKit UI components',
fix: 'Use components from @kit/ui package',
});
}
}
return issues;
}6. Environment Configuration
typescript
// Validate environment setup
async function validateEnvironment() {
const issues = [];
// Required environment variables
const requiredVars = [
// Supabase
'NEXT_PUBLIC_SUPABASE_URL',
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
'SUPABASE_SERVICE_ROLE_KEY',
// App
'NEXT_PUBLIC_APP_URL',
'NEXT_PUBLIC_APP_NAME',
// Optional but recommended
'STRIPE_SECRET_KEY',
'STRIPE_WEBHOOK_SECRET',
'RESEND_API_KEY',
];
const envExample = await fs.readFile('.env.example', 'utf-8').catch(() => '');
for (const varName of requiredVars) {
// Check if documented
if (!envExample.includes(varName)) {
issues.push({
severity: 'medium',
variable: varName,
message: 'Not documented in .env.example',
});
}
// Check if set (in development)
if (!process.env[varName] && requiredVars.slice(0, 5).includes(varName)) {
issues.push({
severity: 'high',
variable: varName,
message: 'Required environment variable not set',
});
}
}
// Check for exposed secrets
const gitFiles = await execAsync('git ls-files').then(r => r.stdout.split('\n'));
for (const file of gitFiles) {
if (file.includes('.env') && !file.includes('.example')) {
issues.push({
severity: 'critical',
file,
message: 'Environment file tracked in git',
fix: 'Remove from git and add to .gitignore',
});
}
}
return issues;
}7. Billing Integration
typescript
// Validate billing setup
async function validateBilling() {
const issues = [];
// Check Stripe configuration
if (process.env.STRIPE_SECRET_KEY) {
// Verify webhook endpoint
const webhookPath = 'apps/web/app/api/stripe/webhook/route.ts';
if (!await pathExists(webhookPath)) {
issues.push({
severity: 'high',
message: 'Stripe webhook endpoint missing',
fix: 'Create webhook handler',
});
} else {
const content = await fs.readFile(webhookPath, 'utf-8');
if (!content.includes('stripe.webhooks.constructEvent')) {
issues.push({
severity: 'high',
message: 'Webhook not verifying signatures',
fix: 'Use stripe.webhooks.constructEvent for security',
});
}
}
// Check subscription sync
const hasSubscriptionTable = await checkTableExists('subscriptions');
if (!hasSubscriptionTable) {
issues.push({
severity: 'high',
message: 'Subscriptions table missing',
fix: 'Run billing migrations',
});
}
}
return issues;
}8. Validation Report
typescript
// Generate validation report
export async function generateValidationReport() {
console.log('✅ Starting MakerKit Validation...\n');
const report = {
timestamp: new Date().toISOString(),
valid: true,
summary: {
passed: 0,
warnings: 0,
errors: 0,
},
categories: {},
};
// Run all validations
const validations = [
{ name: 'Project Structure', fn: validateProjectStructure },
{ name: 'Database Schema', fn: validateDatabaseSchema },
{ name: 'Authentication', fn: validateAuthentication },
{ name: 'TypeScript', fn: validateTypeScript },
{ name: 'MakerKit Patterns', fn: validateMakerKitPatterns },
{ name: 'Environment', fn: validateEnvironment },
{ name: 'Billing', fn: validateBilling },
];
for (const validation of validations) {
console.log(`Validating ${validation.name}...`);
try {
const issues = await validation.fn();
report.categories[validation.name] = issues;
// Update counts
for (const issue of issues) {
if (issue.severity === 'critical' || issue.severity === 'high') {
report.summary.errors++;
report.valid = false;
} else {
report.summary.warnings++;
}
}
if (issues.length === 0) {
report.summary.passed++;
}
} catch (error) {
report.categories[validation.name] = [{
severity: 'critical',
message: `Validation failed: ${error.message}`,
}];
report.summary.errors++;
report.valid = false;
}
}
// Generate reports
const markdown = generateValidationMarkdown(report);
await fs.writeFile('validation-report.md', markdown);
const json = JSON.stringify(report, null, 2);
await fs.writeFile('validation-report.json', json);
// Display summary
console.log('\n📊 Validation Complete!');
console.log(`Status: ${report.valid ? '✅ VALID' : '❌ INVALID'}`);
console.log(`Passed: ${report.summary.passed}/${validations.length}`);
console.log(`Errors: ${report.summary.errors}`);
console.log(`Warnings: ${report.summary.warnings}`);
console.log('\n📄 Report saved to validation-report.md');
return report;
}
function generateValidationMarkdown(report) {
let markdown = '# MakerKit Implementation Validation Report\n\n';
markdown += `Generated: ${report.timestamp}\n\n`;
markdown += `## Summary\n\n`;
markdown += `- **Status**: ${report.valid ? '✅ VALID' : '❌ INVALID'}\n`;
markdown += `- **Errors**: ${report.summary.errors}\n`;
markdown += `- **Warnings**: ${report.summary.warnings}\n\n`;
for (const [category, issues] of Object.entries(report.categories)) {
markdown += `## ${category}\n\n`;
if (issues.length === 0) {
markdown += '✅ All checks passed\n\n';
} else {
for (const issue of issues) {
const icon = issue.severity === 'critical' || issue.severity === 'high' ? '❌' : '⚠️';
markdown += `${icon} **${issue.severity.toUpperCase()}**: ${issue.message}\n`;
if (issue.fix) {
markdown += ` - Fix: ${issue.fix}\n`;
}
markdown += '\n';
}
}
}
return markdown;
}Auto-Fix Capabilities
Some issues can be fixed automatically:
typescript
export async function autoFixValidationIssues() {
console.log('🔧 Attempting automatic fixes...\n');
// Generate missing types
await execAsync('pnpm db:typegen');
// Create missing directories
await createMissingDirectories();
// Update TypeScript config
await fixTypeScriptConfig();
// Generate .env.example
await generateEnvExample();
console.log('✅ Automatic fixes applied');
}License Requirement
Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.
