create-fluxstack 1.16.0 → 1.17.1
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/CHANGELOG.md +80 -0
- package/app/client/src/App.tsx +8 -0
- package/app/client/src/live/AuthDemo.tsx +4 -4
- package/core/build/bundler.ts +40 -26
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +92 -21
- package/core/cli/command-registry.ts +44 -46
- package/core/cli/commands/build.ts +11 -6
- package/core/cli/commands/create.ts +7 -5
- package/core/cli/commands/dev.ts +6 -5
- package/core/cli/commands/help.ts +3 -2
- package/core/cli/commands/make-plugin.ts +8 -7
- package/core/cli/commands/plugin-add.ts +60 -43
- package/core/cli/commands/plugin-deps.ts +73 -57
- package/core/cli/commands/plugin-list.ts +44 -41
- package/core/cli/commands/plugin-remove.ts +33 -22
- package/core/cli/generators/component.ts +770 -769
- package/core/cli/generators/controller.ts +9 -8
- package/core/cli/generators/index.ts +148 -146
- package/core/cli/generators/interactive.ts +228 -227
- package/core/cli/generators/plugin.ts +11 -10
- package/core/cli/generators/prompts.ts +83 -82
- package/core/cli/generators/route.ts +7 -6
- package/core/cli/generators/service.ts +10 -9
- package/core/cli/generators/template-engine.ts +2 -1
- package/core/cli/generators/types.ts +7 -7
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +9 -8
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/standalone.ts +18 -17
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +1 -0
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +73 -113
- package/core/framework/types.ts +2 -2
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +6 -3
- package/core/plugins/built-in/monitoring/index.ts +110 -68
- package/core/plugins/built-in/static/index.ts +2 -2
- package/core/plugins/built-in/swagger/index.ts +9 -9
- package/core/plugins/built-in/vite/index.ts +3 -3
- package/core/plugins/built-in/vite/vite-dev.ts +3 -3
- package/core/plugins/config.ts +50 -47
- package/core/plugins/discovery.ts +10 -4
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +206 -203
- package/core/plugins/manager.ts +21 -20
- package/core/plugins/registry.ts +76 -12
- package/core/plugins/types.ts +14 -14
- package/core/server/framework.ts +3 -189
- package/core/server/live/auto-generated-components.ts +11 -29
- package/core/server/live/index.ts +41 -31
- package/core/server/live/websocket-plugin.ts +11 -1
- package/core/server/middleware/elysia-helpers.ts +16 -15
- package/core/server/middleware/errorHandling.ts +14 -14
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +181 -180
- package/core/server/plugins/static-files-plugin.ts +4 -3
- package/core/server/plugins/swagger.ts +11 -8
- package/core/server/rooms/RoomBroadcaster.ts +11 -10
- package/core/server/rooms/RoomSystem.ts +14 -11
- package/core/server/services/BaseService.ts +7 -7
- package/core/server/services/ServiceContainer.ts +5 -5
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +28 -27
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/config.ts +5 -5
- package/core/types/index.ts +1 -1
- package/core/types/plugin.ts +2 -2
- package/core/types/types.ts +3 -3
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +10 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +30 -20
- package/core/utils/errors/index.ts +54 -46
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +19 -16
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +2 -2
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +13 -3
- package/core/utils/logger/startup-banner.ts +2 -2
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +67 -66
- package/core/utils/version.ts +1 -1
- package/package.json +104 -100
- package/playwright-report/index.html +85 -0
- package/playwright.config.ts +31 -0
- 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/package.json +65 -65
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/vite.config.ts +13 -0
- package/app/client/.live-stubs/LiveAdminPanel.js +0 -5
- package/app/client/.live-stubs/LiveCounter.js +0 -9
- package/app/client/.live-stubs/LiveForm.js +0 -11
- package/app/client/.live-stubs/LiveLocalCounter.js +0 -8
- package/app/client/.live-stubs/LivePingPong.js +0 -10
- package/app/client/.live-stubs/LiveRoomChat.js +0 -11
- package/app/client/.live-stubs/LiveSharedCounter.js +0 -10
- package/app/client/.live-stubs/LiveUpload.js +0 -15
- package/app/server/live/register-components.ts +0 -19
- package/core/build/live-components-generator.ts +0 -321
- package/core/live/ComponentRegistry.ts +0 -403
- package/core/live/types.ts +0 -241
- package/workspace.json +0 -6
|
@@ -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
|
package/core/cli/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { cliRegistry } from "./command-registry"
|
|
9
9
|
import { pluginDiscovery } from "./plugin-discovery"
|
|
10
10
|
import { generateCommand, interactiveGenerateCommand } from "./generators/index"
|
|
11
|
+
import { buildLogger } from "@core/utils/build-logger"
|
|
11
12
|
|
|
12
13
|
// Import modular commands
|
|
13
14
|
import { builtInCommands } from "./commands"
|
|
@@ -51,7 +52,7 @@ async function registerBuiltInCommands() {
|
|
|
51
52
|
handler: async (args, options, context) => {
|
|
52
53
|
const { createPluginAddCommand } = await import('./commands/plugin-add')
|
|
53
54
|
const cmd = createPluginAddCommand()
|
|
54
|
-
await cmd.parseAsync(['node', 'cli', ...args], { from: 'user' })
|
|
55
|
+
await cmd.parseAsync(['node', 'cli', ...args.map(String)], { from: 'user' })
|
|
55
56
|
}
|
|
56
57
|
})
|
|
57
58
|
|
|
@@ -80,7 +81,7 @@ async function registerBuiltInCommands() {
|
|
|
80
81
|
handler: async (args, options, context) => {
|
|
81
82
|
const { createPluginRemoveCommand } = await import('./commands/plugin-remove')
|
|
82
83
|
const cmd = createPluginRemoveCommand()
|
|
83
|
-
await cmd.parseAsync(['node', 'cli', ...args], { from: 'user' })
|
|
84
|
+
await cmd.parseAsync(['node', 'cli', ...args.map(String)], { from: 'user' })
|
|
84
85
|
}
|
|
85
86
|
})
|
|
86
87
|
|
|
@@ -106,7 +107,7 @@ async function registerBuiltInCommands() {
|
|
|
106
107
|
handler: async (args, options, context) => {
|
|
107
108
|
const { createPluginListCommand } = await import('./commands/plugin-list')
|
|
108
109
|
const cmd = createPluginListCommand()
|
|
109
|
-
await cmd.parseAsync(['node', 'cli', ...args], { from: 'user' })
|
|
110
|
+
await cmd.parseAsync(['node', 'cli', ...args.map(String)], { from: 'user' })
|
|
110
111
|
}
|
|
111
112
|
})
|
|
112
113
|
|
|
@@ -116,7 +117,7 @@ async function registerBuiltInCommands() {
|
|
|
116
117
|
category: 'Plugins',
|
|
117
118
|
handler: async (args, options, context) => {
|
|
118
119
|
if (args.length === 0) {
|
|
119
|
-
|
|
120
|
+
buildLogger.info(`
|
|
120
121
|
⚡ FluxStack Plugin Dependencies Manager
|
|
121
122
|
|
|
122
123
|
Usage:
|
|
@@ -134,7 +135,7 @@ Examples:
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
const subcommand = args[0]
|
|
137
|
-
const subArgs = args.slice(1)
|
|
138
|
+
const subArgs = args.slice(1).map(String)
|
|
138
139
|
|
|
139
140
|
const { createPluginDepsCommand } = await import('./commands/plugin-deps')
|
|
140
141
|
const cmd = createPluginDepsCommand()
|
|
@@ -165,8 +166,8 @@ Examples:
|
|
|
165
166
|
}
|
|
166
167
|
break
|
|
167
168
|
default:
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
buildLogger.error(`❌ Unknown subcommand: ${subcommand}`)
|
|
170
|
+
buildLogger.error('Available subcommands: install, list, check, clean')
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
})
|
|
@@ -187,6 +188,6 @@ async function main() {
|
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
main().catch((error) => {
|
|
190
|
-
|
|
191
|
+
buildLogger.error('CLI Error:', error)
|
|
191
192
|
process.exit(1)
|
|
192
193
|
})
|
|
@@ -74,7 +74,7 @@ export class CliPluginDiscovery {
|
|
|
74
74
|
try {
|
|
75
75
|
const pluginModule = await import(pluginPath)
|
|
76
76
|
const plugin = pluginModule.default || Object.values(pluginModule).find(
|
|
77
|
-
(exp:
|
|
77
|
+
(exp: unknown) => exp && typeof exp === 'object' && (exp as Record<string, unknown>).name && (exp as Record<string, unknown>).commands
|
|
78
78
|
) as Plugin
|
|
79
79
|
|
|
80
80
|
if (plugin && plugin.commands) {
|
|
@@ -93,7 +93,7 @@ export class CliPluginDiscovery {
|
|
|
93
93
|
try {
|
|
94
94
|
const pluginModule = await import(pluginIndexPath)
|
|
95
95
|
const plugin = pluginModule.default || Object.values(pluginModule).find(
|
|
96
|
-
(exp:
|
|
96
|
+
(exp: unknown) => exp && typeof exp === 'object' && (exp as Record<string, unknown>).name && (exp as Record<string, unknown>).commands
|
|
97
97
|
) as Plugin
|
|
98
98
|
|
|
99
99
|
if (plugin && plugin.commands) {
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authentication Hook
|
|
3
|
-
* Core FluxStack authentication utilities
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { BaseUser, BaseUserStore } from '../state/index'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Create authentication hook for a user store
|
|
10
|
-
*/
|
|
11
|
-
export function createAuthHook(useUserStore: () => BaseUserStore) {
|
|
12
|
-
return function useAuth() {
|
|
13
|
-
const store = useUserStore()
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
// State
|
|
17
|
-
currentUser: store.currentUser,
|
|
18
|
-
isAuthenticated: store.isAuthenticated,
|
|
19
|
-
isLoading: store.isLoading,
|
|
20
|
-
error: store.error,
|
|
21
|
-
|
|
22
|
-
// Computed
|
|
23
|
-
isAdmin: store.currentUser?.role === 'admin',
|
|
24
|
-
|
|
25
|
-
// Actions
|
|
26
|
-
login: store.login,
|
|
27
|
-
register: store.register,
|
|
28
|
-
logout: store.logout,
|
|
29
|
-
updateProfile: store.updateProfile,
|
|
30
|
-
clearError: store.clearError
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Base auth hook interface
|
|
37
|
-
*/
|
|
38
|
-
export interface AuthHook {
|
|
39
|
-
currentUser: BaseUser | null
|
|
40
|
-
isAuthenticated: boolean
|
|
41
|
-
isLoading: boolean
|
|
42
|
-
error: string | null
|
|
43
|
-
isAdmin: boolean
|
|
44
|
-
login: (credentials: { email: string; password: string }) => Promise<void>
|
|
45
|
-
register: (data: { email: string; password: string; name: string }) => Promise<void>
|
|
46
|
-
logout: () => void
|
|
47
|
-
updateProfile: (data: Partial<BaseUser>) => Promise<void>
|
|
48
|
-
clearError: () => void
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Hook
|
|
3
|
+
* Core FluxStack authentication utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { BaseUser, BaseUserStore } from '../state/index'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create authentication hook for a user store
|
|
10
|
+
*/
|
|
11
|
+
export function createAuthHook(useUserStore: () => BaseUserStore) {
|
|
12
|
+
return function useAuth() {
|
|
13
|
+
const store = useUserStore()
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
// State
|
|
17
|
+
currentUser: store.currentUser,
|
|
18
|
+
isAuthenticated: store.isAuthenticated,
|
|
19
|
+
isLoading: store.isLoading,
|
|
20
|
+
error: store.error,
|
|
21
|
+
|
|
22
|
+
// Computed
|
|
23
|
+
isAdmin: store.currentUser?.role === 'admin',
|
|
24
|
+
|
|
25
|
+
// Actions
|
|
26
|
+
login: store.login,
|
|
27
|
+
register: store.register,
|
|
28
|
+
logout: store.logout,
|
|
29
|
+
updateProfile: store.updateProfile,
|
|
30
|
+
clearError: store.clearError
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Base auth hook interface
|
|
37
|
+
*/
|
|
38
|
+
export interface AuthHook {
|
|
39
|
+
currentUser: BaseUser | null
|
|
40
|
+
isAuthenticated: boolean
|
|
41
|
+
isLoading: boolean
|
|
42
|
+
error: string | null
|
|
43
|
+
isAdmin: boolean
|
|
44
|
+
login: (credentials: { email: string; password: string }) => Promise<void>
|
|
45
|
+
register: (data: { email: string; password: string; name: string }) => Promise<void>
|
|
46
|
+
logout: () => void
|
|
47
|
+
updateProfile: (data: Partial<BaseUser>) => Promise<void>
|
|
48
|
+
clearError: () => void
|
|
49
49
|
}
|
|
@@ -5,19 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import { clientConfig } from '@config'
|
|
7
7
|
import type { LogLevel } from 'vite'
|
|
8
|
+
import { buildLogger } from "../utils/build-logger"
|
|
8
9
|
|
|
9
10
|
type ViteDevServer = Awaited<ReturnType<typeof import('vite')['createServer']>>
|
|
10
11
|
|
|
11
12
|
let viteServer: ViteDevServer | null = null
|
|
12
13
|
|
|
13
|
-
export const startFrontendOnly = async (config:
|
|
14
|
-
const port = config.vitePort
|
|
15
|
-
const host = config.viteHost
|
|
14
|
+
export const startFrontendOnly = async (config: Record<string, unknown> = {}) => {
|
|
15
|
+
const port = (config.vitePort ?? clientConfig.vite.port ?? 5173) as number
|
|
16
|
+
const host = (config.viteHost ?? clientConfig.vite.host ?? 'localhost') as string
|
|
16
17
|
const logLevel = (config.logLevel || clientConfig.vite.logLevel || 'info') as LogLevel
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
buildLogger.info(`⚛️ FluxStack Frontend Only`)
|
|
20
|
+
buildLogger.info(`🌐 http://${host}:${port}`)
|
|
21
|
+
buildLogger.info('')
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
24
|
// Dynamic import of vite
|
|
@@ -29,20 +30,20 @@ export const startFrontendOnly = async (config: any = {}) => {
|
|
|
29
30
|
server: {
|
|
30
31
|
port,
|
|
31
32
|
host,
|
|
32
|
-
strictPort: clientConfig.vite.strictPort
|
|
33
|
+
strictPort: clientConfig.vite.strictPort as boolean | undefined
|
|
33
34
|
},
|
|
34
35
|
logLevel
|
|
35
36
|
})
|
|
36
37
|
|
|
37
38
|
await viteServer.listen()
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
buildLogger.success(`✅ Frontend server ready!`)
|
|
41
|
+
buildLogger.info('')
|
|
41
42
|
|
|
42
43
|
// Setup cleanup on process exit
|
|
43
44
|
const cleanup = async () => {
|
|
44
45
|
if (viteServer) {
|
|
45
|
-
|
|
46
|
+
buildLogger.info('\n🛑 Stopping frontend...')
|
|
46
47
|
await viteServer.close()
|
|
47
48
|
viteServer = null
|
|
48
49
|
process.exit(0)
|
|
@@ -63,15 +64,15 @@ export const startFrontendOnly = async (config: any = {}) => {
|
|
|
63
64
|
(errorMessage.includes('Port') && errorMessage.includes('is in use'))
|
|
64
65
|
|
|
65
66
|
if (isPortInUse) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
buildLogger.error(`❌ Failed to start Vite: Port ${port} is already in use`)
|
|
68
|
+
buildLogger.info(`💡 Try one of these solutions:`)
|
|
69
|
+
buildLogger.info(` 1. Stop the process using port ${port}`)
|
|
70
|
+
buildLogger.info(` 2. Change VITE_PORT in your .env file`)
|
|
71
|
+
buildLogger.info(` 3. Kill the process: ${process.platform === 'win32' ? `netstat -ano | findstr :${port}` : `lsof -ti:${port} | xargs kill -9`}`)
|
|
71
72
|
process.exit(1)
|
|
72
73
|
} else {
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
buildLogger.error('❌ Failed to start Vite server:', errorMessage)
|
|
75
|
+
buildLogger.error('Full error:', error)
|
|
75
76
|
process.exit(1)
|
|
76
77
|
}
|
|
77
78
|
}
|