create-fluxstack 1.10.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -2
- package/Dockerfile +8 -8
- 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 +107 -150
- 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 -60
- 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 +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- 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/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/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -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 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- 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 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- 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 +17 -68
- 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 +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- 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 +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- 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 +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- 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 +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- 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 -69
- package/core/server/plugins/swagger.ts +1 -1
- 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 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -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 -82
- 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/package.json +12 -13
- 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/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- 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/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- 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/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- 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
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { BackButton } from '../components/BackButton'
|
|
3
|
+
|
|
4
|
+
export function ApiTestPage({
|
|
5
|
+
apiResponse,
|
|
6
|
+
isLoading,
|
|
7
|
+
onHealth,
|
|
8
|
+
onGetUsers,
|
|
9
|
+
onCreateUser
|
|
10
|
+
}: {
|
|
11
|
+
apiResponse: string
|
|
12
|
+
isLoading: boolean
|
|
13
|
+
onHealth: () => void
|
|
14
|
+
onGetUsers: () => void
|
|
15
|
+
onCreateUser: () => void
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
|
19
|
+
<div className="flex items-center gap-4 mb-8">
|
|
20
|
+
<BackButton />
|
|
21
|
+
<h1 className="text-3xl font-bold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
|
|
22
|
+
Eden Treaty API Test
|
|
23
|
+
</h1>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
|
27
|
+
<ActionCard
|
|
28
|
+
icon="🏥"
|
|
29
|
+
title="GET /api/health"
|
|
30
|
+
subtitle="Health Check"
|
|
31
|
+
onClick={onHealth}
|
|
32
|
+
disabled={isLoading}
|
|
33
|
+
className="bg-emerald-500/20 border-emerald-500/30 text-emerald-300 hover:bg-emerald-500/30"
|
|
34
|
+
/>
|
|
35
|
+
<ActionCard
|
|
36
|
+
icon="👥"
|
|
37
|
+
title="GET /api/users"
|
|
38
|
+
subtitle="List Users"
|
|
39
|
+
onClick={onGetUsers}
|
|
40
|
+
disabled={isLoading}
|
|
41
|
+
className="bg-blue-500/20 border-blue-500/30 text-blue-300 hover:bg-blue-500/30"
|
|
42
|
+
/>
|
|
43
|
+
<ActionCard
|
|
44
|
+
icon="➕"
|
|
45
|
+
title="POST /api/users"
|
|
46
|
+
subtitle="Create User"
|
|
47
|
+
onClick={onCreateUser}
|
|
48
|
+
disabled={isLoading}
|
|
49
|
+
className="bg-purple-500/20 border-purple-500/30 text-purple-300 hover:bg-purple-500/30"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div className="bg-black/40 backdrop-blur-sm border border-white/10 rounded-2xl p-6">
|
|
54
|
+
<div className="flex items-center justify-between mb-4">
|
|
55
|
+
<h2 className="text-lg font-semibold text-white">Response</h2>
|
|
56
|
+
{isLoading && (
|
|
57
|
+
<div className="flex items-center gap-2 text-yellow-400">
|
|
58
|
+
<div className="w-4 h-4 border-2 border-yellow-400 border-t-transparent rounded-full animate-spin"></div>
|
|
59
|
+
Loading...
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
<pre className="bg-black/60 rounded-xl p-4 overflow-auto max-h-96 text-sm font-mono">
|
|
64
|
+
<code className="text-green-400">
|
|
65
|
+
{apiResponse || '// Click a button above to test the API\\n// Type inference works automatically!'}
|
|
66
|
+
</code>
|
|
67
|
+
</pre>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="mt-8 bg-white/5 border border-white/10 rounded-xl p-6">
|
|
71
|
+
<h3 className="text-lg font-semibold text-white mb-3">How it works</h3>
|
|
72
|
+
<div className="text-gray-400 text-sm space-y-2">
|
|
73
|
+
<p>OK <code className="text-purple-400">api.health.get()</code> - Full type inference from server</p>
|
|
74
|
+
<p>OK <code className="text-purple-400">api.users.post({'{ name, email }'})</code> - Request body is typed</p>
|
|
75
|
+
<p>OK <code className="text-purple-400">{'{ data, error }'}</code> - Response is typed automatically</p>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ActionCard({
|
|
83
|
+
icon,
|
|
84
|
+
title,
|
|
85
|
+
subtitle,
|
|
86
|
+
onClick,
|
|
87
|
+
disabled,
|
|
88
|
+
className
|
|
89
|
+
}: {
|
|
90
|
+
icon: ReactNode
|
|
91
|
+
title: string
|
|
92
|
+
subtitle: string
|
|
93
|
+
onClick: () => void
|
|
94
|
+
disabled: boolean
|
|
95
|
+
className: string
|
|
96
|
+
}) {
|
|
97
|
+
return (
|
|
98
|
+
<button
|
|
99
|
+
onClick={onClick}
|
|
100
|
+
disabled={disabled}
|
|
101
|
+
className={`px-6 py-4 border rounded-xl font-medium transition-all disabled:opacity-50 ${className}`}
|
|
102
|
+
>
|
|
103
|
+
<div className="text-2xl mb-2">{icon}</div>
|
|
104
|
+
<div>{title}</div>
|
|
105
|
+
<div className="text-xs opacity-70 mt-1">{subtitle}</div>
|
|
106
|
+
</button>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Link } from 'react-router'
|
|
2
|
+
import { FaFire } from 'react-icons/fa'
|
|
3
|
+
|
|
4
|
+
export function HomePage({ apiStatus }: { apiStatus: 'checking' | 'online' | 'offline' }) {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-72px)] px-6 text-center">
|
|
7
|
+
<div className="mb-8 animate-pulse-slow">
|
|
8
|
+
<FaFire className="text-8xl text-orange-500 drop-shadow-2xl" />
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<h1 className="text-6xl md:text-7xl font-bold mb-4 bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
|
|
12
|
+
FluxStack
|
|
13
|
+
</h1>
|
|
14
|
+
|
|
15
|
+
<p className="text-xl md:text-2xl text-gray-300 mb-8 max-w-2xl">
|
|
16
|
+
Full-stack TypeScript framework with{' '}
|
|
17
|
+
<span className="text-purple-400 font-semibold">Bun</span>,{' '}
|
|
18
|
+
<span className="text-blue-400 font-semibold">Elysia</span>, and{' '}
|
|
19
|
+
<span className="text-cyan-400 font-semibold">React</span>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<div className="mb-12">
|
|
23
|
+
<div className={`inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
|
24
|
+
apiStatus === 'online'
|
|
25
|
+
? 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/30'
|
|
26
|
+
: apiStatus === 'offline'
|
|
27
|
+
? 'bg-red-500/20 text-red-300 border border-red-500/30'
|
|
28
|
+
: 'bg-yellow-500/20 text-yellow-300 border border-yellow-500/30'
|
|
29
|
+
}`}>
|
|
30
|
+
<div className={`w-2 h-2 rounded-full ${
|
|
31
|
+
apiStatus === 'online' ? 'bg-emerald-400' : apiStatus === 'offline' ? 'bg-red-400' : 'bg-yellow-400'
|
|
32
|
+
}`}></div>
|
|
33
|
+
<span>{apiStatus === 'checking' ? 'Checking API...' : apiStatus === 'online' ? 'API Online' : 'API Offline'}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12 max-w-4xl">
|
|
38
|
+
<div className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl p-6 hover:bg-white/10 transition-all">
|
|
39
|
+
<div className="text-3xl mb-3">⚡</div>
|
|
40
|
+
<h3 className="text-lg font-semibold text-white mb-2">Ultra Rápido</h3>
|
|
41
|
+
<p className="text-gray-400 text-sm">Bun runtime 3x mais rápido que Node.js</p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl p-6 hover:bg-white/10 transition-all">
|
|
45
|
+
<div className="text-3xl mb-3">🔒</div>
|
|
46
|
+
<h3 className="text-lg font-semibold text-white mb-2">Type Safe</h3>
|
|
47
|
+
<p className="text-gray-400 text-sm">Eden Treaty com inferência automática</p>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl p-6 hover:bg-white/10 transition-all">
|
|
51
|
+
<div className="text-3xl mb-3">🔥</div>
|
|
52
|
+
<h3 className="text-lg font-semibold text-white mb-2">Live Components</h3>
|
|
53
|
+
<p className="text-gray-400 text-sm">Estado reativo no servidor estilo Livewire</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div className="mt-4 text-gray-500 text-sm">
|
|
58
|
+
Use a navegação no topo para acessar as demos.
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div className="mt-16 text-gray-500 text-sm">
|
|
62
|
+
<p>Desenvolvido com ❤️ usando TypeScript</p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<style>{`
|
|
66
|
+
@keyframes pulse-slow {
|
|
67
|
+
0%, 100% { opacity: 1; }
|
|
68
|
+
50% { opacity: 0.8; }
|
|
69
|
+
}
|
|
70
|
+
.animate-pulse-slow {
|
|
71
|
+
animation: pulse-slow 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
72
|
+
}
|
|
73
|
+
`}</style>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
package/app/server/app.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* 🎯 Application Instance - Single Source of Truth
|
|
3
3
|
*
|
|
4
4
|
* This instance is used by:
|
|
5
|
-
* - index.ts (full-stack
|
|
6
|
-
* - backend-only.ts (backend standalone mode)
|
|
5
|
+
* - index.ts (full-stack & backend-only modes via FLUXSTACK_MODE env)
|
|
7
6
|
* - Eden Treaty client (type inference)
|
|
8
7
|
*
|
|
9
8
|
* This ensures that the type exported for Eden Treaty is exactly
|
|
@@ -12,13 +11,11 @@
|
|
|
12
11
|
|
|
13
12
|
import { Elysia } from "elysia"
|
|
14
13
|
import { apiRoutes } from "./routes"
|
|
15
|
-
import { envTestRoute } from "./routes/env-test"
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
16
|
* Main application instance with all routes registered
|
|
19
17
|
*/
|
|
20
18
|
export const appInstance = new Elysia()
|
|
21
|
-
.use(envTestRoute) // Environment test/debug endpoint
|
|
22
19
|
.use(apiRoutes) // Main application routes
|
|
23
20
|
|
|
24
21
|
// Export the type correctly for Eden Treaty
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { CreateUserRequest } from '
|
|
1
|
+
import type { CreateUserRequest } from '@app/shared/types'
|
|
2
2
|
|
|
3
|
-
// User type
|
|
4
3
|
export interface User {
|
|
5
4
|
id: number
|
|
6
5
|
name: string
|
|
@@ -8,88 +7,81 @@ export interface User {
|
|
|
8
7
|
createdAt: Date
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
/**
|
|
11
|
+
* In-memory user store for demonstration purposes.
|
|
12
|
+
* Replace with a real database (e.g. Drizzle, Prisma) for production use.
|
|
13
|
+
*/
|
|
15
14
|
export class UsersController {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
private static users: User[] = []
|
|
16
|
+
private static nextId = 1
|
|
17
|
+
|
|
19
18
|
static async getUsers() {
|
|
20
19
|
return {
|
|
21
|
-
|
|
20
|
+
success: true as const,
|
|
21
|
+
users: this.users,
|
|
22
|
+
count: this.users.length
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/**
|
|
26
|
-
* Get user by ID
|
|
27
|
-
*/
|
|
28
26
|
static async getUserById(id: number) {
|
|
29
|
-
const user = users.find(u => u.id === id)
|
|
27
|
+
const user = this.users.find(u => u.id === id)
|
|
30
28
|
if (!user) {
|
|
31
|
-
return
|
|
29
|
+
return {
|
|
30
|
+
success: false as const,
|
|
31
|
+
error: 'Usuario nao encontrado'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
success: true as const,
|
|
36
|
+
user
|
|
32
37
|
}
|
|
33
|
-
return { user }
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
/**
|
|
37
|
-
* Create a new user
|
|
38
|
-
*/
|
|
39
40
|
static async createUser(data: CreateUserRequest) {
|
|
40
|
-
|
|
41
|
-
const existingUser = users.find(u => u.email === data.email)
|
|
41
|
+
const existingUser = this.users.find(u => u.email === data.email)
|
|
42
42
|
if (existingUser) {
|
|
43
43
|
return {
|
|
44
|
-
success: false,
|
|
45
|
-
|
|
44
|
+
success: false as const,
|
|
45
|
+
error: 'Email ja esta em uso'
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// Create new user
|
|
50
49
|
const newUser: User = {
|
|
51
|
-
id: nextId++,
|
|
50
|
+
id: this.nextId++,
|
|
52
51
|
name: data.name,
|
|
53
52
|
email: data.email,
|
|
54
53
|
createdAt: new Date()
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
users.push(newUser)
|
|
56
|
+
this.users.push(newUser)
|
|
58
57
|
|
|
59
58
|
return {
|
|
60
|
-
success: true,
|
|
61
|
-
user: newUser
|
|
59
|
+
success: true as const,
|
|
60
|
+
user: newUser,
|
|
61
|
+
message: 'Usuario criado com sucesso'
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
* Delete user by ID
|
|
67
|
-
*/
|
|
68
65
|
static async deleteUser(id: number) {
|
|
69
|
-
const userIndex = users.findIndex(u => u.id === id)
|
|
66
|
+
const userIndex = this.users.findIndex(u => u.id === id)
|
|
70
67
|
|
|
71
68
|
if (userIndex === -1) {
|
|
72
69
|
return {
|
|
73
|
-
success: false,
|
|
74
|
-
message: '
|
|
70
|
+
success: false as const,
|
|
71
|
+
message: 'Usuario nao encontrado'
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
users.splice(userIndex, 1)
|
|
75
|
+
this.users.splice(userIndex, 1)
|
|
80
76
|
|
|
81
77
|
return {
|
|
82
|
-
success: true,
|
|
83
|
-
|
|
84
|
-
message: 'Usuário deletado com sucesso'
|
|
78
|
+
success: true as const,
|
|
79
|
+
message: 'Usuario deletado com sucesso'
|
|
85
80
|
}
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
/**
|
|
89
|
-
* Reset users array for testing purposes
|
|
90
|
-
*/
|
|
91
83
|
static resetForTesting() {
|
|
92
|
-
users = []
|
|
93
|
-
nextId = 1
|
|
84
|
+
this.users = []
|
|
85
|
+
this.nextId = 1
|
|
94
86
|
}
|
|
95
87
|
}
|
package/app/server/index.ts
CHANGED
|
@@ -1,42 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FluxStack Application Server Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Modos (via FLUXSTACK_MODE ou appConfig.mode):
|
|
5
|
+
* - full-stack: Backend + Vite + LiveComponents (padrão)
|
|
6
|
+
* - backend-only: Backend + LiveComponents (sem Vite)
|
|
7
|
+
*
|
|
8
|
+
* Frontend-only roda direto do core (core/client/standalone-entry.ts)
|
|
9
|
+
*
|
|
10
|
+
* 📖 Docs: ai-context/reference/plugin-security.md
|
|
3
11
|
*/
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
13
|
+
import { FluxStackFramework } from "@core/server"
|
|
14
|
+
import { vitePlugin } from "@core/plugins/built-in/vite"
|
|
15
|
+
import { swaggerPlugin } from "@core/plugins/built-in/swagger"
|
|
16
|
+
import { liveComponentsPlugin } from "@core/server/live/websocket-plugin"
|
|
17
|
+
import { appInstance } from "@server/app"
|
|
18
|
+
import { appConfig } from "@config"
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
import { vitePlugin, swaggerPlugin, liveComponentsPlugin, staticFilesPlugin } from "@/core/server"
|
|
13
|
-
import cryptoAuthPlugin from "@/plugins/crypto-auth"
|
|
14
|
-
// Routes & Components
|
|
15
|
-
import { appInstance } from "./app"
|
|
16
|
-
|
|
17
|
-
// Server
|
|
18
|
-
const app = new FluxStackFramework({
|
|
19
|
-
server: {
|
|
20
|
-
...serverConfig.server,
|
|
21
|
-
cors: serverConfig.cors,
|
|
22
|
-
middleware: []
|
|
23
|
-
},
|
|
24
|
-
app: {
|
|
25
|
-
name: appConfig.name,
|
|
26
|
-
version: appConfig.version
|
|
27
|
-
},
|
|
28
|
-
client: {
|
|
29
|
-
port: serverConfig.server.backendPort,
|
|
30
|
-
proxy: { target: helpers.getServerUrl() },
|
|
31
|
-
build: { sourceMaps: false, minify: false, target: 'es2020', outDir: 'dist' }
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
.use(cryptoAuthPlugin)
|
|
35
|
-
.use(vitePlugin)
|
|
36
|
-
.use(staticFilesPlugin)
|
|
37
|
-
.use(liveComponentsPlugin)
|
|
38
|
-
.routes(appInstance)
|
|
20
|
+
const framework = new FluxStackFramework()
|
|
39
21
|
.use(swaggerPlugin)
|
|
40
|
-
.
|
|
22
|
+
.use(liveComponentsPlugin)
|
|
23
|
+
|
|
24
|
+
// Vite apenas em full-stack
|
|
25
|
+
if (appConfig.mode !== 'backend-only') {
|
|
26
|
+
framework.use(vitePlugin)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
framework.routes(appInstance)
|
|
30
|
+
await framework.listen()
|
|
41
31
|
|
|
42
|
-
export
|
|
32
|
+
export const app = framework
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// LiveChat - Chat compartilhado por sala
|
|
2
|
+
|
|
3
|
+
import { LiveComponent, type FluxStackWebSocket } from '@core/types/types'
|
|
4
|
+
|
|
5
|
+
// Componente Cliente (Ctrl+Click para navegar)
|
|
6
|
+
import type { ChatDemo as _Client } from '@client/src/live/ChatDemo'
|
|
7
|
+
|
|
8
|
+
export type ChatMessage = {
|
|
9
|
+
id: string
|
|
10
|
+
user: string
|
|
11
|
+
text: string
|
|
12
|
+
timestamp: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class LiveChat extends LiveComponent<typeof LiveChat.defaultState> {
|
|
16
|
+
static componentName = 'LiveChat'
|
|
17
|
+
static defaultState = {
|
|
18
|
+
messages: [] as ChatMessage[]
|
|
19
|
+
}
|
|
20
|
+
protected roomType = 'chat'
|
|
21
|
+
private maxMessages = 50
|
|
22
|
+
private static roomHistory = new Map<string, ChatMessage[]>()
|
|
23
|
+
|
|
24
|
+
constructor(initialState: Partial<typeof LiveChat.defaultState> = {}, ws: FluxStackWebSocket, options?: { room?: string; userId?: string }) {
|
|
25
|
+
super(initialState, ws, options)
|
|
26
|
+
|
|
27
|
+
this.onRoomEvent<ChatMessage>('NEW_MESSAGE', (message) => {
|
|
28
|
+
this.addMessage(message)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
if (this.room) {
|
|
32
|
+
const history = LiveChat.roomHistory.get(this.room) || []
|
|
33
|
+
if (history.length > 0) {
|
|
34
|
+
this.setState({ messages: history })
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private addMessage(message: ChatMessage) {
|
|
40
|
+
const next = [...this.state.messages, message].slice(-this.maxMessages)
|
|
41
|
+
if (this.room) {
|
|
42
|
+
LiveChat.roomHistory.set(this.room, next)
|
|
43
|
+
}
|
|
44
|
+
this.setState({ messages: next })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async sendMessage(payload: { user: string; text: string }) {
|
|
48
|
+
const text = payload.text?.trim()
|
|
49
|
+
const user = payload.user?.trim() || 'anonymous'
|
|
50
|
+
|
|
51
|
+
if (!text) {
|
|
52
|
+
throw new Error('Message cannot be empty')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (text.length > 500) {
|
|
56
|
+
throw new Error('Message too long')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const message: ChatMessage = {
|
|
60
|
+
id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
61
|
+
user,
|
|
62
|
+
text,
|
|
63
|
+
timestamp: Date.now()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const next = [...this.state.messages, message].slice(-this.maxMessages)
|
|
67
|
+
if (this.room) {
|
|
68
|
+
LiveChat.roomHistory.set(this.room, next)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.emitRoomEventWithState('NEW_MESSAGE', message, {
|
|
72
|
+
messages: next
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
return { success: true }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// LiveCounter - Contador compartilhado usando Room Events
|
|
2
|
+
|
|
3
|
+
import { LiveComponent, type FluxStackWebSocket } from '@core/types/types'
|
|
4
|
+
|
|
5
|
+
// Componente Cliente (Ctrl+Click para navegar)
|
|
6
|
+
import type { CounterDemo as _Client } from '@client/src/live/CounterDemo'
|
|
7
|
+
|
|
8
|
+
export class LiveCounter extends LiveComponent<typeof LiveCounter.defaultState> {
|
|
9
|
+
static componentName = 'LiveCounter'
|
|
10
|
+
static defaultState = {
|
|
11
|
+
count: 0,
|
|
12
|
+
lastUpdatedBy: null as string | null,
|
|
13
|
+
connectedUsers: 0
|
|
14
|
+
}
|
|
15
|
+
protected roomType = 'counter'
|
|
16
|
+
|
|
17
|
+
constructor(initialState: Partial<typeof LiveCounter.defaultState> = {}, ws: FluxStackWebSocket, options?: { room?: string; userId?: string }) {
|
|
18
|
+
super(initialState, ws, options)
|
|
19
|
+
|
|
20
|
+
this.onRoomEvent<{ count: number; userId: string }>('COUNT_CHANGED', (data) => {
|
|
21
|
+
this.setState({ count: data.count, lastUpdatedBy: data.userId })
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
this.onRoomEvent<{ connectedUsers: number }>('USER_COUNT_CHANGED', (data) => {
|
|
25
|
+
this.setState({ connectedUsers: data.connectedUsers })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
this.notifyUserJoined()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private notifyUserJoined() {
|
|
32
|
+
const newCount = this.state.connectedUsers + 1
|
|
33
|
+
this.emitRoomEventWithState('USER_COUNT_CHANGED', { connectedUsers: newCount }, { connectedUsers: newCount })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async increment() {
|
|
37
|
+
const newCount = this.state.count + 1
|
|
38
|
+
this.emitRoomEventWithState('COUNT_CHANGED', { count: newCount, userId: this.userId || 'anonymous' }, {
|
|
39
|
+
count: newCount,
|
|
40
|
+
lastUpdatedBy: this.userId || 'anonymous'
|
|
41
|
+
})
|
|
42
|
+
return { success: true, count: newCount }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async decrement() {
|
|
46
|
+
const newCount = this.state.count - 1
|
|
47
|
+
this.emitRoomEventWithState('COUNT_CHANGED', { count: newCount, userId: this.userId || 'anonymous' }, {
|
|
48
|
+
count: newCount,
|
|
49
|
+
lastUpdatedBy: this.userId || 'anonymous'
|
|
50
|
+
})
|
|
51
|
+
return { success: true, count: newCount }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async reset() {
|
|
55
|
+
this.emitRoomEventWithState('COUNT_CHANGED', { count: 0, userId: this.userId || 'anonymous' }, {
|
|
56
|
+
count: 0,
|
|
57
|
+
lastUpdatedBy: this.userId || 'anonymous'
|
|
58
|
+
})
|
|
59
|
+
return { success: true, count: 0 }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
destroy() {
|
|
63
|
+
const newCount = Math.max(0, this.state.connectedUsers - 1)
|
|
64
|
+
this.emitRoomEvent('USER_COUNT_CHANGED', { connectedUsers: newCount })
|
|
65
|
+
super.destroy()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// LiveForm - Formulário reativo com estado no servidor
|
|
2
|
+
|
|
3
|
+
import { LiveComponent } from "@core/types/types"
|
|
4
|
+
|
|
5
|
+
// Componente Cliente (Ctrl+Click para navegar)
|
|
6
|
+
import type { FormDemo as _Client } from '@client/src/live/FormDemo'
|
|
7
|
+
|
|
8
|
+
export class LiveForm extends LiveComponent<typeof LiveForm.defaultState> {
|
|
9
|
+
static componentName = 'LiveForm'
|
|
10
|
+
static defaultState = {
|
|
11
|
+
name: '',
|
|
12
|
+
email: '',
|
|
13
|
+
message: '',
|
|
14
|
+
submitted: false,
|
|
15
|
+
submittedAt: null as string | null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async submit() {
|
|
19
|
+
const { name, email, message } = this.state
|
|
20
|
+
|
|
21
|
+
if (!name || !email) {
|
|
22
|
+
throw new Error('Nome e email são obrigatórios')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(`📝 Form submitted:`, { name, email, message })
|
|
26
|
+
|
|
27
|
+
this.setState({
|
|
28
|
+
submitted: true,
|
|
29
|
+
submittedAt: new Date().toISOString()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
data: { name, email, message },
|
|
35
|
+
submittedAt: this.state.submittedAt
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async reset() {
|
|
40
|
+
this.setState({
|
|
41
|
+
name: '',
|
|
42
|
+
email: '',
|
|
43
|
+
message: '',
|
|
44
|
+
submitted: false,
|
|
45
|
+
submittedAt: null
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return { success: true }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async validate() {
|
|
52
|
+
const errors: Record<string, string> = {}
|
|
53
|
+
|
|
54
|
+
if (!this.state.name) errors.name = 'Nome é obrigatório'
|
|
55
|
+
if (!this.state.email) errors.email = 'Email é obrigatório'
|
|
56
|
+
else if (!this.state.email.includes('@')) errors.email = 'Email inválido'
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
valid: Object.keys(errors).length === 0,
|
|
60
|
+
errors
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// LiveLocalCounter - Contador sem eventos de sala
|
|
2
|
+
|
|
3
|
+
import { LiveComponent } from '@core/types/types'
|
|
4
|
+
|
|
5
|
+
// Componente Cliente (Ctrl+Click para navegar)
|
|
6
|
+
import type { CounterDemo as _Client } from '@client/src/live/CounterDemo'
|
|
7
|
+
|
|
8
|
+
export class LiveLocalCounter extends LiveComponent<typeof LiveLocalCounter.defaultState> {
|
|
9
|
+
static componentName = 'LiveLocalCounter'
|
|
10
|
+
static defaultState = {
|
|
11
|
+
count: 0,
|
|
12
|
+
clicks: 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async increment() {
|
|
16
|
+
this.state.count++
|
|
17
|
+
this.state.clicks++
|
|
18
|
+
return { success: true, count: this.state.count }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async decrement() {
|
|
22
|
+
this.state.count--
|
|
23
|
+
this.state.clicks++
|
|
24
|
+
return { success: true, count: this.state.count }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async reset() {
|
|
28
|
+
this.state.count = 0
|
|
29
|
+
this.state.clicks++
|
|
30
|
+
return { success: true, count: 0 }
|
|
31
|
+
}
|
|
32
|
+
}
|