create-fluxstack 1.8.3 โ†’ 1.10.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.
Files changed (52) hide show
  1. package/LIVE_COMPONENTS_REVIEW.md +781 -0
  2. package/README.md +653 -275
  3. package/app/client/src/App.tsx +39 -43
  4. package/app/client/src/lib/eden-api.ts +2 -7
  5. package/app/client/src/live/FileUploadExample.tsx +359 -0
  6. package/app/client/src/live/MinimalLiveClock.tsx +47 -0
  7. package/app/client/src/live/QuickUploadTest.tsx +193 -0
  8. package/app/client/src/main.tsx +10 -10
  9. package/app/client/src/vite-env.d.ts +1 -1
  10. package/app/client/tsconfig.app.json +45 -44
  11. package/app/client/tsconfig.node.json +25 -25
  12. package/app/server/index.ts +30 -103
  13. package/app/server/live/LiveFileUploadComponent.ts +77 -0
  14. package/app/server/live/register-components.ts +19 -19
  15. package/core/build/bundler.ts +202 -55
  16. package/core/build/index.ts +126 -2
  17. package/core/build/live-components-generator.ts +68 -1
  18. package/core/cli/generators/plugin.ts +6 -6
  19. package/core/cli/index.ts +232 -4
  20. package/core/client/LiveComponentsProvider.tsx +3 -9
  21. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -0
  22. package/core/client/hooks/useChunkedUpload.ts +112 -61
  23. package/core/client/hooks/useHybridLiveComponent.ts +80 -26
  24. package/core/client/hooks/useTypedLiveComponent.ts +133 -0
  25. package/core/client/hooks/useWebSocket.ts +4 -16
  26. package/core/client/index.ts +20 -2
  27. package/core/framework/server.ts +181 -8
  28. package/core/live/ComponentRegistry.ts +5 -1
  29. package/core/plugins/built-in/index.ts +8 -5
  30. package/core/plugins/built-in/live-components/commands/create-live-component.ts +55 -63
  31. package/core/plugins/built-in/vite/index.ts +75 -187
  32. package/core/plugins/built-in/vite/vite-dev.ts +88 -0
  33. package/core/plugins/registry.ts +54 -2
  34. package/core/plugins/types.ts +86 -2
  35. package/core/server/index.ts +1 -2
  36. package/core/server/live/ComponentRegistry.ts +14 -5
  37. package/core/server/live/FileUploadManager.ts +22 -25
  38. package/core/server/live/auto-generated-components.ts +29 -26
  39. package/core/server/live/websocket-plugin.ts +19 -5
  40. package/core/server/plugins/static-files-plugin.ts +49 -240
  41. package/core/server/plugins/swagger.ts +33 -33
  42. package/core/types/build.ts +22 -0
  43. package/core/types/plugin.ts +9 -1
  44. package/core/types/types.ts +137 -0
  45. package/core/utils/logger/startup-banner.ts +20 -4
  46. package/core/utils/version.ts +6 -6
  47. package/create-fluxstack.ts +7 -7
  48. package/eslint.config.js +23 -23
  49. package/package.json +3 -2
  50. package/plugins/crypto-auth/server/middlewares.ts +19 -19
  51. package/tsconfig.json +52 -51
  52. package/workspace.json +5 -5
