create-fluxstack 1.0.13 → 1.0.15

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.
Files changed (214) hide show
  1. package/.env.example +29 -29
  2. package/app/client/README.md +69 -69
  3. package/app/client/index.html +14 -13
  4. package/app/client/src/App.tsx +157 -524
  5. package/app/client/src/components/ErrorBoundary.tsx +107 -0
  6. package/app/client/src/components/ErrorDisplay.css +365 -0
  7. package/app/client/src/components/ErrorDisplay.tsx +258 -0
  8. package/app/client/src/components/FluxStackConfig.tsx +1321 -0
  9. package/app/client/src/components/HybridLiveCounter.tsx +140 -0
  10. package/app/client/src/components/LiveClock.tsx +286 -0
  11. package/app/client/src/components/MainLayout.tsx +390 -0
  12. package/app/client/src/components/SidebarNavigation.tsx +391 -0
  13. package/app/client/src/components/StateDemo.tsx +178 -0
  14. package/app/client/src/components/SystemMonitor.tsx +1038 -0
  15. package/app/client/src/components/Teste.tsx +104 -0
  16. package/app/client/src/components/UserProfile.tsx +809 -0
  17. package/app/client/src/hooks/useAuth.ts +39 -0
  18. package/app/client/src/hooks/useNotifications.ts +56 -0
  19. package/app/client/src/lib/eden-api.ts +189 -53
  20. package/app/client/src/lib/errors.ts +340 -0
  21. package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
  22. package/app/client/src/lib/index.ts +45 -0
  23. package/app/client/src/main.tsx +3 -2
  24. package/app/client/src/pages/ApiDocs.tsx +182 -0
  25. package/app/client/src/pages/Demo.tsx +174 -0
  26. package/app/client/src/pages/HybridLive.tsx +263 -0
  27. package/app/client/src/pages/Overview.tsx +155 -0
  28. package/app/client/src/store/README.md +43 -0
  29. package/app/client/src/store/index.ts +16 -0
  30. package/app/client/src/store/slices/uiSlice.ts +151 -0
  31. package/app/client/src/store/slices/userSlice.ts +161 -0
  32. package/app/client/src/test/README.md +257 -0
  33. package/app/client/src/test/setup.ts +70 -0
  34. package/app/client/src/test/types.ts +12 -0
  35. package/app/client/src/vite-env.d.ts +1 -1
  36. package/app/client/tsconfig.app.json +44 -43
  37. package/app/client/tsconfig.json +7 -7
  38. package/app/client/tsconfig.node.json +25 -25
  39. package/app/client/zustand-setup.md +65 -0
  40. package/app/server/controllers/users.controller.ts +68 -68
  41. package/app/server/index.ts +9 -1
  42. package/app/server/live/CounterComponent.ts +191 -0
  43. package/app/server/live/FluxStackConfig.ts +529 -0
  44. package/app/server/live/LiveClockComponent.ts +214 -0
  45. package/app/server/live/SidebarNavigation.ts +156 -0
  46. package/app/server/live/SystemMonitor.ts +594 -0
  47. package/app/server/live/SystemMonitorIntegration.ts +151 -0
  48. package/app/server/live/TesteComponent.ts +87 -0
  49. package/app/server/live/UserProfileComponent.ts +135 -0
  50. package/app/server/live/register-components.ts +28 -0
  51. package/app/server/middleware/auth.ts +136 -0
  52. package/app/server/middleware/errorHandling.ts +250 -0
  53. package/app/server/middleware/index.ts +10 -0
  54. package/app/server/middleware/rateLimit.ts +193 -0
  55. package/app/server/middleware/requestLogging.ts +215 -0
  56. package/app/server/middleware/validation.ts +270 -0
  57. package/app/server/routes/index.ts +14 -2
  58. package/app/server/routes/upload.ts +92 -0
  59. package/app/server/routes/users.routes.ts +2 -9
  60. package/app/server/services/NotificationService.ts +302 -0
  61. package/app/server/services/UserService.ts +222 -0
  62. package/app/server/services/index.ts +46 -0
  63. package/core/cli/commands/plugin-deps.ts +263 -0
  64. package/core/cli/generators/README.md +339 -0
  65. package/core/cli/generators/component.ts +770 -0
  66. package/core/cli/generators/controller.ts +299 -0
  67. package/core/cli/generators/index.ts +144 -0
  68. package/core/cli/generators/interactive.ts +228 -0
  69. package/core/cli/generators/prompts.ts +83 -0
  70. package/core/cli/generators/route.ts +513 -0
  71. package/core/cli/generators/service.ts +465 -0
  72. package/core/cli/generators/template-engine.ts +154 -0
  73. package/core/cli/generators/types.ts +71 -0
  74. package/core/cli/generators/utils.ts +192 -0
  75. package/core/cli/index.ts +69 -0
  76. package/core/cli/plugin-discovery.ts +16 -85
  77. package/core/client/fluxstack.ts +17 -0
  78. package/core/client/hooks/index.ts +7 -0
  79. package/core/client/hooks/state-validator.ts +130 -0
  80. package/core/client/hooks/useAuth.ts +49 -0
  81. package/core/client/hooks/useChunkedUpload.ts +258 -0
  82. package/core/client/hooks/useHybridLiveComponent.ts +967 -0
  83. package/core/client/hooks/useWebSocket.ts +373 -0
  84. package/core/client/index.ts +47 -0
  85. package/core/client/state/createStore.ts +193 -0
  86. package/core/client/state/index.ts +15 -0
  87. package/core/config/env-dynamic.ts +1 -1
  88. package/core/config/env.ts +2 -1
  89. package/core/config/runtime-config.ts +3 -3
  90. package/core/config/schema.ts +84 -49
  91. package/core/framework/server.ts +30 -0
  92. package/core/index.ts +25 -0
  93. package/core/live/ComponentRegistry.ts +399 -0
  94. package/core/live/types.ts +164 -0
  95. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
  96. package/core/plugins/built-in/live-components/index.ts +27 -0
  97. package/core/plugins/built-in/logger/index.ts +1 -1
  98. package/core/plugins/built-in/monitoring/index.ts +1 -1
  99. package/core/plugins/built-in/static/index.ts +1 -1
  100. package/core/plugins/built-in/swagger/index.ts +1 -1
  101. package/core/plugins/built-in/vite/index.ts +1 -1
  102. package/core/plugins/dependency-manager.ts +384 -0
  103. package/core/plugins/index.ts +5 -1
  104. package/core/plugins/manager.ts +7 -3
  105. package/core/plugins/registry.ts +88 -10
  106. package/core/plugins/types.ts +11 -11
  107. package/core/server/framework.ts +43 -0
  108. package/core/server/index.ts +11 -1
  109. package/core/server/live/ComponentRegistry.ts +1017 -0
  110. package/core/server/live/FileUploadManager.ts +272 -0
  111. package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
  112. package/core/server/live/SingleConnectionManager.ts +0 -0
  113. package/core/server/live/StateSignature.ts +644 -0
  114. package/core/server/live/WebSocketConnectionManager.ts +688 -0
  115. package/core/server/live/websocket-plugin.ts +435 -0
  116. package/core/server/middleware/errorHandling.ts +141 -0
  117. package/core/server/middleware/index.ts +16 -0
  118. package/core/server/plugins/static-files-plugin.ts +232 -0
  119. package/core/server/services/BaseService.ts +95 -0
  120. package/core/server/services/ServiceContainer.ts +144 -0
  121. package/core/server/services/index.ts +9 -0
  122. package/core/templates/create-project.ts +196 -33
  123. package/core/testing/index.ts +10 -0
  124. package/core/testing/setup.ts +74 -0
  125. package/core/types/build.ts +38 -14
  126. package/core/types/types.ts +319 -0
  127. package/core/utils/env-runtime.ts +7 -0
  128. package/core/utils/errors/handlers.ts +264 -39
  129. package/core/utils/errors/index.ts +528 -18
  130. package/core/utils/errors/middleware.ts +114 -0
  131. package/core/utils/logger/formatters.ts +222 -0
  132. package/core/utils/logger/index.ts +167 -48
  133. package/core/utils/logger/middleware.ts +253 -0
  134. package/core/utils/logger/performance.ts +384 -0
  135. package/core/utils/logger/transports.ts +365 -0
  136. package/create-fluxstack.ts +296 -296
  137. package/fluxstack.config.ts +17 -1
  138. package/package-template.json +66 -66
  139. package/package.json +31 -6
  140. package/public/README.md +16 -0
  141. package/vite.config.ts +29 -14
  142. package/.claude/settings.local.json +0 -74
  143. package/.github/workflows/ci-build-tests.yml +0 -480
  144. package/.github/workflows/dependency-management.yml +0 -324
  145. package/.github/workflows/release-validation.yml +0 -355
  146. package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
  147. package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
  148. package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
  149. package/CLAUDE.md +0 -200
  150. package/Dockerfile +0 -58
  151. package/Dockerfile.backend +0 -52
  152. package/Dockerfile.frontend +0 -54
  153. package/README-Docker.md +0 -85
  154. package/ai-context/00-QUICK-START.md +0 -86
  155. package/ai-context/README.md +0 -88
  156. package/ai-context/development/eden-treaty-guide.md +0 -362
  157. package/ai-context/development/patterns.md +0 -382
  158. package/ai-context/development/plugins-guide.md +0 -572
  159. package/ai-context/examples/crud-complete.md +0 -626
  160. package/ai-context/project/architecture.md +0 -399
  161. package/ai-context/project/overview.md +0 -213
  162. package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
  163. package/ai-context/recent-changes/type-inference-fix.md +0 -223
  164. package/ai-context/reference/environment-vars.md +0 -384
  165. package/ai-context/reference/troubleshooting.md +0 -407
  166. package/app/client/src/components/TestPage.tsx +0 -453
  167. package/bun.lock +0 -1063
  168. package/bunfig.toml +0 -16
  169. package/core/__tests__/integration.test.ts +0 -227
  170. package/core/build/index.ts +0 -186
  171. package/core/config/__tests__/config-loader.test.ts +0 -554
  172. package/core/config/__tests__/config-merger.test.ts +0 -657
  173. package/core/config/__tests__/env-converter.test.ts +0 -372
  174. package/core/config/__tests__/env-processor.test.ts +0 -431
  175. package/core/config/__tests__/env.test.ts +0 -452
  176. package/core/config/__tests__/integration.test.ts +0 -418
  177. package/core/config/__tests__/loader.test.ts +0 -331
  178. package/core/config/__tests__/schema.test.ts +0 -129
  179. package/core/config/__tests__/validator.test.ts +0 -318
  180. package/core/framework/__tests__/server.test.ts +0 -233
  181. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  182. package/core/plugins/__tests__/manager.test.ts +0 -398
  183. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  184. package/core/plugins/__tests__/registry.test.ts +0 -335
  185. package/core/utils/__tests__/errors.test.ts +0 -139
  186. package/core/utils/__tests__/helpers.test.ts +0 -297
  187. package/core/utils/__tests__/logger.test.ts +0 -141
  188. package/create-test-app.ts +0 -156
  189. package/docker-compose.microservices.yml +0 -75
  190. package/docker-compose.simple.yml +0 -57
  191. package/docker-compose.yml +0 -71
  192. package/eslint.config.js +0 -23
  193. package/flux-cli.ts +0 -214
  194. package/nginx-lb.conf +0 -37
  195. package/publish.sh +0 -63
  196. package/run-clean.ts +0 -26
  197. package/run-env-tests.ts +0 -313
  198. package/tailwind.config.js +0 -34
  199. package/tests/__mocks__/api.ts +0 -56
  200. package/tests/fixtures/users.ts +0 -69
  201. package/tests/integration/api/users.routes.test.ts +0 -221
  202. package/tests/setup.ts +0 -29
  203. package/tests/unit/app/client/App-simple.test.tsx +0 -56
  204. package/tests/unit/app/client/App.test.tsx.skip +0 -237
  205. package/tests/unit/app/client/eden-api.test.ts +0 -186
  206. package/tests/unit/app/client/simple.test.tsx +0 -23
  207. package/tests/unit/app/controllers/users.controller.test.ts +0 -150
  208. package/tests/unit/core/create-project.test.ts.skip +0 -95
  209. package/tests/unit/core/framework.test.ts +0 -144
  210. package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
  211. package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
  212. package/tests/utils/test-helpers.ts +0 -61
  213. package/vitest.config.ts +0 -50
  214. package/workspace.json +0 -6
