UNPKG

@mriridescent/rezilient

Version:

REZILIENT.js - Revolutionary framework for scaffolding complete production-ready resilient applications. Features offline-first architecture, AI-awareness, carbon-conscious computing, quantum-ready patterns, and self-healing capabilities.

1,936 lines (1,728 loc) โ€ข 93.3 kB
#!/usr/bin/env node /** * REZILIENT.js Application Scaffolding CLI * Creates production-ready resilient applications with all framework features */ import fs from 'fs'; import path from 'path'; import { execSync } from 'child_process'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); function getCategoryIcon(category) { const icons = { 'Frontend': '๐Ÿ“ฑ', 'Backend': '๐Ÿš€', 'Mobile': '๐Ÿ“ฑ', 'Desktop': '๐Ÿ–ฅ๏ธ', 'Full-Stack': '๐ŸŒ', 'PWA': 'โšก', 'Extension': '๐Ÿ”ง', 'Library': '๐Ÿ“š', 'Micro Frontend': '๐Ÿงฉ', 'Serverless': 'โ˜๏ธ', 'Edge': 'โšก', 'Static Site': '๐Ÿ“„' }; return icons[category] || '๐Ÿ”ง'; } function getScriptsForFramework(framework) { switch (framework) { case 'nextjs': return { dev: 'next dev', build: 'next build', start: 'next start', lint: 'next lint', test: 'jest', 'test:coverage': 'jest --coverage' }; case 'remix': return { dev: 'remix dev', build: 'remix build', start: 'remix-serve build', test: 'jest', 'test:coverage': 'jest --coverage' }; case 'nestjs': return { dev: 'nest start --watch', build: 'nest build', start: 'node dist/main', 'start:prod': 'node dist/main', test: 'jest', 'test:coverage': 'jest --coverage' }; case 'node': return { dev: 'nodemon src/main.js', start: 'node src/main.js', test: 'jest', 'test:coverage': 'jest --coverage' }; case 'vite': default: return { dev: 'vite', build: 'vite build', preview: 'vite preview', test: 'jest', 'test:coverage': 'jest --coverage' }; } } const TEMPLATES = { // ๐ŸŒ FRONTEND APPLICATIONS 'react-app': { name: 'React Application (Vite)', description: 'Modern React app with REZILIENT.js integration using Vite', category: 'Frontend', framework: 'vite', dependencies: { 'react': '^18.2.0', 'react-dom': '^18.2.0', 'react-router-dom': '^6.8.0', 'rezilient.js': 'file:../' }, devDependencies: { 'vite': '^5.0.0', '@vitejs/plugin-react': '^4.0.0', 'jest': '^29.0.0', 'jest-environment-jsdom': '^29.0.0', '@testing-library/react': '^13.0.0', '@testing-library/jest-dom': '^6.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', 'typescript': '^5.0.0', 'tailwindcss': '^3.3.0', 'autoprefixer': '^10.4.0', 'postcss': '^8.4.0' } }, 'nextjs-app': { name: 'Next.js Application', description: 'Full-stack Next.js app with REZILIENT.js integration', category: 'Full-Stack', framework: 'nextjs', dependencies: { 'next': '^14.0.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', 'rezilient.js': 'file:../' }, devDependencies: { '@types/node': '^20.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', 'typescript': '^5.0.0', 'eslint': '^8.0.0', 'eslint-config-next': '^14.0.0', 'tailwindcss': '^3.3.0', 'autoprefixer': '^10.4.0', 'postcss': '^8.4.0' } }, 'vue-app': { name: 'Vue.js Application', description: 'Modern Vue 3 app with Composition API and REZILIENT.js', category: 'Frontend', framework: 'vue', dependencies: { 'vue': '^3.3.0', 'vue-router': '^4.2.0', 'pinia': '^2.1.0', 'rezilient.js': 'file:../' }, devDependencies: { '@vitejs/plugin-vue': '^4.0.0', 'vite': '^5.0.0', '@vue/test-utils': '^2.4.0', 'vitest': '^0.34.0', 'typescript': '^5.0.0', '@vue/tsconfig': '^0.4.0' } }, 'angular-app': { name: 'Angular Application', description: 'Enterprise Angular app with REZILIENT.js integration', category: 'Frontend', framework: 'angular', dependencies: { '@angular/animations': '^17.0.0', '@angular/common': '^17.0.0', '@angular/compiler': '^17.0.0', '@angular/core': '^17.0.0', '@angular/forms': '^17.0.0', '@angular/platform-browser': '^17.0.0', '@angular/platform-browser-dynamic': '^17.0.0', '@angular/router': '^17.0.0', 'rxjs': '^7.8.0', 'tslib': '^2.3.0', 'zone.js': '^0.14.0', 'rezilient.js': 'file:../' }, devDependencies: { '@angular-devkit/build-angular': '^17.0.0', '@angular/cli': '^17.0.0', '@angular/compiler-cli': '^17.0.0', '@types/jasmine': '^4.3.0', 'jasmine-core': '^4.6.0', 'karma': '^6.4.0', 'karma-chrome-launcher': '^3.2.0', 'karma-coverage': '^2.2.0', 'karma-jasmine': '^5.1.0', 'karma-jasmine-html-reporter': '^2.1.0', 'typescript': '^5.0.0' } }, 'svelte-app': { name: 'Svelte Application', description: 'Lightweight Svelte app with REZILIENT.js integration', category: 'Frontend', framework: 'svelte', dependencies: { 'rezilient.js': 'file:../' }, devDependencies: { '@sveltejs/adapter-auto': '^2.0.0', '@sveltejs/kit': '^1.20.4', 'svelte': '^4.0.5', 'typescript': '^5.0.0', 'vite': '^4.4.2', '@sveltejs/vite-plugin-svelte': '^2.4.2' } }, 'remix-app': { name: 'Remix Application', description: 'Modern Remix app with REZILIENT.js integration', category: 'Full-Stack', framework: 'remix', dependencies: { '@remix-run/node': '^2.0.0', '@remix-run/react': '^2.0.0', '@remix-run/serve': '^2.0.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', 'rezilient.js': 'file:../' }, devDependencies: { '@remix-run/dev': '^2.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', 'typescript': '^5.0.0', 'vite': '^5.0.0' } }, 'astro-app': { name: 'Astro Application', description: 'Static site generator with REZILIENT.js integration', category: 'Static Site', framework: 'astro', dependencies: { 'astro': '^4.0.0', 'rezilient.js': 'file:../' }, devDependencies: { '@astrojs/react': '^3.0.0', '@astrojs/tailwind': '^5.0.0', '@astrojs/typescript': '^1.0.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', 'tailwindcss': '^3.3.0', 'typescript': '^5.0.0' } }, 'vanilla-js': { name: 'Vanilla JavaScript', description: 'Pure JavaScript app with all REZILIENT.js features', category: 'Frontend', framework: 'vite', dependencies: { 'rezilient.js': 'file:../' }, devDependencies: { 'vite': '^5.0.0', 'typescript': '^5.0.0' } }, // ๐Ÿš€ BACKEND APPLICATIONS 'node-api': { name: 'Node.js API', description: 'Backend API with carbon-aware scheduling and resilient patterns', category: 'Backend', framework: 'node', dependencies: { 'express': '^4.18.0', 'cors': '^2.8.5', 'helmet': '^7.0.0', 'morgan': '^1.10.0', 'compression': '^1.7.4', 'dotenv': '^16.3.0', 'rezilient.js': 'file:../' }, devDependencies: { 'nodemon': '^3.0.0', '@types/node': '^20.0.0', '@types/express': '^4.17.0', '@types/cors': '^2.8.0', 'typescript': '^5.0.0', 'jest': '^29.0.0', 'supertest': '^6.3.0' } }, 'fastify-api': { name: 'Fastify API', description: 'High-performance Fastify API with REZILIENT.js', category: 'Backend', framework: 'fastify', dependencies: { 'fastify': '^4.24.0', '@fastify/cors': '^8.4.0', '@fastify/helmet': '^11.1.0', '@fastify/compress': '^6.4.0', 'dotenv': '^16.3.0', 'rezilient.js': 'file:../' }, devDependencies: { '@types/node': '^20.0.0', 'typescript': '^5.0.0', 'tap': '^18.0.0', 'nodemon': '^3.0.0' } }, 'nestjs-api': { name: 'NestJS API', description: 'Enterprise-grade NestJS API with REZILIENT.js integration', category: 'Backend', framework: 'nestjs', dependencies: { '@nestjs/common': '^10.0.0', '@nestjs/core': '^10.0.0', '@nestjs/platform-express': '^10.0.0', '@nestjs/config': '^3.0.0', '@nestjs/swagger': '^7.0.0', 'reflect-metadata': '^0.1.13', 'rxjs': '^7.8.0', 'class-validator': '^0.14.0', 'class-transformer': '^0.5.0', 'rezilient.js': 'file:../' }, devDependencies: { '@nestjs/cli': '^10.0.0', '@nestjs/schematics': '^10.0.0', '@nestjs/testing': '^10.0.0', '@types/express': '^4.17.0', '@types/jest': '^29.0.0', '@types/node': '^20.0.0', 'jest': '^29.0.0', 'typescript': '^5.0.0' } }, 'koa-api': { name: 'Koa.js API', description: 'Modern Koa.js API with async/await and REZILIENT.js', category: 'Backend', framework: 'koa', dependencies: { 'koa': '^2.14.0', '@koa/cors': '^4.0.0', '@koa/router': '^12.0.0', 'koa-bodyparser': '^4.4.0', 'koa-helmet': '^7.0.0', 'koa-compress': '^5.1.0', 'dotenv': '^16.3.0', 'rezilient.js': 'file:../' }, devDependencies: { '@types/koa': '^2.13.0', '@types/node': '^20.0.0', 'typescript': '^5.0.0', 'jest': '^29.0.0', 'supertest': '^6.3.0', 'nodemon': '^3.0.0' } }, 'graphql-api': { name: 'GraphQL API', description: 'GraphQL API with Apollo Server and REZILIENT.js', category: 'Backend', framework: 'graphql', dependencies: { 'apollo-server-express': '^3.12.0', 'graphql': '^16.8.0', 'express': '^4.18.0', 'graphql-tools': '^9.0.0', 'dataloader': '^2.2.0', 'rezilient.js': 'file:../' }, devDependencies: { '@types/node': '^20.0.0', 'typescript': '^5.0.0', 'jest': '^29.0.0', 'nodemon': '^3.0.0' } }, // ๐Ÿ“ฑ MOBILE APPLICATIONS 'react-native-app': { name: 'React Native App', description: 'Cross-platform mobile app with REZILIENT.js', category: 'Mobile', framework: 'react-native', dependencies: { 'react': '^18.2.0', 'react-native': '^0.72.0', '@react-navigation/native': '^6.1.0', '@react-navigation/stack': '^6.3.0', 'react-native-screens': '^3.25.0', 'react-native-safe-area-context': '^4.7.0', 'rezilient.js': 'file:../' }, devDependencies: { '@babel/core': '^7.20.0', '@babel/preset-env': '^7.20.0', '@babel/runtime': '^7.20.0', '@react-native/metro-config': '^0.72.0', '@tsconfig/react-native': '^3.0.0', '@types/react': '^18.0.0', 'metro-react-native-babel-preset': '^0.76.0', 'typescript': '^5.0.0' } }, 'expo-app': { name: 'Expo Application', description: 'Expo React Native app with REZILIENT.js integration', category: 'Mobile', framework: 'expo', dependencies: { 'expo': '^49.0.0', 'react': '^18.2.0', 'react-native': '^0.72.0', '@expo/vector-icons': '^13.0.0', 'expo-router': '^2.0.0', 'rezilient.js': 'file:../' }, devDependencies: { '@babel/core': '^7.20.0', '@types/react': '^18.0.0', 'typescript': '^5.0.0' } }, 'ionic-app': { name: 'Ionic Application', description: 'Hybrid mobile app with Ionic and REZILIENT.js', category: 'Mobile', framework: 'ionic', dependencies: { '@ionic/react': '^7.0.0', '@ionic/react-router': '^7.0.0', 'ionicons': '^7.0.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', 'react-router': '^6.8.0', 'react-router-dom': '^6.8.0', 'rezilient.js': 'file:../' }, devDependencies: { '@ionic/cli': '^7.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', 'typescript': '^5.0.0', 'vite': '^5.0.0' } }, // ๐Ÿ–ฅ๏ธ DESKTOP APPLICATIONS 'electron-app': { name: 'Electron Desktop App', description: 'Cross-platform desktop app with Electron and REZILIENT.js', category: 'Desktop', framework: 'electron', dependencies: { 'electron': '^27.0.0', 'rezilient.js': 'file:../' }, devDependencies: { 'electron-builder': '^24.6.0', 'concurrently': '^8.2.0', 'typescript': '^5.0.0', '@types/node': '^20.0.0' } }, 'tauri-app': { name: 'Tauri Desktop App', description: 'Lightweight desktop app with Tauri and REZILIENT.js', category: 'Desktop', framework: 'tauri', dependencies: { '@tauri-apps/api': '^1.5.0', 'rezilient.js': 'file:../' }, devDependencies: { '@tauri-apps/cli': '^1.5.0', 'vite': '^5.0.0', 'typescript': '^5.0.0' } }, // ๐ŸŒ FULL-STACK APPLICATIONS 'mern-stack': { name: 'MERN Stack Application', description: 'MongoDB, Express, React, Node.js with REZILIENT.js', category: 'Full-Stack', framework: 'mern', dependencies: { 'express': '^4.18.0', 'mongoose': '^7.5.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', 'cors': '^2.8.5', 'dotenv': '^16.3.0', 'rezilient.js': 'file:../' }, devDependencies: { 'concurrently': '^8.2.0', 'nodemon': '^3.0.0', 'vite': '^5.0.0', '@vitejs/plugin-react': '^4.0.0', 'typescript': '^5.0.0' } }, 'mean-stack': { name: 'MEAN Stack Application', description: 'MongoDB, Express, Angular, Node.js with REZILIENT.js', category: 'Full-Stack', framework: 'mean', dependencies: { '@angular/core': '^17.0.0', 'express': '^4.18.0', 'mongoose': '^7.5.0', 'cors': '^2.8.5', 'dotenv': '^16.3.0', 'rezilient.js': 'file:../' }, devDependencies: { '@angular/cli': '^17.0.0', 'concurrently': '^8.2.0', 'nodemon': '^3.0.0', 'typescript': '^5.0.0' } }, 't3-stack': { name: 'T3 Stack Application', description: 'Next.js, TypeScript, tRPC, Prisma with REZILIENT.js', category: 'Full-Stack', framework: 't3', dependencies: { 'next': '^14.0.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', '@trpc/client': '^10.40.0', '@trpc/server': '^10.40.0', '@trpc/react-query': '^10.40.0', '@prisma/client': '^5.4.0', 'zod': '^3.22.0', 'rezilient.js': 'file:../' }, devDependencies: { 'prisma': '^5.4.0', '@types/node': '^20.0.0', '@types/react': '^18.0.0', 'typescript': '^5.0.0' } }, // ๐Ÿ”ง SPECIALIZED APPLICATIONS 'pwa-app': { name: 'Progressive Web App', description: 'PWA with service workers and REZILIENT.js', category: 'PWA', framework: 'pwa', dependencies: { 'workbox-webpack-plugin': '^7.0.0', 'rezilient.js': 'file:../' }, devDependencies: { 'webpack': '^5.88.0', 'webpack-cli': '^5.1.0', 'webpack-dev-server': '^4.15.0', 'html-webpack-plugin': '^5.5.0', 'typescript': '^5.0.0' } }, 'chrome-extension': { name: 'Chrome Extension', description: 'Browser extension with REZILIENT.js integration', category: 'Extension', framework: 'extension', dependencies: { 'rezilient.js': 'file:../' }, devDependencies: { '@types/chrome': '^0.0.246', 'webpack': '^5.88.0', 'webpack-cli': '^5.1.0', 'typescript': '^5.0.0' } }, 'web-components': { name: 'Web Components Library', description: 'Custom elements library with REZILIENT.js', category: 'Library', framework: 'web-components', dependencies: { 'lit': '^3.0.0', 'rezilient.js': 'file:../' }, devDependencies: { '@web/dev-server': '^0.4.0', '@web/test-runner': '^0.18.0', 'typescript': '^5.0.0' } }, 'micro-frontend': { name: 'Micro Frontend', description: 'Module federation micro frontend with REZILIENT.js', category: 'Micro Frontend', framework: 'micro-frontend', dependencies: { 'react': '^18.2.0', 'react-dom': '^18.2.0', 'rezilient.js': 'file:../' }, devDependencies: { '@module-federation/webpack': '^2.0.0', 'webpack': '^5.88.0', 'webpack-cli': '^5.1.0', 'webpack-dev-server': '^4.15.0', 'typescript': '^5.0.0' } }, 'serverless-app': { name: 'Serverless Application', description: 'AWS Lambda functions with REZILIENT.js', category: 'Serverless', framework: 'serverless', dependencies: { 'aws-lambda': '^1.0.7', 'rezilient.js': 'file:../' }, devDependencies: { 'serverless': '^3.34.0', 'serverless-webpack': '^5.13.0', 'webpack': '^5.88.0', 'typescript': '^5.0.0', '@types/aws-lambda': '^8.10.0' } }, 'edge-function': { name: 'Edge Function', description: 'Vercel/Cloudflare edge functions with REZILIENT.js', category: 'Edge', framework: 'edge', dependencies: { 'rezilient.js': 'file:../' }, devDependencies: { '@vercel/node': '^3.0.0', 'typescript': '^5.0.0' } }, 'react-admin-dashboard': { name: 'React Admin Dashboard', description: 'Production-ready React admin dashboard with REZILIENT.js integration', category: 'Frontend', framework: 'vite', dependencies: { 'react': '^18.2.0', 'react-dom': '^18.2.0', 'rezilient.js': 'file:../', '@mui/material': '^5.0.0' }, devDependencies: { 'vite': '^5.0.0', '@vitejs/plugin-react': '^4.0.0', 'jest': '^29.0.0', 'jest-environment-jsdom': '^29.0.0', '@testing-library/react': '^13.0.0', '@testing-library/jest-dom': '^6.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', 'typescript': '^5.0.0' } }, 'backoffice-app': { name: 'Backoffice Application', description: 'Enterprise back-office app with REZILIENT.js integration', category: 'Backend', framework: 'node', dependencies: { 'express': '^4.18.0', 'rezilient.js': 'file:../' }, devDependencies: { 'nodemon': '^2.0.0', 'jest': '^29.0.0' } }, 'shop-app': { name: 'Shop/E-commerce Application', description: 'Headless e-commerce app with REZILIENT.js integration', category: 'Full-Stack', framework: 'nextjs', dependencies: { 'next': '^14.0.0', 'react': '^18.2.0', 'react-dom': '^18.2.0', 'rezilient.js': 'file:../' }, devDependencies: { '@types/node': '^20.0.0', '@types/react': '^18.0.0', '@types/react-dom': '^18.0.0', 'typescript': '^5.0.0', 'eslint': '^8.0.0', 'eslint-config-next': '^14.0.0' } }, 'analytics-dashboard': { name: 'Analytics Dashboard', description: 'Data visualization dashboard with REZILIENT.js integration', category: 'Frontend', framework: 'vite', dependencies: { 'react': '^18.2.0', 'react-dom': '^18.2.0', 'rezilient.js': 'file:../', 'recharts': '^2.0.0' }, devDependencies: { 'vite': '^5.0.0', '@vitejs/plugin-react': '^4.0.0', 'jest': '^29.0.0' } }, 'test-suite-app': { name: 'Test Suite Application', description: 'Automated testing suite with REZILIENT.js integration', category: 'Testing & QA', framework: 'node', dependencies: { 'rezilient.js': 'file:../', 'jest': '^29.0.0' }, devDependencies: { 'nodemon': '^2.0.0' } }, // Legacy and Deprecated Templates 'legacy-react': { name: 'Legacy React Template', description: 'Deprecated: Legacy React template with class components', category: 'Frontend', framework: 'react', dependencies: { 'react': '^17.0.2', 'react-dom': '^17.0.2', 'rezilient.js': 'file:../' }, devDependencies: { 'webpack': '^4.44.2', 'babel-loader': '^8.2.2', 'jest': '^26.6.0' } }, 'deprecated-angular': { name: 'Deprecated Angular Template', description: 'Deprecated: AngularJS 1.x template', category: 'Frontend', framework: 'angular', dependencies: { 'angular': '^1.8.2', 'rezilient.js': 'file:../' }, devDependencies: { 'gulp': '^4.0.2', 'karma': '^6.3.4', 'jest': '^26.6.0' } } }; function createDirectoryStructure(appPath, template) { const templateConfig = TEMPLATES[template]; const framework = templateConfig.framework; // Base directories for all projects const baseDirs = [ 'src', 'public', 'docs', 'tests', '.github/workflows', '.github/ISSUE_TEMPLATE', '.vscode' ]; // Framework-specific directories const frameworkDirs = { 'nextjs': [ 'src/app', 'src/app/api', 'src/app/api/items', 'src/components', 'src/lib', 'src/hooks', 'src/utils', 'src/styles', 'public/icons', 'public/images' ], 'remix': [ 'app', 'app/routes', 'app/components', 'app/lib', 'app/utils', 'app/styles', 'public/build', 'public/icons' ], 'nestjs': [ 'src/modules', 'src/controllers', 'src/services', 'src/guards', 'src/decorators', 'src/dto', 'src/entities', 'src/config', 'test/unit', 'test/e2e' ], 'node': [ 'src/routes', 'src/controllers', 'src/services', 'src/middleware', 'src/utils', 'src/config', 'logs' ], 'vite': [ 'src/components', 'src/utils', 'src/assets', 'src/styles', 'public/icons', 'public/images' ] }; // Create base directories baseDirs.forEach(dir => { fs.mkdirSync(path.join(appPath, dir), { recursive: true }); }); // Create framework-specific directories if (frameworkDirs[framework]) { frameworkDirs[framework].forEach(dir => { fs.mkdirSync(path.join(appPath, dir), { recursive: true }); }); } console.log(`๐Ÿ“ Created comprehensive directory structure for ${framework}`); } function createAllProjectFiles(appPath, template, appName) { const templateConfig = TEMPLATES[template]; console.log(`๐Ÿ“„ Creating all project files...`); // Create documentation files createDocumentationFiles(appPath, template, appName); // Create configuration files createConfigurationFiles(appPath, template); // Create source code files createSourceFiles(appPath, template, appName); // Create test files createTestFiles(appPath, template); // Create GitHub workflows createGitHubWorkflows(appPath, template); // Create VS Code settings createVSCodeSettings(appPath); console.log(`โœ… All project files created successfully!`); } function createDocumentationFiles(appPath, template, appName) { const templateConfig = TEMPLATES[template]; // Create README.md const readmeContent = `# ${appName} > Production-ready ${templateConfig.name} built with REZILIENT.js [![REZILIENT.js](https://img.shields.io/badge/Built%20with-REZILIENT.js-green.svg)](https://github.com/rezilient-js/rezilient.js) [![Carbon Aware](https://img.shields.io/badge/carbon-aware-green.svg)](https://www.co2signal.com/) [![Accessibility](https://img.shields.io/badge/a11y-WCAG%202.1%20AAA-blue.svg)](https://www.w3.org/WAI/WCAG21/quickref/) ## ๐ŸŒŸ Revolutionary Features This application includes all 6 revolutionary REZILIENT.js features: - ๐ŸŒฑ **Carbon-Aware Computing** - Automatically optimizes operations based on grid carbon intensity - ๐Ÿง  **AI-Bias Detection** - Built-in fairness metrics and bias detection for AI systems - ๐Ÿ”„ **Self-Healing Architecture** - Automatic error recovery and circuit breaker patterns - ๐Ÿ“ฑ **Offline-First Design** - Seamless online/offline transitions with intelligent sync - โšก **Quantum-Ready Patterns** - Future-proof architecture for quantum computing integration - โ™ฟ **Accessibility-First** - WCAG 2.1 AAA compliance built into every component ## ๐Ÿš€ Quick Start \`\`\`bash # Install dependencies npm install # Start development server npm run dev # Build for production npm run build # Run tests npm test \`\`\` ## ๐Ÿ“ Project Structure \`\`\` ${appName}/ โ”œโ”€โ”€ src/ # Source code โ”œโ”€โ”€ public/ # Static assets โ”œโ”€โ”€ docs/ # Documentation โ”œโ”€โ”€ tests/ # Test files โ”œโ”€โ”€ .github/workflows/ # CI/CD workflows โ”œโ”€โ”€ .vscode/ # VS Code settings โ”œโ”€โ”€ package.json # Dependencies โ”œโ”€โ”€ README.md # This file โ””โ”€โ”€ ...config files \`\`\` ## ๐ŸŒฑ Carbon-Aware Features This application automatically: - Monitors grid carbon intensity in real-time - Schedules heavy operations during low-carbon periods - Provides carbon footprint metrics - Optimizes energy consumption ## โ™ฟ Accessibility Features Built-in accessibility includes: - WCAG 2.1 AAA compliance - Screen reader optimization - Keyboard navigation support - High contrast mode support - Reduced motion preferences ## ๐Ÿงช Testing \`\`\`bash # Run all tests npm test # Run tests with coverage npm run test:coverage # Run tests in watch mode npm run test:watch \`\`\` ## ๐Ÿš€ Deployment This application is production-ready and can be deployed to: - Vercel - Netlify - AWS - Google Cloud - Azure - Any static hosting service ## ๐Ÿ“š Documentation - [REZILIENT.js Documentation](https://rezilient.js.org) - [API Reference](./docs/api.md) - [Contributing Guide](./docs/contributing.md) ## ๐Ÿค Contributing 1. Fork the repository 2. Create your feature branch (\`git checkout -b feature/amazing-feature\`) 3. Commit your changes (\`git commit -m 'Add amazing feature'\`) 4. Push to the branch (\`git push origin feature/amazing-feature\`) 5. Open a Pull Request ## ๐Ÿ“„ License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## ๐ŸŒŸ Powered by REZILIENT.js Built with [REZILIENT.js](https://rezilient.js.org) - The revolutionary framework for sustainable, accessible, and resilient applications. `; fs.writeFileSync(path.join(appPath, 'README.md'), readmeContent); // Create LICENSE file const licenseContent = `MIT License Copyright (c) ${new Date().getFullYear()} ${appName} Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`; fs.writeFileSync(path.join(appPath, 'LICENSE'), licenseContent); console.log(`๐Ÿ“ Created documentation files (README.md, LICENSE)`); } function createConfigurationFiles(appPath, template) { const templateConfig = TEMPLATES[template]; const framework = templateConfig.framework; // Create .gitignore const gitignoreContent = `# Dependencies node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* # Production builds /build /dist /.next /out # Environment variables .env .env.local .env.development.local .env.test.local .env.production.local # IDE files .vscode/ .idea/ *.swp *.swo # OS files .DS_Store Thumbs.db # Logs logs *.log # Runtime data pids *.pid *.seed *.pid.lock # Coverage directory used by tools like istanbul coverage/ *.lcov # nyc test coverage .nyc_output # Dependency directories jspm_packages/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Storybook build outputs .out .storybook-out # Temporary folders tmp/ temp/ # REZILIENT.js specific .rezilient/ carbon-cache/ `; fs.writeFileSync(path.join(appPath, '.gitignore'), gitignoreContent); // Create .env.example const envContent = `# REZILIENT.js Configuration REZILIENT_VERSION=1.0.0 CARBON_AWARE_ENABLED=true OFFLINE_FIRST_ENABLED=true AI_BIAS_DETECTION_ENABLED=true # Carbon Intensity API CARBON_INTENSITY_API_KEY=your_api_key_here CARBON_INTENSITY_REGION=US # Application Settings NODE_ENV=development PORT=3000 # Database (if applicable) DATABASE_URL=your_database_url_here # API Keys API_KEY=your_api_key_here `; fs.writeFileSync(path.join(appPath, '.env.example'), envContent); // Framework-specific configuration files if (framework === 'nextjs') { // Copy Next.js config from templates const nextConfigPath = path.join(__dirname, '..', 'templates', 'nextjs', 'next.config.js'); if (fs.existsSync(nextConfigPath)) { fs.copyFileSync(nextConfigPath, path.join(appPath, 'next.config.js')); } // Create tailwind.config.js const tailwindConfig = `/** @type {import('tailwindcss').Config} */ module.exports = { content: [ './src/pages/**/*.{js,ts,jsx,tsx,mdx}', './src/components/**/*.{js,ts,jsx,tsx,mdx}', './src/app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { colors: { 'carbon-low': '#10b981', 'carbon-medium': '#f59e0b', 'carbon-high': '#ef4444', }, }, }, plugins: [], }`; fs.writeFileSync(path.join(appPath, 'tailwind.config.js'), tailwindConfig); } // Create TypeScript config const tsConfig = `{ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "es6"], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./src/*"], "rezilient.js": ["./node_modules/rezilient.js"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }`; fs.writeFileSync(path.join(appPath, 'tsconfig.json'), tsConfig); console.log(`โš™๏ธ Created configuration files (.gitignore, .env.example, tsconfig.json)`); } function createSourceFiles(appPath, template, appName) { const templateConfig = TEMPLATES[template]; const framework = templateConfig.framework; console.log(`๐Ÿ“ Creating source files for ${framework}...`); switch (framework) { case 'nextjs': createNextJSSourceFiles(appPath, appName); break; case 'remix': createRemixSourceFiles(appPath, appName); break; case 'nestjs': createNestJSSourceFiles(appPath, appName); break; case 'node': createNodeSourceFiles(appPath, appName); break; case 'vite': default: if (template === 'react-app') { createReactSourceFiles(appPath, appName); } else { createVanillaSourceFiles(appPath, appName); } break; } console.log(`โœ… Source files created for ${framework}`); } function createNextJSSourceFiles(appPath, appName) { // App Router layout const layoutContent = `import './globals.css' import { Inter } from 'next/font/google' import { AetherProvider } from 'rezilient.js' const inter = Inter({ subsets: ['latin'] }) export const metadata = { title: '${appName}', description: 'Production-ready resilient application built with REZILIENT.js', } export default function RootLayout({ children }) { return ( <html lang="en"> <body className={inter.className}> <AetherProvider config={{ carbonAware: true, offlineFirst: true, aiAware: true, accessibilityFirst: true, selfHealing: true, biasDetection: true }} > {children} </AetherProvider> </body> </html> ) }`; fs.writeFileSync(path.join(appPath, 'src', 'app', 'layout.tsx'), layoutContent); // Main page const pageContent = `'use client' import { useState, useEffect } from 'react' import { useAetherStore, useCarbonAware, useOfflineFirst } from 'rezilient.js' export default function Home() { const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const { carbonIntensity, isLowCarbon } = useCarbonAware() const { isOnline, syncStatus } = useOfflineFirst() const [store, updateStore] = useAetherStore({ count: 0 }) useEffect(() => { loadItems() }, []) const loadItems = async () => { setLoading(true) try { const response = await fetch('/api/items') const data = await response.json() setItems(data.items || []) } catch (error) { console.error('Failed to load items:', error) } finally { setLoading(false) } } return ( <main className="container mx-auto px-4 py-8"> <div className="text-center mb-8"> <h1 className="text-4xl font-bold mb-4">๐ŸŒŸ ${appName}</h1> <p className="text-lg text-gray-600 mb-6"> Production-ready resilient application built with REZILIENT.js </p> <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8"> <div className="bg-white p-4 rounded-lg shadow"> <h3 className="font-semibold">Carbon Status</h3> <p className={\`text-lg \${isLowCarbon ? 'text-green-600' : 'text-yellow-600'}\`}> {isLowCarbon ? '๐ŸŸข Low Impact' : '๐ŸŸก Medium Impact'} </p> <p className="text-sm text-gray-500"> Intensity: {carbonIntensity?.toFixed(2) || 'N/A'} </p> </div> <div className="bg-white p-4 rounded-lg shadow"> <h3 className="font-semibold">Connection</h3> <p className={\`text-lg \${isOnline ? 'text-green-600' : 'text-red-600'}\`}> {isOnline ? '๐ŸŸข Online' : '๐Ÿ”ด Offline'} </p> <p className="text-sm text-gray-500"> Sync: {syncStatus || 'Ready'} </p> </div> <div className="bg-white p-4 rounded-lg shadow"> <h3 className="font-semibold">Counter</h3> <div className="flex items-center justify-center gap-2"> <button onClick={() => updateStore(s => ({ ...s, count: s.count - 1 }))} className="bg-blue-500 text-white px-3 py-1 rounded" > - </button> <span className="text-lg font-bold">{store.count}</span> <button onClick={() => updateStore(s => ({ ...s, count: s.count + 1 }))} className="bg-blue-500 text-white px-3 py-1 rounded" > + </button> </div> </div> </div> </div> <div className="max-w-4xl mx-auto"> <h2 className="text-2xl font-bold mb-4">Resilient Data</h2> {loading ? ( <div className="text-center py-8"> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto"></div> <p className="mt-4">Loading with carbon awareness...</p> </div> ) : ( <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> {items.map((item, index) => ( <div key={index} className="bg-white p-4 rounded-lg shadow"> <h3 className="font-semibold">{item.name || \`Item \${index + 1}\`}</h3> <p className="text-sm text-gray-500"> {item.timestamp ? new Date(item.timestamp).toLocaleString() : 'No timestamp'} </p> </div> ))} </div> )} </div> </main> ) }`; fs.writeFileSync(path.join(appPath, 'src', 'app', 'page.tsx'), pageContent); // API route const apiContent = `import { NextRequest, NextResponse } from 'next/server' import { CarbonAwareScheduler } from 'rezilient.js' const scheduler = new CarbonAwareScheduler() export async function GET(request: NextRequest) { try { // Schedule data generation based on carbon intensity const items = await scheduler.schedule( () => generateItems(), { priority: 'normal', carbonThreshold: 'medium' } ) const carbonIntensity = await scheduler.getCurrentCarbonIntensity() return NextResponse.json({ items, meta: { carbonIntensity, processedAt: new Date().toISOString(), framework: 'REZILIENT.js v1.0.0' } }) } catch (error) { return NextResponse.json( { error: 'Failed to fetch items' }, { status: 500 } ) } } function generateItems() { return Array.from({ length: 12 }, (_, i) => ({ id: i + 1, name: \`Resilient Item \${i + 1}\`, timestamp: new Date().toISOString(), carbonOptimized: true })) }`; fs.writeFileSync(path.join(appPath, 'src', 'app', 'api', 'items', 'route.ts'), apiContent); // Global CSS const globalCSS = `@tailwind base; @tailwind components; @tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } } body { color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, transparent, rgb(var(--background-end-rgb)) ) rgb(var(--background-start-rgb)); } /* REZILIENT.js specific styles */ .carbon-low { @apply text-green-600; } .carbon-medium { @apply text-yellow-600; } .carbon-high { @apply text-red-600; } .offline-indicator { @apply fixed top-4 right-4 bg-red-500 text-white px-3 py-1 rounded-full text-sm; } .loading-carbon { @apply animate-pulse bg-green-100; }`; fs.writeFileSync(path.join(appPath, 'src', 'app', 'globals.css'), globalCSS); } function createReactSourceFiles(appPath, appName) { // Main App component const appComponent = `import React from 'react'; import { AetherComponent, useAetherStore, useCarbonAware, useOfflineFirst } from 'rezilient.js'; import './App.css'; function App() { const [items, setItems] = React.useState([]); const [loading, setLoading] = React.useState(false); const { carbonIntensity, isLowCarbon } = useCarbonAware(); const { isOnline, syncStatus } = useOfflineFirst(); const [store, updateStore] = useAetherStore({ count: 0 }); React.useEffect(() => { loadItems(); }, []); const loadItems = async () => { setLoading(true); try { const response = await fetch('/api/items'); const data = await response.json(); setItems(data.items || []); } catch (error) { console.error('Failed to load items:', error); } finally { setLoading(false); } }; return ( <div className="app"> <header className="app-header"> <h1>๐ŸŒŸ ${appName}</h1> <p>Production-ready resilient application built with REZILIENT.js</p> <div className="status-grid"> <div className="status-card"> <h3>Carbon Status</h3> <p className={isLowCarbon ? 'carbon-low' : 'carbon-medium'}> {isLowCarbon ? '๐ŸŸข Low Impact' : '๐ŸŸก Medium Impact'} </p> <small>Intensity: {carbonIntensity?.toFixed(2) || 'N/A'}</small> </div> <div className="status-card"> <h3>Connection</h3> <p className={isOnline ? 'online' : 'offline'}> {isOnline ? '๐ŸŸข Online' : '๐Ÿ”ด Offline'} </p> <small>Sync: {syncStatus || 'Ready'}</small> </div> <div className="status-card"> <h3>Counter</h3> <div className="counter"> <button onClick={() => updateStore(s => ({ ...s, count: s.count - 1 }))}>-</button> <span>{store.count}</span> <button onClick={() => updateStore(s => ({ ...s, count: s.count + 1 }))}>+</button> </div> </div> </div> </header> <main className="app-main"> <h2>Resilient Data</h2> {loading ? ( <div className="loading"> <div className="spinner"></div> <p>Loading with carbon awareness...</p> </div> ) : ( <div className="items-grid"> {items.map((item, index) => ( <div key={index} className="item-card"> <h3>{item.name || \`Item \${index + 1}\`}</h3> <p>{item.timestamp ? new Date(item.timestamp).toLocaleString() : 'No timestamp'}</p> </div> ))} </div> )} </main> <footer className="app-footer"> <p>Powered by REZILIENT.js Framework v1.0.0</p> </footer> </div> ); } export default App;`; fs.writeFileSync(path.join(appPath, 'src', 'App.jsx'), appComponent); // Main entry point const mainJs = `import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.jsx'; import './index.css'; // Initialize REZILIENT.js framework import { initializeAether } from 'rezilient.js'; initializeAether({ // Core Features carbonAware: true, offlineFirst: true, aiAware: true, accessibilityFirst: true, selfHealing: true, biasDetection: true, // Production Configuration enableRealCarbonData: true, enableEnergyMonitoring: true, enableBatteryAPI: true, enablePerformanceAPI: true, enableRealBiasDetection: true, enablePredictiveHealing: true, performanceMonitoring: true, // Development debug: process.env.NODE_ENV === 'development' }); ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <App /> </React.StrictMode> );`; fs.writeFileSync(path.join(appPath, 'src', 'main.jsx'), mainJs); // CSS files const appCSS = `/* REZILIENT.js App Styles */ .app { min-height: 100vh; display: flex; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; } .app-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2rem; text-align: center; } .app-header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; } .status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-top: 2rem; max-width: 800px; margin-left: auto; margin-right: auto; } .status-card { background: rgba(255, 255, 255, 0.1); padding: 1rem; border-radius: 8px; backdrop-filter: blur(10px); } .counter { display: flex; align-items: center; justify-content: center; gap: 1rem; } .counter button { background: #4CAF50; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; font-size: 1.2rem; } .counter button:hover { background: #45a049; } .app-main { flex: 1; padding: 2rem; max-width: 1200px; margin: 0 auto; width: 100%; } .items-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem; margin-top: 1rem; } .item-card { background: white; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-left: 4px solid #4CAF50; } .loading { text-align: center; padding: 2rem; } .spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 40px; height: 40px; animation: spin 2s linear infinite; margin: 0 auto 1rem; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .carbon-low { color: #4CAF50; } .carbon-medium { color: #FF9800; } .carbon-high { color: #F44336; } .online { color: #4CAF50; } .offline { color: #F44336; } .app-footer { background: #333; color: white; text-align: center; padding: 1rem; } /* Accessibility improvements */ @media (prefers-reduced-motion: reduce) { .spinner { animation: none; } } @media (max-width: 768px) { .app-header h1 { font-size: 2rem; } .status-grid { grid-template-columns: 1fr; } }`; fs.writeFileSync(path.join(appPath, 'src', 'App.css'), appCSS); const indexCSS = `/* REZILIENT.js Global Styles */ :root { --primary-color: #667eea; --secondary-color: #764ba2; --success-color: #4CAF50; --warning-color: #FF9800; --error-color: #F44336; --text-color: #333; --background-color: #f5f5f5; } * { box-sizing: border-box; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: var(--background-color); color: var(--text-color); line-height: 1.6; } #root { min-height: 100vh; } /* High contrast mode support */ @media (prefers-contrast: high) { :root { --primary-color: #000; --secondary-color: #000; --background-color: #fff; --text-color: #000; } } /* Reduced motion support */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* Focus indicators for accessibility */ button:focus, input:focus, select:focus, textarea:focus { outline: 2px solid var(--primary-color); outline-offset: 2px; } /* Screen reader only content */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }`; fs.writeFileSync(path.join(appPath, 'src', 'index.css'), indexCSS); } function createVanillaSourceFiles(appPath, appName) { const mainJs = `import { AetherComponent, AetherStore, initializeAether } from 'rezilient.js'; // Initialize framework initializeAether({ carbonAware: true, offlineFirst: true, aiAware: true }); class ${appName.replace(/[^a-zA-Z0-9]/g, '')}App extends AetherComponent { constructor() { super(); this.store = new AetherStore({ count: 0, items: [] }); this.init(); } async init() { await this.render(); this.setupEventListeners(); } async render() { const state = this.store.get(); document.getElementById('app').innerHTML = \` <div class="app"> <h1>๐ŸŒŸ ${appName}</h1> <p>Production-ready resilient application</p> <div class="counter"> <button id="decrement">-</button> <span>\${state.count}</span> <button id="increment">+</button> </div> <div class="status"> <p>Carbon Impact: \${this.getCarbonStatus()}</p> <p>Online: \${this.isOnline ? 'โœ…' : 'โŒ'}</p> </div> </div> \`; } setupEventListeners() { document.getElementById('increment')?.addEventListener('click', () => { this.store.update(state => ({ ...state, count: state.count + 1 })); this.render(); }); document.getElementById('decrement')?.addEventListener('click', () => { this.store.update(state => ({ ...state, count: state.count - 1 })); this.render(); }); } getCarbonStatus() { const impact = this.carbonAwareScheduler.getCurrentCarbonIntensity(); return impact > 0.7 ? '๐Ÿ”ด High' : impact > 0.4 ? '๐ŸŸก Medium' : '๐ŸŸข Low'; } } // Initialize app new ${appName.replace(/[^a-zA-Z0-9]/g, '')}App();`; fs.writeFileSync(path.join(appPath, 'src', 'main.js'), mainJs); } function createNodeSourceFiles(appPath, appName) { const serverJs = `import express from 'express'; import cors from 'cors'; import { CarbonAwareScheduler, AetherPrinciples } from 'rezilient.js'; const app = express(); const port = process.env.PORT || 3000; // Initialize REZILIENT.js for backend const scheduler = new CarbonAwareScheduler(); const principles = new AetherPrinciples(); app.use(cors()); app.use(express.json()); // Carbon-aware middleware app.use(async (req, res, next) => { const carbonIntensity = await scheduler.getCurrentCarbonIntensity(); req.carbonIntensity = carbonIntensity; // Add carbon headers res.set('X-Carbon-Intensity', carbonIntensity.toString()); res.set('X-Powered-By', 'REZILIENT.js'); next(); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'healthy', carbonIntensity: req.carbonIntensity, timestamp: new Date().toISOString(), framework: 'REZILIENT.js v1.0.0' }); }); // Example API endpoint with carbon awareness app.get('/api/items', async (req, res) => { try { // Schedule data processing based on carbon intensity const items = await scheduler.schedule( () => generateItems(), { priority: 'normal', carbonThreshold: 'medium' } ); res.json({ items, meta: { carbonIntensity: req.carbonIntensity, processedAt: new Date().toISOString() } }); } catch (error) { res.status(500).json({ error: error.message }