create-fluxstack 1.16.0 → 1.17.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/app/client/src/App.tsx +8 -0
  3. package/app/client/src/live/AuthDemo.tsx +4 -4
  4. package/core/build/bundler.ts +40 -26
  5. package/core/build/flux-plugins-generator.ts +325 -325
  6. package/core/build/index.ts +92 -21
  7. package/core/cli/command-registry.ts +44 -46
  8. package/core/cli/commands/build.ts +11 -6
  9. package/core/cli/commands/create.ts +7 -5
  10. package/core/cli/commands/dev.ts +6 -5
  11. package/core/cli/commands/help.ts +3 -2
  12. package/core/cli/commands/make-plugin.ts +8 -7
  13. package/core/cli/commands/plugin-add.ts +60 -43
  14. package/core/cli/commands/plugin-deps.ts +73 -57
  15. package/core/cli/commands/plugin-list.ts +44 -41
  16. package/core/cli/commands/plugin-remove.ts +33 -22
  17. package/core/cli/generators/component.ts +770 -769
  18. package/core/cli/generators/controller.ts +9 -8
  19. package/core/cli/generators/index.ts +148 -146
  20. package/core/cli/generators/interactive.ts +228 -227
  21. package/core/cli/generators/plugin.ts +11 -10
  22. package/core/cli/generators/prompts.ts +83 -82
  23. package/core/cli/generators/route.ts +7 -6
  24. package/core/cli/generators/service.ts +10 -9
  25. package/core/cli/generators/template-engine.ts +2 -1
  26. package/core/cli/generators/types.ts +7 -7
  27. package/core/cli/generators/utils.ts +191 -191
  28. package/core/cli/index.ts +9 -8
  29. package/core/cli/plugin-discovery.ts +2 -2
  30. package/core/client/hooks/useAuth.ts +48 -48
  31. package/core/client/standalone.ts +18 -17
  32. package/core/client/state/createStore.ts +192 -192
  33. package/core/client/state/index.ts +14 -14
  34. package/core/config/index.ts +1 -0
  35. package/core/framework/client.ts +131 -131
  36. package/core/framework/index.ts +7 -7
  37. package/core/framework/server.ts +72 -112
  38. package/core/framework/types.ts +2 -2
  39. package/core/plugins/built-in/live-components/commands/create-live-component.ts +6 -3
  40. package/core/plugins/built-in/monitoring/index.ts +110 -68
  41. package/core/plugins/built-in/static/index.ts +2 -2
  42. package/core/plugins/built-in/swagger/index.ts +9 -9
  43. package/core/plugins/built-in/vite/index.ts +3 -3
  44. package/core/plugins/built-in/vite/vite-dev.ts +3 -3
  45. package/core/plugins/config.ts +50 -47
  46. package/core/plugins/discovery.ts +10 -4
  47. package/core/plugins/executor.ts +2 -2
  48. package/core/plugins/index.ts +206 -203
  49. package/core/plugins/manager.ts +21 -20
  50. package/core/plugins/registry.ts +76 -12
  51. package/core/plugins/types.ts +14 -14
  52. package/core/server/framework.ts +3 -189
  53. package/core/server/live/auto-generated-components.ts +11 -29
  54. package/core/server/live/index.ts +41 -31
  55. package/core/server/live/websocket-plugin.ts +11 -1
  56. package/core/server/middleware/elysia-helpers.ts +16 -15
  57. package/core/server/middleware/errorHandling.ts +14 -14
  58. package/core/server/middleware/index.ts +31 -31
  59. package/core/server/plugins/database.ts +181 -180
  60. package/core/server/plugins/static-files-plugin.ts +4 -3
  61. package/core/server/plugins/swagger.ts +11 -8
  62. package/core/server/rooms/RoomBroadcaster.ts +11 -10
  63. package/core/server/rooms/RoomSystem.ts +14 -11
  64. package/core/server/services/BaseService.ts +7 -7
  65. package/core/server/services/ServiceContainer.ts +5 -5
  66. package/core/server/services/index.ts +8 -8
  67. package/core/templates/create-project.ts +28 -27
  68. package/core/testing/index.ts +9 -9
  69. package/core/testing/setup.ts +73 -73
  70. package/core/types/api.ts +168 -168
  71. package/core/types/config.ts +5 -5
  72. package/core/types/index.ts +1 -1
  73. package/core/types/plugin.ts +2 -2
  74. package/core/types/types.ts +3 -3
  75. package/core/utils/build-logger.ts +324 -324
  76. package/core/utils/config-schema.ts +480 -480
  77. package/core/utils/env.ts +10 -8
  78. package/core/utils/errors/codes.ts +114 -114
  79. package/core/utils/errors/handlers.ts +30 -20
  80. package/core/utils/errors/index.ts +54 -46
  81. package/core/utils/errors/middleware.ts +113 -113
  82. package/core/utils/helpers.ts +19 -16
  83. package/core/utils/logger/colors.ts +114 -114
  84. package/core/utils/logger/config.ts +2 -2
  85. package/core/utils/logger/formatter.ts +82 -82
  86. package/core/utils/logger/group-logger.ts +101 -101
  87. package/core/utils/logger/index.ts +13 -3
  88. package/core/utils/logger/startup-banner.ts +2 -2
  89. package/core/utils/logger/winston-logger.ts +152 -152
  90. package/core/utils/monitoring/index.ts +211 -211
  91. package/core/utils/sync-version.ts +67 -66
  92. package/core/utils/version.ts +1 -1
  93. package/package.json +104 -100
  94. package/playwright-report/index.html +85 -0
  95. package/playwright.config.ts +31 -0
  96. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  97. package/plugins/crypto-auth/client/components/index.ts +11 -11
  98. package/plugins/crypto-auth/client/index.ts +11 -11
  99. package/plugins/crypto-auth/package.json +65 -65
  100. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  101. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +6 -5
  102. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +6 -5
  103. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +3 -3
  104. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  105. package/plugins/crypto-auth/server/middlewares.ts +19 -19
  106. package/vite.config.ts +13 -0
  107. package/app/client/.live-stubs/LiveAdminPanel.js +0 -5
  108. package/app/client/.live-stubs/LiveCounter.js +0 -9
  109. package/app/client/.live-stubs/LiveForm.js +0 -11
  110. package/app/client/.live-stubs/LiveLocalCounter.js +0 -8
  111. package/app/client/.live-stubs/LivePingPong.js +0 -10
  112. package/app/client/.live-stubs/LiveRoomChat.js +0 -11
  113. package/app/client/.live-stubs/LiveSharedCounter.js +0 -10
  114. package/app/client/.live-stubs/LiveUpload.js +0 -15
  115. package/app/server/live/register-components.ts +0 -19
  116. package/core/build/live-components-generator.ts +0 -321
  117. package/core/live/ComponentRegistry.ts +0 -403
  118. package/core/live/types.ts +0 -241
  119. package/workspace.json +0 -6
