create-fluxstack 1.0.13 → 1.0.14
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/.env.example +29 -29
- package/app/client/README.md +69 -69
- package/app/client/index.html +14 -13
- package/app/client/src/App.tsx +157 -524
- package/app/client/src/components/ErrorBoundary.tsx +107 -0
- package/app/client/src/components/ErrorDisplay.css +365 -0
- package/app/client/src/components/ErrorDisplay.tsx +258 -0
- package/app/client/src/components/FluxStackConfig.tsx +1321 -0
- package/app/client/src/components/HybridLiveCounter.tsx +140 -0
- package/app/client/src/components/LiveClock.tsx +286 -0
- package/app/client/src/components/MainLayout.tsx +390 -0
- package/app/client/src/components/SidebarNavigation.tsx +391 -0
- package/app/client/src/components/StateDemo.tsx +178 -0
- package/app/client/src/components/SystemMonitor.tsx +1038 -0
- package/app/client/src/components/Teste.tsx +104 -0
- package/app/client/src/components/UserProfile.tsx +809 -0
- package/app/client/src/hooks/useAuth.ts +39 -0
- package/app/client/src/hooks/useNotifications.ts +56 -0
- package/app/client/src/lib/eden-api.ts +189 -53
- package/app/client/src/lib/errors.ts +340 -0
- package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
- package/app/client/src/lib/index.ts +45 -0
- package/app/client/src/main.tsx +3 -2
- package/app/client/src/pages/ApiDocs.tsx +182 -0
- package/app/client/src/pages/Demo.tsx +174 -0
- package/app/client/src/pages/HybridLive.tsx +263 -0
- package/app/client/src/pages/Overview.tsx +155 -0
- package/app/client/src/store/README.md +43 -0
- package/app/client/src/store/index.ts +16 -0
- package/app/client/src/store/slices/uiSlice.ts +151 -0
- package/app/client/src/store/slices/userSlice.ts +161 -0
- package/app/client/src/test/README.md +257 -0
- package/app/client/src/test/setup.ts +70 -0
- package/app/client/src/test/types.ts +12 -0
- package/app/client/src/vite-env.d.ts +1 -1
- package/app/client/tsconfig.app.json +44 -43
- package/app/client/tsconfig.json +7 -7
- package/app/client/tsconfig.node.json +25 -25
- package/app/client/zustand-setup.md +65 -0
- package/app/server/controllers/users.controller.ts +68 -68
- package/app/server/index.ts +9 -1
- package/app/server/live/CounterComponent.ts +191 -0
- package/app/server/live/FluxStackConfig.ts +529 -0
- package/app/server/live/LiveClockComponent.ts +214 -0
- package/app/server/live/SidebarNavigation.ts +156 -0
- package/app/server/live/SystemMonitor.ts +594 -0
- package/app/server/live/SystemMonitorIntegration.ts +151 -0
- package/app/server/live/TesteComponent.ts +87 -0
- package/app/server/live/UserProfileComponent.ts +135 -0
- package/app/server/live/register-components.ts +28 -0
- package/app/server/middleware/auth.ts +136 -0
- package/app/server/middleware/errorHandling.ts +250 -0
- package/app/server/middleware/index.ts +10 -0
- package/app/server/middleware/rateLimit.ts +193 -0
- package/app/server/middleware/requestLogging.ts +215 -0
- package/app/server/middleware/validation.ts +270 -0
- package/app/server/routes/index.ts +14 -2
- package/app/server/routes/upload.ts +92 -0
- package/app/server/routes/users.routes.ts +2 -9
- package/app/server/services/NotificationService.ts +302 -0
- package/app/server/services/UserService.ts +222 -0
- package/app/server/services/index.ts +46 -0
- package/core/cli/commands/plugin-deps.ts +263 -0
- package/core/cli/generators/README.md +339 -0
- package/core/cli/generators/component.ts +770 -0
- package/core/cli/generators/controller.ts +299 -0
- package/core/cli/generators/index.ts +144 -0
- package/core/cli/generators/interactive.ts +228 -0
- package/core/cli/generators/prompts.ts +83 -0
- package/core/cli/generators/route.ts +513 -0
- package/core/cli/generators/service.ts +465 -0
- package/core/cli/generators/template-engine.ts +154 -0
- package/core/cli/generators/types.ts +71 -0
- package/core/cli/generators/utils.ts +192 -0
- package/core/cli/index.ts +69 -0
- package/core/cli/plugin-discovery.ts +16 -85
- package/core/client/fluxstack.ts +17 -0
- package/core/client/hooks/index.ts +7 -0
- package/core/client/hooks/state-validator.ts +130 -0
- package/core/client/hooks/useAuth.ts +49 -0
- package/core/client/hooks/useChunkedUpload.ts +258 -0
- package/core/client/hooks/useHybridLiveComponent.ts +967 -0
- package/core/client/hooks/useWebSocket.ts +373 -0
- package/core/client/index.ts +47 -0
- package/core/client/state/createStore.ts +193 -0
- package/core/client/state/index.ts +15 -0
- package/core/config/env-dynamic.ts +1 -1
- package/core/config/env.ts +2 -1
- package/core/config/runtime-config.ts +3 -3
- package/core/config/schema.ts +84 -49
- package/core/framework/server.ts +30 -0
- package/core/index.ts +25 -0
- package/core/live/ComponentRegistry.ts +399 -0
- package/core/live/types.ts +164 -0
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
- package/core/plugins/built-in/live-components/index.ts +27 -0
- package/core/plugins/built-in/logger/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +1 -1
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +1 -1
- package/core/plugins/built-in/vite/index.ts +1 -1
- package/core/plugins/dependency-manager.ts +384 -0
- package/core/plugins/index.ts +5 -1
- package/core/plugins/manager.ts +7 -3
- package/core/plugins/registry.ts +88 -10
- package/core/plugins/types.ts +11 -11
- package/core/server/framework.ts +43 -0
- package/core/server/index.ts +11 -1
- package/core/server/live/ComponentRegistry.ts +1017 -0
- package/core/server/live/FileUploadManager.ts +272 -0
- package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
- package/core/server/live/SingleConnectionManager.ts +0 -0
- package/core/server/live/StateSignature.ts +644 -0
- package/core/server/live/WebSocketConnectionManager.ts +688 -0
- package/core/server/live/websocket-plugin.ts +435 -0
- package/core/server/middleware/errorHandling.ts +141 -0
- package/core/server/middleware/index.ts +16 -0
- package/core/server/plugins/static-files-plugin.ts +232 -0
- package/core/server/services/BaseService.ts +95 -0
- package/core/server/services/ServiceContainer.ts +144 -0
- package/core/server/services/index.ts +9 -0
- package/core/templates/create-project.ts +46 -2
- package/core/testing/index.ts +10 -0
- package/core/testing/setup.ts +74 -0
- package/core/types/build.ts +38 -14
- package/core/types/types.ts +319 -0
- package/core/utils/env-runtime.ts +7 -0
- package/core/utils/errors/handlers.ts +264 -39
- package/core/utils/errors/index.ts +528 -18
- package/core/utils/errors/middleware.ts +114 -0
- package/core/utils/logger/formatters.ts +222 -0
- package/core/utils/logger/index.ts +167 -48
- package/core/utils/logger/middleware.ts +253 -0
- package/core/utils/logger/performance.ts +384 -0
- package/core/utils/logger/transports.ts +365 -0
- package/create-fluxstack.ts +296 -296
- package/fluxstack.config.ts +17 -1
- package/package-template.json +66 -66
- package/package.json +31 -6
- package/public/README.md +16 -0
- package/vite.config.ts +29 -14
- package/.claude/settings.local.json +0 -74
- package/.github/workflows/ci-build-tests.yml +0 -480
- package/.github/workflows/dependency-management.yml +0 -324
- package/.github/workflows/release-validation.yml +0 -355
- package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
- package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
- package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
- package/CLAUDE.md +0 -200
- package/Dockerfile +0 -58
- package/Dockerfile.backend +0 -52
- package/Dockerfile.frontend +0 -54
- package/README-Docker.md +0 -85
- package/ai-context/00-QUICK-START.md +0 -86
- package/ai-context/README.md +0 -88
- package/ai-context/development/eden-treaty-guide.md +0 -362
- package/ai-context/development/patterns.md +0 -382
- package/ai-context/development/plugins-guide.md +0 -572
- package/ai-context/examples/crud-complete.md +0 -626
- package/ai-context/project/architecture.md +0 -399
- package/ai-context/project/overview.md +0 -213
- package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
- package/ai-context/recent-changes/type-inference-fix.md +0 -223
- package/ai-context/reference/environment-vars.md +0 -384
- package/ai-context/reference/troubleshooting.md +0 -407
- package/app/client/src/components/TestPage.tsx +0 -453
- package/bun.lock +0 -1063
- package/bunfig.toml +0 -16
- package/core/__tests__/integration.test.ts +0 -227
- package/core/build/index.ts +0 -186
- package/core/config/__tests__/config-loader.test.ts +0 -554
- package/core/config/__tests__/config-merger.test.ts +0 -657
- package/core/config/__tests__/env-converter.test.ts +0 -372
- package/core/config/__tests__/env-processor.test.ts +0 -431
- package/core/config/__tests__/env.test.ts +0 -452
- package/core/config/__tests__/integration.test.ts +0 -418
- package/core/config/__tests__/loader.test.ts +0 -331
- package/core/config/__tests__/schema.test.ts +0 -129
- package/core/config/__tests__/validator.test.ts +0 -318
- package/core/framework/__tests__/server.test.ts +0 -233
- package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
- package/core/plugins/__tests__/manager.test.ts +0 -398
- package/core/plugins/__tests__/monitoring.test.ts +0 -401
- package/core/plugins/__tests__/registry.test.ts +0 -335
- package/core/utils/__tests__/errors.test.ts +0 -139
- package/core/utils/__tests__/helpers.test.ts +0 -297
- package/core/utils/__tests__/logger.test.ts +0 -141
- package/create-test-app.ts +0 -156
- package/docker-compose.microservices.yml +0 -75
- package/docker-compose.simple.yml +0 -57
- package/docker-compose.yml +0 -71
- package/eslint.config.js +0 -23
- package/flux-cli.ts +0 -214
- package/nginx-lb.conf +0 -37
- package/publish.sh +0 -63
- package/run-clean.ts +0 -26
- package/run-env-tests.ts +0 -313
- package/tailwind.config.js +0 -34
- package/tests/__mocks__/api.ts +0 -56
- package/tests/fixtures/users.ts +0 -69
- package/tests/integration/api/users.routes.test.ts +0 -221
- package/tests/setup.ts +0 -29
- package/tests/unit/app/client/App-simple.test.tsx +0 -56
- package/tests/unit/app/client/App.test.tsx.skip +0 -237
- package/tests/unit/app/client/eden-api.test.ts +0 -186
- package/tests/unit/app/client/simple.test.tsx +0 -23
- package/tests/unit/app/controllers/users.controller.test.ts +0 -150
- package/tests/unit/core/create-project.test.ts.skip +0 -95
- package/tests/unit/core/framework.test.ts +0 -144
- package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
- package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
- package/tests/utils/test-helpers.ts +0 -61
- package/vitest.config.ts +0 -50
- package/workspace.json +0 -6
|
@@ -0,0 +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
|
+
|
|
192
|
+
export const generatorUtils = GeneratorUtils
|
package/core/cli/index.ts
CHANGED
|
@@ -5,12 +5,81 @@ import { ProjectCreator } from "../templates/create-project"
|
|
|
5
5
|
import { getConfigSync } from "../config"
|
|
6
6
|
import { cliRegistry } from "./command-registry"
|
|
7
7
|
import { pluginDiscovery } from "./plugin-discovery"
|
|
8
|
+
import { generateCommand, interactiveGenerateCommand } from "./generators/index.js"
|
|
8
9
|
|
|
9
10
|
const command = process.argv[2]
|
|
10
11
|
const args = process.argv.slice(3)
|
|
11
12
|
|
|
12
13
|
// Register built-in commands
|
|
13
14
|
async function registerBuiltInCommands() {
|
|
15
|
+
// Register generate commands
|
|
16
|
+
cliRegistry.register(generateCommand)
|
|
17
|
+
cliRegistry.register(interactiveGenerateCommand)
|
|
18
|
+
|
|
19
|
+
// Register plugin dependency commands
|
|
20
|
+
cliRegistry.register({
|
|
21
|
+
name: 'plugin:deps',
|
|
22
|
+
description: 'Gerenciar dependências de plugins',
|
|
23
|
+
category: 'Plugins',
|
|
24
|
+
handler: async (args, options, context) => {
|
|
25
|
+
if (args.length === 0) {
|
|
26
|
+
console.log(`
|
|
27
|
+
⚡ FluxStack Plugin Dependencies Manager
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
flux plugin:deps install Install plugin dependencies
|
|
31
|
+
flux plugin:deps list List plugin dependencies
|
|
32
|
+
flux plugin:deps check Check for dependency conflicts
|
|
33
|
+
flux plugin:deps clean Clean unused dependencies
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
flux plugin:deps install --dry-run # Show what would be installed
|
|
37
|
+
flux plugin:deps list --plugin crypto-auth # Show specific plugin deps
|
|
38
|
+
flux plugin:deps check # Check for conflicts
|
|
39
|
+
`)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle subcommands
|
|
44
|
+
const subcommand = args[0]
|
|
45
|
+
const subArgs = args.slice(1)
|
|
46
|
+
|
|
47
|
+
// Import dinamicamente para evitar problemas de inicialização
|
|
48
|
+
const { createPluginDepsCommand } = await import('./commands/plugin-deps')
|
|
49
|
+
const cmd = createPluginDepsCommand()
|
|
50
|
+
|
|
51
|
+
switch (subcommand) {
|
|
52
|
+
case 'install':
|
|
53
|
+
const installCmd = cmd.commands.find(c => c.name() === 'install')
|
|
54
|
+
if (installCmd) {
|
|
55
|
+
await installCmd.parseAsync(['node', 'cli', ...subArgs], { from: 'user' })
|
|
56
|
+
}
|
|
57
|
+
break
|
|
58
|
+
case 'list':
|
|
59
|
+
const listCmd = cmd.commands.find(c => c.name() === 'list')
|
|
60
|
+
if (listCmd) {
|
|
61
|
+
await listCmd.parseAsync(['node', 'cli', ...subArgs], { from: 'user' })
|
|
62
|
+
}
|
|
63
|
+
break
|
|
64
|
+
case 'check':
|
|
65
|
+
const checkCmd = cmd.commands.find(c => c.name() === 'check')
|
|
66
|
+
if (checkCmd) {
|
|
67
|
+
await checkCmd.parseAsync(['node', 'cli', ...subArgs], { from: 'user' })
|
|
68
|
+
}
|
|
69
|
+
break
|
|
70
|
+
case 'clean':
|
|
71
|
+
const cleanCmd = cmd.commands.find(c => c.name() === 'clean')
|
|
72
|
+
if (cleanCmd) {
|
|
73
|
+
await cleanCmd.parseAsync(['node', 'cli', ...subArgs], { from: 'user' })
|
|
74
|
+
}
|
|
75
|
+
break
|
|
76
|
+
default:
|
|
77
|
+
console.error(`❌ Unknown subcommand: ${subcommand}`)
|
|
78
|
+
console.error('Available subcommands: install, list, check, clean')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
14
83
|
// Help command
|
|
15
84
|
cliRegistry.register({
|
|
16
85
|
name: 'help',
|
|
@@ -11,39 +11,35 @@ export class CliPluginDiscovery {
|
|
|
11
11
|
// 1. Load built-in plugins with CLI commands
|
|
12
12
|
await this.loadBuiltInPlugins()
|
|
13
13
|
|
|
14
|
-
// 2. Load
|
|
15
|
-
await this.loadExternalPlugins()
|
|
16
|
-
|
|
17
|
-
// 3. Load local plugins from project
|
|
14
|
+
// 2. Load local plugins from project
|
|
18
15
|
await this.loadLocalPlugins()
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
private async loadBuiltInPlugins(): Promise<void> {
|
|
22
|
-
const builtInPluginsDir = join(__dirname, '../
|
|
19
|
+
const builtInPluginsDir = join(__dirname, '../plugins/built-in')
|
|
23
20
|
|
|
24
21
|
if (!existsSync(builtInPluginsDir)) {
|
|
25
22
|
return
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
try {
|
|
29
|
-
|
|
30
|
-
const potentialPlugins =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'swagger',
|
|
34
|
-
'static',
|
|
35
|
-
'database'
|
|
36
|
-
]
|
|
26
|
+
const fs = await import('fs')
|
|
27
|
+
const potentialPlugins = fs.readdirSync(builtInPluginsDir, { withFileTypes: true })
|
|
28
|
+
.filter(entry => entry.isDirectory())
|
|
29
|
+
.map(entry => entry.name)
|
|
37
30
|
|
|
38
31
|
for (const pluginName of potentialPlugins) {
|
|
39
32
|
try {
|
|
40
|
-
const pluginPath = join(builtInPluginsDir,
|
|
33
|
+
const pluginPath = join(builtInPluginsDir, pluginName, 'index.ts')
|
|
41
34
|
if (existsSync(pluginPath)) {
|
|
42
35
|
const pluginModule = await import(pluginPath)
|
|
43
|
-
const plugin = pluginModule[`${pluginName}Plugin`] as Plugin
|
|
44
36
|
|
|
45
|
-
if (
|
|
46
|
-
|
|
37
|
+
if (pluginModule.commands) {
|
|
38
|
+
for (const command of pluginModule.commands) {
|
|
39
|
+
cliRegistry.register(command)
|
|
40
|
+
}
|
|
41
|
+
this.loadedPlugins.add(pluginName)
|
|
42
|
+
logger.debug(`Registered ${pluginModule.commands.length} CLI commands from built-in plugin: ${pluginName}`)
|
|
47
43
|
}
|
|
48
44
|
}
|
|
49
45
|
} catch (error) {
|
|
@@ -55,74 +51,9 @@ export class CliPluginDiscovery {
|
|
|
55
51
|
}
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (!existsSync(nodeModulesDir)) {
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const fs = await import('fs')
|
|
67
|
-
const entries = fs.readdirSync(nodeModulesDir, { withFileTypes: true })
|
|
68
|
-
|
|
69
|
-
for (const entry of entries) {
|
|
70
|
-
if (!entry.isDirectory()) continue
|
|
71
|
-
|
|
72
|
-
// Check for FluxStack plugins (convention: fluxstack-plugin-*)
|
|
73
|
-
if (entry.name.startsWith('fluxstack-plugin-')) {
|
|
74
|
-
await this.loadExternalPlugin(entry.name)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Check for scoped packages (@fluxstack/plugin-*)
|
|
78
|
-
if (entry.name.startsWith('@fluxstack')) {
|
|
79
|
-
const scopedDir = join(nodeModulesDir, entry.name)
|
|
80
|
-
if (existsSync(scopedDir)) {
|
|
81
|
-
const scopedEntries = fs.readdirSync(scopedDir, { withFileTypes: true })
|
|
82
|
-
for (const scopedEntry of scopedEntries) {
|
|
83
|
-
if (scopedEntry.isDirectory() && scopedEntry.name.startsWith('plugin-')) {
|
|
84
|
-
await this.loadExternalPlugin(`${entry.name}/${scopedEntry.name}`)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch (error) {
|
|
91
|
-
logger.debug('Failed to scan external plugins:', error)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private async loadExternalPlugin(packageName: string): Promise<void> {
|
|
96
|
-
try {
|
|
97
|
-
const packagePath = join(process.cwd(), 'node_modules', packageName)
|
|
98
|
-
const packageJsonPath = join(packagePath, 'package.json')
|
|
99
|
-
|
|
100
|
-
if (!existsSync(packageJsonPath)) {
|
|
101
|
-
return
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const packageJson = JSON.parse(await import('fs').then(fs =>
|
|
105
|
-
fs.readFileSync(packageJsonPath, 'utf-8')
|
|
106
|
-
))
|
|
107
|
-
|
|
108
|
-
// Check if this is a FluxStack plugin
|
|
109
|
-
if (packageJson.fluxstack?.plugin) {
|
|
110
|
-
const entryPoint = packageJson.main || 'index.js'
|
|
111
|
-
const pluginPath = join(packagePath, entryPoint)
|
|
112
|
-
|
|
113
|
-
if (existsSync(pluginPath)) {
|
|
114
|
-
const pluginModule = await import(pluginPath)
|
|
115
|
-
const plugin = pluginModule.default || pluginModule[packageJson.fluxstack.plugin] as Plugin
|
|
116
|
-
|
|
117
|
-
if (plugin && plugin.commands) {
|
|
118
|
-
this.registerPluginCommands(plugin)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
} catch (error) {
|
|
123
|
-
logger.debug(`Failed to load external plugin ${packageName}:`, error)
|
|
124
|
-
}
|
|
125
|
-
}
|
|
54
|
+
// Métodos removidos - não carregamos mais plugins do node_modules
|
|
55
|
+
// private async loadExternalPlugins(): Promise<void> { ... }
|
|
56
|
+
// private async loadExternalPlugin(packageName: string): Promise<void> { ... }
|
|
126
57
|
|
|
127
58
|
private async loadLocalPlugins(): Promise<void> {
|
|
128
59
|
const localPluginsDir = join(process.cwd(), 'plugins')
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// 🔥 FluxStack Client Library - Main Entry Point
|
|
2
|
+
// This is the primary interface for all FluxStack client functionality
|
|
3
|
+
|
|
4
|
+
// Re-export everything from the main index
|
|
5
|
+
export * from './index'
|
|
6
|
+
|
|
7
|
+
// Convenience aliases for common hooks and utilities
|
|
8
|
+
export {
|
|
9
|
+
useHybridLiveComponent as useLive,
|
|
10
|
+
useWebSocket as useWS,
|
|
11
|
+
useChunkedUpload as useUpload,
|
|
12
|
+
StateValidator as Validator
|
|
13
|
+
} from './index'
|
|
14
|
+
|
|
15
|
+
// Default export for easy importing
|
|
16
|
+
import * as FluxStack from './index'
|
|
17
|
+
export default FluxStack
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// 🔥 State Validation Utilities
|
|
2
|
+
|
|
3
|
+
import type { StateValidation, StateConflict, HybridState } from '../../types/types'
|
|
4
|
+
|
|
5
|
+
export class StateValidator {
|
|
6
|
+
/**
|
|
7
|
+
* Generate checksum for state object
|
|
8
|
+
*/
|
|
9
|
+
static generateChecksum(state: any): string {
|
|
10
|
+
const json = JSON.stringify(state, Object.keys(state).sort())
|
|
11
|
+
let hash = 0
|
|
12
|
+
for (let i = 0; i < json.length; i++) {
|
|
13
|
+
const char = json.charCodeAt(i)
|
|
14
|
+
hash = ((hash << 5) - hash) + char
|
|
15
|
+
hash = hash & hash // Convert to 32-bit integer
|
|
16
|
+
}
|
|
17
|
+
return Math.abs(hash).toString(16)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create validation metadata
|
|
22
|
+
*/
|
|
23
|
+
static createValidation(
|
|
24
|
+
state: any,
|
|
25
|
+
source: 'client' | 'server' | 'mount' = 'client'
|
|
26
|
+
): StateValidation {
|
|
27
|
+
return {
|
|
28
|
+
checksum: this.generateChecksum(state),
|
|
29
|
+
version: Date.now(),
|
|
30
|
+
timestamp: Date.now(),
|
|
31
|
+
source
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compare two states and detect conflicts
|
|
37
|
+
*/
|
|
38
|
+
static detectConflicts<T>(
|
|
39
|
+
clientState: T,
|
|
40
|
+
serverState: T,
|
|
41
|
+
excludeFields: string[] = ['lastUpdated', 'version']
|
|
42
|
+
): StateConflict[] {
|
|
43
|
+
const conflicts: StateConflict[] = []
|
|
44
|
+
|
|
45
|
+
const clientKeys = Object.keys(clientState as any)
|
|
46
|
+
const serverKeys = Object.keys(serverState as any)
|
|
47
|
+
const allKeys = Array.from(new Set([...clientKeys, ...serverKeys]))
|
|
48
|
+
|
|
49
|
+
for (const key of allKeys) {
|
|
50
|
+
if (excludeFields.includes(key)) continue
|
|
51
|
+
|
|
52
|
+
const clientValue = (clientState as any)?.[key]
|
|
53
|
+
const serverValue = (serverState as any)?.[key]
|
|
54
|
+
|
|
55
|
+
if (JSON.stringify(clientValue) !== JSON.stringify(serverValue)) {
|
|
56
|
+
conflicts.push({
|
|
57
|
+
property: key as string,
|
|
58
|
+
clientValue,
|
|
59
|
+
serverValue,
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
resolved: false
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return conflicts
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Merge states with conflict resolution
|
|
71
|
+
*/
|
|
72
|
+
static mergeStates<T>(
|
|
73
|
+
clientState: T,
|
|
74
|
+
serverState: T,
|
|
75
|
+
conflicts: StateConflict[],
|
|
76
|
+
strategy: 'client' | 'server' | 'smart' = 'smart'
|
|
77
|
+
): T {
|
|
78
|
+
const merged = { ...clientState }
|
|
79
|
+
|
|
80
|
+
for (const conflict of conflicts) {
|
|
81
|
+
switch (strategy) {
|
|
82
|
+
case 'client':
|
|
83
|
+
// Keep client value
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
case 'server':
|
|
87
|
+
(merged as any)[conflict.property] = conflict.serverValue
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
case 'smart':
|
|
91
|
+
// Smart resolution based on field type and context
|
|
92
|
+
if (conflict.property === 'lastUpdated') {
|
|
93
|
+
// Server timestamp wins
|
|
94
|
+
(merged as any)[conflict.property] = conflict.serverValue
|
|
95
|
+
} else if (typeof conflict.serverValue === 'number' && typeof conflict.clientValue === 'number') {
|
|
96
|
+
// For numbers, use the higher value (e.g., counters)
|
|
97
|
+
(merged as any)[conflict.property] = Math.max(conflict.serverValue, conflict.clientValue)
|
|
98
|
+
} else {
|
|
99
|
+
// Default to server for other types
|
|
100
|
+
(merged as any)[conflict.property] = conflict.serverValue
|
|
101
|
+
}
|
|
102
|
+
break
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return merged
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Validate state integrity
|
|
111
|
+
*/
|
|
112
|
+
static validateState<T>(hybridState: HybridState<T>): boolean {
|
|
113
|
+
const currentChecksum = this.generateChecksum(hybridState.data)
|
|
114
|
+
return currentChecksum === hybridState.validation.checksum
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Update validation after state change
|
|
119
|
+
*/
|
|
120
|
+
static updateValidation<T>(
|
|
121
|
+
hybridState: HybridState<T>,
|
|
122
|
+
source: 'client' | 'server' | 'mount' = 'client'
|
|
123
|
+
): HybridState<T> {
|
|
124
|
+
return {
|
|
125
|
+
...hybridState,
|
|
126
|
+
validation: this.createValidation(hybridState.data, source),
|
|
127
|
+
status: 'synced'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Hook
|
|
3
|
+
* Core FluxStack authentication utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { BaseUser, BaseUserStore } from '../state/index.js'
|
|
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
|
+
}
|