create-fluxstack 1.10.1 → 1.12.0

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 (257) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LLMD/INDEX.md +64 -0
  4. package/LLMD/MAINTENANCE.md +197 -0
  5. package/LLMD/MIGRATION.md +156 -0
  6. package/LLMD/config/.gitkeep +1 -0
  7. package/LLMD/config/declarative-system.md +268 -0
  8. package/LLMD/config/environment-vars.md +327 -0
  9. package/LLMD/config/runtime-reload.md +401 -0
  10. package/LLMD/core/.gitkeep +1 -0
  11. package/LLMD/core/build-system.md +599 -0
  12. package/LLMD/core/framework-lifecycle.md +229 -0
  13. package/LLMD/core/plugin-system.md +451 -0
  14. package/LLMD/patterns/.gitkeep +1 -0
  15. package/LLMD/patterns/anti-patterns.md +297 -0
  16. package/LLMD/patterns/project-structure.md +264 -0
  17. package/LLMD/patterns/type-safety.md +440 -0
  18. package/LLMD/reference/.gitkeep +1 -0
  19. package/LLMD/reference/cli-commands.md +250 -0
  20. package/LLMD/reference/plugin-hooks.md +357 -0
  21. package/LLMD/reference/routing.md +39 -0
  22. package/LLMD/reference/troubleshooting.md +364 -0
  23. package/LLMD/resources/.gitkeep +1 -0
  24. package/LLMD/resources/controllers.md +465 -0
  25. package/LLMD/resources/live-components.md +703 -0
  26. package/LLMD/resources/live-rooms.md +482 -0
  27. package/LLMD/resources/live-upload.md +130 -0
  28. package/LLMD/resources/plugins-external.md +617 -0
  29. package/LLMD/resources/routes-eden.md +254 -0
  30. package/README.md +37 -17
  31. package/app/client/index.html +0 -1
  32. package/app/client/src/App.tsx +107 -150
  33. package/app/client/src/components/AppLayout.tsx +68 -0
  34. package/app/client/src/components/BackButton.tsx +13 -0
  35. package/app/client/src/components/DemoPage.tsx +20 -0
  36. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  37. package/app/client/src/lib/eden-api.ts +85 -60
  38. package/app/client/src/live/ChatDemo.tsx +107 -0
  39. package/app/client/src/live/CounterDemo.tsx +206 -0
  40. package/app/client/src/live/FormDemo.tsx +119 -0
  41. package/app/client/src/live/RoomChatDemo.tsx +242 -0
  42. package/app/client/src/live/UploadDemo.tsx +21 -0
  43. package/app/client/src/main.tsx +4 -1
  44. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  45. package/app/client/src/pages/HomePage.tsx +76 -0
  46. package/app/server/app.ts +1 -4
  47. package/app/server/controllers/users.controller.ts +36 -44
  48. package/app/server/index.ts +25 -35
  49. package/app/server/live/LiveChat.ts +77 -0
  50. package/app/server/live/LiveCounter.ts +67 -0
  51. package/app/server/live/LiveForm.ts +63 -0
  52. package/app/server/live/LiveLocalCounter.ts +32 -0
  53. package/app/server/live/LiveRoomChat.ts +285 -0
  54. package/app/server/live/LiveUpload.ts +81 -0
  55. package/app/server/routes/index.ts +3 -1
  56. package/app/server/routes/room.routes.ts +117 -0
  57. package/app/server/routes/users.routes.ts +35 -27
  58. package/app/shared/types/index.ts +14 -2
  59. package/config/app.config.ts +2 -62
  60. package/config/client.config.ts +2 -95
  61. package/config/database.config.ts +2 -99
  62. package/config/fluxstack.config.ts +25 -45
  63. package/config/index.ts +57 -38
  64. package/config/monitoring.config.ts +2 -114
  65. package/config/plugins.config.ts +2 -80
  66. package/config/server.config.ts +2 -68
  67. package/config/services.config.ts +2 -130
  68. package/config/system/app.config.ts +29 -0
  69. package/config/system/build.config.ts +49 -0
  70. package/config/system/client.config.ts +68 -0
  71. package/config/system/database.config.ts +17 -0
  72. package/config/system/fluxstack.config.ts +114 -0
  73. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  74. package/config/system/monitoring.config.ts +114 -0
  75. package/config/system/plugins.config.ts +84 -0
  76. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  77. package/config/system/server.config.ts +68 -0
  78. package/config/system/services.config.ts +46 -0
  79. package/config/{system.config.ts → system/system.config.ts} +1 -1
  80. package/core/build/flux-plugins-generator.ts +325 -325
  81. package/core/build/index.ts +39 -27
  82. package/core/build/live-components-generator.ts +3 -3
  83. package/core/build/optimizer.ts +235 -235
  84. package/core/cli/command-registry.ts +6 -4
  85. package/core/cli/commands/build.ts +79 -0
  86. package/core/cli/commands/create.ts +54 -0
  87. package/core/cli/commands/dev.ts +101 -0
  88. package/core/cli/commands/help.ts +34 -0
  89. package/core/cli/commands/index.ts +34 -0
  90. package/core/cli/commands/make-plugin.ts +90 -0
  91. package/core/cli/commands/plugin-add.ts +197 -0
  92. package/core/cli/commands/plugin-deps.ts +2 -2
  93. package/core/cli/commands/plugin-list.ts +208 -0
  94. package/core/cli/commands/plugin-remove.ts +170 -0
  95. package/core/cli/generators/component.ts +769 -769
  96. package/core/cli/generators/controller.ts +1 -1
  97. package/core/cli/generators/index.ts +146 -146
  98. package/core/cli/generators/interactive.ts +227 -227
  99. package/core/cli/generators/plugin.ts +2 -2
  100. package/core/cli/generators/prompts.ts +82 -82
  101. package/core/cli/generators/route.ts +6 -6
  102. package/core/cli/generators/service.ts +2 -2
  103. package/core/cli/generators/template-engine.ts +4 -3
  104. package/core/cli/generators/types.ts +2 -2
  105. package/core/cli/generators/utils.ts +191 -191
  106. package/core/cli/index.ts +115 -686
  107. package/core/cli/plugin-discovery.ts +2 -2
  108. package/core/client/LiveComponentsProvider.tsx +60 -8
  109. package/core/client/api/eden.ts +183 -0
  110. package/core/client/api/index.ts +11 -0
  111. package/core/client/components/Live.tsx +104 -0
  112. package/core/client/fluxstack.ts +1 -9
  113. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  114. package/core/client/hooks/state-validator.ts +1 -1
  115. package/core/client/hooks/useAuth.ts +48 -48
  116. package/core/client/hooks/useChunkedUpload.ts +85 -35
  117. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  118. package/core/client/hooks/useLiveComponent.ts +800 -0
  119. package/core/client/hooks/useLiveUpload.ts +71 -0
  120. package/core/client/hooks/useRoom.ts +409 -0
  121. package/core/client/hooks/useRoomProxy.ts +382 -0
  122. package/core/client/index.ts +17 -68
  123. package/core/client/standalone-entry.ts +8 -0
  124. package/core/client/standalone.ts +74 -53
  125. package/core/client/state/createStore.ts +192 -192
  126. package/core/client/state/index.ts +14 -14
  127. package/core/config/index.ts +70 -291
  128. package/core/config/schema.ts +42 -723
  129. package/core/framework/client.ts +131 -131
  130. package/core/framework/index.ts +7 -7
  131. package/core/framework/server.ts +47 -40
  132. package/core/framework/types.ts +2 -2
  133. package/core/index.ts +23 -4
  134. package/core/live/ComponentRegistry.ts +3 -3
  135. package/core/live/types.ts +77 -0
  136. package/core/plugins/built-in/index.ts +134 -134
  137. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
  138. package/core/plugins/built-in/live-components/index.ts +1 -1
  139. package/core/plugins/built-in/monitoring/index.ts +111 -47
  140. package/core/plugins/built-in/static/index.ts +1 -1
  141. package/core/plugins/built-in/swagger/index.ts +68 -265
  142. package/core/plugins/built-in/vite/index.ts +85 -185
  143. package/core/plugins/built-in/vite/vite-dev.ts +10 -16
  144. package/core/plugins/config.ts +9 -7
  145. package/core/plugins/dependency-manager.ts +31 -1
  146. package/core/plugins/discovery.ts +19 -7
  147. package/core/plugins/executor.ts +2 -2
  148. package/core/plugins/index.ts +203 -203
  149. package/core/plugins/manager.ts +27 -39
  150. package/core/plugins/module-resolver.ts +19 -8
  151. package/core/plugins/registry.ts +255 -19
  152. package/core/plugins/types.ts +20 -53
  153. package/core/server/framework.ts +66 -43
  154. package/core/server/index.ts +15 -15
  155. package/core/server/live/ComponentRegistry.ts +78 -71
  156. package/core/server/live/FileUploadManager.ts +23 -10
  157. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  158. package/core/server/live/LiveRoomManager.ts +261 -0
  159. package/core/server/live/RoomEventBus.ts +234 -0
  160. package/core/server/live/RoomStateManager.ts +172 -0
  161. package/core/server/live/StateSignature.ts +643 -643
  162. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  163. package/core/server/live/auto-generated-components.ts +21 -9
  164. package/core/server/live/index.ts +14 -0
  165. package/core/server/live/websocket-plugin.ts +214 -67
  166. package/core/server/middleware/elysia-helpers.ts +7 -2
  167. package/core/server/middleware/errorHandling.ts +1 -1
  168. package/core/server/middleware/index.ts +31 -31
  169. package/core/server/plugins/database.ts +180 -180
  170. package/core/server/plugins/static-files-plugin.ts +69 -69
  171. package/core/server/plugins/swagger.ts +1 -1
  172. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  173. package/core/server/rooms/RoomSystem.ts +463 -0
  174. package/core/server/rooms/index.ts +13 -0
  175. package/core/server/services/BaseService.ts +1 -1
  176. package/core/server/services/ServiceContainer.ts +1 -1
  177. package/core/server/services/index.ts +8 -8
  178. package/core/templates/create-project.ts +12 -12
  179. package/core/testing/index.ts +9 -9
  180. package/core/testing/setup.ts +73 -73
  181. package/core/types/api.ts +168 -168
  182. package/core/types/build.ts +219 -219
  183. package/core/types/config.ts +56 -26
  184. package/core/types/index.ts +4 -4
  185. package/core/types/plugin.ts +107 -107
  186. package/core/types/types.ts +353 -14
  187. package/core/utils/build-logger.ts +324 -324
  188. package/core/utils/config-schema.ts +480 -480
  189. package/core/utils/env.ts +2 -8
  190. package/core/utils/errors/codes.ts +114 -114
  191. package/core/utils/errors/handlers.ts +36 -1
  192. package/core/utils/errors/index.ts +49 -5
  193. package/core/utils/errors/middleware.ts +113 -113
  194. package/core/utils/helpers.ts +6 -16
  195. package/core/utils/index.ts +17 -17
  196. package/core/utils/logger/colors.ts +114 -114
  197. package/core/utils/logger/config.ts +13 -9
  198. package/core/utils/logger/formatter.ts +82 -82
  199. package/core/utils/logger/group-logger.ts +101 -101
  200. package/core/utils/logger/index.ts +6 -1
  201. package/core/utils/logger/stack-trace.ts +3 -1
  202. package/core/utils/logger/startup-banner.ts +82 -82
  203. package/core/utils/logger/winston-logger.ts +152 -152
  204. package/core/utils/monitoring/index.ts +211 -211
  205. package/core/utils/sync-version.ts +66 -66
  206. package/core/utils/version.ts +1 -1
  207. package/create-fluxstack.ts +8 -7
  208. package/package.json +12 -13
  209. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  210. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  211. package/plugins/crypto-auth/client/components/index.ts +11 -11
  212. package/plugins/crypto-auth/client/index.ts +11 -11
  213. package/plugins/crypto-auth/config/index.ts +1 -1
  214. package/plugins/crypto-auth/index.ts +4 -4
  215. package/plugins/crypto-auth/package.json +65 -65
  216. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  217. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  218. package/plugins/crypto-auth/server/index.ts +21 -21
  219. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  220. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  221. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  222. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  223. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  224. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  225. package/tsconfig.api-strict.json +16 -0
  226. package/tsconfig.json +48 -52
  227. package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
  228. package/types/global.d.ts +29 -29
  229. package/types/vitest.d.ts +8 -8
  230. package/vite.config.ts +38 -62
  231. package/vitest.config.live.ts +10 -9
  232. package/vitest.config.ts +29 -17
  233. package/app/client/README.md +0 -69
  234. package/app/client/SIMPLIFICATION.md +0 -140
  235. package/app/client/frontend-only.ts +0 -12
  236. package/app/client/src/live/FileUploadExample.tsx +0 -359
  237. package/app/client/src/live/MinimalLiveClock.tsx +0 -47
  238. package/app/client/src/live/QuickUploadTest.tsx +0 -193
  239. package/app/client/tsconfig.app.json +0 -45
  240. package/app/client/tsconfig.json +0 -7
  241. package/app/client/zustand-setup.md +0 -65
  242. package/app/server/backend-only.ts +0 -18
  243. package/app/server/live/LiveClockComponent.ts +0 -215
  244. package/app/server/live/LiveFileUploadComponent.ts +0 -77
  245. package/app/server/routes/env-test.ts +0 -110
  246. package/core/client/hooks/index.ts +0 -7
  247. package/core/client/hooks/useHybridLiveComponent.ts +0 -685
  248. package/core/client/hooks/useTypedLiveComponent.ts +0 -133
  249. package/core/client/hooks/useWebSocket.ts +0 -361
  250. package/core/config/env.ts +0 -546
  251. package/core/config/loader.ts +0 -522
  252. package/core/config/runtime-config.ts +0 -327
  253. package/core/config/validator.ts +0 -540
  254. package/core/server/backend-entry.ts +0 -51
  255. package/core/server/standalone.ts +0 -106
  256. package/core/utils/regenerate-files.ts +0 -69
  257. package/fluxstack.config.ts +0 -354
