@xeno-js/cli 0.1.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 (172) hide show
  1. package/.github/.copilot-instruction.md +73 -0
  2. package/.github/agents/git-operator.agent.md +86 -0
  3. package/.github/agents/unit-tester.agent.md +68 -0
  4. package/.husky/commit-msg +1 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.husky/pre-push +1 -0
  7. package/.prettierignore +7 -0
  8. package/.prettierrc.json +37 -0
  9. package/LICENSE +15 -0
  10. package/README.md +236 -0
  11. package/commitlint.config.cjs +39 -0
  12. package/dist/domain/contracts/icommand-cli.contracts.js +2 -0
  13. package/dist/domain/contracts/icommand-runner.contracts.js +2 -0
  14. package/dist/domain/contracts/idispatch.contracts.js +2 -0
  15. package/dist/domain/contracts/ifile-service.contracts.js +2 -0
  16. package/dist/domain/contracts/igenerator.contracts.js +2 -0
  17. package/dist/domain/contracts/index.js +2 -0
  18. package/dist/domain/contracts/iscaffold-strategy.contracts.js +2 -0
  19. package/dist/domain/index.js +2 -0
  20. package/dist/generator.contracts.js +13 -0
  21. package/dist/index.js +31 -0
  22. package/dist/infrastructure/bootstrap/bootstrapper.js +32 -0
  23. package/dist/infrastructure/bootstrap/index.js +17 -0
  24. package/dist/infrastructure/builder/builder.js +16 -0
  25. package/dist/infrastructure/builder/builder.test.js +30 -0
  26. package/dist/infrastructure/builder/index.js +17 -0
  27. package/dist/infrastructure/command_launcher/command.utils.js +30 -0
  28. package/dist/infrastructure/command_launcher/index.js +2 -0
  29. package/dist/infrastructure/commands/command-runner.service.js +36 -0
  30. package/dist/infrastructure/commands/generate.command.js +48 -0
  31. package/dist/infrastructure/commands/help.command.js +44 -0
  32. package/dist/infrastructure/commands/index.js +20 -0
  33. package/dist/infrastructure/commands/new-project.command.js +43 -0
  34. package/dist/infrastructure/dispatcher/dispatcher.js +39 -0
  35. package/dist/infrastructure/dispatcher/index.js +17 -0
  36. package/dist/infrastructure/file/file.service.js +13 -0
  37. package/dist/infrastructure/file/index.js +17 -0
  38. package/dist/infrastructure/generators/bootstrap.generator.js +118 -0
  39. package/dist/infrastructure/generators/core/bootstrap.generator.js +118 -0
  40. package/dist/infrastructure/generators/core/command/cqrs.generator.js +49 -0
  41. package/dist/infrastructure/generators/core/command/generate-command.generator.js +163 -0
  42. package/dist/infrastructure/generators/core/command/generate-query.generator.js +131 -0
  43. package/dist/infrastructure/generators/core/drizzle-sql-lite.generator.js +78 -0
  44. package/dist/infrastructure/generators/core/drizzle.generator.js +85 -0
  45. package/dist/infrastructure/generators/core/env.generator.js +69 -0
  46. package/dist/infrastructure/generators/core/index.js +25 -0
  47. package/dist/infrastructure/generators/core/main.generator.js +45 -0
  48. package/dist/infrastructure/generators/core/packagejson.generator.js +78 -0
  49. package/dist/infrastructure/generators/core/readme.generator.js +431 -0
  50. package/dist/infrastructure/generators/core/registry.generator.js +46 -0
  51. package/dist/infrastructure/generators/core/tsconfig.generator.js +41 -0
  52. package/dist/infrastructure/generators/core-packagejson.generator.js +78 -0
  53. package/dist/infrastructure/generators/drizzle-sql-lite.generator.js +78 -0
  54. package/dist/infrastructure/generators/drizzle.generator.js +85 -0
  55. package/dist/infrastructure/generators/env-vue.generator.js +40 -0
  56. package/dist/infrastructure/generators/env.generator.js +71 -0
  57. package/dist/infrastructure/generators/gitignore.generator.js +62 -0
  58. package/dist/infrastructure/generators/index.js +26 -0
  59. package/dist/infrastructure/generators/main.generator.js +45 -0
  60. package/dist/infrastructure/generators/readme.generator.js +431 -0
  61. package/dist/infrastructure/generators/registry.generator.js +46 -0
  62. package/dist/infrastructure/generators/tsconfig-vue.generator.js +39 -0
  63. package/dist/infrastructure/generators/tsconfig.generator.js +41 -0
  64. package/dist/infrastructure/generators/vite-config.generator.js +39 -0
  65. package/dist/infrastructure/generators/vue/app-vue.generator.js +26 -0
  66. package/dist/infrastructure/generators/vue/bootstrap-ts.generator.js +59 -0
  67. package/dist/infrastructure/generators/vue/command_query/cqrs.generator.js +36 -0
  68. package/dist/infrastructure/generators/vue/command_query/generate-command-vue.generator.js +139 -0
  69. package/dist/infrastructure/generators/vue/command_query/generate-query-vue.generator.js +147 -0
  70. package/dist/infrastructure/generators/vue/env.generator.js +40 -0
  71. package/dist/infrastructure/generators/vue/index-html.generator.js +29 -0
  72. package/dist/infrastructure/generators/vue/index.js +28 -0
  73. package/dist/infrastructure/generators/vue/main.generator.js +45 -0
  74. package/dist/infrastructure/generators/vue/packagejson.generator.js +59 -0
  75. package/dist/infrastructure/generators/vue/readme.generator.js +394 -0
  76. package/dist/infrastructure/generators/vue/registry-ts.generator.js +28 -0
  77. package/dist/infrastructure/generators/vue/router.generator.js +33 -0
  78. package/dist/infrastructure/generators/vue/tailwind.generator.js +19 -0
  79. package/dist/infrastructure/generators/vue/tsconfig.generator.js +39 -0
  80. package/dist/infrastructure/generators/vue/use-app.generator.js +43 -0
  81. package/dist/infrastructure/generators/vue/vite-config.generator.js +39 -0
  82. package/dist/infrastructure/generators/vue-packagejson.generator.js +59 -0
  83. package/dist/infrastructure/generators/vue-readme.generator.js +53 -0
  84. package/dist/infrastructure/generators/vue-source-files.generator.js +136 -0
  85. package/dist/infrastructure/index.js +21 -0
  86. package/dist/infrastructure/strategies/core-scaffold.strategy.js +99 -0
  87. package/dist/infrastructure/strategies/index.js +18 -0
  88. package/dist/infrastructure/strategies/vue-scaffold.strategy.js +79 -0
  89. package/dist/scaffolding.engine.js +60 -0
  90. package/dist/shared/constants/command.constants.js +19 -0
  91. package/dist/shared/constants/core.constants.js +10 -0
  92. package/dist/shared/constants/index.js +18 -0
  93. package/dist/shared/index.js +18 -0
  94. package/dist/shared/types/common.types.js +2 -0
  95. package/dist/shared/types/index.js +2 -0
  96. package/dist/shared/utils/guards.utils.js +310 -0
  97. package/dist/shared/utils/index.js +18 -0
  98. package/dist/shared/utils/string.utils.js +62 -0
  99. package/eslint.config.mjs +305 -0
  100. package/lint-staged.config.mjs +5 -0
  101. package/logo/logo.png +0 -0
  102. package/package.json +65 -0
  103. package/src/domain/contracts/icommand-cli.contracts.ts +13 -0
  104. package/src/domain/contracts/icommand-runner.contracts.ts +3 -0
  105. package/src/domain/contracts/idispatch.contracts.ts +3 -0
  106. package/src/domain/contracts/ifile-service.contracts.ts +3 -0
  107. package/src/domain/contracts/igenerator.contracts.ts +5 -0
  108. package/src/domain/contracts/index.ts +6 -0
  109. package/src/domain/contracts/iscaffold-strategy.contracts.ts +7 -0
  110. package/src/domain/index.ts +1 -0
  111. package/src/index.ts +30 -0
  112. package/src/infrastructure/bootstrap/bootstrapper.ts +30 -0
  113. package/src/infrastructure/bootstrap/index.ts +1 -0
  114. package/src/infrastructure/builder/builder.test.ts +36 -0
  115. package/src/infrastructure/builder/builder.ts +15 -0
  116. package/src/infrastructure/builder/index.ts +1 -0
  117. package/src/infrastructure/command_launcher/command.utils.ts +31 -0
  118. package/src/infrastructure/command_launcher/index.ts +0 -0
  119. package/src/infrastructure/commands/command-runner.service.ts +36 -0
  120. package/src/infrastructure/commands/generate.command.ts +55 -0
  121. package/src/infrastructure/commands/help.command.ts +63 -0
  122. package/src/infrastructure/commands/index.ts +4 -0
  123. package/src/infrastructure/commands/new-project.command.ts +47 -0
  124. package/src/infrastructure/dispatcher/dispatcher.ts +45 -0
  125. package/src/infrastructure/dispatcher/index.ts +1 -0
  126. package/src/infrastructure/file/file.service.ts +14 -0
  127. package/src/infrastructure/file/index.ts +1 -0
  128. package/src/infrastructure/generators/core/bootstrap.generator.ts +118 -0
  129. package/src/infrastructure/generators/core/command/cqrs.generator.ts +63 -0
  130. package/src/infrastructure/generators/core/command/generate-command.generator.ts +191 -0
  131. package/src/infrastructure/generators/core/command/generate-query.generator.ts +150 -0
  132. package/src/infrastructure/generators/core/drizzle-sql-lite.generator.ts +76 -0
  133. package/src/infrastructure/generators/core/drizzle.generator.ts +86 -0
  134. package/src/infrastructure/generators/core/env.generator.ts +69 -0
  135. package/src/infrastructure/generators/core/index.ts +9 -0
  136. package/src/infrastructure/generators/core/main.generator.ts +41 -0
  137. package/src/infrastructure/generators/core/packagejson.generator.ts +70 -0
  138. package/src/infrastructure/generators/core/readme.generator.ts +427 -0
  139. package/src/infrastructure/generators/core/registry.generator.ts +40 -0
  140. package/src/infrastructure/generators/core/tsconfig.generator.ts +39 -0
  141. package/src/infrastructure/generators/gitignore.generator.ts +60 -0
  142. package/src/infrastructure/generators/vue/app-vue.generator.ts +21 -0
  143. package/src/infrastructure/generators/vue/bootstrap-ts.generator.ts +60 -0
  144. package/src/infrastructure/generators/vue/command_query/cqrs.generator.ts +36 -0
  145. package/src/infrastructure/generators/vue/command_query/generate-command-vue.generator.ts +153 -0
  146. package/src/infrastructure/generators/vue/command_query/generate-query-vue.generator.ts +161 -0
  147. package/src/infrastructure/generators/vue/env.generator.ts +39 -0
  148. package/src/infrastructure/generators/vue/index-html.generator.ts +24 -0
  149. package/src/infrastructure/generators/vue/index.ts +12 -0
  150. package/src/infrastructure/generators/vue/main.generator.ts +41 -0
  151. package/src/infrastructure/generators/vue/packagejson.generator.ts +50 -0
  152. package/src/infrastructure/generators/vue/readme.generator.ts +389 -0
  153. package/src/infrastructure/generators/vue/registry-ts.generator.ts +26 -0
  154. package/src/infrastructure/generators/vue/router.generator.ts +35 -0
  155. package/src/infrastructure/generators/vue/tailwind.generator.ts +17 -0
  156. package/src/infrastructure/generators/vue/tsconfig.generator.ts +38 -0
  157. package/src/infrastructure/generators/vue/use-app.generator.ts +38 -0
  158. package/src/infrastructure/generators/vue/vite-config.generator.ts +35 -0
  159. package/src/infrastructure/index.ts +5 -0
  160. package/src/infrastructure/strategies/core-scaffold.strategy.ts +100 -0
  161. package/src/infrastructure/strategies/index.ts +2 -0
  162. package/src/infrastructure/strategies/vue-scaffold.strategy.ts +79 -0
  163. package/src/shared/constants/command.constants.ts +18 -0
  164. package/src/shared/constants/core.constants.ts +7 -0
  165. package/src/shared/constants/index.ts +2 -0
  166. package/src/shared/index.ts +3 -0
  167. package/src/shared/types/common.types.ts +68 -0
  168. package/src/shared/types/index.ts +1 -0
  169. package/src/shared/utils/guards.utils.ts +334 -0
  170. package/src/shared/utils/index.ts +2 -0
  171. package/src/shared/utils/string.utils.ts +60 -0
  172. package/tsconfig.json +15 -0
