create-fluxstack 1.0.11 → 1.0.13
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/.claude/settings.local.json +3 -1
- package/.env.example +3 -26
- package/ai-context/development/plugins-guide.md +49 -45
- package/core/config/__tests__/config-loader.test.ts +10 -47
- package/core/config/loader.ts +8 -32
- package/create-fluxstack.ts +7 -7
- package/package-template.json +2 -2
- package/package.json +1 -1
|
@@ -65,7 +65,9 @@
|
|
|
65
65
|
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\test-cli-update/**)",
|
|
66
66
|
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\test-gitignore/**)",
|
|
67
67
|
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\test-plugins-guide/**)",
|
|
68
|
-
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\test-plugins-guide\\ai-context\\development/**)"
|
|
68
|
+
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\test-plugins-guide\\ai-context\\development/**)",
|
|
69
|
+
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\test-env-fix/**)",
|
|
70
|
+
"Read(/C:\\Users\\Marcos\\Documents\\GitHub\\aviso-projeto\\final-test/**)"
|
|
69
71
|
],
|
|
70
72
|
"deny": []
|
|
71
73
|
}
|
package/.env.example
CHANGED
|
@@ -10,36 +10,13 @@ HOST=localhost
|
|
|
10
10
|
# Frontend Configuration
|
|
11
11
|
FRONTEND_PORT=5173
|
|
12
12
|
VITE_API_URL=http://localhost:3000
|
|
13
|
+
VITE_APP_NAME=FluxStack
|
|
14
|
+
VITE_APP_VERSION=1.0.0
|
|
15
|
+
VITE_NODE_ENV=development
|
|
13
16
|
|
|
14
17
|
# Backend Configuration
|
|
15
18
|
BACKEND_PORT=3001
|
|
16
19
|
|
|
17
|
-
# Database (example - configure based on your needs)
|
|
18
|
-
# DATABASE_URL=postgresql://user:password@localhost:5432/fluxstack
|
|
19
|
-
# DATABASE_HOST=localhost
|
|
20
|
-
# DATABASE_PORT=5432
|
|
21
|
-
# DATABASE_NAME=fluxstack
|
|
22
|
-
# DATABASE_USER=user
|
|
23
|
-
# DATABASE_PASSWORD=password
|
|
24
|
-
|
|
25
|
-
# Authentication (example)
|
|
26
|
-
# JWT_SECRET=your-super-secret-jwt-key
|
|
27
|
-
# JWT_EXPIRES_IN=24h
|
|
28
|
-
|
|
29
|
-
# External APIs (example)
|
|
30
|
-
# STRIPE_SECRET_KEY=sk_test_...
|
|
31
|
-
# STRIPE_PUBLISHABLE_KEY=pk_test_...
|
|
32
|
-
|
|
33
|
-
# Email Service (example)
|
|
34
|
-
# SMTP_HOST=smtp.gmail.com
|
|
35
|
-
# SMTP_PORT=587
|
|
36
|
-
# SMTP_USER=your-email@gmail.com
|
|
37
|
-
# SMTP_PASS=your-app-password
|
|
38
|
-
|
|
39
|
-
# File Upload (example)
|
|
40
|
-
# UPLOAD_PATH=uploads
|
|
41
|
-
# MAX_FILE_SIZE=10485760
|
|
42
|
-
|
|
43
20
|
# CORS Configuration
|
|
44
21
|
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
|
|
45
22
|
CORS_METHODS=GET,POST,PUT,DELETE,OPTIONS
|
|
@@ -92,61 +92,65 @@ app.use(authPlugin)
|
|
|
92
92
|
### 2. Advanced Plugin with Lifecycle Hooks
|
|
93
93
|
|
|
94
94
|
```typescript
|
|
95
|
-
// app/server/plugins/
|
|
95
|
+
// app/server/plugins/monitoring.ts
|
|
96
96
|
import { Elysia } from 'elysia'
|
|
97
97
|
import type { PluginContext, FluxStackPlugin } from '@/core/plugins/types'
|
|
98
98
|
|
|
99
|
-
export interface
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
export interface MonitoringConfig {
|
|
100
|
+
metricsEndpoint?: string
|
|
101
|
+
enableHealthCheck?: boolean
|
|
102
|
+
collectMetrics?: boolean
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
export const
|
|
106
|
-
name: '
|
|
105
|
+
export const createMonitoringPlugin = (config: MonitoringConfig): FluxStackPlugin => ({
|
|
106
|
+
name: 'monitoring',
|
|
107
107
|
version: '1.0.0',
|
|
108
108
|
dependencies: [],
|
|
109
109
|
|
|
110
110
|
setup: async (context: PluginContext) => {
|
|
111
|
-
context.logger.info('Setting up
|
|
111
|
+
context.logger.info('Setting up monitoring plugin')
|
|
112
112
|
|
|
113
|
-
// Initialize
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
query: db.query.bind(db),
|
|
119
|
-
transaction: db.transaction.bind(db)
|
|
113
|
+
// Initialize metrics collection
|
|
114
|
+
const metrics = {
|
|
115
|
+
requests: 0,
|
|
116
|
+
errors: 0,
|
|
117
|
+
startTime: Date.now()
|
|
120
118
|
}
|
|
119
|
+
|
|
120
|
+
return { metrics }
|
|
121
121
|
},
|
|
122
122
|
|
|
123
123
|
onServerStart: async (context: PluginContext) => {
|
|
124
|
-
context.logger.info('
|
|
125
|
-
//
|
|
124
|
+
context.logger.info('Monitoring plugin started')
|
|
125
|
+
// Start periodic health checks, metrics collection
|
|
126
126
|
},
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
context.
|
|
130
|
-
// Clean up connections
|
|
128
|
+
onRequest: async (context: PluginContext) => {
|
|
129
|
+
context.metrics.requests++
|
|
131
130
|
},
|
|
132
131
|
|
|
133
|
-
plugin: new Elysia({ name: '
|
|
134
|
-
.
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
plugin: new Elysia({ name: 'monitoring' })
|
|
133
|
+
.get('/metrics', () => ({
|
|
134
|
+
requests: context.metrics.requests,
|
|
135
|
+
errors: context.metrics.errors,
|
|
136
|
+
uptime: Date.now() - context.metrics.startTime
|
|
137
|
+
}))
|
|
138
|
+
.get('/health', () => ({
|
|
139
|
+
status: 'healthy',
|
|
140
|
+
timestamp: new Date().toISOString()
|
|
137
141
|
}))
|
|
138
142
|
})
|
|
139
143
|
|
|
140
144
|
// Usage
|
|
141
|
-
import {
|
|
145
|
+
import { createMonitoringPlugin } from './plugins/monitoring'
|
|
142
146
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
const monitoringPlugin = createMonitoringPlugin({
|
|
148
|
+
metricsEndpoint: '/metrics',
|
|
149
|
+
enableHealthCheck: true,
|
|
150
|
+
collectMetrics: true
|
|
147
151
|
})
|
|
148
152
|
|
|
149
|
-
app.use(
|
|
153
|
+
app.use(monitoringPlugin)
|
|
150
154
|
```
|
|
151
155
|
|
|
152
156
|
### 3. Plugin with Configuration Schema
|
|
@@ -330,19 +334,19 @@ app/server/plugins/
|
|
|
330
334
|
│ │ ├── jwt.ts
|
|
331
335
|
│ │ └── oauth.ts
|
|
332
336
|
│ └── middleware/ # Auth-related middleware
|
|
333
|
-
├── database/
|
|
334
|
-
│ ├── index.ts # Database plugin
|
|
335
|
-
│ ├── migrations/ # Database migrations
|
|
336
|
-
│ └── seeds/ # Database seeds
|
|
337
337
|
├── cache/
|
|
338
338
|
│ ├── index.ts # Cache plugin
|
|
339
339
|
│ ├── providers/ # Different cache providers
|
|
340
340
|
│ │ ├── redis.ts
|
|
341
341
|
│ │ └── memory.ts
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
├── monitoring/
|
|
343
|
+
│ ├── index.ts # Monitoring plugin
|
|
344
|
+
│ ├── metrics.ts # Custom metrics
|
|
345
|
+
│ └── health.ts # Health checks
|
|
346
|
+
└── validation/
|
|
347
|
+
├── index.ts # Validation plugin
|
|
348
|
+
├── schemas/ # Validation schemas
|
|
349
|
+
└── middleware/ # Validation middleware
|
|
346
350
|
```
|
|
347
351
|
|
|
348
352
|
### Plugin Registration
|
|
@@ -350,23 +354,23 @@ app/server/plugins/
|
|
|
350
354
|
```typescript
|
|
351
355
|
// app/server/plugins/index.ts
|
|
352
356
|
export { authPlugin } from './auth'
|
|
353
|
-
export { createDatabasePlugin } from './database'
|
|
354
357
|
export { createCachePlugin } from './cache'
|
|
355
|
-
export {
|
|
358
|
+
export { createMonitoringPlugin } from './monitoring'
|
|
359
|
+
export { validationPlugin } from './validation'
|
|
356
360
|
|
|
357
361
|
// app/server/index.ts
|
|
358
362
|
import {
|
|
359
363
|
authPlugin,
|
|
360
|
-
createDatabasePlugin,
|
|
361
364
|
createCachePlugin,
|
|
362
|
-
|
|
365
|
+
createMonitoringPlugin,
|
|
366
|
+
validationPlugin
|
|
363
367
|
} from './plugins'
|
|
364
368
|
|
|
365
369
|
// Register plugins in order of dependency
|
|
366
|
-
app.use(
|
|
367
|
-
app.use(createCachePlugin({ provider: '
|
|
370
|
+
app.use(validationPlugin)
|
|
371
|
+
app.use(createCachePlugin({ provider: 'memory', ttl: 300 }))
|
|
368
372
|
app.use(authPlugin)
|
|
369
|
-
app.use(
|
|
373
|
+
app.use(createMonitoringPlugin({ enableHealthCheck: true }))
|
|
370
374
|
```
|
|
371
375
|
|
|
372
376
|
## 🔧 Plugin Configuration
|
|
@@ -36,7 +36,9 @@ describe('Configuration Loader', () => {
|
|
|
36
36
|
// Clear relevant environment variables
|
|
37
37
|
for (const key in process.env) {
|
|
38
38
|
if (key.startsWith('FLUXSTACK_') || key.startsWith('PORT') || key.startsWith('HOST') ||
|
|
39
|
-
key.startsWith('NODE_ENV'))
|
|
39
|
+
key.startsWith('NODE_ENV') || key.startsWith('CORS_') || key.startsWith('LOG_') ||
|
|
40
|
+
key.startsWith('MONITORING_') || key.startsWith('METRICS_') || key.startsWith('PROFILING_') ||
|
|
41
|
+
key.startsWith('BUILD_')) {
|
|
40
42
|
delete process.env[key]
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -109,45 +111,6 @@ describe('Configuration Loader', () => {
|
|
|
109
111
|
expect(config.server?.cors?.credentials).toBe(true)
|
|
110
112
|
})
|
|
111
113
|
|
|
112
|
-
test('handles database configuration from environment', () => {
|
|
113
|
-
process.env.DATABASE_URL = 'postgresql://user:pass@localhost:5432/db'
|
|
114
|
-
process.env.DATABASE_HOST = 'db.example.com'
|
|
115
|
-
process.env.DATABASE_PORT = '5433'
|
|
116
|
-
process.env.DATABASE_SSL = 'true'
|
|
117
|
-
|
|
118
|
-
const config = getConfigSync()
|
|
119
|
-
|
|
120
|
-
expect(config.database?.url).toBe('postgresql://user:pass@localhost:5432/db')
|
|
121
|
-
expect(config.database?.host).toBe('db.example.com')
|
|
122
|
-
expect(config.database?.port).toBe(5433)
|
|
123
|
-
expect(config.database?.ssl).toBe(true)
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
test('handles authentication configuration', () => {
|
|
127
|
-
process.env.JWT_SECRET = 'my-secret-key'
|
|
128
|
-
process.env.JWT_EXPIRES_IN = '7d'
|
|
129
|
-
process.env.JWT_ALGORITHM = 'HS256'
|
|
130
|
-
|
|
131
|
-
const config = getConfigSync()
|
|
132
|
-
|
|
133
|
-
expect(config.auth?.secret).toBe('my-secret-key')
|
|
134
|
-
expect(config.auth?.expiresIn).toBe('7d')
|
|
135
|
-
expect(config.auth?.algorithm).toBe('HS256')
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
test('handles email configuration', () => {
|
|
139
|
-
process.env.SMTP_HOST = 'smtp.gmail.com'
|
|
140
|
-
process.env.SMTP_PORT = '587'
|
|
141
|
-
process.env.SMTP_USER = 'user@example.com'
|
|
142
|
-
process.env.SMTP_SECURE = 'true'
|
|
143
|
-
|
|
144
|
-
const config = getConfigSync()
|
|
145
|
-
|
|
146
|
-
expect(config.email?.host).toBe('smtp.gmail.com')
|
|
147
|
-
expect(config.email?.port).toBe(587)
|
|
148
|
-
expect(config.email?.user).toBe('user@example.com')
|
|
149
|
-
expect(config.email?.secure).toBe(true)
|
|
150
|
-
})
|
|
151
114
|
|
|
152
115
|
test('handles monitoring configuration', () => {
|
|
153
116
|
process.env.MONITORING_ENABLED = 'true'
|
|
@@ -164,10 +127,10 @@ describe('Configuration Loader', () => {
|
|
|
164
127
|
})
|
|
165
128
|
|
|
166
129
|
test('handles build configuration', () => {
|
|
167
|
-
process.env.
|
|
168
|
-
process.env.
|
|
169
|
-
process.env.
|
|
170
|
-
process.env.
|
|
130
|
+
process.env.FLUXSTACK_BUILD_TARGET = 'docker'
|
|
131
|
+
process.env.FLUXSTACK_BUILD_OUTDIR = 'build'
|
|
132
|
+
process.env.FLUXSTACK_BUILD_MINIFY = 'true'
|
|
133
|
+
process.env.FLUXSTACK_BUILD_SOURCEMAPS = 'false'
|
|
171
134
|
|
|
172
135
|
reloadConfig()
|
|
173
136
|
const config = getConfigSync()
|
|
@@ -565,12 +528,12 @@ describe('Configuration Loader', () => {
|
|
|
565
528
|
test('handles configuration with warnings gracefully', () => {
|
|
566
529
|
// Test with invalid environment variables that should generate warnings
|
|
567
530
|
process.env.PORT = 'invalid-port' // Should fall back to default
|
|
568
|
-
process.env.LOG_LEVEL = '
|
|
569
|
-
process.env.
|
|
531
|
+
process.env.LOG_LEVEL = 'debug' // Valid value
|
|
532
|
+
process.env.FLUXSTACK_BUILD_TARGET = 'bun' // Valid value
|
|
570
533
|
|
|
571
534
|
expect(() => {
|
|
572
535
|
const config = getConfigSync()
|
|
573
|
-
// Should not throw, but use defaults
|
|
536
|
+
// Should not throw, but use defaults for invalid values
|
|
574
537
|
expect(typeof config.server?.port).toBe('number')
|
|
575
538
|
expect(['debug', 'info', 'warn', 'error']).toContain(config.logging?.level)
|
|
576
539
|
expect(['bun', 'node', 'docker']).toContain(config.build?.target)
|
package/core/config/loader.ts
CHANGED
|
@@ -56,6 +56,7 @@ const ENV_MAPPINGS = {
|
|
|
56
56
|
|
|
57
57
|
// Server configuration
|
|
58
58
|
'PORT': 'server.port',
|
|
59
|
+
'FLUXSTACK_PORT': 'server.port',
|
|
59
60
|
'HOST': 'server.host',
|
|
60
61
|
'FLUXSTACK_API_PREFIX': 'server.apiPrefix',
|
|
61
62
|
'CORS_ORIGINS': 'server.cors.origins',
|
|
@@ -107,34 +108,9 @@ const ENV_MAPPINGS = {
|
|
|
107
108
|
'PROFILING_SAMPLE_RATE': 'monitoring.profiling.sampleRate',
|
|
108
109
|
'FLUXSTACK_PROFILING_SAMPLE_RATE': 'monitoring.profiling.sampleRate',
|
|
109
110
|
|
|
110
|
-
//
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
'DATABASE_PORT': 'database.port',
|
|
114
|
-
'DATABASE_NAME': 'database.database',
|
|
115
|
-
'DATABASE_USER': 'database.user',
|
|
116
|
-
'DATABASE_PASSWORD': 'database.password',
|
|
117
|
-
'DATABASE_SSL': 'database.ssl',
|
|
118
|
-
'DATABASE_POOL_SIZE': 'database.poolSize',
|
|
119
|
-
|
|
120
|
-
// Auth configuration
|
|
121
|
-
'JWT_SECRET': 'auth.secret',
|
|
122
|
-
'JWT_EXPIRES_IN': 'auth.expiresIn',
|
|
123
|
-
'JWT_ALGORITHM': 'auth.algorithm',
|
|
124
|
-
'JWT_ISSUER': 'auth.issuer',
|
|
125
|
-
|
|
126
|
-
// Email configuration
|
|
127
|
-
'SMTP_HOST': 'email.host',
|
|
128
|
-
'SMTP_PORT': 'email.port',
|
|
129
|
-
'SMTP_USER': 'email.user',
|
|
130
|
-
'SMTP_PASSWORD': 'email.password',
|
|
131
|
-
'SMTP_SECURE': 'email.secure',
|
|
132
|
-
'SMTP_FROM': 'email.from',
|
|
133
|
-
|
|
134
|
-
// Storage configuration
|
|
135
|
-
'UPLOAD_PATH': 'storage.uploadPath',
|
|
136
|
-
'MAX_FILE_SIZE': 'storage.maxFileSize',
|
|
137
|
-
'STORAGE_PROVIDER': 'storage.provider'
|
|
111
|
+
// Plugin configuration
|
|
112
|
+
'FLUXSTACK_PLUGINS_ENABLED': 'plugins.enabled',
|
|
113
|
+
'FLUXSTACK_PLUGINS_DISABLED': 'plugins.disabled'
|
|
138
114
|
} as const
|
|
139
115
|
|
|
140
116
|
/**
|
|
@@ -231,12 +207,12 @@ function loadFromEnvironment(prefix = 'FLUXSTACK_'): Partial<FluxStackConfig> {
|
|
|
231
207
|
try {
|
|
232
208
|
// Determine target type from config path
|
|
233
209
|
let targetType = 'string'
|
|
234
|
-
if (configPath.includes('port') || configPath.includes('maxAge') || configPath.includes('collectInterval') || configPath.includes('sampleRate')
|
|
210
|
+
if (configPath.includes('port') || configPath.includes('maxAge') || configPath.includes('collectInterval') || configPath.includes('sampleRate')) {
|
|
235
211
|
targetType = 'number'
|
|
236
|
-
} else if (configPath.includes('
|
|
237
|
-
targetType = 'boolean'
|
|
238
|
-
} else if (configPath.includes('origins') || configPath.includes('methods') || configPath.includes('headers') || configPath.includes('exporters')) {
|
|
212
|
+
} else if (configPath.includes('origins') || configPath.includes('methods') || configPath.includes('headers') || configPath.includes('exporters') || configPath.includes('plugins.enabled') || configPath.includes('plugins.disabled')) {
|
|
239
213
|
targetType = 'array'
|
|
214
|
+
} else if (configPath.includes('enabled') || configPath.includes('credentials') || configPath.includes('minify') || configPath.includes('treeshake') || configPath.includes('compress') || configPath.includes('splitChunks') || configPath.includes('bundleAnalyzer') || configPath.includes('sourceMaps') || configPath.includes('clean')) {
|
|
215
|
+
targetType = 'boolean'
|
|
240
216
|
}
|
|
241
217
|
|
|
242
218
|
const parsedValue = parseEnvValue(envValue, targetType)
|
package/create-fluxstack.ts
CHANGED
|
@@ -109,16 +109,16 @@ program
|
|
|
109
109
|
writeFileSync(packageJsonPath, JSON.stringify(fallbackPackageJson, null, 2))
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
// Create .env from .env.example and set development mode
|
|
112
|
+
// Create .env from .env.example and set development mode + project name
|
|
113
113
|
const envExamplePath = join(projectPath, '.env.example')
|
|
114
114
|
const envPath = join(projectPath, '.env')
|
|
115
115
|
if (existsSync(envExamplePath)) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
writeFileSync(envPath,
|
|
116
|
+
let envContent = readFileSync(envExamplePath, 'utf-8')
|
|
117
|
+
// Set development mode
|
|
118
|
+
envContent = envContent.replace('NODE_ENV=production', 'NODE_ENV=development')
|
|
119
|
+
// Customize app name to match project name
|
|
120
|
+
envContent = envContent.replace('VITE_APP_NAME=FluxStack', `VITE_APP_NAME=${projectName}`)
|
|
121
|
+
writeFileSync(envPath, envContent)
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// Customize README.md
|
package/package-template.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "PROJECT_NAME",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "PROJECT_NAME -
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "PROJECT_NAME - Modern full-stack TypeScript application built with FluxStack",
|
|
5
5
|
"keywords": ["fluxstack", "bun", "typescript", "full-stack", "elysia", "react", "vite"],
|
|
6
6
|
"author": "Your Name",
|
|
7
7
|
"license": "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-fluxstack",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "⚡ Modern full-stack TypeScript framework with Elysia + React + Bun",
|
|
5
5
|
"keywords": ["framework", "full-stack", "typescript", "elysia", "react", "bun", "vite"],
|
|
6
6
|
"author": "FluxStack Team",
|