@@ -0,0 +1,232 @@
1
+ // 🔥 FluxStack Static Files Plugin - Serve Public Files
2
+
3
+ import { existsSync, statSync } from 'fs'
4
+ import { join, extname, resolve } from 'path'
5
+ import type { Plugin, PluginContext } from '../plugins/types'
6
+
7
+ export interface StaticFilesConfig {
8
+ publicDir?: string // Default: 'public'
9
+ uploadsDir?: string // Default: 'uploads'
10
+ cacheMaxAge?: number // Default: 1 year in seconds
11
+ enableUploads?: boolean // Default: true
12
+ enablePublic?: boolean // Default: true
13
+ publicRoute?: string // Default: '/public' (can be '/static' in dev)
14
+ uploadsRoute?: string // Default: '/uploads'
15
+ }
16
+
17
+ export const staticFilesPlugin: Plugin = {
18
+ name: 'static-files',
19
+ version: '1.0.0',
20
+ description: 'Serve static files and uploads with proper caching and security',
21
+ author: 'FluxStack Team',
22
+ priority: 'normal',
23
+ category: 'core',
24
+ tags: ['static', 'files', 'uploads', 'public'],
25
+
26
+ setup: async (context: PluginContext) => {
27
+ context.logger.info('📁 Setting up Static Files plugin...')
28
+
29
+ const config: StaticFilesConfig = {
30
+ publicDir: 'public',
31
+ uploadsDir: 'uploads',
32
+ cacheMaxAge: 31536000, // 1 year
33
+ enableUploads: true,
34
+ enablePublic: true,
35
+ publicRoute: '/api/static', // Use /api/static in dev to avoid Vite conflicts
36
+ uploadsRoute: '/api/uploads',
37
+ ...context.config.staticFiles
38
+ }
39
+
40
+ const projectRoot = process.cwd()
41
+ const publicPath = resolve(projectRoot, config.publicDir!)
42
+ const uploadsPath = resolve(projectRoot, config.uploadsDir!)
43
+
44
+ // MIME types mapping
45
+ const getMimeType = (extension: string): string => {
46
+ const mimeTypes: Record<string, string> = {
47
+ // Images
48
+ '.jpg': 'image/jpeg',
49
+ '.jpeg': 'image/jpeg',
50
+ '.png': 'image/png',
51
+ '.gif': 'image/gif',
52
+ '.webp': 'image/webp',
53
+ '.svg': 'image/svg+xml',
54
+ '.ico': 'image/x-icon',
55
+
56
+ // Documents
57
+ '.pdf': 'application/pdf',
58
+ '.txt': 'text/plain',
59
+ '.json': 'application/json',
60
+ '.xml': 'application/xml',
61
+
62
+ // Web assets
63
+ '.css': 'text/css',
64
+ '.js': 'application/javascript',
65
+ '.html': 'text/html',
66
+ '.htm': 'text/html',
67
+
68
+ // Fonts
69
+ '.woff': 'font/woff',
70
+ '.woff2': 'font/woff2',
71
+ '.ttf': 'font/ttf',
72
+ '.otf': 'font/otf',
73
+
74
+ // Audio/Video
75
+ '.mp3': 'audio/mpeg',
76
+ '.mp4': 'video/mp4',
77
+ '.webm': 'video/webm',
78
+ '.ogg': 'audio/ogg'
79
+ }
80
+
81
+ return mimeTypes[extension.toLowerCase()] || 'application/octet-stream'
82
+ }
83
+
84
+ // Security check for path traversal
85
+ const isPathSafe = (filePath: string, basePath: string): boolean => {
86
+ const resolvedPath = resolve(basePath, filePath)
87
+ return resolvedPath.startsWith(basePath)
88
+ }
89
+
90
+ // Generic file serving function
91
+ const serveFile = async (filePath: string, set: any) => {
92
+ try {
93
+ if (!existsSync(filePath)) {
94
+ set.status = 404
95
+ return {
96
+ error: 'File not found',
97
+ path: filePath.replace(projectRoot, ''),
98
+ timestamp: new Date().toISOString()
99
+ }
100
+ }
101
+
102
+ const stats = statSync(filePath)
103
+ if (!stats.isFile()) {
104
+ set.status = 404
105
+ return { error: 'Not a file' }
106
+ }
107
+
108
+ // Set appropriate headers
109
+ const extension = extname(filePath).toLowerCase()
110
+ const mimeType = getMimeType(extension)
111
+
112
+ set.headers['content-type'] = mimeType
113
+ set.headers['content-length'] = stats.size.toString()
114
+ set.headers['last-modified'] = stats.mtime.toUTCString()
115
+ set.headers['cache-control'] = `public, max-age=${config.cacheMaxAge}`
116
+ set.headers['etag'] = `"${stats.mtime.getTime()}-${stats.size}"`
117
+
118
+ // Security headers for images
119
+ if (mimeType.startsWith('image/')) {
120
+ set.headers['x-content-type-options'] = 'nosniff'
121
+ }
122
+
123
+ context.logger.debug(`📁 Serving file: ${filePath.replace(projectRoot, '')}`, {
124
+ size: stats.size,
125
+ mimeType,
126
+ lastModified: stats.mtime
127
+ })
128
+
129
+ return Bun.file(filePath)
130
+
131
+ } catch (error: any) {
132
+ context.logger.error('❌ File serving error:', error.message)
133
+ set.status = 500
134
+ return { error: 'Failed to serve file' }
135
+ }
136
+ }
137
+
138
+ // Add static file routes
139
+ if (config.enablePublic) {
140
+ const publicRoutePattern = `${config.publicRoute}/*`
141
+ context.app.get(publicRoutePattern, ({ params, set }) => {
142
+ const filePath = params['*'] || ''
143
+
144
+ if (!isPathSafe(filePath, publicPath)) {
145
+ set.status = 400
146
+ return { error: 'Invalid file path' }
147
+ }
148
+
149
+ const fullPath = join(publicPath, filePath)
150
+ return serveFile(fullPath, set)
151
+ })
152
+
153
+ context.logger.info(`📁 Public files route enabled: ${publicRoutePattern} → ${config.publicDir}`)
154
+ }
155
+
156
+ if (config.enableUploads) {
157
+ const uploadsRoutePattern = `${config.uploadsRoute}/*`
158
+ context.app.get(uploadsRoutePattern, ({ params, set }) => {
159
+ const filePath = params['*'] || ''
160
+
161
+ if (!isPathSafe(filePath, uploadsPath)) {
162
+ set.status = 400
163
+ return { error: 'Invalid file path' }
164
+ }
165
+
166
+ const fullPath = join(uploadsPath, filePath)
167
+ return serveFile(fullPath, set)
168
+ })
169
+
170
+ context.logger.info(`📁 Uploads route enabled: ${uploadsRoutePattern} → ${config.uploadsDir}`)
171
+ }
172
+
173
+ // Static files info endpoint
174
+ context.app.get('/api/static/info', () => {
175
+ return {
176
+ success: true,
177
+ config: {
178
+ publicDir: config.publicDir,
179
+ uploadsDir: config.uploadsDir,
180
+ enablePublic: config.enablePublic,
181
+ enableUploads: config.enableUploads,
182
+ cacheMaxAge: config.cacheMaxAge
183
+ },
184
+ paths: {
185
+ publicPath,
186
+ uploadsPath,
187
+ publicUrl: config.publicRoute,
188
+ uploadsUrl: config.uploadsRoute
189
+ },
190
+ timestamp: new Date().toISOString()
191
+ }
192
+ })
193
+
194
+ // Create directories if they don't exist
195
+ const { mkdir } = await import('fs/promises')
196
+
197
+ if (config.enablePublic && !existsSync(publicPath)) {
198
+ await mkdir(publicPath, { recursive: true })
199
+ context.logger.info(`📁 Created public directory: ${publicPath}`)
200
+ }
201
+
202
+ if (config.enableUploads && !existsSync(uploadsPath)) {
203
+ await mkdir(uploadsPath, { recursive: true })
204
+ await mkdir(join(uploadsPath, 'avatars'), { recursive: true })
205
+ context.logger.info(`📁 Created uploads directory: ${uploadsPath}`)
206
+ }
207
+
208
+ context.logger.info('📁 Static Files plugin setup complete', {
209
+ publicEnabled: config.enablePublic,
210
+ uploadsEnabled: config.enableUploads,
211
+ publicPath: config.enablePublic ? publicPath : 'disabled',
212
+ uploadsPath: config.enableUploads ? uploadsPath : 'disabled'
213
+ })
214
+ },
215
+
216
+ onServerStart: async (context: PluginContext) => {
217
+ const config = {
218
+ enablePublic: true,
219
+ enableUploads: true,
220
+ publicRoute: '/api/static',
221
+ uploadsRoute: '/api/uploads',
222
+ ...context.config.staticFiles
223
+ }
224
+ context.logger.info('📁 Static Files plugin ready', {
225
+ routes: [
226
+ config.enablePublic ? `${config.publicRoute}/*` : null,
227
+ config.enableUploads ? `${config.uploadsRoute}/*` : null,
228
+ '/api/static/info'
229
+ ].filter(Boolean)
230
+ })
231
+ }
232
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Base Service Class
3
+ * Core FluxStack service infrastructure
4
+ *
5
+ * Provides common functionality for all services including:
6
+ * - Logging with service context
7
+ * - Configuration access
8
+ * - Service container integration
9
+ */
10
+
11
+ import type { Logger } from '../../utils/logger/index.js'
12
+
13
+ export interface ServiceContext {
14
+ config: any
15
+ logger: Logger
16
+ services?: ServiceContainer
17
+ }
18
+
19
+ export interface ServiceContainer {
20
+ get<T>(name: string): T
21
+ register<T>(name: string, service: T): void
22
+ }
23
+
24
+ export abstract class BaseService {
25
+ protected config: any
26
+ protected logger: Logger
27
+ protected services?: ServiceContainer
28
+
29
+ constructor(context: ServiceContext) {
30
+ this.config = context.config
31
+ this.logger = context.logger.child({ service: this.constructor.name })
32
+ this.services = context.services
33
+ }
34
+
35
+ /**
36
+ * Get service from container
37
+ */
38
+ protected getService<T>(name: string): T {
39
+ if (!this.services) {
40
+ throw new Error('Service container not available')
41
+ }
42
+ return this.services.get<T>(name)
43
+ }
44
+
45
+ /**
46
+ * Log service operation
47
+ */
48
+ protected logOperation(operation: string, data?: any) {
49
+ this.logger.info(`${operation}`, data)
50
+ }
51
+
52
+ /**
53
+ * Log service error
54
+ */
55
+ protected logError(operation: string, error: Error, data?: any) {
56
+ this.logger.error(`${operation} failed`, { error: error.message, data })
57
+ }
58
+
59
+ /**
60
+ * Initialize service (override in subclasses)
61
+ */
62
+ async initialize(): Promise<void> {
63
+ this.logger.info('Service initialized')
64
+ }
65
+
66
+ /**
67
+ * Execute operation with logging
68
+ */
69
+ protected async executeWithLogging<T>(
70
+ operation: string,
71
+ fn: () => Promise<T> | T,
72
+ metadata?: any
73
+ ): Promise<T> {
74
+ this.logOperation(`Starting ${operation}`, metadata)
75
+ try {
76
+ const result = await fn()
77
+ this.logOperation(`Completed ${operation}`, metadata)
78
+ return result
79
+ } catch (error) {
80
+ this.logError(operation, error as Error, metadata)
81
+ throw error
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Validate required fields
87
+ */
88
+ protected validateRequired(data: any, fields: string[]): void {
89
+ for (const field of fields) {
90
+ if (!data[field]) {
91
+ throw new Error(`Missing required field: ${field}`)
92
+ }
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Service Container
3
+ * Core FluxStack dependency injection container
4
+ *
5
+ * Provides service registration, resolution, and lifecycle management
6
+ */
7
+
8
+ import type { Logger } from '../../utils/logger/index.js'
9
+
10
+ export interface ServiceDefinition {
11
+ factory: (container: ServiceContainer) => any
12
+ singleton?: boolean
13
+ dependencies?: string[]
14
+ }
15
+
16
+ export class ServiceContainer {
17
+ private services = new Map<string, any>()
18
+ private definitions = new Map<string, ServiceDefinition>()
19
+ private singletons = new Map<string, any>()
20
+ private logger: Logger
21
+
22
+ constructor(logger: Logger) {
23
+ this.logger = logger.child({ component: 'ServiceContainer' })
24
+ }
25
+
26
+ /**
27
+ * Register a service definition
28
+ */
29
+ register<T>(name: string, definition: ServiceDefinition | T): void {
30
+ if (typeof definition === 'object' && definition !== null && 'factory' in definition) {
31
+ this.definitions.set(name, definition as ServiceDefinition)
32
+ this.logger.debug(`Registered service definition: ${name}`)
33
+ } else {
34
+ this.services.set(name, definition)
35
+ this.logger.debug(`Registered service instance: ${name}`)
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Get a service instance
41
+ */
42
+ get<T>(name: string): T {
43
+ // Check for direct service instance
44
+ if (this.services.has(name)) {
45
+ return this.services.get(name) as T
46
+ }
47
+
48
+ // Check for singleton instance
49
+ if (this.singletons.has(name)) {
50
+ return this.singletons.get(name) as T
51
+ }
52
+
53
+ // Check for service definition
54
+ const definition = this.definitions.get(name)
55
+ if (!definition) {
56
+ throw new Error(`Service not found: ${name}`)
57
+ }
58
+
59
+ // Resolve dependencies first
60
+ if (definition.dependencies) {
61
+ for (const dep of definition.dependencies) {
62
+ if (!this.has(dep)) {
63
+ throw new Error(`Dependency not found: ${dep} (required by ${name})`)
64
+ }
65
+ }
66
+ }
67
+
68
+ // Create service instance
69
+ const instance = definition.factory(this)
70
+
71
+ // Store singleton if needed
72
+ if (definition.singleton) {
73
+ this.singletons.set(name, instance)
74
+ }
75
+
76
+ this.logger.debug(`Created service instance: ${name}`)
77
+ return instance as T
78
+ }
79
+
80
+ /**
81
+ * Check if service exists
82
+ */
83
+ has(name: string): boolean {
84
+ return this.services.has(name) ||
85
+ this.definitions.has(name) ||
86
+ this.singletons.has(name)
87
+ }
88
+
89
+ /**
90
+ * Remove a service
91
+ */
92
+ remove(name: string): void {
93
+ this.services.delete(name)
94
+ this.definitions.delete(name)
95
+ this.singletons.delete(name)
96
+ this.logger.debug(`Removed service: ${name}`)
97
+ }
98
+
99
+ /**
100
+ * Get all registered service names
101
+ */
102
+ getServiceNames(): string[] {
103
+ const names = new Set<string>()
104
+
105
+ // Use Array.from to avoid iterator issues
106
+ Array.from(this.services.keys()).forEach(name => names.add(name))
107
+ Array.from(this.definitions.keys()).forEach(name => names.add(name))
108
+ Array.from(this.singletons.keys()).forEach(name => names.add(name))
109
+
110
+ return Array.from(names)
111
+ }
112
+
113
+ /**
114
+ * Register multiple services at once
115
+ */
116
+ registerMany(services: Array<{
117
+ name: string
118
+ constructor: any
119
+ dependencies?: string[]
120
+ singleton?: boolean
121
+ }>): void {
122
+ for (const service of services) {
123
+ this.register(service.name, {
124
+ factory: (container) => new service.constructor({
125
+ config: {},
126
+ logger: this.logger,
127
+ services: container
128
+ }),
129
+ dependencies: service.dependencies,
130
+ singleton: service.singleton
131
+ })
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Clear all services
137
+ */
138
+ clear(): void {
139
+ this.services.clear()
140
+ this.definitions.clear()
141
+ this.singletons.clear()
142
+ this.logger.debug('Cleared all services')
143
+ }
144
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Core Server Services
3
+ * FluxStack service infrastructure exports
4
+ */
5
+
6
+ export { BaseService } from './BaseService.js'
7
+ export { ServiceContainer } from './ServiceContainer.js'
8
+ export type { ServiceContext, ServiceContainer as IServiceContainer } from './BaseService.js'
9
+ export type { ServiceDefinition } from './ServiceContainer.js'