create-fluxstack 1.9.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.
- package/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LIVE_COMPONENTS_REVIEW.md +781 -0
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +109 -156
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -65
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +242 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +13 -10
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/client/src/vite-env.d.ts +1 -1
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +24 -107
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +285 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/live/register-components.ts +19 -19
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/bundler.ts +4 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +159 -27
- package/core/build/live-components-generator.ts +70 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -558
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +63 -17
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -0
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +170 -69
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +18 -51
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +227 -47
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +7 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -131
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1074
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +94 -306
- package/core/plugins/built-in/vite/vite-dev.ts +82 -0
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +309 -21
- package/core/plugins/types.ts +106 -55
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -16
- package/core/server/live/ComponentRegistry.ts +91 -75
- package/core/server/live/FileUploadManager.ts +41 -31
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +41 -26
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +233 -72
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -260
- package/core/server/plugins/swagger.ts +33 -33
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -218
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -99
- package/core/types/types.ts +490 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -66
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/eslint.config.js +23 -23
- package/package.json +14 -15
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +10 -14
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +1 -1
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/workspace.json +5 -5
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/tsconfig.app.json +0 -44
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -631
- package/core/client/hooks/useWebSocket.ts +0 -373
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- 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 '
|
|
58
|
-
import { logger } from '
|
|
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
|
-
//
|
|
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 "
|
|
2
|
-
import type { Logger } from "
|
|
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
|