@@ -79,34 +79,18 @@ export class Bundler {
79
79
  let liveComponentsGenerator: any = null
80
80
 
81
81
  try {
82
- // ๐Ÿš€ PRE-BUILD: Auto-generate Live Components registration
83
- const generatorModule = await import('./live-components-generator')
84
- liveComponentsGenerator = generatorModule.liveComponentsGenerator
85
- const discoveredComponents = await liveComponentsGenerator.preBuild()
86
-
87
- // ๐Ÿ”Œ PRE-BUILD: Auto-generate FluxStack Plugins registration
88
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
89
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
90
- const discoveredPlugins = await fluxPluginsGenerator.preBuild()
91
-
82
+ // Run pre-build steps
83
+ liveComponentsGenerator = await this.runPreBuildSteps()
84
+
92
85
  // Ensure output directory exists
93
- if (!existsSync(this.config.outDir)) {
94
- mkdirSync(this.config.outDir, { recursive: true })
95
- }
86
+ this.ensureOutputDirectory()
96
87
 
97
- const external = [
98
- "@tailwindcss/vite",
99
- "tailwindcss",
100
- "lightningcss",
101
- "vite",
102
- "@vitejs/plugin-react",
103
- ...(this.config.external || []),
104
- ...(options.external || [])
105
- ]
88
+ // Get external dependencies
89
+ const external = this.getExternalDependencies(options)
106
90
 
107
91
  const buildArgs = [
108
- "bun", "build",
109
- entryPoint,
92
+ "bun", "build",
93
+ entryPoint,
110
94
  "--outdir", this.config.outDir,
111
95
  "--target", this.config.target,
112
96
  ...external.flatMap(ext => ["--external", ext])
@@ -132,20 +116,12 @@ export class Bundler {
132
116
  const exitCode = await buildProcess.exited
133
117
  const duration = Date.now() - startTime
134
118
 
135
- // ๐Ÿงน POST-BUILD: Handle auto-generated registration file
136
- // (liveComponentsGenerator already available from above)
137
-
138
119
  if (exitCode === 0) {
139
120
  buildLogger.success(`Server bundle completed in ${buildLogger.formatDuration(duration)}`)
140
-
141
- // Keep generated files for production (they're now baked into bundle)
142
- await liveComponentsGenerator.postBuild(false)
143
-
144
- // Cleanup plugins registry
145
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
146
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
147
- await fluxPluginsGenerator.postBuild(false)
148
-
121
+
122
+ // Run post-build cleanup
123
+ await this.runPostBuildCleanup(liveComponentsGenerator)
124
+
149
125
  return {
150
126
  success: true,
151
127
  duration,
@@ -154,15 +130,10 @@ export class Bundler {
154
130
  }
155
131
  } else {
156
132
  buildLogger.error("Server bundle failed")
157
-
158
- // Restore original files since build failed
159
- await liveComponentsGenerator.postBuild(false)
160
-
161
- // Restore plugins registry
162
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
163
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
164
- await fluxPluginsGenerator.postBuild(false)
165
-
133
+
134
+ // Run post-build cleanup
135
+ await this.runPostBuildCleanup(liveComponentsGenerator)
136
+
166
137
  const stderr = await new Response(buildProcess.stderr).text()
167
138
  return {
168
139
  success: false,
@@ -172,21 +143,142 @@ export class Bundler {
172
143
  }
173
144
  } catch (error) {
174
145
  const duration = Date.now() - startTime
175
-
146
+
147
+ // ๐Ÿงน CLEANUP: Restore original files on error
148
+ try {
149
+ await this.runPostBuildCleanup(liveComponentsGenerator)
150
+ } catch (cleanupError) {
151
+ buildLogger.warn(`Failed to cleanup generated files: ${cleanupError}`)
152
+ }
153
+
154
+ return {
155
+ success: false,
156
+ duration,
157
+ error: error instanceof Error ? error.message : "Unknown error"
158
+ }
159
+ }
160
+ }
161
+
162
+ async compileToExecutable(entryPoint: string, outputName: string = "app", options: BundleOptions = {}): Promise<BundleResult> {
163
+ buildLogger.section('Executable Build', '๐Ÿ“ฆ')
164
+
165
+ const startTime = Date.now()
166
+ let liveComponentsGenerator: any = null
167
+
168
+ try {
169
+ // Run pre-build steps
170
+ liveComponentsGenerator = await this.runPreBuildSteps()
171
+
172
+ // Ensure output directory exists
173
+ this.ensureOutputDirectory()
174
+
175
+ const outputPath = join(this.config.outDir, outputName)
176
+
177
+ // Get external dependencies
178
+ const external = this.getExternalDependencies(options)
179
+
180
+ // Use target from options or fall back to config
181
+ const target = options.target || this.config.target
182
+
183
+ const buildArgs = [
184
+ "bun", "build",
185
+ entryPoint,
186
+ "--compile",
187
+ "--outfile", outputPath,
188
+ "--target", target,
189
+ ...external.flatMap(ext => ["--external", ext])
190
+ ]
191
+
192
+ if (this.config.sourceMaps) {
193
+ buildArgs.push("--sourcemap")
194
+ }
195
+
196
+ if (this.config.minify) {
197
+ buildArgs.push("--minify")
198
+ }
199
+
200
+ // Add Windows-specific options if provided
201
+ if (options.executable?.windows) {
202
+ const winOpts = options.executable.windows
203
+ if (winOpts.hideConsole) {
204
+ buildArgs.push("--windows-hide-console")
205
+ }
206
+ if (winOpts.icon) {
207
+ buildArgs.push("--windows-icon", winOpts.icon)
208
+ }
209
+ if (winOpts.title) {
210
+ buildArgs.push("--windows-title", winOpts.title)
211
+ }
212
+ if (winOpts.publisher) {
213
+ buildArgs.push("--windows-publisher", winOpts.publisher)
214
+ }
215
+ if (winOpts.version) {
216
+ buildArgs.push("--windows-version", winOpts.version)
217
+ }
218
+ if (winOpts.description) {
219
+ buildArgs.push("--windows-description", winOpts.description)
220
+ }
221
+ if (winOpts.copyright) {
222
+ buildArgs.push("--windows-copyright", winOpts.copyright)
223
+ }
224
+ }
225
+
226
+ // Add custom build arguments if provided
227
+ if (options.executable?.customArgs) {
228
+ buildArgs.push(...options.executable.customArgs)
229
+ }
230
+
231
+ buildLogger.step(`Compiling ${entryPoint} to ${outputPath}...`)
232
+
233
+ const buildProcess = spawn({
234
+ cmd: buildArgs,
235
+ stdout: "pipe",
236
+ stderr: "pipe",
237
+ env: {
238
+ ...process.env,
239
+ NODE_ENV: 'production',
240
+ ...options.env
241
+ }
242
+ })
243
+
244
+ const exitCode = await buildProcess.exited
245
+ const duration = Date.now() - startTime
246
+
247
+ if (exitCode === 0) {
248
+ buildLogger.success(`Executable compiled in ${buildLogger.formatDuration(duration)}`)
249
+
250
+ // Run post-build cleanup
251
+ await this.runPostBuildCleanup(liveComponentsGenerator)
252
+
253
+ return {
254
+ success: true,
255
+ duration,
256
+ outputPath,
257
+ entryPoint: outputPath
258
+ }
259
+ } else {
260
+ buildLogger.error("Executable compilation failed")
261
+
262
+ // Run post-build cleanup
263
+ await this.runPostBuildCleanup(liveComponentsGenerator)
264
+
265
+ const stderr = await new Response(buildProcess.stderr).text()
266
+ return {
267
+ success: false,
268
+ duration,
269
+ error: stderr || "Executable compilation failed"
270
+ }
271
+ }
272
+ } catch (error) {
273
+ const duration = Date.now() - startTime
274
+
176
275
  // ๐Ÿงน CLEANUP: Restore original files on error
177
276
  try {
178
- if (liveComponentsGenerator) {
179
- await liveComponentsGenerator.postBuild(false)
180
- }
181
-
182
- // Cleanup plugins registry
183
- const pluginsGeneratorModule = await import('./flux-plugins-generator')
184
- const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
185
- await fluxPluginsGenerator.postBuild(false)
277
+ await this.runPostBuildCleanup(liveComponentsGenerator)
186
278
  } catch (cleanupError) {
187
279
  buildLogger.warn(`Failed to cleanup generated files: ${cleanupError}`)
188
280
  }
189
-
281
+
190
282
  return {
191
283
  success: false,
192
284
  duration,
@@ -195,6 +287,61 @@ export class Bundler {
195
287
  }
196
288
  }
197
289
 
290
+ /**
291
+ * Get list of external dependencies that should not be bundled
292
+ */
293
+ private getExternalDependencies(options: BundleOptions = {}): string[] {
294
+ return [
295
+ "@tailwindcss/vite",
296
+ "tailwindcss",
297
+ "lightningcss",
298
+ "vite",
299
+ "@vitejs/plugin-react",
300
+ "rollup",
301
+ ...(this.config.external || []),
302
+ ...(options.external || [])
303
+ ]
304
+ }
305
+
306
+ /**
307
+ * Ensure output directory exists
308
+ */
309
+ private ensureOutputDirectory(): void {
310
+ if (!existsSync(this.config.outDir)) {
311
+ mkdirSync(this.config.outDir, { recursive: true })
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Run pre-build steps (Live Components and Plugins generation)
317
+ */
318
+ private async runPreBuildSteps(): Promise<any> {
319
+ // ๐Ÿš€ PRE-BUILD: Auto-generate Live Components registration
320
+ const generatorModule = await import('./live-components-generator')
321
+ const liveComponentsGenerator = generatorModule.liveComponentsGenerator
322
+ await liveComponentsGenerator.preBuild()
323
+
324
+ // ๐Ÿ”Œ PRE-BUILD: Auto-generate FluxStack Plugins registration
325
+ const pluginsGeneratorModule = await import('./flux-plugins-generator')
326
+ const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
327
+ await fluxPluginsGenerator.preBuild()
328
+
329
+ return liveComponentsGenerator
330
+ }
331
+
332
+ /**
333
+ * Run post-build cleanup
334
+ */
335
+ private async runPostBuildCleanup(liveComponentsGenerator: any): Promise<void> {
336
+ if (liveComponentsGenerator) {
337
+ await liveComponentsGenerator.postBuild(false)
338
+ }
339
+
340
+ const pluginsGeneratorModule = await import('./flux-plugins-generator')
341
+ const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
342
+ await fluxPluginsGenerator.postBuild(false)
343
+ }
344
+
198
345
  private async getClientAssets(): Promise<string[]> {
199
346
  // This would analyze the build output to get asset information
200
347
  // For now, return empty array - can be enhanced later
@@ -1,4 +1,4 @@
1
- import { copyFileSync, writeFileSync, existsSync, mkdirSync, readFileSync } from "fs"
1
+ import { copyFileSync, writeFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs"
2
2
  import { join } from "path"
3
3
  import type { FluxStackConfig } from "../config"
4
4
  import type { BuildResult, BuildManifest } from "../types/build"
@@ -6,14 +6,18 @@ import { Bundler } from "./bundler"
6
6
  import { Optimizer } from "./optimizer"
7
7
  import { FLUXSTACK_VERSION } from "../utils/version"
8
8
  import { buildLogger } from "../utils/build-logger"
9
+ import type { PluginRegistry } from "../plugins/registry"
10
+ import type { BuildContext, BuildAssetContext, BuildErrorContext } from "../plugins/types"
9
11
 
10
12
  export class FluxStackBuilder {
11
13
  private config: FluxStackConfig
12
14
  private bundler: Bundler
13
15
  private optimizer: Optimizer
16
+ private pluginRegistry?: PluginRegistry
14
17
 
15
- constructor(config: FluxStackConfig) {
18
+ constructor(config: FluxStackConfig, pluginRegistry?: PluginRegistry) {
16
19
  this.config = config
20
+ this.pluginRegistry = pluginRegistry
17
21
 
18
22
  // Initialize bundler with configuration
19
23
  this.bundler = new Bundler({
@@ -47,6 +51,12 @@ export class FluxStackBuilder {
47
51
  return await this.bundler.bundleServer("app/server/index.ts")
48
52
  }
49
53
 
54
+ async buildExecutable(outputName?: string, options?: import("../types/build").BundleOptions) {
55
+ // Use app name from config as default, fallback to "FluxStack"
56
+ const name = outputName || this.config.app.name || "FluxStack"
57
+ return await this.bundler.compileToExecutable("app/server/index.ts", name, options)
58
+ }
59
+
50
60
  async createDockerFiles() {
51
61
  buildLogger.section('Docker Configuration', '๐Ÿณ')
52
62
 
@@ -234,7 +244,17 @@ MONITORING_ENABLED=true
234
244
 
235
245
  const startTime = Date.now()
236
246
 
247
+ const buildContext: BuildContext = {
248
+ target: this.config.build.target,
249
+ outDir: this.config.build.outDir,
250
+ mode: (this.config.build.mode || 'production') as 'development' | 'production',
251
+ config: this.config
252
+ }
253
+
237
254
  try {
255
+ // Execute onBeforeBuild hooks
256
+ await this.executePluginHooks('onBeforeBuild', buildContext)
257
+
238
258
  // Pre-build checks (version sync, etc.)
239
259
  await this.runPreBuildChecks()
240
260
 
@@ -246,6 +266,9 @@ MONITORING_ENABLED=true
246
266
  await this.clean()
247
267
  }
248
268
 
269
+ // Execute onBuild hooks
270
+ await this.executePluginHooks('onBuild', buildContext)
271
+
249
272
  // Build client and server
250
273
  const clientResult = await this.buildClient()
251
274
  const serverResult = await this.buildServer()
@@ -253,6 +276,16 @@ MONITORING_ENABLED=true
253
276
  // Check if builds were successful
254
277
  if (!clientResult.success || !serverResult.success) {
255
278
  const errorMessage = clientResult.error || serverResult.error || "Build failed"
279
+
280
+ // Execute onBuildError hooks
281
+ const buildErrorContext: BuildErrorContext = {
282
+ error: new Error(errorMessage),
283
+ file: undefined,
284
+ line: undefined,
285
+ column: undefined
286
+ }
287
+ await this.executePluginHooks('onBuildError', buildErrorContext)
288
+
256
289
  return {
257
290
  success: false,
258
291
  duration: Date.now() - startTime,
@@ -273,6 +306,9 @@ MONITORING_ENABLED=true
273
306
  }
274
307
  }
275
308
 
309
+ // Process assets and execute onBuildAsset hooks
310
+ await this.processAssets(this.config.build.outDir)
311
+
276
312
  // Optimize build if enabled
277
313
  let optimizationResult
278
314
  if (this.config.build.optimize) {
@@ -287,6 +323,9 @@ MONITORING_ENABLED=true
287
323
 
288
324
  const duration = Date.now() - startTime
289
325
 
326
+ // Execute onBuildComplete hooks
327
+ await this.executePluginHooks('onBuildComplete', buildContext)
328
+
290
329
  // Print build summary
291
330
  buildLogger.summary('Build Completed Successfully', [
292
331
  { label: 'Build Time', value: buildLogger.formatDuration(duration), highlight: true },
@@ -319,6 +358,15 @@ MONITORING_ENABLED=true
319
358
 
320
359
  buildLogger.error(`Build failed: ${errorMessage}`)
321
360
 
361
+ // Execute onBuildError hooks
362
+ const buildErrorContext: BuildErrorContext = {
363
+ error: error instanceof Error ? error : new Error(errorMessage),
364
+ file: undefined,
365
+ line: undefined,
366
+ column: undefined
367
+ }
368
+ await this.executePluginHooks('onBuildError', buildErrorContext)
369
+
322
370
  return {
323
371
  success: false,
324
372
  duration,
@@ -409,4 +457,80 @@ MONITORING_ENABLED=true
409
457
  }
410
458
  }
411
459
  }
460
+
461
+ /**
462
+ * Execute plugin hooks for build process
463
+ */
464
+ private async executePluginHooks(hookName: string, context: any): Promise<void> {
465
+ if (!this.pluginRegistry) return
466
+
467
+ const loadOrder = this.pluginRegistry.getLoadOrder()
468
+
469
+ for (const pluginName of loadOrder) {
470
+ const plugin = this.pluginRegistry.get(pluginName)
471
+ if (!plugin) continue
472
+
473
+ const hookFn = (plugin as any)[hookName]
474
+ if (typeof hookFn === 'function') {
475
+ try {
476
+ await hookFn(context)
477
+ } catch (error) {
478
+ buildLogger.error(`Plugin '${pluginName}' ${hookName} hook failed: ${error instanceof Error ? error.message : String(error)}`)
479
+ }
480
+ }
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Process build assets and execute onBuildAsset hooks
486
+ */
487
+ private async processAssets(outDir: string): Promise<void> {
488
+ if (!this.pluginRegistry) return
489
+ if (!existsSync(outDir)) return
490
+
491
+ try {
492
+ const processDirectory = async (dir: string) => {
493
+ const entries = readdirSync(dir, { withFileTypes: true })
494
+
495
+ for (const entry of entries) {
496
+ const fullPath = join(dir, entry.name)
497
+
498
+ if (entry.isDirectory()) {
499
+ await processDirectory(fullPath)
500
+ } else if (entry.isFile()) {
501
+ const stat = statSync(fullPath)
502
+ const assetType = this.getAssetType(entry.name)
503
+
504
+ const assetContext: BuildAssetContext = {
505
+ assetPath: fullPath,
506
+ assetType,
507
+ size: stat.size,
508
+ content: undefined // Could read file if plugins need it
509
+ }
510
+
511
+ await this.executePluginHooks('onBuildAsset', assetContext)
512
+ }
513
+ }
514
+ }
515
+
516
+ await processDirectory(outDir)
517
+ } catch (error) {
518
+ buildLogger.warn(`Failed to process assets: ${error instanceof Error ? error.message : String(error)}`)
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Determine asset type from file extension
524
+ */
525
+ private getAssetType(filename: string): 'js' | 'css' | 'html' | 'image' | 'font' | 'other' {
526
+ const ext = filename.split('.').pop()?.toLowerCase()
527
+
528
+ if (ext === 'js' || ext === 'mjs' || ext === 'cjs') return 'js'
529
+ if (ext === 'css') return 'css'
530
+ if (ext === 'html' || ext === 'htm') return 'html'
531
+ if (ext === 'png' || ext === 'jpg' || ext === 'jpeg' || ext === 'gif' || ext === 'svg' || ext === 'webp') return 'image'
532
+ if (ext === 'woff' || ext === 'woff2' || ext === 'ttf' || ext === 'eot' || ext === 'otf') return 'font'
533
+
534
+ return 'other'
535
+ }
412
536
  }
@@ -16,6 +16,9 @@ export class LiveComponentsGenerator {
16
16
  private componentsPath: string
17
17
  private registrationFilePath: string
18
18
  private backupFilePath: string
19
+ private entryPointPath: string
20
+ private entryPointBackupPath: string
21
+ private readonly importLine = 'import "@/core/server/live/auto-generated-components"'
19
22
 
20
23
  constructor() {
21
24
  // Scan components from app/ directory (user code)
@@ -24,6 +27,10 @@ export class LiveComponentsGenerator {
24
27
  // Generate registration file in core/ directory (framework code - protected from user modifications)
25
28
  this.registrationFilePath = join(process.cwd(), 'core', 'server', 'live', 'auto-generated-components.ts')
26
29
  this.backupFilePath = join(process.cwd(), 'core', 'server', 'live', 'auto-generated-components.backup.ts')
30
+
31
+ // Entry point for import injection
32
+ this.entryPointPath = join(process.cwd(), 'app', 'server', 'index.ts')
33
+ this.entryPointBackupPath = join(process.cwd(), 'app', 'server', 'index.ts.backup')
27
34
  }
28
35
 
29
36
  /**
@@ -147,6 +154,60 @@ ${components.map(comp => ` ${comp.className}`).join(',\n')}
147
154
  }
148
155
  }
149
156
 
157
+ /**
158
+ * Inject the auto-generated components import into the entry point
159
+ */
160
+ injectImportIntoEntryPoint(): void {
161
+ if (!existsSync(this.entryPointPath)) {
162
+ buildLogger.warn('Entry point not found, skipping import injection')
163
+ return
164
+ }
165
+
166
+ const content = readFileSync(this.entryPointPath, 'utf-8')
167
+
168
+ // Check if import already exists
169
+ if (content.includes(this.importLine)) {
170
+ return // Already injected
171
+ }
172
+
173
+ // Backup original file
174
+ writeFileSync(this.entryPointBackupPath, content)
175
+
176
+ // Find the best place to inject (after other imports)
177
+ const lines = content.split('\n')
178
+ let lastImportIndex = -1
179
+
180
+ for (let i = 0; i < lines.length; i++) {
181
+ const line = lines[i].trim()
182
+ if (line.startsWith('import ') || line.startsWith('import{')) {
183
+ lastImportIndex = i
184
+ }
185
+ }
186
+
187
+ // Inject after the last import
188
+ if (lastImportIndex >= 0) {
189
+ lines.splice(lastImportIndex + 1, 0, this.importLine)
190
+ } else {
191
+ // No imports found, add at the beginning
192
+ lines.unshift(this.importLine)
193
+ }
194
+
195
+ writeFileSync(this.entryPointPath, lines.join('\n'))
196
+ buildLogger.step('Injected auto-generated components import into entry point')
197
+ }
198
+
199
+ /**
200
+ * Remove the injected import and restore original entry point
201
+ */
202
+ removeInjectedImport(): void {
203
+ if (existsSync(this.entryPointBackupPath)) {
204
+ const backupContent = readFileSync(this.entryPointBackupPath, 'utf-8')
205
+ writeFileSync(this.entryPointPath, backupContent)
206
+ unlinkSync(this.entryPointBackupPath)
207
+ buildLogger.step('Restored original entry point')
208
+ }
209
+ }
210
+
150
211
  /**
151
212
  * Check if the current registration file is auto-generated
152
213
  */
@@ -190,11 +251,14 @@ ${components.map(comp => ` ${comp.className}`).join(',\n')}
190
251
 
191
252
  this.generateRegistrationFile(components)
192
253
 
254
+ // Inject import into entry point for build
255
+ this.injectImportIntoEntryPoint()
256
+
193
257
  return components
194
258
  }
195
259
 
196
260
  /**
197
- * Post-build hook: Clean up backup file
261
+ * Post-build hook: Clean up backup file and restore entry point
198
262
  * Note: Since the generated file is now in core/, we always keep it (it's bundled into production)
199
263
  */
200
264
  async postBuild(keepGenerated: boolean = true): Promise<void> {
@@ -206,6 +270,9 @@ ${components.map(comp => ` ${comp.className}`).join(',\n')}
206
270
  unlinkSync(this.backupFilePath)
207
271
  buildLogger.step('Removed backup file')
208
272
  }
273
+
274
+ // Restore original entry point (remove injected import)
275
+ this.removeInjectedImport()
209
276
  }
210
277
 
211
278
  /**
@@ -131,7 +131,7 @@ export default {{camelName}}Config
131
131
  },
132
132
  {
133
133
  path: 'plugins/{{name}}/index.ts',
134
- content: `import type { FluxStackPlugin, PluginContext } from '@/core/types/plugin'
134
+ content: `import type { ErrorContext, FluxStack, PluginContext, RequestContext, ResponseContext } from "@/core/plugins/types"
135
135
  // โœ… Plugin imports its own configuration
136
136
  import { {{camelName}}Config } from './config'
137
137
 
@@ -139,7 +139,7 @@ import { {{camelName}}Config } from './config'
139
139
  * {{pascalName}} Plugin
140
140
  * {{description}}
141
141
  */
142
- export class {{pascalName}}Plugin implements FluxStackPlugin {
142
+ export class {{pascalName}}Plugin implements FluxStack.Plugin {
143
143
  name = '{{name}}'
144
144
  version = '1.0.0'
145
145
 
@@ -173,7 +173,7 @@ export class {{pascalName}}Plugin implements FluxStackPlugin {
173
173
  /**
174
174
  * Request hook - called on each request
175
175
  */
176
- async onRequest?(context: PluginContext, request: Request): Promise<void> {
176
+ async onRequest?(context: RequestContext): Promise<void> {
177
177
  if (!{{camelName}}Config.enabled) return
178
178
 
179
179
  // Add request processing logic
@@ -182,7 +182,7 @@ export class {{pascalName}}Plugin implements FluxStackPlugin {
182
182
  /**
183
183
  * Response hook - called on each response
184
184
  */
185
- async onResponse?(context: PluginContext, response: Response): Promise<void> {
185
+ async onResponse?(context: ResponseContext): Promise<void> {
186
186
  if (!{{camelName}}Config.enabled) return
187
187
 
188
188
  // Add response processing logic
@@ -191,8 +191,8 @@ export class {{pascalName}}Plugin implements FluxStackPlugin {
191
191
  /**
192
192
  * Error hook - called when errors occur
193
193
  */
194
- async onError?(context: PluginContext, error: Error): Promise<void> {
195
- console.error(\`[{{name}}] Error:\`, error)
194
+ async onError?(context: ErrorContext): Promise<void> {
195
+ console.error(\`[{{name}}] Error:\`, context.error)
196
196
 
197
197
  // Add error handling logic
198
198
  }