@@ -1,228 +1,229 @@
1
- import type { CliCommand } from "../../plugins/types"
2
- import { generatorRegistry } from "./index"
3
- import type { GeneratorContext, GeneratorOptions } from "./types"
4
- import { promptSystem } from "./prompts"
5
-
6
- export const interactiveGenerateCommand: CliCommand = {
7
- name: 'generate:interactive',
8
- description: 'Generate code interactively with prompts',
9
- usage: 'flux generate:interactive',
10
- aliases: ['gi', 'gen:i'],
11
- category: 'Development',
12
- examples: [
13
- 'flux generate:interactive',
14
- 'flux gi'
15
- ],
16
- handler: async (args, options, context) => {
17
- console.log('šŸŽÆ FluxStack Interactive Code Generator\n')
18
-
19
- // Select generator type
20
- const generators = generatorRegistry.getAll()
21
- const generatorChoices = generators.map(gen => ({
22
- name: `${gen.name} - ${gen.description}`,
23
- value: gen.name
24
- }))
25
-
26
- const selectedType = await promptSystem.select(
27
- 'What would you like to generate?',
28
- generatorChoices
29
- )
30
-
31
- const generator = generatorRegistry.get(selectedType)
32
- if (!generator) {
33
- console.error(`āŒ Generator not found: ${selectedType}`)
34
- return
35
- }
36
-
37
- // Get name
38
- const name = await promptSystem.input(
39
- `Enter the ${selectedType} name:`,
40
- undefined,
41
- (value) => {
42
- if (!value.trim()) {
43
- return 'Name is required'
44
- }
45
- if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value)) {
46
- return 'Name must start with a letter and contain only letters, numbers, hyphens, and underscores'
47
- }
48
- return true
49
- }
50
- )
51
-
52
- // Get template variant (if applicable)
53
- let template: string | undefined
54
- if (selectedType === 'controller') {
55
- template = await promptSystem.select(
56
- 'Choose controller template:',
57
- [
58
- { name: 'CRUD - Full CRUD operations with validation', value: 'crud' },
59
- { name: 'Minimal - Basic structure only', value: 'minimal' }
60
- ]
61
- )
62
- } else if (selectedType === 'component') {
63
- template = await promptSystem.select(
64
- 'Choose component template:',
65
- [
66
- { name: 'Basic - Simple component', value: 'basic' },
67
- { name: 'Functional - Component with hooks', value: 'functional' },
68
- { name: 'Page - Page component with layout', value: 'page' },
69
- { name: 'Form - Form component with validation', value: 'form' },
70
- { name: 'Full - Complete with tests and stories', value: 'full' }
71
- ]
72
- )
73
- } else if (selectedType === 'service') {
74
- template = await promptSystem.select(
75
- 'Choose service template:',
76
- [
77
- { name: 'CRUD - Full service with repository', value: 'crud' },
78
- { name: 'Repository - Service with repository pattern', value: 'repository' },
79
- { name: 'Minimal - Basic structure only', value: 'minimal' }
80
- ]
81
- )
82
- } else if (selectedType === 'route') {
83
- template = await promptSystem.select(
84
- 'Choose route template:',
85
- [
86
- { name: 'CRUD - Full REST API routes', value: 'crud' },
87
- { name: 'Auth - Authentication routes', value: 'auth' },
88
- { name: 'Minimal - Basic routes only', value: 'minimal' }
89
- ]
90
- )
91
- }
92
-
93
- // Get custom path (optional)
94
- const useCustomPath = await promptSystem.confirm(
95
- 'Do you want to specify a custom path?',
96
- false
97
- )
98
-
99
- let customPath: string | undefined
100
- if (useCustomPath) {
101
- customPath = await promptSystem.input(
102
- 'Enter custom path (relative to project root):'
103
- )
104
- }
105
-
106
- // Confirm overwrite if files exist
107
- const force = await promptSystem.confirm(
108
- 'Overwrite existing files if they exist?',
109
- false
110
- )
111
-
112
- // Show dry run first
113
- console.log('\nšŸ“‹ Preview of files to be generated:\n')
114
-
115
- const generatorContext: GeneratorContext = {
116
- workingDir: context.workingDir,
117
- config: context.config,
118
- logger: context.logger,
119
- utils: context.utils
120
- }
121
-
122
- const generatorOptions: GeneratorOptions = {
123
- name,
124
- path: customPath,
125
- template,
126
- force,
127
- dryRun: true
128
- }
129
-
130
- try {
131
- await generator.generate(generatorContext, generatorOptions)
132
-
133
- const proceed = await promptSystem.confirm(
134
- '\nProceed with generation?',
135
- true
136
- )
137
-
138
- if (!proceed) {
139
- console.log('āŒ Generation cancelled')
140
- return
141
- }
142
-
143
- // Generate for real
144
- generatorOptions.dryRun = false
145
- await generator.generate(generatorContext, generatorOptions)
146
-
147
- console.log(`\nāœ… Successfully generated ${selectedType}: ${name}`)
148
-
149
- // Ask if user wants to generate related files
150
- await suggestRelatedGenerations(selectedType, name, generatorContext)
151
-
152
- } catch (error) {
153
- console.error(`āŒ Failed to generate ${selectedType}:`, error instanceof Error ? error.message : String(error))
154
- throw error
155
- }
156
- }
157
- }
158
-
159
- async function suggestRelatedGenerations(
160
- generatedType: string,
161
- name: string,
162
- context: GeneratorContext
163
- ): Promise<void> {
164
- const suggestions: Array<{ type: string; description: string }> = []
165
-
166
- switch (generatedType) {
167
- case 'controller':
168
- suggestions.push(
169
- { type: 'service', description: `Generate ${name} service for business logic` },
170
- { type: 'route', description: `Generate ${name} routes to expose the controller` }
171
- )
172
- break
173
- case 'service':
174
- suggestions.push(
175
- { type: 'controller', description: `Generate ${name} controller to use this service` }
176
- )
177
- break
178
- case 'route':
179
- suggestions.push(
180
- { type: 'controller', description: `Generate ${name} controller for route handlers` }
181
- )
182
- break
183
- case 'component':
184
- // No automatic suggestions for components
185
- break
186
- }
187
-
188
- if (suggestions.length === 0) {
189
- return
190
- }
191
-
192
- console.log('\nšŸ’” Suggested next steps:')
193
- for (const suggestion of suggestions) {
194
- console.log(` • ${suggestion.description}`)
195
- }
196
-
197
- const generateRelated = await promptSystem.confirm(
198
- '\nWould you like to generate related files now?',
199
- false
200
- )
201
-
202
- if (!generateRelated) {
203
- return
204
- }
205
-
206
- for (const suggestion of suggestions) {
207
- const shouldGenerate = await promptSystem.confirm(
208
- `Generate ${suggestion.type} for ${name}?`,
209
- true
210
- )
211
-
212
- if (shouldGenerate) {
213
- const generator = generatorRegistry.get(suggestion.type)
214
- if (generator) {
215
- try {
216
- await generator.generate(context, {
217
- name,
218
- force: false,
219
- dryRun: false
220
- })
221
- console.log(`āœ… Generated ${suggestion.type}: ${name}`)
222
- } catch (error) {
223
- console.error(`āŒ Failed to generate ${suggestion.type}:`, error instanceof Error ? error.message : String(error))
224
- }
225
- }
226
- }
227
- }
1
+ import type { CliCommand } from "../../plugins/types"
2
+ import { generatorRegistry } from "./index"
3
+ import type { GeneratorContext, GeneratorOptions } from "./types"
4
+ import { promptSystem } from "./prompts"
5
+ import { buildLogger } from "@core/utils/build-logger"
6
+
7
+ export const interactiveGenerateCommand: CliCommand = {
8
+ name: 'generate:interactive',
9
+ description: 'Generate code interactively with prompts',
10
+ usage: 'flux generate:interactive',
11
+ aliases: ['gi', 'gen:i'],
12
+ category: 'Development',
13
+ examples: [
14
+ 'flux generate:interactive',
15
+ 'flux gi'
16
+ ],
17
+ handler: async (args, options, context) => {
18
+ buildLogger.info('šŸŽÆ FluxStack Interactive Code Generator\n')
19
+
20
+ // Select generator type
21
+ const generators = generatorRegistry.getAll()
22
+ const generatorChoices = generators.map(gen => ({
23
+ name: `${gen.name} - ${gen.description}`,
24
+ value: gen.name
25
+ }))
26
+
27
+ const selectedType = await promptSystem.select(
28
+ 'What would you like to generate?',
29
+ generatorChoices
30
+ ) as string
31
+
32
+ const generator = generatorRegistry.get(selectedType)
33
+ if (!generator) {
34
+ buildLogger.error(`Generator not found: ${selectedType}`)
35
+ return
36
+ }
37
+
38
+ // Get name
39
+ const name = await promptSystem.input(
40
+ `Enter the ${selectedType} name:`,
41
+ undefined,
42
+ (value) => {
43
+ if (!value.trim()) {
44
+ return 'Name is required'
45
+ }
46
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value)) {
47
+ return 'Name must start with a letter and contain only letters, numbers, hyphens, and underscores'
48
+ }
49
+ return true
50
+ }
51
+ )
52
+
53
+ // Get template variant (if applicable)
54
+ let template: string | undefined
55
+ if (selectedType === 'controller') {
56
+ template = await promptSystem.select(
57
+ 'Choose controller template:',
58
+ [
59
+ { name: 'CRUD - Full CRUD operations with validation', value: 'crud' },
60
+ { name: 'Minimal - Basic structure only', value: 'minimal' }
61
+ ]
62
+ ) as string | undefined
63
+ } else if (selectedType === 'component') {
64
+ template = await promptSystem.select(
65
+ 'Choose component template:',
66
+ [
67
+ { name: 'Basic - Simple component', value: 'basic' },
68
+ { name: 'Functional - Component with hooks', value: 'functional' },
69
+ { name: 'Page - Page component with layout', value: 'page' },
70
+ { name: 'Form - Form component with validation', value: 'form' },
71
+ { name: 'Full - Complete with tests and stories', value: 'full' }
72
+ ]
73
+ ) as string | undefined
74
+ } else if (selectedType === 'service') {
75
+ template = await promptSystem.select(
76
+ 'Choose service template:',
77
+ [
78
+ { name: 'CRUD - Full service with repository', value: 'crud' },
79
+ { name: 'Repository - Service with repository pattern', value: 'repository' },
80
+ { name: 'Minimal - Basic structure only', value: 'minimal' }
81
+ ]
82
+ ) as string | undefined
83
+ } else if (selectedType === 'route') {
84
+ template = await promptSystem.select(
85
+ 'Choose route template:',
86
+ [
87
+ { name: 'CRUD - Full REST API routes', value: 'crud' },
88
+ { name: 'Auth - Authentication routes', value: 'auth' },
89
+ { name: 'Minimal - Basic routes only', value: 'minimal' }
90
+ ]
91
+ ) as string | undefined
92
+ }
93
+
94
+ // Get custom path (optional)
95
+ const useCustomPath = await promptSystem.confirm(
96
+ 'Do you want to specify a custom path?',
97
+ false
98
+ )
99
+
100
+ let customPath: string | undefined
101
+ if (useCustomPath) {
102
+ customPath = await promptSystem.input(
103
+ 'Enter custom path (relative to project root):'
104
+ )
105
+ }
106
+
107
+ // Confirm overwrite if files exist
108
+ const force = await promptSystem.confirm(
109
+ 'Overwrite existing files if they exist?',
110
+ false
111
+ )
112
+
113
+ // Show dry run first
114
+ buildLogger.info('\nšŸ“‹ Preview of files to be generated:\n')
115
+
116
+ const generatorContext: GeneratorContext = {
117
+ workingDir: context.workingDir,
118
+ config: context.config,
119
+ logger: context.logger,
120
+ utils: context.utils
121
+ }
122
+
123
+ const generatorOptions: GeneratorOptions = {
124
+ name,
125
+ path: customPath,
126
+ template,
127
+ force,
128
+ dryRun: true
129
+ }
130
+
131
+ try {
132
+ await generator.generate(generatorContext, generatorOptions)
133
+
134
+ const proceed = await promptSystem.confirm(
135
+ '\nProceed with generation?',
136
+ true
137
+ )
138
+
139
+ if (!proceed) {
140
+ buildLogger.warn('Generation cancelled')
141
+ return
142
+ }
143
+
144
+ // Generate for real
145
+ generatorOptions.dryRun = false
146
+ await generator.generate(generatorContext, generatorOptions)
147
+
148
+ buildLogger.success(`Successfully generated ${selectedType}: ${name}`)
149
+
150
+ // Ask if user wants to generate related files
151
+ await suggestRelatedGenerations(selectedType, name, generatorContext)
152
+
153
+ } catch (error) {
154
+ buildLogger.error(`Failed to generate ${selectedType}: ${error instanceof Error ? error.message : String(error)}`)
155
+ throw error
156
+ }
157
+ }
158
+ }
159
+
160
+ async function suggestRelatedGenerations(
161
+ generatedType: string,
162
+ name: string,
163
+ context: GeneratorContext
164
+ ): Promise<void> {
165
+ const suggestions: Array<{ type: string; description: string }> = []
166
+
167
+ switch (generatedType) {
168
+ case 'controller':
169
+ suggestions.push(
170
+ { type: 'service', description: `Generate ${name} service for business logic` },
171
+ { type: 'route', description: `Generate ${name} routes to expose the controller` }
172
+ )
173
+ break
174
+ case 'service':
175
+ suggestions.push(
176
+ { type: 'controller', description: `Generate ${name} controller to use this service` }
177
+ )
178
+ break
179
+ case 'route':
180
+ suggestions.push(
181
+ { type: 'controller', description: `Generate ${name} controller for route handlers` }
182
+ )
183
+ break
184
+ case 'component':
185
+ // No automatic suggestions for components
186
+ break
187
+ }
188
+
189
+ if (suggestions.length === 0) {
190
+ return
191
+ }
192
+
193
+ buildLogger.info('\nšŸ’” Suggested next steps:')
194
+ for (const suggestion of suggestions) {
195
+ buildLogger.info(` • ${suggestion.description}`)
196
+ }
197
+
198
+ const generateRelated = await promptSystem.confirm(
199
+ '\nWould you like to generate related files now?',
200
+ false
201
+ )
202
+
203
+ if (!generateRelated) {
204
+ return
205
+ }
206
+
207
+ for (const suggestion of suggestions) {
208
+ const shouldGenerate = await promptSystem.confirm(
209
+ `Generate ${suggestion.type} for ${name}?`,
210
+ true
211
+ )
212
+
213
+ if (shouldGenerate) {
214
+ const generator = generatorRegistry.get(suggestion.type)
215
+ if (generator) {
216
+ try {
217
+ await generator.generate(context, {
218
+ name,
219
+ force: false,
220
+ dryRun: false
221
+ })
222
+ buildLogger.success(`Generated ${suggestion.type}: ${name}`)
223
+ } catch (error) {
224
+ buildLogger.error(`Failed to generate ${suggestion.type}: ${error instanceof Error ? error.message : String(error)}`)
225
+ }
226
+ }
227
+ }
228
+ }
228
229
  }