@@ -0,0 +1,50 @@
1
+ import path from 'node:path'
2
+
3
+ import { type IFileService, type IGenerator } from '../../../domain'
4
+ import type { VueOptions } from '../../../shared'
5
+
6
+ export class PackageJsonVueGenerator implements IGenerator<VueOptions> {
7
+ constructor(private readonly _fileService: IFileService) {}
8
+
9
+ public async generate(projectPath: string, options: VueOptions): Promise<void> {
10
+ const packageJson = {
11
+ name: options.targetDir,
12
+ version: '1.0.0',
13
+ private: true,
14
+ type: 'module',
15
+ scripts: {
16
+ dev: 'vite',
17
+ build: 'vue-tsc -b && vite build',
18
+ preview: 'vite preview',
19
+ g: 'xeno-js generate',
20
+ } as Record<string, string>,
21
+ dependencies: {
22
+ '@xeno-js/vue': 'latest',
23
+ 'vue': '^3.5.0',
24
+ } as Record<string, string>,
25
+ devDependencies: {
26
+ '@types/node': '^20.0.0',
27
+ '@vitejs/plugin-vue': '^5.1.0',
28
+ 'typescript': '^5.5.0',
29
+ 'vite': '^5.4.0',
30
+ 'vue-tsc': '^2.1.0',
31
+ } as Record<string, string>,
32
+ }
33
+
34
+ if (options.axios) packageJson.dependencies['axios'] = '^1.7.0'
35
+ if (options.cockatiel) packageJson.dependencies['cockatiel'] = '^4.0.0'
36
+ if (options.supabase) packageJson.dependencies['@supabase/supabase-js'] = '^2.45.0'
37
+ if (options.sentry) packageJson.dependencies['@sentry/vue'] = '^8.28.0'
38
+ if (options.pinia) packageJson.dependencies['pinia'] = '^2.2.0'
39
+ if (options.router) packageJson.dependencies['vue-router'] = '^4.4.0'
40
+ if (options.zod) packageJson.dependencies['zod'] = '^4.4.3'
41
+
42
+ if (options.tailwind) {
43
+ packageJson.devDependencies['tailwindcss'] = '^4.0.0'
44
+ packageJson.devDependencies['@tailwindcss/vite'] = '^4.0.0'
45
+ }
46
+
47
+ const filePath = path.join(projectPath, 'package.json')
48
+ await this._fileService.writeFileRecursive(filePath, JSON.stringify(packageJson, null, 2))
49
+ }
50
+ }
@@ -0,0 +1,389 @@
1
+ // src/infrastructure/generators/vue-readme.generator.ts
2
+ import path from 'node:path'
3
+
4
+ import { type IFileService, type IGenerator } from '../../../domain'
5
+ import type { VueOptions } from '../../../shared'
6
+
7
+ export class ReadmeVueGenerator implements IGenerator<VueOptions> {
8
+ constructor(private readonly _fileService: IFileService) {}
9
+
10
+ public async generate(projectPath: string, options: VueOptions): Promise<void> {
11
+ const content = `# ${options.targetDir}
12
+
13
+ <div align="center">
14
+ <img src="logo/logo.png" alt="Xeno Logo" width="140" />
15
+
16
+ <h1>Xeno Vue</h1>
17
+
18
+ <p><em>Enterprise-grade DDD & CQRS framework for Vue.js</em></p>
19
+
20
+ <p>
21
+ <a href="https://github.com/xeno-js/xeno-js">
22
+ <img src="https://img.shields.io/badge/Powered%20by-Xeno-blueviolet?style=flat-square" alt="Powered by Xeno" />
23
+ </a>
24
+ <a href="https://github.com/xeno-js/xeno-fe/blob/main/LICENSE">
25
+ <img src="https://img.shields.io/npm/l/@xeno-js/vue?style=flat-square" alt="License: ISC" />
26
+ </a>
27
+ <a href="https://www.npmjs.com/package/@xeno-js/vue">
28
+ <img src="https://img.shields.io/npm/v/@xeno-js/vue?style=flat-square" alt="NPM Version" />
29
+ </a>
30
+ <a href="https://buymeacoffee.com/xenojs">
31
+ <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
32
+ </a>
33
+ </p>
34
+ </div>
35
+
36
+ ---
37
+
38
+ ## What is Xeno Vue?
39
+
40
+ **Xeno Vue** (\`@xeno-js/vue\`) is an enterprise-grade, deterministic
41
+ architectural framework that brings the strictness of **Domain-Driven Design
42
+ (DDD)** and **Command Query Responsibility Segregation (CQRS)** natively to the
43
+ browser.
44
+
45
+ By shifting operational logic, remote data fetching, and state mutations away
46
+ from Vue components and Pinia stores, Xeno ensures your frontend architecture
47
+ remains pristine, highly testable, and completely decoupled from the UI layer.
48
+ It treats the browser as a complex distributed client, not just a document
49
+ viewer.
50
+
51
+ ---
52
+
53
+ ## 💡 Why Choose Xeno Vue?
54
+
55
+ Modern frontend development often leads to "Spaghetti State" where API calls,
56
+ business rules, and DOM manipulations are tightly coupled inside components.
57
+ Xeno Vue fixes this with an emphasis on pure Dependency Injection (DI) and clean
58
+ boundaries.
59
+
60
+ - **Zero-Magic Dependency Injection**: Xeno provides an explicit
61
+ \`XenoAppBuilder\` to construct your IoC container at bootstrap. No hidden Vue
62
+ plugins doing implicit injections. You have total control over the dependency
63
+ graph.
64
+ - **Total UI Decoupling**: Vue components act strictly as the Presentation
65
+ Layer. Business logic, API calls, and CQRS handlers live in pure, isolated
66
+ TypeScript classes. You can swap Vue for React tomorrow without touching your
67
+ core domain.
68
+ - **Enterprise-Grade Client Resiliency**: The browser is a hostile, unreliable
69
+ environment. Xeno Vue natively integrates \`Cockatiel\` and \`Axios\` to provide
70
+ out-of-the-box circuit breakers, retries with jitter, and bulkheads directly
71
+ in the client.
72
+ - **Frontend Middleware Pipeline**: Handle Authentication, CSRF validation,
73
+ aggressive Query Caching, and Performance logging _before_ a command is
74
+ executed or an API call fires, using the native \`Mediator\` pipeline.
75
+
76
+ ---
77
+
78
+ ## 📖 Documentation & Getting Started
79
+
80
+ To explore the architecture, programmatic configurations, and extension
81
+ workflows of Xeno Vue, read the full technical manuals located inside the main
82
+ documentation hub:
83
+
84
+ - **[Framework Documentation Repository](https://www.xeno-js.it/vue/overview)**
85
+
86
+ Inside, you will find exhaustive, step-by-step assembly guides covering frontend
87
+ IoC building (\`XenoAppBuilder\`), Vue \`Provide/Inject\` boundaries, and CQRS
88
+ composables.
89
+
90
+ ---
91
+
92
+ ## 🚀 Live Executable Demos
93
+
94
+ Want to see how Xeno Vue works? Check out the functional example application
95
+ showcasing end-to-end command/query segregation, resilient HTTP fetching, and
96
+ pure Composition API integration.
97
+
98
+ - **[Xeno Vue Demo Architecture](https://www.xeno-js.it/demo)**
99
+
100
+ ---
101
+
102
+ ## 📦 Installation
103
+
104
+ Install the Vue package:
105
+
106
+ \`\`\`bash
107
+ npm install @xeno-js/vue
108
+
109
+ \`\`\`
110
+
111
+ Xeno uses **Optional Peer Dependencies**. You only install the external
112
+ libraries you actually need.
113
+
114
+ \`\`\`bash
115
+ # Example: Install tools only if you enable them in the builder
116
+ npm install axios cockatiel zod @supabase/supabase-js @sentry/vue
117
+
118
+ \`\`\`
119
+
120
+ ---
121
+
122
+ ## ⚡ Bootstrapping & Component Example
123
+
124
+ Below is an architectural example of how to configure the Xeno \`XenoAppBuilder\`,
125
+ inject it into the Vue application, and consume it using Composition API
126
+ Composables.
127
+
128
+ ### 1. Initialize the Container (\`src/bootstrap.ts\`)
129
+
130
+ \`\`\`typescript
131
+ import { XenoAppBuilder, LOG_LEVEL, TOKENS } from '@xeno-js/vue'
132
+ import { BffRemoteDataSource } from './infrastructure'
133
+
134
+ import type { RemoteDataSource, XenoVueRegistry } from '@xeno-js/vue'
135
+
136
+ export type MyRegistry = XenoVueRegistry<{
137
+ BFF_REMOTE_DS: RemoteDataSource
138
+ }>
139
+
140
+ // Create the root IoC container context for the browser
141
+ const builder = XenoAppBuilder.create<MyRegistry>()
142
+ .addContext((opts) => {
143
+ opts.contextAccessor = useContextStore()
144
+ })
145
+ .addLogger((opts, config) => {
146
+ opts.console = config.get('VITE_APP_ENV') === 'development'
147
+ opts.level = LOG_LEVEL.DEBUG
148
+ })
149
+ .addAuth((opts, config) => {
150
+ opts.url = config.getOrThrow('VITE_SUPABASE_URL')
151
+ opts.key = config.getOrThrow('VITE_SUPABASE_KEY')
152
+ })
153
+ .addHttpCore('BFF_REMOTE_DS', (opts, config) => {
154
+ opts.client = {
155
+ baseURL: config.getOrThrow('VITE_API_BASE_URL'),
156
+ timeoutMs: 10000,
157
+ }
158
+ opts.resilience.retry.attempts = 3
159
+ opts.factory = (http, resilience) =>
160
+ new BffRemoteDataSource(http, resilience)
161
+ })
162
+ .addPipeline((config) => {
163
+ config.queryCaching = true // Enable in-memory caching for Queries
164
+ })
165
+
166
+ export async function bootstrap() {
167
+ return await builder.build()
168
+ }
169
+ \`\`\`
170
+
171
+ ### 2. Inject into Vue (\`src/main.ts\`)
172
+
173
+ \`\`\`typescript
174
+ import { createApp } from 'vue'
175
+ import App from './App.vue'
176
+ import { bootstrap } from './bootstrap'
177
+ import { XENO_SERVICES_KEY, ServicesUtils } from '@xeno-js/vue'
178
+
179
+ async function mountApp() {
180
+ try {
181
+ const app = createApp(App)
182
+ const container = await bootstrap()
183
+
184
+ // Provide the Xeno IoC container globally to all components
185
+ app.provide(XENO_SERVICES_KEY, container)
186
+
187
+ // Register global services utility for composables
188
+ ServicesUtils.setGlobalServices(container)
189
+
190
+ app.mount('#app')
191
+ } catch (error) {
192
+ console.error('Critical error during frontend bootstrap:', error)
193
+ }
194
+ }
195
+
196
+ mountApp()
197
+ \`\`\`
198
+
199
+ ### 3. CQRS Composable Usage (\`src/features/users/use-create-user.ts\`)
200
+
201
+ \`\`\`typescript
202
+ import { ref } from 'vue'
203
+ import { Result } from '@xeno-js/shared'
204
+ import { ServicesUtils } from '@/use-app'
205
+ import { CreateUserCommand } from './create-user.command'
206
+
207
+ export function useCreateUser() {
208
+ const loading = ref(false)
209
+ const error = ref<string | null>(null)
210
+
211
+ const execute = async (payload: { email: string; name: string }) => {
212
+ if (loading.value) return Result.fail(new Error('Already executing'))
213
+ loading.value = true
214
+ error.value = null
215
+
216
+ try {
217
+ // Safely resolve the Mediator from the Xeno Container
218
+ const { mediator, BFF_REMOTE_DS } = ServicesUtils.useApp()
219
+ const command = new CreateUserCommand()
220
+
221
+ const result = await mediator.send(command, async () => {
222
+ const apiResult = await bffRemoteDs.post('/v1/users', payload)
223
+
224
+ if (!apiResult.isOk()) return Result.fail(apiResult.getErrorOrThrow())
225
+ return Result.ok(apiResult.getValueOrThrow())
226
+ })
227
+
228
+ if (!result.isOk()) {
229
+ error.value = result.getErrorOrThrow().message
230
+ }
231
+
232
+ return result
233
+ } finally {
234
+ loading.value = false
235
+ }
236
+ }
237
+
238
+ return { loading, error, execute }
239
+ }
240
+ \`\`\`
241
+
242
+ ---
243
+
244
+ ## 🛠 Scaffold your project with CLI
245
+
246
+ Xeno includes an official CLI tool, \`@xeno-js/cli\`, designed to bootstrap your
247
+ new application in seconds. It offers an interactive setup for Vue projects,
248
+ instantly scaffolding the CQRS files, composables, and dependency injection
249
+ boundaries.
250
+
251
+ \`\`\`bash
252
+ # Generate a new Vue project
253
+ npx @xeno-js/cli new my-frontend-app --vue
254
+
255
+ # Generate a Vue Command with its Composable
256
+ npx @xeno-js/cli g command CreateUser --vue
257
+
258
+ \`\`\`
259
+
260
+ Check the
261
+ **[CLI Documentation](https://www.google.com/search?q=https://www.xeno-js.it/cli/overview&utm_source=gemini)**
262
+ for full options.
263
+
264
+ ---
265
+
266
+ ## 🤝 For Contributors
267
+
268
+ We welcome contributions to Xeno! To maintain the highest code quality and
269
+ stability of the core framework, **direct pushes to the \`main\` and \`develop\`
270
+ branches are strictly prohibited.** Please follow this Git Flow to contribute:
271
+
272
+ 1. **Branch off from \`develop**\`: Create a new branch for your feature or
273
+ bugfix.
274
+
275
+ \`\`\`bash
276
+ git checkout develop
277
+ git pull origin develop
278
+ git checkout -b feat/your-awesome-feature
279
+
280
+ \`\`\`
281
+
282
+ 2. **Make your changes**: Write your code and ensure it passes all local checks
283
+ (linting, types, and tests).
284
+
285
+ \`\`\`bash
286
+ npm run check
287
+
288
+ \`\`\`
289
+
290
+ 3. **Commit your changes**: We enforce
291
+ [Conventional Commits](https://www.conventionalcommits.org/?utm_source=gemini).
292
+ Husky will verify your commit message format.
293
+ 4. **Commit Format:**
294
+
295
+ \`\`\`bash
296
+ feat(scope): add new feature
297
+ fix(scope): resolve bug
298
+ chore(scope): update dependencies
299
+
300
+ \`\`\`
301
+
302
+ 5. **Submit a Pull Request (PR)**: Push your branch to GitHub and open a Pull
303
+ Request targeting the **\`develop\`** branch.
304
+ 6. **Review**: The repository owner will review your code, run pipeline tests,
305
+ and merge it into \`develop\`.
306
+
307
+ _Note: The \`main\` branch is strictly reserved for production releases. Code
308
+ flows from feature branches ➡️ \`develop\` ➡️ \`main\`._
309
+
310
+ ### Scripts
311
+
312
+ | Command | Description |
313
+ | ----------------------- | ---------------------------------------------- |
314
+ | \`npm run build\` | Builds the TypeScript source code into \`dist/\` |
315
+ | \`npm run typecheck\` | Checks types without emitting files |
316
+ | \`npm run lint\` | Runs ESLint |
317
+ | \`npm run format\` | Formats code with Prettier |
318
+ | \`npm run test\` | Runs the Vitest test suite |
319
+ | \`npm run test:coverage\` | Runs tests and generates a coverage report |
320
+
321
+ ### Code Quality (Husky & Git Hooks)
322
+
323
+ This project strictly enforces code quality rules before pushing to the
324
+ repository:
325
+
326
+ - **\`pre-commit\`**: Runs \`lint-staged\` on staged files (ESLint + Prettier).
327
+ - **\`commit-msg\`**: Checks commit messages with \`commitlint\` (we use
328
+ Conventional Commits).
329
+ - **\`pre-push\`**: Runs type checking, linting, and testing before code leaves
330
+ your machine.
331
+
332
+ ---
333
+
334
+ ## 🌱 Support & Appreciation
335
+
336
+ Building, benchmarking, and maintaining a progressive, enterprise-ready
337
+ open-source framework requires a massive amount of continuous dedication and
338
+ architectural engineering.
339
+
340
+ If Xeno has brought value to your development workflows, helped decouple your
341
+ core business logic, or simplified your system infrastructure layout, consider
342
+ supporting its open-source lifecycle. Your backing directly accelerates our
343
+ strategic roadmap for new out-of-the-box transport integrations and keeps the
344
+ documentation pristine.
345
+
346
+ **Want to know how you can contribute or sponsor Xeno?** We rely on the
347
+ commitment of our community to keep the project independent and thriving.
348
+ Whether you are an individual developer or a business using Xeno, your support
349
+ makes a real difference.
350
+
351
+ 👉
352
+ **[Read our support guidelines and find out how to help](https://www.xeno-js.it/support-us)**
353
+
354
+ Thank you for being part of this decoupled open-source journey!
355
+
356
+ <amp-bounce>
357
+ </amp-bounce>
358
+ <a href="https://www.buymeacoffee.com/xenojs" target="_blank">
359
+ <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42" style="height: 42px !important;" />
360
+ </a>
361
+
362
+ ---
363
+
364
+ ## 🛡️ Powered by Xeno
365
+
366
+ If you are using Xeno in your project, let the world know! Add this badge to
367
+ your README:
368
+
369
+ \`\`\`html
370
+ <a
371
+ href="[https://github.com/xeno-js/xeno-fe](https://github.com/xeno-js/xeno-fe)"
372
+ target="_blank"
373
+ >
374
+ <img
375
+ src="[https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square](https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square)"
376
+ alt="Powered by Xeno"
377
+ height="20"
378
+ />
379
+ </a>
380
+ \`\`\`
381
+
382
+ ## 📄 License
383
+
384
+ Copyright (c) 2026 Xeno. Licensed under the
385
+ [ISC License](https://www.google.com/search?q=LICENSE&utm_source=gemini).
386
+ `
387
+ await this._fileService.writeFileRecursive(path.join(projectPath, 'README.md'), content)
388
+ }
389
+ }
@@ -0,0 +1,26 @@
1
+ import path from 'node:path'
2
+
3
+ import { type IFileService, type IGenerator } from '../../../domain'
4
+ import type { VueOptions } from '../../../shared'
5
+
6
+ export class RegistryGenerator implements IGenerator<VueOptions> {
7
+ constructor(private readonly _fileService: IFileService) {}
8
+
9
+ public async generate(projectPath: string, _options: VueOptions): Promise<void> {
10
+ const registryTs = `import type { XenoVueRegistry } from '@xeno-js/vue';
11
+
12
+ /**
13
+ * @file registry.ts
14
+ * @description Centralized Type Contract for Dependency Injection in Vue.
15
+ */
16
+ export interface MyRegistry extends XenoVueRegistry {
17
+ // Add your frontend injection tokens here
18
+ // e.g., USER_DATA_SOURCE: IUserDataSource;
19
+ }
20
+ `
21
+ await this._fileService.writeFileRecursive(
22
+ path.join(projectPath, 'src', 'registry.ts'),
23
+ registryTs,
24
+ )
25
+ }
26
+ }
@@ -0,0 +1,35 @@
1
+ import path from 'node:path'
2
+
3
+ import { type IFileService, type IGenerator } from '../../../domain'
4
+ import type { VueOptions } from '../../../shared'
5
+
6
+ export class RouterGenerator implements IGenerator<VueOptions> {
7
+ constructor(private readonly _fileService: IFileService) {}
8
+
9
+ public async generate(projectPath: string, options: VueOptions): Promise<void> {
10
+ // 6. Optional router and assets
11
+ if (options.router) {
12
+ const routerTs = `import { createRouter, createWebHistory } from 'vue-router';
13
+
14
+ export default createRouter({
15
+ history: createWebHistory(),
16
+ routes: [
17
+ {
18
+ path: '/',
19
+ name: 'Home',
20
+ component: () => import('../views/HomeView.vue')
21
+ }
22
+ ]
23
+ });`
24
+ await this._fileService.writeFileRecursive(
25
+ path.join(projectPath, 'src', 'router', 'index.ts'),
26
+ routerTs,
27
+ )
28
+
29
+ await this._fileService.writeFileRecursive(
30
+ path.join(projectPath, 'src', 'views', 'HomeView.vue'),
31
+ `<template>\n <h1>Home View</h1>\n</template>`,
32
+ )
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,17 @@
1
+ import path from 'node:path'
2
+
3
+ import { type IFileService, type IGenerator } from '../../../domain'
4
+ import type { VueOptions } from '../../../shared'
5
+
6
+ export class TailwindGenerator implements IGenerator<VueOptions> {
7
+ constructor(private readonly _fileService: IFileService) {}
8
+
9
+ public async generate(projectPath: string, options: VueOptions): Promise<void> {
10
+ if (options.tailwind) {
11
+ await this._fileService.writeFileRecursive(
12
+ path.join(projectPath, 'src', 'assets', 'style.css'),
13
+ `@import "tailwindcss";\n`,
14
+ )
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,38 @@
1
+ // src/infrastructure/generators/tsconfig-vue.generator.ts
2
+ import path from 'node:path'
3
+
4
+ import { type IFileService, type IGenerator } from '../../../domain'
5
+ import type { VueOptions } from '../../../shared'
6
+
7
+ export class TsconfigVueGenerator implements IGenerator<VueOptions> {
8
+ constructor(private readonly _fileService: IFileService) {}
9
+
10
+ public async generate(projectPath: string, _options: VueOptions): Promise<void> {
11
+ const tsconfig = {
12
+ compilerOptions: {
13
+ target: 'ES2022',
14
+ useDefineForClassFields: true,
15
+ module: 'ESNext',
16
+ moduleResolution: 'bundler',
17
+ strict: true,
18
+ jsx: 'preserve',
19
+ resolveJsonModule: true,
20
+ isolatedModules: true,
21
+ esModuleInterop: true,
22
+ lib: ['ES2022', 'DOM', 'DOM.Iterable'],
23
+ skipLibCheck: true,
24
+ noEmit: true,
25
+ types: ['vite/client'],
26
+ paths: {
27
+ '@/*': ['./src/*'],
28
+ },
29
+ },
30
+ include: ['src/**/*.ts', 'src/**/*.d.ts', 'src/**/*.tsx', 'src/**/*.vue'],
31
+ }
32
+
33
+ await this._fileService.writeFileRecursive(
34
+ path.join(projectPath, 'tsconfig.json'),
35
+ JSON.stringify(tsconfig, null, 2),
36
+ )
37
+ }
38
+ }
@@ -0,0 +1,38 @@
1
+ import path from 'node:path'
2
+
3
+ import { type IFileService, type IGenerator } from '../../../domain'
4
+ import type { VueOptions } from '../../../shared'
5
+
6
+ export class UseAppVueGenerator implements IGenerator<VueOptions> {
7
+ constructor(private readonly _fileService: IFileService) {}
8
+
9
+ public async generate(projectPath: string, _options: VueOptions): Promise<void> {
10
+ const content = `import type { Nullable } from "@xeno-js/vue";
11
+ import { Guards, XENO_SERVICES_KEY } from "@xeno-js/vue";
12
+ import { inject } from "vue";
13
+ import type { MyRegistry } from "./registry";
14
+
15
+ let globalServices: Nullable<MyRegistry> = null;
16
+
17
+ export const ServicesUtils = Object.freeze({
18
+ // Metodo per registrare i servizi al bootstrap
19
+ setGlobalServices(services: MyRegistry) {
20
+ globalServices = services;
21
+ },
22
+ useApp(): MyRegistry {
23
+ if (Guards.isDefined(globalServices))
24
+ return globalServices;
25
+
26
+ const services = inject<MyRegistry>(XENO_SERVICES_KEY);
27
+ if (!services)
28
+ throw new Error(
29
+ '[Xeno Error]: XenoServices not found. Did you provide them using app.provide(XENO_SERVICES_KEY, services)?',
30
+ );
31
+
32
+ return services;
33
+ }
34
+ } as const);
35
+ `
36
+ await this._fileService.writeFileRecursive(path.join(projectPath, 'src', 'use-app.ts'), content)
37
+ }
38
+ }
@@ -0,0 +1,35 @@
1
+ // src/infrastructure/generators/viteconfig.generator.ts
2
+ import path from 'node:path'
3
+
4
+ import { type IFileService, type IGenerator } from '../../../domain'
5
+ import type { VueOptions } from '../../../shared'
6
+
7
+ export class ViteconfigGenerator implements IGenerator<VueOptions> {
8
+ constructor(private readonly _fileService: IFileService) {}
9
+
10
+ public async generate(projectPath: string, options: VueOptions): Promise<void> {
11
+ const tailwindImport = options.tailwind ? `\nimport tailwindcss from '@tailwindcss/vite';` : ''
12
+ const tailwindPlugin = options.tailwind ? `\n tailwindcss(),` : ''
13
+
14
+ const content = `import { defineConfig } from 'vite';
15
+ import vue from '@vitejs/plugin-vue';${tailwindImport}
16
+ import path from 'node:path';
17
+
18
+ export default defineConfig({
19
+ plugins: [
20
+ vue(),${tailwindPlugin}
21
+ ],
22
+ resolve: {
23
+ alias: {
24
+ '@': path.resolve(__dirname, './src'),
25
+ },
26
+ },
27
+ server: {
28
+ port: 5173,
29
+ }
30
+ });
31
+ `
32
+ const filePath = path.join(projectPath, 'vite.config.ts')
33
+ await this._fileService.writeFileRecursive(filePath, content)
34
+ }
35
+ }
@@ -0,0 +1,5 @@
1
+ export * from './bootstrap'
2
+ export * from './commands'
3
+ export * from './dispatcher'
4
+ export * from './file'
5
+ export * from './strategies'