@@ -54,8 +54,8 @@ export class ServiceGenerator implements Generator {
54
54
  path: 'app/server/services/{{kebabName}}.service.ts',
55
55
  content: `import { {{pascalName}}, Create{{pascalName}}, Update{{pascalName}} } from '../schemas/{{kebabName}}.schema'
56
56
  import { {{pascalName}}Repository } from '../repositories/{{kebabName}}.repository'
57
- import { ValidationError, NotFoundError } from '@/core/utils/errors'
58
- import { logger } from '@/core/utils/logger'
57
+ import { ValidationError, NotFoundError } from '@core/utils/errors'
58
+ import { logger } from '@core/utils/logger'
59
59
 
60
60
  export class {{pascalName}}Service {
61
61
  private repository: {{pascalName}}Repository
@@ -77,7 +77,9 @@ export class TemplateEngine {
77
77
  options: GeneratorOptions
78
78
  ): Promise<Record<string, any>> {
79
79
  const variables: Record<string, any> = {
80
- // Built-in variables
80
+ // Spread options first so built-in variables take precedence
81
+ ...options,
82
+ // Built-in variables (override any same-named keys from options)
81
83
  name: options.name,
82
84
  Name: this.capitalize(options.name),
83
85
  NAME: options.name.toUpperCase(),
@@ -90,8 +92,7 @@ export class TemplateEngine {
90
92
  date: new Date().toLocaleDateString(),
91
93
  year: new Date().getFullYear(),
92
94
  author: 'FluxStack Developer',
93
- projectName: context.config.app?.name || 'fluxstack-app',
94
- ...options
95
+ projectName: context.config.app?.name || 'fluxstack-app'
95
96
  }
96
97
 
97
98
  // Process template-specific variables
@@ -1,5 +1,5 @@
1
- import type { FluxStackConfig } from "../../config/schema"
2
- import type { Logger } from "@/core/utils/logger/index"
1
+ import type { FluxStackConfig } from "@config"
2
+ import type { Logger } from "@core/utils/logger/index"
3
3
  import type { PluginUtils } from "../../plugins/types"
4
4
 
5
5
  export interface GeneratorContext {
@@ -1,192 +1,192 @@
1
- import { existsSync } from "fs"
2
- import { join } from "path"
3
-
4
- export class GeneratorUtils {
5
- static validateName(name: string): { valid: boolean; error?: string } {
6
- if (!name || typeof name !== 'string') {
7
- return { valid: false, error: 'Name is required' }
8
- }
9
-
10
- const trimmed = name.trim()
11
- if (trimmed.length === 0) {
12
- return { valid: false, error: 'Name cannot be empty' }
13
- }
14
-
15
- if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(trimmed)) {
16
- return { valid: false, error: 'Name must start with a letter and contain only letters, numbers, hyphens, and underscores' }
17
- }
18
-
19
- if (trimmed.length > 100) {
20
- return { valid: false, error: 'Name must be less than 100 characters' }
21
- }
22
-
23
- return { valid: true }
24
- }
25
-
26
- static validatePath(path: string): { valid: boolean; error?: string } {
27
- if (!path || typeof path !== 'string') {
28
- return { valid: true } // Path is optional
29
- }
30
-
31
- const trimmed = path.trim()
32
- if (trimmed.length === 0) {
33
- return { valid: true }
34
- }
35
-
36
- // Check for invalid characters
37
- if (/[<>:"|?*]/.test(trimmed)) {
38
- return { valid: false, error: 'Path contains invalid characters' }
39
- }
40
-
41
- // Check for absolute paths (should be relative)
42
- if (trimmed.startsWith('/') || /^[a-zA-Z]:/.test(trimmed)) {
43
- return { valid: false, error: 'Path should be relative to project root' }
44
- }
45
-
46
- return { valid: true }
47
- }
48
-
49
- static checkFileExists(workingDir: string, filePath: string): boolean {
50
- const fullPath = join(workingDir, filePath)
51
- return existsSync(fullPath)
52
- }
53
-
54
- static getDefaultPath(type: string, name: string): string {
55
- const kebabName = this.toKebabCase(name)
56
- const pascalName = this.toPascalCase(name)
57
-
58
- switch (type) {
59
- case 'controller':
60
- return `app/server/controllers/${kebabName}.controller.ts`
61
- case 'service':
62
- return `app/server/services/${kebabName}.service.ts`
63
- case 'route':
64
- return `app/server/routes/${kebabName}.routes.ts`
65
- case 'component':
66
- return `app/client/src/components/${pascalName}/${pascalName}.tsx`
67
- default:
68
- return `${kebabName}.ts`
69
- }
70
- }
71
-
72
- static getRelatedFiles(type: string, name: string): string[] {
73
- const kebabName = this.toKebabCase(name)
74
- const pascalName = this.toPascalCase(name)
75
-
76
- switch (type) {
77
- case 'controller':
78
- return [
79
- `app/server/services/${kebabName}.service.ts`,
80
- `app/server/schemas/${kebabName}.schema.ts`,
81
- `app/server/routes/${kebabName}.routes.ts`
82
- ]
83
- case 'service':
84
- return [
85
- `app/server/repositories/${kebabName}.repository.ts`,
86
- `app/server/controllers/${kebabName}.controller.ts`
87
- ]
88
- case 'component':
89
- return [
90
- `app/client/src/components/${pascalName}/${pascalName}.css`,
91
- `app/client/src/components/${pascalName}/index.ts`,
92
- `app/client/src/components/${pascalName}/${pascalName}.test.tsx`,
93
- `app/client/src/components/${pascalName}/${pascalName}.stories.tsx`
94
- ]
95
- case 'route':
96
- return [
97
- `app/server/controllers/${kebabName}.controller.ts`,
98
- `app/server/middleware/${kebabName}.middleware.ts`
99
- ]
100
- default:
101
- return []
102
- }
103
- }
104
-
105
- static formatFileSize(bytes: number): string {
106
- const units = ['B', 'KB', 'MB', 'GB']
107
- let size = bytes
108
- let unitIndex = 0
109
-
110
- while (size >= 1024 && unitIndex < units.length - 1) {
111
- size /= 1024
112
- unitIndex++
113
- }
114
-
115
- return `${size.toFixed(1)} ${units[unitIndex]}`
116
- }
117
-
118
- static getTemplateVariables(name: string, additionalVars: Record<string, any> = {}): Record<string, any> {
119
- return {
120
- name,
121
- Name: this.capitalize(name),
122
- NAME: name.toUpperCase(),
123
- kebabName: this.toKebabCase(name),
124
- camelName: this.toCamelCase(name),
125
- pascalName: this.toPascalCase(name),
126
- snakeName: this.toSnakeCase(name),
127
- timestamp: new Date().toISOString(),
128
- date: new Date().toLocaleDateString(),
129
- time: new Date().toLocaleTimeString(),
130
- year: new Date().getFullYear(),
131
- month: new Date().getMonth() + 1,
132
- day: new Date().getDate(),
133
- ...additionalVars
134
- }
135
- }
136
-
137
- // String transformation utilities
138
- static capitalize(str: string): string {
139
- return str.charAt(0).toUpperCase() + str.slice(1)
140
- }
141
-
142
- static toCamelCase(str: string): string {
143
- return str
144
- .replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
145
- .replace(/^[A-Z]/, char => char.toLowerCase())
146
- }
147
-
148
- static toPascalCase(str: string): string {
149
- return this.capitalize(this.toCamelCase(str))
150
- }
151
-
152
- static toKebabCase(str: string): string {
153
- return str
154
- .replace(/([a-z])([A-Z])/g, '$1-$2')
155
- .replace(/[\s_]+/g, '-')
156
- .toLowerCase()
157
- }
158
-
159
- static toSnakeCase(str: string): string {
160
- return str
161
- .replace(/([a-z])([A-Z])/g, '$1_$2')
162
- .replace(/[\s-]+/g, '_')
163
- .toLowerCase()
164
- }
165
-
166
- static pluralize(str: string): string {
167
- // Simple pluralization - in a real implementation you'd use a proper library
168
- if (str.endsWith('y')) {
169
- return str.slice(0, -1) + 'ies'
170
- }
171
- if (str.endsWith('s') || str.endsWith('sh') || str.endsWith('ch') || str.endsWith('x') || str.endsWith('z')) {
172
- return str + 'es'
173
- }
174
- return str + 's'
175
- }
176
-
177
- static singularize(str: string): string {
178
- // Simple singularization - in a real implementation you'd use a proper library
179
- if (str.endsWith('ies')) {
180
- return str.slice(0, -3) + 'y'
181
- }
182
- if (str.endsWith('es')) {
183
- return str.slice(0, -2)
184
- }
185
- if (str.endsWith('s') && !str.endsWith('ss')) {
186
- return str.slice(0, -1)
187
- }
188
- return str
189
- }
190
- }
191
-
1
+ import { existsSync } from "fs"
2
+ import { join } from "path"
3
+
4
+ export class GeneratorUtils {
5
+ static validateName(name: string): { valid: boolean; error?: string } {
6
+ if (!name || typeof name !== 'string') {
7
+ return { valid: false, error: 'Name is required' }
8
+ }
9
+
10
+ const trimmed = name.trim()
11
+ if (trimmed.length === 0) {
12
+ return { valid: false, error: 'Name cannot be empty' }
13
+ }
14
+
15
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(trimmed)) {
16
+ return { valid: false, error: 'Name must start with a letter and contain only letters, numbers, hyphens, and underscores' }
17
+ }
18
+
19
+ if (trimmed.length > 100) {
20
+ return { valid: false, error: 'Name must be less than 100 characters' }
21
+ }
22
+
23
+ return { valid: true }
24
+ }
25
+
26
+ static validatePath(path: string): { valid: boolean; error?: string } {
27
+ if (!path || typeof path !== 'string') {
28
+ return { valid: true } // Path is optional
29
+ }
30
+
31
+ const trimmed = path.trim()
32
+ if (trimmed.length === 0) {
33
+ return { valid: true }
34
+ }
35
+
36
+ // Check for invalid characters
37
+ if (/[<>:"|?*]/.test(trimmed)) {
38
+ return { valid: false, error: 'Path contains invalid characters' }
39
+ }
40
+
41
+ // Check for absolute paths (should be relative)
42
+ if (trimmed.startsWith('/') || /^[a-zA-Z]:/.test(trimmed)) {
43
+ return { valid: false, error: 'Path should be relative to project root' }
44
+ }
45
+
46
+ return { valid: true }
47
+ }
48
+
49
+ static checkFileExists(workingDir: string, filePath: string): boolean {
50
+ const fullPath = join(workingDir, filePath)
51
+ return existsSync(fullPath)
52
+ }
53
+
54
+ static getDefaultPath(type: string, name: string): string {
55
+ const kebabName = this.toKebabCase(name)
56
+ const pascalName = this.toPascalCase(name)
57
+
58
+ switch (type) {
59
+ case 'controller':
60
+ return `app/server/controllers/${kebabName}.controller.ts`
61
+ case 'service':
62
+ return `app/server/services/${kebabName}.service.ts`
63
+ case 'route':
64
+ return `app/server/routes/${kebabName}.routes.ts`
65
+ case 'component':
66
+ return `app/client/src/components/${pascalName}/${pascalName}.tsx`
67
+ default:
68
+ return `${kebabName}.ts`
69
+ }
70
+ }
71
+
72
+ static getRelatedFiles(type: string, name: string): string[] {
73
+ const kebabName = this.toKebabCase(name)
74
+ const pascalName = this.toPascalCase(name)
75
+
76
+ switch (type) {
77
+ case 'controller':
78
+ return [
79
+ `app/server/services/${kebabName}.service.ts`,
80
+ `app/server/schemas/${kebabName}.schema.ts`,
81
+ `app/server/routes/${kebabName}.routes.ts`
82
+ ]
83
+ case 'service':
84
+ return [
85
+ `app/server/repositories/${kebabName}.repository.ts`,
86
+ `app/server/controllers/${kebabName}.controller.ts`
87
+ ]
88
+ case 'component':
89
+ return [
90
+ `app/client/src/components/${pascalName}/${pascalName}.css`,
91
+ `app/client/src/components/${pascalName}/index.ts`,
92
+ `app/client/src/components/${pascalName}/${pascalName}.test.tsx`,
93
+ `app/client/src/components/${pascalName}/${pascalName}.stories.tsx`
94
+ ]
95
+ case 'route':
96
+ return [
97
+ `app/server/controllers/${kebabName}.controller.ts`,
98
+ `app/server/middleware/${kebabName}.middleware.ts`
99
+ ]
100
+ default:
101
+ return []
102
+ }
103
+ }
104
+
105
+ static formatFileSize(bytes: number): string {
106
+ const units = ['B', 'KB', 'MB', 'GB']
107
+ let size = bytes
108
+ let unitIndex = 0
109
+
110
+ while (size >= 1024 && unitIndex < units.length - 1) {
111
+ size /= 1024
112
+ unitIndex++
113
+ }
114
+
115
+ return `${size.toFixed(1)} ${units[unitIndex]}`
116
+ }
117
+
118
+ static getTemplateVariables(name: string, additionalVars: Record<string, any> = {}): Record<string, any> {
119
+ return {
120
+ name,
121
+ Name: this.capitalize(name),
122
+ NAME: name.toUpperCase(),
123
+ kebabName: this.toKebabCase(name),
124
+ camelName: this.toCamelCase(name),
125
+ pascalName: this.toPascalCase(name),
126
+ snakeName: this.toSnakeCase(name),
127
+ timestamp: new Date().toISOString(),
128
+ date: new Date().toLocaleDateString(),
129
+ time: new Date().toLocaleTimeString(),
130
+ year: new Date().getFullYear(),
131
+ month: new Date().getMonth() + 1,
132
+ day: new Date().getDate(),
133
+ ...additionalVars
134
+ }
135
+ }
136
+
137
+ // String transformation utilities
138
+ static capitalize(str: string): string {
139
+ return str.charAt(0).toUpperCase() + str.slice(1)
140
+ }
141
+
142
+ static toCamelCase(str: string): string {
143
+ return str
144
+ .replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
145
+ .replace(/^[A-Z]/, char => char.toLowerCase())
146
+ }
147
+
148
+ static toPascalCase(str: string): string {
149
+ return this.capitalize(this.toCamelCase(str))
150
+ }
151
+
152
+ static toKebabCase(str: string): string {
153
+ return str
154
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
155
+ .replace(/[\s_]+/g, '-')
156
+ .toLowerCase()
157
+ }
158
+
159
+ static toSnakeCase(str: string): string {
160
+ return str
161
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
162
+ .replace(/[\s-]+/g, '_')
163
+ .toLowerCase()
164
+ }
165
+
166
+ static pluralize(str: string): string {
167
+ // Simple pluralization - in a real implementation you'd use a proper library
168
+ if (str.endsWith('y')) {
169
+ return str.slice(0, -1) + 'ies'
170
+ }
171
+ if (str.endsWith('s') || str.endsWith('sh') || str.endsWith('ch') || str.endsWith('x') || str.endsWith('z')) {
172
+ return str + 'es'
173
+ }
174
+ return str + 's'
175
+ }
176
+
177
+ static singularize(str: string): string {
178
+ // Simple singularization - in a real implementation you'd use a proper library
179
+ if (str.endsWith('ies')) {
180
+ return str.slice(0, -3) + 'y'
181
+ }
182
+ if (str.endsWith('es')) {
183
+ return str.slice(0, -2)
184
+ }
185
+ if (str.endsWith('s') && !str.endsWith('ss')) {
186
+ return str.slice(0, -1)
187
+ }
188
+ return str
189
+ }
190
+ }
191
+
192
192
  export const generatorUtils = GeneratorUtils