@@ -1,6 +1,7 @@
1
1
  import type { Generator } from "./index"
2
2
  import type { GeneratorContext, GeneratorOptions, Template } from "./types"
3
3
  import { templateEngine } from "./template-engine"
4
+ import { buildLogger } from "@core/utils/build-logger"
4
5
  import { join } from "path"
5
6
 
6
7
  export class PluginGenerator implements Generator {
@@ -17,9 +18,9 @@ export class PluginGenerator implements Generator {
17
18
  const files = await templateEngine.processTemplate(template, context, options)
18
19
 
19
20
  if (options.dryRun) {
20
- console.log(`\nšŸ“‹ Would generate plugin '${options.name}':\n`)
21
+ buildLogger.info(`\nšŸ“‹ Would generate plugin '${options.name}':\n`)
21
22
  for (const file of files) {
22
- console.log(`${file.action === 'create' ? 'šŸ“„' : 'āœļø'} ${file.path}`)
23
+ buildLogger.info(`${file.action === 'create' ? 'šŸ“„' : 'āœļø'} ${file.path}`)
23
24
  }
24
25
  return
25
26
  }
@@ -31,14 +32,14 @@ export class PluginGenerator implements Generator {
31
32
  await template.hooks.afterGenerate(context, options, filePaths)
32
33
  }
33
34
 
34
- console.log(`\nāœ… Generated plugin '${options.name}' with ${files.length} files`)
35
- console.log(`\nšŸ“¦ Next steps:`)
36
- console.log(` 1. Configure plugin in plugins/${options.name}/config/index.ts`)
37
- console.log(` 2. Set environment variables (optional): ${options.name.toUpperCase().replace(/-/g, '_')}_*`)
38
- console.log(` 3. Implement your plugin logic in plugins/${options.name}/index.ts`)
39
- console.log(` 4. Add server-side code in plugins/${options.name}/server/ (optional)`)
40
- console.log(` 5. Add client-side code in plugins/${options.name}/client/ (optional)`)
41
- console.log(` 6. Run: bun run dev`)
35
+ buildLogger.success(`Generated plugin '${options.name}' with ${files.length} files`)
36
+ buildLogger.info(`\nšŸ“¦ Next steps:`)
37
+ buildLogger.info(` 1. Configure plugin in plugins/${options.name}/config/index.ts`)
38
+ buildLogger.info(` 2. Set environment variables (optional): ${options.name.toUpperCase().replace(/-/g, '_')}_*`)
39
+ buildLogger.info(` 3. Implement your plugin logic in plugins/${options.name}/index.ts`)
40
+ buildLogger.info(` 4. Add server-side code in plugins/${options.name}/server/ (optional)`)
41
+ buildLogger.info(` 5. Add client-side code in plugins/${options.name}/client/ (optional)`)
42
+ buildLogger.info(` 6. Run: bun run dev`)
42
43
  }
43
44
 
44
45
  private getTemplate(templateName?: string): Template {