@better-fullstack/types 2.5.0 → 2.6.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 (44) hide show
  1. package/dist/{defaults-CbLlpynl.d.mts → defaults-7VECSARb.d.mts} +2 -2
  2. package/dist/defaults-7VECSARb.d.mts.map +1 -0
  3. package/dist/{defaults-BzWJP6p0.mjs → defaults-Bp46Ajyg.mjs} +2 -1
  4. package/dist/defaults-Bp46Ajyg.mjs.map +1 -0
  5. package/dist/defaults.d.mts +3 -3
  6. package/dist/defaults.mjs +1 -1
  7. package/dist/index.d.mts +52 -6
  8. package/dist/index.d.mts.map +1 -1
  9. package/dist/index.mjs +22 -20
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/json-schema.d.mts +180 -0
  12. package/dist/json-schema.d.mts.map +1 -1
  13. package/dist/json-schema.mjs +1 -1
  14. package/dist/{schemas-CtieNRfz.mjs → schemas-CH1UszP5.mjs} +36 -9
  15. package/dist/schemas-CH1UszP5.mjs.map +1 -0
  16. package/dist/{schemas-BRAokgPa.d.mts → schemas-CY0MaZmU.d.mts} +191 -10
  17. package/dist/schemas-CY0MaZmU.d.mts.map +1 -0
  18. package/dist/schemas.d.mts +2 -2
  19. package/dist/schemas.mjs +2 -2
  20. package/dist/{stack-graph-CXTiPofQ.mjs → stack-graph-AWbdUrN3.mjs} +615 -131
  21. package/dist/stack-graph-AWbdUrN3.mjs.map +1 -0
  22. package/dist/{stack-graph-BPw9KsNm.d.mts → stack-graph-DkJ944IX.d.mts} +7 -3
  23. package/dist/stack-graph-DkJ944IX.d.mts.map +1 -0
  24. package/dist/stack-graph.d.mts +4 -4
  25. package/dist/stack-graph.mjs +3 -3
  26. package/dist/{stack-translation-D7KkQm8W.d.mts → stack-translation-C9yYLNwM.d.mts} +27 -7
  27. package/dist/stack-translation-C9yYLNwM.d.mts.map +1 -0
  28. package/dist/{stack-translation-B-zArJLM.mjs → stack-translation-Cn6zGJ5q.mjs} +253 -26
  29. package/dist/stack-translation-Cn6zGJ5q.mjs.map +1 -0
  30. package/dist/stack-translation.d.mts +5 -5
  31. package/dist/stack-translation.mjs +4 -4
  32. package/dist/{types-Br0NsnBr.d.mts → types-fGT5qf7O.d.mts} +5 -3
  33. package/dist/types-fGT5qf7O.d.mts.map +1 -0
  34. package/dist/types.d.mts +3 -3
  35. package/package.json +1 -1
  36. package/dist/defaults-BzWJP6p0.mjs.map +0 -1
  37. package/dist/defaults-CbLlpynl.d.mts.map +0 -1
  38. package/dist/schemas-BRAokgPa.d.mts.map +0 -1
  39. package/dist/schemas-CtieNRfz.mjs.map +0 -1
  40. package/dist/stack-graph-BPw9KsNm.d.mts.map +0 -1
  41. package/dist/stack-graph-CXTiPofQ.mjs.map +0 -1
  42. package/dist/stack-translation-B-zArJLM.mjs.map +0 -1
  43. package/dist/stack-translation-D7KkQm8W.d.mts.map +0 -1
  44. package/dist/types-Br0NsnBr.d.mts.map +0 -1
@@ -1,5 +1,430 @@
1
- import { $i as RUST_CACHING_VALUES, A as CSS_FRAMEWORK_VALUES, An as GO_OBSERVABILITY_VALUES, At as ELIXIR_OBSERVABILITY_VALUES, B as DOTNET_DEPLOY_VALUES, Br as MOBILE_TESTING_VALUES, Cn as GO_CACHING_VALUES, Ct as ELIXIR_EMAIL_VALUES, Dn as GO_LOGGING_VALUES, Dt as ELIXIR_JOBS_VALUES, En as GO_DI_VALUES, Et as ELIXIR_I18N_VALUES, F as DATABASE_VALUES, Fn as GO_TEMPLATING_VALUES, Fr as MOBILE_LIBRARIES_VALUES, Ft as ELIXIR_VALIDATION_VALUES, G as DOTNET_ORM_VALUES, H as DOTNET_JOB_QUEUE_VALUES, In as GO_TESTING_VALUES, Ir as MOBILE_NAVIGATION_VALUES, It as ELIXIR_WEB_FRAMEWORK_VALUES, J as DOTNET_VALIDATION_VALUES, Ji as RATE_LIMIT_VALUES, K as DOTNET_REALTIME_VALUES, L as DOTNET_API_VALUES, La as STATE_MANAGEMENT_VALUES, Ln as GO_VALIDATION_VALUES, Lr as MOBILE_OTA_VALUES, Lt as EMAIL_VALUES, Mn as GO_PROTO_TOOLING_VALUES, Mr as LOGGING_VALUES, Mt as ELIXIR_QUALITY_VALUES, N as DART_MOBILE_VALUES, Nn as GO_QUALITY_VALUES, Nt as ELIXIR_REALTIME_VALUES, O as CMS_VALUES, Oa as SEARCH_VALUES, On as GO_MESSAGE_QUEUE_VALUES, Or as KOTLIN_MOBILE_LIBRARIES_VALUES, P as DATABASE_SETUP_VALUES, Pn as GO_REALTIME_VALUES, Pr as MOBILE_DEEP_LINKING_VALUES, Pt as ELIXIR_TESTING_VALUES, Qi as RUST_AUTH_VALUES, Qr as ORM_VALUES, R as DOTNET_AUTH_VALUES, Ra as SWIFT_MOBILE_VALUES, Rn as GO_WEB_FRAMEWORK_VALUES, Rr as MOBILE_PUSH_VALUES, Rt as EXAMPLES_VALUES, Si as PYTHON_WEB_FRAMEWORK_VALUES, Sn as GO_AUTH_VALUES, St as ELIXIR_DOCUMENTATION_VALUES, T as CACHING_VALUES, Tn as GO_CONFIG_VALUES, Tt as ELIXIR_HTTP_VALUES, U as DOTNET_LIBRARIES_VALUES, V as DOTNET_FRONTEND_VALUES, Vr as MOBILE_UI_VALUES, W as DOTNET_OBSERVABILITY_VALUES, Xi as RUNTIME_VALUES, Xr as OBSERVABILITY_VALUES, Y as DOTNET_WEB_FRAMEWORK_VALUES, Ya as StackPartRoleSchema, Yi as REALTIME_VALUES, Zi as RUST_API_VALUES, _i as PYTHON_REALTIME_VALUES, _r as JOB_QUEUE_VALUES, _t as ELIXIR_APPLICATION_FRAMEWORK_VALUES, a as ANALYTICS_VALUES, aa as RUST_MESSAGE_QUEUE_VALUES, ai as PYTHON_CACHING_VALUES, ao as UI_LIBRARY_VALUES, ar as INTEGRATIONS_VALUES, bi as PYTHON_TESTING_VALUES, bt as ELIXIR_CLUSTERING_VALUES, c as API_VALUES, ca as RUST_REALTIME_VALUES, ci as PYTHON_DATA_VALUES, cr as JAVA_API_VALUES, d as AUTH_VALUES, di as PYTHON_MEDIA_VALUES, dn as FEATURE_FLAGS_VALUES, dr as JAVA_LANGUAGE_VALUES, ea as RUST_CLI_VALUES, fi as PYTHON_MESSAGE_QUEUE_VALUES, fn as FILE_STORAGE_VALUES, fo as WEB_DEPLOY_VALUES, fr as JAVA_LIBRARIES_VALUES, gi as PYTHON_QUALITY_VALUES, gr as JAVA_WEB_FRAMEWORK_VALUES, gt as ELIXIR_API_VALUES, hi as PYTHON_PACKAGE_MANAGER_VALUES, hn as FRONTEND_VALUES, hr as JAVA_TESTING_LIBRARIES_VALUES, ht as EFFECT_VALUES, i as AI_VALUES, ia as RUST_LOGGING_VALUES, ii as PYTHON_AUTH_VALUES, jn as GO_ORM_VALUES, jt as ELIXIR_ORM_VALUES, ka as SERVER_DEPLOY_VALUES, kn as GO_MIGRATIONS_VALUES, kr as KOTLIN_MOBILE_VALUES, kt as ELIXIR_LIBRARIES_VALUES, l as APP_PLATFORM_ADDON_VALUES, la as RUST_TEMPLATING_VALUES, li as PYTHON_GRAPHQL_VALUES, lr as JAVA_AUTH_VALUES, mi as PYTHON_ORM_VALUES, mn as FORMS_VALUES, mr as JAVA_ORM_VALUES, na as RUST_FRONTEND_VALUES, ni as PYTHON_AI_VALUES, o as ANIMATION_VALUES, oa as RUST_OBSERVABILITY_VALUES, oi as PYTHON_CLI_VALUES, oo as VALIDATION_VALUES, pi as PYTHON_OBSERVABILITY_VALUES, pn as FILE_UPLOAD_VALUES, pr as JAVA_LOGGING_VALUES, pt as ECOMMERCE_VALUES, q as DOTNET_TESTING_VALUES, ra as RUST_LIBRARIES_VALUES, ri as PYTHON_API_VALUES, rr as I18N_VALUES, sa as RUST_ORM_VALUES, si as PYTHON_CLOUD_SDK_VALUES, so as VECTOR_DB_VALUES, t as ADDONS_VALUES, ta as RUST_ERROR_HANDLING_VALUES, ti as PAYMENTS_VALUES, to as TESTING_VALUES, ua as RUST_WEB_FRAMEWORK_VALUES, ui as PYTHON_HTTP_CLIENT_VALUES, ur as JAVA_BUILD_TOOL_VALUES, vi as PYTHON_SERVER_VALUES, vt as ELIXIR_AUTH_VALUES, wn as GO_CLI_VALUES, wt as ELIXIR_HTTP_SERVER_VALUES, xi as PYTHON_VALIDATION_VALUES, xn as GO_API_VALUES, xt as ELIXIR_DEPLOY_VALUES, y as BACKEND_VALUES, yi as PYTHON_TASK_QUEUE_VALUES, yt as ELIXIR_CACHING_VALUES, z as DOTNET_CACHING_VALUES, zr as MOBILE_STORAGE_VALUES } from "./schemas-CtieNRfz.mjs";
1
+ import { $a as StackPartRoleSchema, $i as REALTIME_VALUES, A as CMS_VALUES, An as GO_MESSAGE_QUEUE_VALUES, Ar as KOTLIN_MOBILE_LIBRARIES_VALUES, B as DOTNET_AUTH_VALUES, Bn as GO_WEB_FRAMEWORK_VALUES, Br as MOBILE_PUSH_VALUES, Bt as EXAMPLES_VALUES, Ci as PYTHON_TESTING_VALUES, Cn as GO_API_VALUES, Ct as ELIXIR_DEPLOY_VALUES, D as CACHING_VALUES, Dn as GO_CONFIG_VALUES, Dt as ELIXIR_HTTP_VALUES, En as GO_CLI_VALUES, Et as ELIXIR_HTTP_SERVER_VALUES, F as DART_MOBILE_VALUES, Fn as GO_QUALITY_VALUES, Ft as ELIXIR_REALTIME_VALUES, G as DOTNET_LIBRARIES_VALUES, H as DOTNET_DEPLOY_VALUES, Ha as SWIFT_MOBILE_VALUES, Hr as MOBILE_TESTING_VALUES, I as DATABASE_SETUP_VALUES, In as GO_REALTIME_VALUES, Ir as MOBILE_DEEP_LINKING_VALUES, It as ELIXIR_TESTING_VALUES, J as DOTNET_REALTIME_VALUES, K as DOTNET_OBSERVABILITY_VALUES, L as DATABASE_VALUES, Ln as GO_TEMPLATING_VALUES, Lr as MOBILE_LIBRARIES_VALUES, Lt as ELIXIR_VALIDATION_VALUES, M as CSS_FRAMEWORK_VALUES, Ma as SEARCH_VALUES, Mn as GO_OBSERVABILITY_VALUES, Mt as ELIXIR_OBSERVABILITY_VALUES, Na as SERVER_DEPLOY_VALUES, Nn as GO_ORM_VALUES, Nt as ELIXIR_ORM_VALUES, On as GO_DI_VALUES, Ot as ELIXIR_I18N_VALUES, Pn as GO_PROTO_TOOLING_VALUES, Pr as LOGGING_VALUES, Pt as ELIXIR_QUALITY_VALUES, Qi as RATE_LIMIT_VALUES, Qr as OBSERVABILITY_VALUES, Rn as GO_TESTING_VALUES, Rr as MOBILE_NAVIGATION_VALUES, Rt as ELIXIR_WEB_FRAMEWORK_VALUES, Si as PYTHON_TASK_QUEUE_VALUES, St as ELIXIR_CLUSTERING_VALUES, Ti as PYTHON_WEB_FRAMEWORK_VALUES, Tn as GO_CACHING_VALUES, Tt as ELIXIR_EMAIL_VALUES, U as DOTNET_FRONTEND_VALUES, Ur as MOBILE_UI_VALUES, V as DOTNET_CACHING_VALUES, Va as STATE_MANAGEMENT_VALUES, Vr as MOBILE_STORAGE_VALUES, W as DOTNET_JOB_QUEUE_VALUES, X as DOTNET_VALIDATION_VALUES, Y as DOTNET_TESTING_VALUES, Z as DOTNET_WEB_FRAMEWORK_VALUES, _i as PYTHON_ORM_VALUES, _n as FRONTEND_VALUES, _r as JAVA_TESTING_LIBRARIES_VALUES, _t as EFFECT_VALUES, a as ANALYTICS_VALUES, aa as RUST_ERROR_HANDLING_VALUES, ai as PYTHON_AI_VALUES, ao as TESTING_VALUES, ar as I18N_VALUES, b as BOT_PROTECTION_VALUES, bi as PYTHON_REALTIME_VALUES, bt as ELIXIR_AUTH_VALUES, c as API_VALUES, ca as RUST_LOGGING_VALUES, ci as PYTHON_CACHING_VALUES, d as AUTH_VALUES, da as RUST_ORM_VALUES, di as PYTHON_DATA_VALUES, do as VECTOR_DB_VALUES, dr as JAVA_AUTH_VALUES, ea as RUNTIME_VALUES, ei as ORM_VALUES, fa as RUST_REALTIME_VALUES, fi as PYTHON_GRAPHQL_VALUES, fr as JAVA_BUILD_TOOL_VALUES, gi as PYTHON_OBSERVABILITY_VALUES, gn as FORMS_VALUES, go as WEB_DEPLOY_VALUES, gr as JAVA_ORM_VALUES, hi as PYTHON_MESSAGE_QUEUE_VALUES, hn as FILE_UPLOAD_VALUES, hr as JAVA_LOGGING_VALUES, ht as ECOMMERCE_VALUES, i as AI_VALUES, ia as RUST_CLI_VALUES, jn as GO_MIGRATIONS_VALUES, jr as KOTLIN_MOBILE_VALUES, jt as ELIXIR_LIBRARIES_VALUES, kn as GO_LOGGING_VALUES, kt as ELIXIR_JOBS_VALUES, la as RUST_MESSAGE_QUEUE_VALUES, li as PYTHON_CLI_VALUES, lo as UI_LIBRARY_VALUES, ma as RUST_WEB_FRAMEWORK_VALUES, mi as PYTHON_MEDIA_VALUES, mn as FILE_STORAGE_VALUES, mr as JAVA_LIBRARIES_VALUES, na as RUST_AUTH_VALUES, o as ANIMATION_VALUES, oa as RUST_FRONTEND_VALUES, oi as PYTHON_API_VALUES, pa as RUST_TEMPLATING_VALUES, pi as PYTHON_HTTP_CLIENT_VALUES, pn as FEATURE_FLAGS_VALUES, pr as JAVA_LANGUAGE_VALUES, q as DOTNET_ORM_VALUES, ra as RUST_CACHING_VALUES, ri as PAYMENTS_VALUES, sa as RUST_LIBRARIES_VALUES, si as PYTHON_AUTH_VALUES, sr as INTEGRATIONS_VALUES, ta as RUST_API_VALUES, ua as RUST_OBSERVABILITY_VALUES, ui as PYTHON_CLOUD_SDK_VALUES, uo as VALIDATION_VALUES, ur as JAVA_API_VALUES, vi as PYTHON_PACKAGE_MANAGER_VALUES, vr as JAVA_WEB_FRAMEWORK_VALUES, vt as ELIXIR_API_VALUES, wi as PYTHON_VALIDATION_VALUES, wn as GO_AUTH_VALUES, wt as ELIXIR_DOCUMENTATION_VALUES, xi as PYTHON_SERVER_VALUES, xt as ELIXIR_CACHING_VALUES, y as BACKEND_VALUES, yi as PYTHON_QUALITY_VALUES, yr as JOB_QUEUE_VALUES, yt as ELIXIR_APPLICATION_FRAMEWORK_VALUES, z as DOTNET_API_VALUES, zn as GO_VALIDATION_VALUES, zr as MOBILE_OTA_VALUES, zt as EMAIL_VALUES } from "./schemas-CH1UszP5.mjs";
2
2
 
3
+ //#region src/tooling-capabilities.ts
4
+ const TOOLING_CATEGORY_IDS = [
5
+ "toolchain",
6
+ "workspaceRunner",
7
+ "codeQuality",
8
+ "gitHooks",
9
+ "staticAnalysis",
10
+ "aiTooling",
11
+ "documentation",
12
+ "appPlatforms",
13
+ "testingTools",
14
+ "dataClient",
15
+ "frontendUtilities",
16
+ "httpClient",
17
+ "codeGeneration",
18
+ "developerEnvironment",
19
+ "containerOrchestration",
20
+ "apiGateway",
21
+ "continuousIntegration",
22
+ "backendUtilities"
23
+ ];
24
+ const TOOLING_CATEGORIES = [
25
+ {
26
+ id: "toolchain",
27
+ label: "JavaScript Toolchain",
28
+ description: "Choose the primary development toolchain for JavaScript workspaces.",
29
+ selectionMode: "single"
30
+ },
31
+ {
32
+ id: "workspaceRunner",
33
+ label: "Workspace Runner",
34
+ description: "Choose one task runner and cache for a modular monorepo.",
35
+ selectionMode: "single"
36
+ },
37
+ {
38
+ id: "codeQuality",
39
+ label: "Code Quality",
40
+ description: "Choose one coherent linting and formatting profile.",
41
+ selectionMode: "single"
42
+ },
43
+ {
44
+ id: "gitHooks",
45
+ label: "Git Hooks",
46
+ description: "Choose one Git hook manager.",
47
+ selectionMode: "single"
48
+ },
49
+ {
50
+ id: "staticAnalysis",
51
+ label: "Static Analysis",
52
+ description: "Add compatible project and secret analysis checks.",
53
+ selectionMode: "multiple"
54
+ },
55
+ {
56
+ id: "aiTooling",
57
+ label: "AI Tooling",
58
+ description: "Install compatible agent rules, MCP guidance, and skills.",
59
+ selectionMode: "multiple"
60
+ },
61
+ {
62
+ id: "documentation",
63
+ label: "Documentation App",
64
+ description: "Choose one generated documentation application.",
65
+ selectionMode: "single"
66
+ },
67
+ {
68
+ id: "appPlatforms",
69
+ label: "App Platforms",
70
+ description: "Add compatible desktop, mobile, extension, terminal, or PWA targets.",
71
+ selectionMode: "multiple",
72
+ ownerRole: "frontend"
73
+ },
74
+ {
75
+ id: "testingTools",
76
+ label: "Testing Support",
77
+ description: "Add complementary API mocking and component-workshop tools.",
78
+ selectionMode: "multiple",
79
+ ownerRole: "frontend"
80
+ },
81
+ {
82
+ id: "dataClient",
83
+ label: "Data Client",
84
+ description: "Choose one primary remote-data client for each frontend.",
85
+ selectionMode: "single",
86
+ ownerRole: "frontend"
87
+ },
88
+ {
89
+ id: "frontendUtilities",
90
+ label: "Frontend Utilities",
91
+ description: "Add compatible, independent frontend libraries.",
92
+ selectionMode: "multiple",
93
+ ownerRole: "frontend"
94
+ },
95
+ {
96
+ id: "httpClient",
97
+ label: "HTTP Client",
98
+ description: "Choose an explicit HTTP client or use the platform fetch API.",
99
+ selectionMode: "single",
100
+ ownerRole: "frontend"
101
+ },
102
+ {
103
+ id: "codeGeneration",
104
+ label: "API Code Generation",
105
+ description: "Generate typed clients from compatible GraphQL or OpenAPI contracts.",
106
+ selectionMode: "multiple"
107
+ },
108
+ {
109
+ id: "developerEnvironment",
110
+ label: "Developer Environment",
111
+ description: "Generate a reproducible editor and container development environment.",
112
+ selectionMode: "multiple"
113
+ },
114
+ {
115
+ id: "containerOrchestration",
116
+ label: "Local Containers",
117
+ description: "Generate local container orchestration for project services.",
118
+ selectionMode: "single"
119
+ },
120
+ {
121
+ id: "apiGateway",
122
+ label: "API Gateway",
123
+ description: "Choose a gateway for compatible containerized APIs.",
124
+ selectionMode: "single"
125
+ },
126
+ {
127
+ id: "continuousIntegration",
128
+ label: "Continuous Integration",
129
+ description: "Choose generated CI automation for the repository.",
130
+ selectionMode: "single"
131
+ },
132
+ {
133
+ id: "backendUtilities",
134
+ label: "Backend Utilities",
135
+ description: "Add compatible server-side helper packages and generated utilities.",
136
+ selectionMode: "multiple",
137
+ ownerRole: "backend"
138
+ }
139
+ ];
140
+ const option = (category, id, label, description, toolIds = id === "none" ? [] : [id]) => ({
141
+ category,
142
+ id,
143
+ label,
144
+ description,
145
+ toolIds
146
+ });
147
+ const TOOLING_SELECTION_OPTIONS = [
148
+ option("toolchain", "modular", "Modular", "Choose each JavaScript tool independently.", []),
149
+ option("toolchain", "vite-plus", "Vite+", "Unified Vite, Vitest, Oxlint, Oxfmt, Rolldown, tsdown, and workspace runner (Beta)."),
150
+ option("workspaceRunner", "none", "None", "Use package-manager workspace scripts only."),
151
+ option("workspaceRunner", "turborepo", "Turborepo", "Task runner with remote caching."),
152
+ option("workspaceRunner", "nx", "Nx", "Task runner with a project graph and caching."),
153
+ option("codeQuality", "none", "None", "Do not generate a linting and formatting profile."),
154
+ option("codeQuality", "biome", "Biome", "Biome linting and formatting."),
155
+ option("codeQuality", "eslint-prettier", "ESLint + Prettier", "ESLint flat config with Prettier formatting.", ["eslint", "prettier"]),
156
+ option("codeQuality", "oxlint", "Oxlint + Oxfmt", "Oxc linting and formatting.", ["oxlint"]),
157
+ option("codeQuality", "ultracite", "Ultracite", "Opinionated Biome profile with AI support."),
158
+ option("gitHooks", "none", "None", "Do not generate a Git hook manager."),
159
+ option("gitHooks", "lefthook", "Lefthook", "Fast language-independent Git hooks."),
160
+ option("gitHooks", "husky", "Husky", "JavaScript-native Git hooks."),
161
+ option("staticAnalysis", "knip", "Knip", "Find unused files, exports, and dependencies."),
162
+ option("staticAnalysis", "gitleaks", "Gitleaks", "Detect secrets in Git history and changes."),
163
+ option("aiTooling", "ruler", "Ruler", "Centralize and distribute AI coding rules."),
164
+ option("aiTooling", "mcp", "MCP Guidance", "Generate stack-aware MCP recommendations."),
165
+ option("aiTooling", "skills", "Agent Skills", "Install curated stack-aware agent skills."),
166
+ option("documentation", "none", "None", "Do not generate a documentation application."),
167
+ option("documentation", "starlight", "Starlight", "Astro-powered documentation application."),
168
+ option("documentation", "fumadocs", "Fumadocs", "Next.js-powered documentation application."),
169
+ option("appPlatforms", "pwa", "PWA", "Installable and offline-capable web application."),
170
+ option("appPlatforms", "tauri", "Tauri", "Rust-powered desktop shell."),
171
+ option("appPlatforms", "electron", "Electron", "JavaScript-powered desktop shell."),
172
+ option("appPlatforms", "capacitor", "Capacitor", "iOS and Android shell for a web application."),
173
+ option("appPlatforms", "wxt", "WXT", "Browser extension application."),
174
+ option("appPlatforms", "opentui", "OpenTUI", "Terminal user interface application."),
175
+ option("testingTools", "msw", "MSW", "Network-level API mocking."),
176
+ option("testingTools", "storybook", "Storybook", "Component workshop and visual testing."),
177
+ option("dataClient", "none", "None", "Use framework-native data loading."),
178
+ option("dataClient", "swr", "SWR", "React Hooks for remote data."),
179
+ option("dataClient", "tanstack-query", "TanStack Query", "Async server-state management."),
180
+ option("dataClient", "apollo-client", "Apollo Client", "GraphQL client and normalized cache."),
181
+ option("frontendUtilities", "tanstack-table", "TanStack Table", "Headless tables."),
182
+ option("frontendUtilities", "tanstack-virtual", "TanStack Virtual", "Virtualized lists and grids."),
183
+ option("frontendUtilities", "tanstack-db", "TanStack DB", "Reactive client data store (Beta)."),
184
+ option("frontendUtilities", "tanstack-pacer", "TanStack Pacer", "Rate and queue utilities (Beta)."),
185
+ option("frontendUtilities", "firebase", "Firebase JS SDK", "Firebase client services."),
186
+ option("httpClient", "none", "Platform Fetch", "Use the platform fetch API."),
187
+ option("httpClient", "axios", "Axios", "HTTP client with interceptors and cancellation."),
188
+ option("codeGeneration", "graphql-codegen", "GraphQL Code Generator", "Generate typed GraphQL operations."),
189
+ option("codeGeneration", "openapi-typescript", "openapi-typescript", "Generate types from OpenAPI."),
190
+ option("developerEnvironment", "devcontainer", "Dev Container", "Generate a containerized editor environment."),
191
+ option("containerOrchestration", "none", "None", "Do not generate local container orchestration."),
192
+ option("containerOrchestration", "docker-compose", "Docker Compose", "Run project services in local containers."),
193
+ option("apiGateway", "none", "None", "Do not generate an API gateway."),
194
+ option("apiGateway", "kong", "Kong Gateway", "DB-less gateway for containerized APIs."),
195
+ option("continuousIntegration", "none", "None", "Do not generate CI automation."),
196
+ option("continuousIntegration", "github-actions", "GitHub Actions", "Repository CI for install, check, and build."),
197
+ option("backendUtilities", "backend-utils", "Backend Utilities", "Generated handlers, responses, and error helpers.")
198
+ ];
199
+ const TOOLING_CAPABILITIES = [
200
+ {
201
+ toolId: "vite-plus",
202
+ category: "toolchain",
203
+ role: "toolchain",
204
+ ecosystem: "universal"
205
+ },
206
+ {
207
+ toolId: "turborepo",
208
+ category: "workspaceRunner",
209
+ role: "workspaceRunner",
210
+ ecosystem: "universal"
211
+ },
212
+ {
213
+ toolId: "nx",
214
+ category: "workspaceRunner",
215
+ role: "workspaceRunner",
216
+ ecosystem: "universal"
217
+ },
218
+ {
219
+ toolId: "biome",
220
+ category: "codeQuality",
221
+ role: "codeQuality",
222
+ ecosystem: "universal"
223
+ },
224
+ {
225
+ toolId: "eslint",
226
+ category: "codeQuality",
227
+ role: "codeQuality",
228
+ ecosystem: "universal"
229
+ },
230
+ {
231
+ toolId: "prettier",
232
+ category: "codeQuality",
233
+ role: "codeQuality",
234
+ ecosystem: "universal"
235
+ },
236
+ {
237
+ toolId: "oxlint",
238
+ category: "codeQuality",
239
+ role: "codeQuality",
240
+ ecosystem: "universal"
241
+ },
242
+ {
243
+ toolId: "ultracite",
244
+ category: "codeQuality",
245
+ role: "codeQuality",
246
+ ecosystem: "universal"
247
+ },
248
+ {
249
+ toolId: "lefthook",
250
+ category: "gitHooks",
251
+ role: "gitHooks",
252
+ ecosystem: "universal"
253
+ },
254
+ {
255
+ toolId: "husky",
256
+ category: "gitHooks",
257
+ role: "gitHooks",
258
+ ecosystem: "universal"
259
+ },
260
+ {
261
+ toolId: "knip",
262
+ category: "staticAnalysis",
263
+ role: "staticAnalysis",
264
+ ecosystem: "typescript"
265
+ },
266
+ {
267
+ toolId: "gitleaks",
268
+ category: "staticAnalysis",
269
+ role: "staticAnalysis",
270
+ ecosystem: "universal"
271
+ },
272
+ {
273
+ toolId: "ruler",
274
+ category: "aiTooling",
275
+ role: "aiTooling",
276
+ ecosystem: "universal"
277
+ },
278
+ {
279
+ toolId: "mcp",
280
+ category: "aiTooling",
281
+ role: "aiTooling",
282
+ ecosystem: "universal"
283
+ },
284
+ {
285
+ toolId: "skills",
286
+ category: "aiTooling",
287
+ role: "aiTooling",
288
+ ecosystem: "universal"
289
+ },
290
+ {
291
+ toolId: "starlight",
292
+ category: "documentation",
293
+ role: "documentation",
294
+ ecosystem: "universal"
295
+ },
296
+ {
297
+ toolId: "fumadocs",
298
+ category: "documentation",
299
+ role: "documentation",
300
+ ecosystem: "universal"
301
+ },
302
+ ...[
303
+ "pwa",
304
+ "tauri",
305
+ "electron",
306
+ "capacitor"
307
+ ].map((toolId) => ({
308
+ toolId,
309
+ category: "appPlatforms",
310
+ role: "appPlatform",
311
+ ecosystem: "typescript",
312
+ ownerRole: "frontend"
313
+ })),
314
+ ...["wxt", "opentui"].map((toolId) => ({
315
+ toolId,
316
+ category: "appPlatforms",
317
+ role: "appPlatform",
318
+ ecosystem: "typescript"
319
+ })),
320
+ {
321
+ toolId: "msw",
322
+ category: "testingTools",
323
+ role: "testing",
324
+ ecosystem: "typescript"
325
+ },
326
+ {
327
+ toolId: "storybook",
328
+ category: "testingTools",
329
+ role: "testing",
330
+ ecosystem: "typescript",
331
+ ownerRole: "frontend"
332
+ },
333
+ ...[
334
+ "swr",
335
+ "tanstack-query",
336
+ "apollo-client"
337
+ ].map((toolId) => ({
338
+ toolId,
339
+ category: "dataClient",
340
+ role: "dataFetching",
341
+ ecosystem: "typescript",
342
+ ownerRole: "frontend"
343
+ })),
344
+ ...[
345
+ "tanstack-table",
346
+ "tanstack-virtual",
347
+ "tanstack-db",
348
+ "tanstack-pacer",
349
+ "firebase"
350
+ ].map((toolId) => ({
351
+ toolId,
352
+ category: "frontendUtilities",
353
+ role: "libraries",
354
+ ecosystem: "typescript",
355
+ ownerRole: "frontend"
356
+ })),
357
+ {
358
+ toolId: "axios",
359
+ category: "httpClient",
360
+ role: "httpClient",
361
+ ecosystem: "typescript",
362
+ ownerRole: "frontend"
363
+ },
364
+ ...["graphql-codegen", "openapi-typescript"].map((toolId) => ({
365
+ toolId,
366
+ category: "codeGeneration",
367
+ role: "codeGeneration",
368
+ ecosystem: "universal"
369
+ })),
370
+ {
371
+ toolId: "devcontainer",
372
+ category: "developerEnvironment",
373
+ role: "developerEnvironment",
374
+ ecosystem: "universal"
375
+ },
376
+ {
377
+ toolId: "docker-compose",
378
+ category: "containerOrchestration",
379
+ role: "containerOrchestration",
380
+ ecosystem: "universal"
381
+ },
382
+ {
383
+ toolId: "kong",
384
+ category: "apiGateway",
385
+ role: "apiGateway",
386
+ ecosystem: "universal"
387
+ },
388
+ {
389
+ toolId: "github-actions",
390
+ category: "continuousIntegration",
391
+ role: "continuousIntegration",
392
+ ecosystem: "universal"
393
+ },
394
+ {
395
+ toolId: "backend-utils",
396
+ category: "backendUtilities",
397
+ role: "backendUtilities",
398
+ ecosystem: "typescript",
399
+ ownerRole: "backend"
400
+ }
401
+ ];
402
+ const TOOLING_TOOL_IDS = TOOLING_CAPABILITIES.map((capability) => capability.toolId);
403
+ function getToolingCategory(category) {
404
+ return TOOLING_CATEGORIES.find((definition) => definition.id === category);
405
+ }
406
+ function getToolingCapability(toolId) {
407
+ return TOOLING_CAPABILITIES.find((definition) => definition.toolId === toolId);
408
+ }
409
+ function getToolingCapabilities(category) {
410
+ return TOOLING_CAPABILITIES.filter((definition) => definition.category === category);
411
+ }
412
+ function getToolingSelectionOptions(category) {
413
+ return TOOLING_SELECTION_OPTIONS.filter((definition) => definition.category === category);
414
+ }
415
+ function isToolingOverlayPart(part) {
416
+ const capability = getToolingCapability(part.toolId);
417
+ if (!capability || part.source === "provided") return false;
418
+ return capability.role === part.role && (part.ecosystem === void 0 || capability.ecosystem === part.ecosystem);
419
+ }
420
+ function isToolingOverlayOnly(parts) {
421
+ return (parts ?? []).every((part) => isToolingOverlayPart(part));
422
+ }
423
+ function getSelectedToolingOption(category, selectedToolIds) {
424
+ return getToolingSelectionOptions(category).find((selection) => selection.toolIds.length === 0 ? getToolingCapabilities(category).every((capability) => !selectedToolIds.includes(capability.toolId)) : selection.toolIds.every((toolId) => selectedToolIds.includes(toolId)));
425
+ }
426
+
427
+ //#endregion
3
428
  //#region src/stack-compatibility-rules.ts
4
429
  const WEB_FRAMEWORKS = [
5
430
  "tanstack-router",
@@ -557,7 +982,8 @@ const LEGACY_TYPESCRIPT_FRONTEND_SINGLE_CATEGORIES = {
557
982
  animation: "animation",
558
983
  fileUpload: "fileUpload",
559
984
  i18n: "i18n",
560
- analytics: "analytics"
985
+ analytics: "analytics",
986
+ botProtection: "botProtection"
561
987
  };
562
988
  const LEGACY_DATABASE_SINGLE_CATEGORIES = { dbSetup: "dbSetup" };
563
989
  const LEGACY_MOBILE_SINGLE_CATEGORIES = {
@@ -592,45 +1018,6 @@ const LEGACY_BACKEND_ARRAY_CATEGORIES_BY_ECOSYSTEM = {
592
1018
  },
593
1019
  elixir: { libraries: "elixirLibraries" }
594
1020
  };
595
- const CODE_QUALITY_ADDONS = new Set([
596
- "biome",
597
- "eslint",
598
- "prettier",
599
- "oxlint",
600
- "ultracite",
601
- "lefthook",
602
- "husky",
603
- "gitleaks"
604
- ]);
605
- const TYPESCRIPT_CODE_QUALITY_ADDONS = new Set(["knip"]);
606
- const DOCUMENTATION_ADDONS = new Set(["starlight", "fumadocs"]);
607
- const FRONTEND_APP_PLATFORM_ADDONS = new Set(APP_PLATFORM_ADDON_VALUES);
608
- const FRONTEND_DATA_FETCHING_ADDONS = new Set([
609
- "swr",
610
- "apollo-client",
611
- "tanstack-query",
612
- "tanstack-table",
613
- "tanstack-virtual",
614
- "tanstack-db",
615
- "tanstack-pacer"
616
- ]);
617
- const FRONTEND_HTTP_CLIENT_ADDONS = new Set(["axios"]);
618
- const FRONTEND_LIBRARY_ADDONS = new Set(["firebase"]);
619
- const FRONTEND_TESTING_ADDONS = new Set(["msw", "storybook"]);
620
- const WORKSPACE_TOOLING_ADDONS = new Set([
621
- "turborepo",
622
- "nx",
623
- "docker-compose",
624
- "devcontainer",
625
- "kong",
626
- "github-actions",
627
- "ruler",
628
- "mcp",
629
- "skills",
630
- "backend-utils",
631
- "graphql-codegen",
632
- "openapi-typescript"
633
- ]);
634
1021
  const DOCKER_COMPOSE_COMPATIBLE_ECOSYSTEMS = new Set([
635
1022
  "typescript",
636
1023
  "python",
@@ -638,16 +1025,48 @@ const DOCKER_COMPOSE_COMPATIBLE_ECOSYSTEMS = new Set([
638
1025
  "rust",
639
1026
  "java"
640
1027
  ]);
641
- const LEGACY_ADDON_GRAPH_ROLES = new Set([
642
- "appPlatform",
643
- "codeQuality",
644
- "dataFetching",
645
- "documentation",
646
- "testing",
647
- "workspaceTooling",
648
- "httpClient",
649
- "libraries"
650
- ]);
1028
+ const LEGACY_ADDON_GRAPH_ROLES = new Set([...TOOLING_CAPABILITIES.map((capability) => capability.role), "workspaceTooling"]);
1029
+ function getHistoricalAddonRole(toolId) {
1030
+ if ([
1031
+ "biome",
1032
+ "eslint",
1033
+ "prettier",
1034
+ "oxlint",
1035
+ "ultracite",
1036
+ "lefthook",
1037
+ "husky",
1038
+ "knip",
1039
+ "gitleaks"
1040
+ ].includes(toolId)) return "codeQuality";
1041
+ if (["starlight", "fumadocs"].includes(toolId)) return "documentation";
1042
+ if ([
1043
+ "pwa",
1044
+ "tauri",
1045
+ "electron",
1046
+ "capacitor",
1047
+ "wxt",
1048
+ "opentui"
1049
+ ].includes(toolId)) return "appPlatform";
1050
+ if ([
1051
+ "swr",
1052
+ "apollo-client",
1053
+ "tanstack-query",
1054
+ "tanstack-table",
1055
+ "tanstack-virtual",
1056
+ "tanstack-db",
1057
+ "tanstack-pacer"
1058
+ ].includes(toolId)) return "dataFetching";
1059
+ if (toolId === "axios") return "httpClient";
1060
+ if (toolId === "firebase") return "libraries";
1061
+ if (["msw", "storybook"].includes(toolId)) return "testing";
1062
+ if (getToolingCapability(toolId)) return "workspaceTooling";
1063
+ }
1064
+ function getHistoricalAddonEcosystem(toolId) {
1065
+ const role = getHistoricalAddonRole(toolId);
1066
+ if (!role) return void 0;
1067
+ if (role === "workspaceTooling") return "universal";
1068
+ return getToolingCapability(toolId)?.ecosystem;
1069
+ }
651
1070
  const LEGACY_ARRAY_CATEGORIES = new Set([
652
1071
  "addons",
653
1072
  "examples",
@@ -667,48 +1086,26 @@ const LEGACY_ARRAY_CATEGORIES = new Set([
667
1086
  "elixirLibraries"
668
1087
  ]);
669
1088
  function getAddonStackPartBinding(toolId) {
670
- if (TYPESCRIPT_CODE_QUALITY_ADDONS.has(toolId)) return {
671
- role: "codeQuality",
672
- ecosystem: "typescript"
673
- };
674
- if (CODE_QUALITY_ADDONS.has(toolId)) return {
675
- role: "codeQuality",
676
- ecosystem: "universal"
677
- };
678
- if (DOCUMENTATION_ADDONS.has(toolId)) return {
679
- role: "documentation",
680
- ecosystem: "universal"
681
- };
682
- if (FRONTEND_APP_PLATFORM_ADDONS.has(toolId)) return {
683
- role: "appPlatform",
684
- ecosystem: "typescript",
685
- ownerRole: "frontend"
686
- };
687
- if (FRONTEND_DATA_FETCHING_ADDONS.has(toolId)) return {
688
- role: "dataFetching",
689
- ecosystem: "typescript",
690
- ownerRole: "frontend"
691
- };
692
- if (FRONTEND_HTTP_CLIENT_ADDONS.has(toolId)) return {
693
- role: "httpClient",
694
- ecosystem: "typescript",
695
- ownerRole: "frontend"
696
- };
697
- if (FRONTEND_LIBRARY_ADDONS.has(toolId)) return {
698
- role: "libraries",
699
- ecosystem: "typescript",
700
- ownerRole: "frontend"
701
- };
702
- if (FRONTEND_TESTING_ADDONS.has(toolId)) return {
703
- role: "testing",
704
- ecosystem: "typescript",
705
- ownerRole: "frontend"
706
- };
707
- if (WORKSPACE_TOOLING_ADDONS.has(toolId)) return {
708
- role: "workspaceTooling",
709
- ecosystem: "universal"
1089
+ const capability = getToolingCapability(toolId);
1090
+ if (!capability) return void 0;
1091
+ return {
1092
+ role: capability.role,
1093
+ ecosystem: capability.ecosystem,
1094
+ ownerRole: capability.ownerRole
710
1095
  };
711
1096
  }
1097
+ function toolingOverlayStackParts(addons, source = "selected") {
1098
+ return (addons ?? []).filter((toolId) => toolId !== "none").flatMap((toolId) => {
1099
+ const capability = getToolingCapability(toolId);
1100
+ if (!capability) return [];
1101
+ return [createStackPart({
1102
+ role: capability.role,
1103
+ ecosystem: capability.ecosystem,
1104
+ toolId,
1105
+ source
1106
+ })];
1107
+ });
1108
+ }
712
1109
  const OWNER_ROLES_BY_SCOPED_ROLE = {
713
1110
  ...Object.fromEntries([...Object.keys(LEGACY_TYPESCRIPT_BACKEND_SINGLE_CATEGORIES), ...Object.keys(LEGACY_TYPESCRIPT_BACKEND_INFRA_CATEGORIES)].map((role) => [role, ["backend"]])),
714
1111
  ...Object.fromEntries(Object.keys(LEGACY_TYPESCRIPT_FRONTEND_SINGLE_CATEGORIES).map((role) => [role, ["frontend"]])),
@@ -961,26 +1358,28 @@ const STACK_TOOL_DEFINITIONS = [
961
1358
  ...defineTools(FILE_UPLOAD_VALUES, "fileUpload", "typescript", "fileUpload"),
962
1359
  ...defineTools(I18N_VALUES, "i18n", "typescript", "i18n"),
963
1360
  ...defineTools(ANALYTICS_VALUES, "analytics", "typescript", "analytics"),
964
- ...defineTools(ADDONS_VALUES.filter((value) => CODE_QUALITY_ADDONS.has(value)), "codeQuality", "universal", void 0, {
965
- allowMultiple: true,
966
- ownerless: true
967
- }),
968
- ...defineTools(ADDONS_VALUES.filter((value) => TYPESCRIPT_CODE_QUALITY_ADDONS.has(value)), "codeQuality", "typescript", void 0, {
969
- allowMultiple: true,
970
- ownerless: true
1361
+ ...defineTools(BOT_PROTECTION_VALUES, "botProtection", "typescript", "botProtection"),
1362
+ ...TOOLING_CAPABILITIES.map((capability) => {
1363
+ const category = getToolingCategory(capability.category);
1364
+ return {
1365
+ toolId: capability.toolId,
1366
+ roles: [capability.role],
1367
+ ecosystems: [capability.ecosystem],
1368
+ allowMultiple: category?.selectionMode === "multiple" || capability.category === "codeQuality",
1369
+ ownerless: capability.ownerRole === void 0
1370
+ };
971
1371
  }),
972
- ...defineTools(ADDONS_VALUES.filter((value) => DOCUMENTATION_ADDONS.has(value)), "documentation", "universal", void 0, {
973
- allowMultiple: true,
974
- ownerless: true
975
- }),
976
- ...defineTools(ADDONS_VALUES.filter((value) => FRONTEND_APP_PLATFORM_ADDONS.has(value)), "appPlatform", "typescript", void 0, { allowMultiple: true }),
977
- ...defineTools(ADDONS_VALUES.filter((value) => FRONTEND_DATA_FETCHING_ADDONS.has(value)), "dataFetching", "typescript", void 0, { allowMultiple: true }),
978
- ...defineTools(ADDONS_VALUES.filter((value) => FRONTEND_HTTP_CLIENT_ADDONS.has(value)), "httpClient", "typescript", void 0, { allowMultiple: true }),
979
- ...defineTools(ADDONS_VALUES.filter((value) => FRONTEND_LIBRARY_ADDONS.has(value)), "libraries", "typescript", void 0, { allowMultiple: true }),
980
- ...defineTools(ADDONS_VALUES.filter((value) => FRONTEND_TESTING_ADDONS.has(value)), "testing", "typescript", void 0, { allowMultiple: true }),
981
- ...defineTools(ADDONS_VALUES.filter((value) => WORKSPACE_TOOLING_ADDONS.has(value)), "workspaceTooling", "universal", void 0, {
982
- allowMultiple: true,
983
- ownerless: true
1372
+ ...TOOLING_CAPABILITIES.flatMap((capability) => {
1373
+ const historicalRole = getHistoricalAddonRole(capability.toolId);
1374
+ if (!historicalRole || historicalRole === capability.role) return [];
1375
+ return [{
1376
+ toolId: capability.toolId,
1377
+ roles: [historicalRole],
1378
+ ecosystems: [getHistoricalAddonEcosystem(capability.toolId) ?? capability.ecosystem],
1379
+ selectable: false,
1380
+ allowMultiple: true,
1381
+ ownerless: historicalRole === "workspaceTooling" || capability.ownerRole === void 0
1382
+ }];
984
1383
  }),
985
1384
  ...defineTools(EXAMPLES_VALUES, "examples", "universal", void 0, {
986
1385
  allowMultiple: true,
@@ -1725,35 +2124,77 @@ function createAddonCompatibilityIssue(part, context) {
1725
2124
  toolId: part.toolId,
1726
2125
  message: "TanStack Query is already included via the selected API layer."
1727
2126
  });
1728
- if ([
1729
- "tanstack-query",
1730
- "tanstack-table",
1731
- "tanstack-virtual",
1732
- "tanstack-db",
1733
- "tanstack-pacer"
1734
- ].includes(part.toolId) && frontendTool === "astro" && context.settings?.astroIntegration === "none") return createStackGraphIssue({
2127
+ }
2128
+ if ((part.role === "dataFetching" || part.role === "libraries") && [
2129
+ "tanstack-query",
2130
+ "tanstack-table",
2131
+ "tanstack-virtual",
2132
+ "tanstack-db",
2133
+ "tanstack-pacer"
2134
+ ].includes(part.toolId) && frontendTool === "astro" && context.settings?.astroIntegration === "none") return createStackGraphIssue({
2135
+ code: "INCOMPATIBLE_GRAPH_SELECTION",
2136
+ partId: part.id,
2137
+ role: part.role,
2138
+ toolId: part.toolId,
2139
+ message: "TanStack libraries with Astro require a UI framework integration (React, Vue, Svelte, or Solid)"
2140
+ });
2141
+ if (part.role === "toolchain" && part.toolId === "vite-plus") {
2142
+ if (!(context.selectedEcosystemsByRoleList?.frontend ?? (context.primaryEcosystemsByRole?.frontend ? [context.primaryEcosystemsByRole.frontend] : [])).includes("typescript")) return createStackGraphIssue({
1735
2143
  code: "INCOMPATIBLE_GRAPH_SELECTION",
1736
2144
  partId: part.id,
1737
2145
  role: part.role,
1738
2146
  toolId: part.toolId,
1739
- message: "TanStack libraries with Astro require a UI framework integration (React, Vue, Svelte, or Solid)"
2147
+ message: "Vite+ requires a generated TypeScript web frontend"
2148
+ });
2149
+ const conflictingTool = [
2150
+ ...context.selectedToolIdsByRoleList?.workspaceRunner ?? [],
2151
+ ...context.selectedToolIdsByRoleList?.codeQuality ?? [],
2152
+ ...context.selectedToolIdsByRoleList?.gitHooks ?? [],
2153
+ ...(context.selectedToolIdsByRoleList?.workspaceTooling ?? []).filter((toolId) => ["turborepo", "nx"].includes(toolId))
2154
+ ].find((toolId) => toolId !== "none");
2155
+ if (conflictingTool) return createStackGraphIssue({
2156
+ code: "INCOMPATIBLE_GRAPH_SELECTION",
2157
+ partId: part.id,
2158
+ role: part.role,
2159
+ toolId: part.toolId,
2160
+ message: `Vite+ owns workspace tasks, code quality, and commit hooks; remove '${conflictingTool}'`
1740
2161
  });
1741
2162
  }
1742
- if (part.role === "workspaceTooling") {
1743
- const workspaceTooling = context.selectedToolIdsByRoleList?.workspaceTooling ?? [];
1744
- if (part.toolId === "nx" && workspaceTooling.includes("turborepo")) return createStackGraphIssue({
2163
+ if (part.role === "workspaceRunner") {
2164
+ const workspaceRunners = [
2165
+ "turborepo",
2166
+ "nx",
2167
+ "vite-plus"
2168
+ ];
2169
+ if ([...context.selectedToolIdsByRoleList?.workspaceRunner ?? [], ...context.selectedToolIdsByRoleList?.workspaceTooling ?? []].find((toolId) => toolId !== part.toolId && workspaceRunners.includes(toolId))) return createStackGraphIssue({
1745
2170
  code: "INCOMPATIBLE_GRAPH_SELECTION",
1746
2171
  partId: part.id,
1747
2172
  role: part.role,
1748
2173
  toolId: part.toolId,
1749
- message: "Choose either Nx or Turborepo, not both"
2174
+ message: "Choose one workspace runner: Turborepo, Nx, or Vite+"
1750
2175
  });
1751
- if (part.toolId === "turborepo" && workspaceTooling.includes("nx")) return createStackGraphIssue({
2176
+ }
2177
+ if ((part.role === "workspaceRunner" || part.role === "codeQuality" || part.role === "gitHooks") && context.selectedToolIdsByRoleList?.toolchain?.includes("vite-plus")) return createStackGraphIssue({
2178
+ code: "INCOMPATIBLE_GRAPH_SELECTION",
2179
+ partId: part.id,
2180
+ role: part.role,
2181
+ toolId: part.toolId,
2182
+ message: "Vite+ owns workspace tasks, code quality, and commit hooks"
2183
+ });
2184
+ if (part.role === "workspaceTooling" || part.role === "containerOrchestration" || part.role === "developerEnvironment" || part.role === "apiGateway" || part.role === "backendUtilities") {
2185
+ const workspaceTooling = context.selectedToolIdsByRoleList?.workspaceTooling ?? [];
2186
+ const workspaceRunners = [
2187
+ "turborepo",
2188
+ "nx",
2189
+ "vite-plus"
2190
+ ];
2191
+ const otherWorkspaceRunner = workspaceTooling.find((toolId) => toolId !== part.toolId && workspaceRunners.includes(toolId));
2192
+ if (workspaceRunners.includes(part.toolId) && otherWorkspaceRunner) return createStackGraphIssue({
1752
2193
  code: "INCOMPATIBLE_GRAPH_SELECTION",
1753
2194
  partId: part.id,
1754
2195
  role: part.role,
1755
2196
  toolId: part.toolId,
1756
- message: "Choose either Turborepo or Nx, not both"
2197
+ message: "Choose one workspace runner: Turborepo, Nx, or Vite+"
1757
2198
  });
1758
2199
  if (part.toolId === "docker-compose" || part.toolId === "devcontainer" || part.toolId === "kong") {
1759
2200
  const title = part.toolId === "devcontainer" ? "DevContainer" : part.toolId === "kong" ? "Kong Gateway" : "Docker Compose";
@@ -2198,6 +2639,12 @@ function getStackPartCompatibilityIssue(part, context) {
2198
2639
  function getStackPartCompatibilityIssueForPart(part, parts) {
2199
2640
  return getStackPartCompatibilityIssue(part, getStackPartOptionContextForPart(part, parts));
2200
2641
  }
2642
+ function hasJavaScriptWorkspaceRoot(parts) {
2643
+ return parts?.some((part) => !part.ownerPartId && part.source !== "provided" && part.toolId !== "none" && (part.ecosystem === "typescript" || part.ecosystem === "react-native") && (part.role === "frontend" || part.role === "backend" || part.role === "mobile")) ?? false;
2644
+ }
2645
+ function hasVitePlusWorkspaceRoot(parts) {
2646
+ return parts?.some((part) => !part.ownerPartId && part.source !== "provided" && part.toolId !== "none" && part.ecosystem === "typescript" && part.role === "frontend") ?? false;
2647
+ }
2201
2648
  function getStackToolDefinitions(context) {
2202
2649
  if (!context) return [...STACK_TOOL_DEFINITIONS];
2203
2650
  return STACK_TOOL_DEFINITIONS.filter((definition) => {
@@ -2326,6 +2773,15 @@ function getSelectedToolIdsByRoleList(parts) {
2326
2773
  }
2327
2774
  return selectedToolIdsByRoleList;
2328
2775
  }
2776
+ function getSelectedEcosystemsByRoleList(parts) {
2777
+ const selectedEcosystemsByRoleList = {};
2778
+ for (const part of parts) {
2779
+ if (part.source === "provided" || part.ownerPartId || part.toolId === "none") continue;
2780
+ selectedEcosystemsByRoleList[part.role] ??= [];
2781
+ selectedEcosystemsByRoleList[part.role]?.push(part.ecosystem);
2782
+ }
2783
+ return selectedEcosystemsByRoleList;
2784
+ }
2329
2785
  function getPrimaryToolContext(parts) {
2330
2786
  const primaryToolIdsByRole = {};
2331
2787
  const primaryEcosystemsByRole = {};
@@ -2373,6 +2829,7 @@ function getStackPartOptionContextForPart(part, parts) {
2373
2829
  siblingToolIdsByRoleList: getSiblingToolIdsByRoleList(part, parts),
2374
2830
  selectedToolIdsByRole: getSelectedToolIdsByRole(parts),
2375
2831
  selectedToolIdsByRoleList: getSelectedToolIdsByRoleList(parts),
2832
+ selectedEcosystemsByRoleList: getSelectedEcosystemsByRoleList(parts),
2376
2833
  primaryToolIdsByRole,
2377
2834
  primaryEcosystemsByRole
2378
2835
  };
@@ -2390,12 +2847,12 @@ function addLegacyPart(parts, role, ecosystem, toolId, source, ownerPartId, sett
2390
2847
  parts.push(part);
2391
2848
  return part;
2392
2849
  }
2393
- function addLegacyAddonPart(parts, toolId, source, frontendPart) {
2850
+ function addLegacyAddonPart(parts, toolId, source, frontendPart, backendPart) {
2394
2851
  if (!toolId || toolId === "none") return;
2395
2852
  const binding = getAddonStackPartBinding(toolId);
2396
2853
  if (!binding) return;
2397
- const ownerPartId = binding.ownerRole === "frontend" ? frontendPart?.id : void 0;
2398
- if (binding.ownerRole === "frontend" && !ownerPartId) return;
2854
+ const ownerPartId = binding.ownerRole === "frontend" ? frontendPart?.id : binding.ownerRole === "backend" ? backendPart?.id : void 0;
2855
+ if (binding.ownerRole && !ownerPartId) return;
2399
2856
  addLegacyPart(parts, binding.role, binding.ecosystem, toolId, source, ownerPartId);
2400
2857
  }
2401
2858
  function appendUniqueLegacyArrayValue(config, category, value) {
@@ -2407,7 +2864,8 @@ function getLegacyArrayCategoryForPart(part) {
2407
2864
  if (part.role === "examples") return "examples";
2408
2865
  if (LEGACY_ADDON_GRAPH_ROLES.has(part.role)) {
2409
2866
  const binding = getAddonStackPartBinding(part.toolId);
2410
- if (binding && binding.role === part.role && binding.ecosystem === part.ecosystem) return "addons";
2867
+ const historicalRole = getHistoricalAddonRole(part.toolId);
2868
+ if (binding && (binding.role === part.role || historicalRole === part.role) && (binding.ecosystem === part.ecosystem || getHistoricalAddonEcosystem(part.toolId) === part.ecosystem)) return "addons";
2411
2869
  }
2412
2870
  const legacyCategory = findDefinition(part)?.legacyCategory;
2413
2871
  if (legacyCategory && LEGACY_ARRAY_CATEGORIES.has(legacyCategory)) return legacyCategory;
@@ -2439,7 +2897,7 @@ function legacyProjectConfigToStackParts(config, source = "legacy") {
2439
2897
  const databasePart = addLegacyPart(parts, "database", "universal", config.database, source);
2440
2898
  const capabilityOwner = backendPart?.id ?? frontendPart?.id ?? mobilePart?.id ?? databasePart?.id;
2441
2899
  if (databasePart) addLegacyPart(parts, "dbSetup", "universal", config.dbSetup, source, databasePart.id);
2442
- for (const addon of config.addons ?? []) addLegacyAddonPart(parts, addon, source, frontendPart);
2900
+ for (const addon of config.addons ?? []) addLegacyAddonPart(parts, addon, source, frontendPart, backendPart);
2443
2901
  for (const example of config.examples ?? []) addLegacyPart(parts, "examples", "universal", example, source);
2444
2902
  if (config.ecosystem === void 0 || config.ecosystem === "typescript" || config.ecosystem === "react-native") {
2445
2903
  addLegacyPart(parts, "orm", "typescript", config.orm, source, capabilityOwner);
@@ -2747,9 +3205,35 @@ function validateStackParts(parts) {
2747
3205
  message: `'${selected.toolId}' conflicts with '${provided.toolId}' provided by '${provider?.toolId ?? "another part"}'.`
2748
3206
  });
2749
3207
  }
3208
+ const toolingByCategoryAndScope = /* @__PURE__ */ new Map();
3209
+ for (const part of parts) {
3210
+ if (part.source === "provided") continue;
3211
+ const capability = getToolingCapability(part.toolId);
3212
+ if (!capability || capability.role !== part.role || capability.ecosystem !== part.ecosystem) continue;
3213
+ const key = `${part.ownerPartId ?? "root"}:${capability.category}`;
3214
+ toolingByCategoryAndScope.set(key, [...toolingByCategoryAndScope.get(key) ?? [], part]);
3215
+ }
3216
+ for (const categoryParts of toolingByCategoryAndScope.values()) {
3217
+ const first = categoryParts[0];
3218
+ if (!first) continue;
3219
+ const capability = getToolingCapability(first.toolId);
3220
+ if (!capability || getToolingCategory(capability.category)?.selectionMode !== "single") continue;
3221
+ if (categoryParts.every((part) => part.source === "legacy")) continue;
3222
+ const selected = [...new Set(categoryParts.map((part) => part.toolId))].sort();
3223
+ if (!getToolingSelectionOptions(capability.category).some((selection) => {
3224
+ const expected = [...selection.toolIds].sort();
3225
+ return expected.length === selected.length && expected.every((toolId, index) => toolId === selected[index]);
3226
+ })) issues.push({
3227
+ code: "INCOHERENT_TOOLING_PROFILE",
3228
+ partId: first.id,
3229
+ role: first.role,
3230
+ toolId: first.toolId,
3231
+ message: `Choose one complete '${getToolingCategory(capability.category)?.label}' profile.`
3232
+ });
3233
+ }
2750
3234
  return { issues };
2751
3235
  }
2752
3236
 
2753
3237
  //#endregion
2754
- export { isBackendUtilsCompatibleBackend as C, requiresChatSdkVercelAIForExamples as E, hasTauriCompatibleFrontend as S, isExampleChatSdkAllowed as T, UI_LIBRARY_COMPATIBILITY as _, getAddonStackPartBinding as a, hasPWACompatibleFrontend as b, getStackPartOptions as c, materializeProvidedStackParts as d, parseStackPartSpecs as f, BACKEND_UTILS_COMPATIBLE_BACKENDS as g, validateStackParts as h, formatStackPartSpec as i, getStackToolDefinitions as l, stackPartsToLegacyProjectConfigPartial as m, STACK_TOOL_DEFINITIONS as n, getRoleTargetPath as o, stackGraphToLegacyProjectConfigForEcosystem as p, createStackPart as r, getStackPartCompatibilityIssueForPart as s, ELIXIR_UNSUPPORTED_GRAPH_TOOLS as t, legacyProjectConfigToStackParts as u, getUnsupportedWebDeployFrontend as v, isExampleAIAllowed as w, hasTanStackAICompatibleFrontend as x, hasDockerComposeCompatibleFrontend as y };
2755
- //# sourceMappingURL=stack-graph-CXTiPofQ.mjs.map
3238
+ export { TOOLING_CAPABILITIES as A, isToolingOverlayOnly as B, hasPWACompatibleFrontend as C, isExampleAIAllowed as D, isBackendUtilsCompatibleBackend as E, getSelectedToolingOption as F, getToolingCapabilities as I, getToolingCapability as L, TOOLING_CATEGORY_IDS as M, TOOLING_SELECTION_OPTIONS as N, isExampleChatSdkAllowed as O, TOOLING_TOOL_IDS as P, getToolingCategory as R, hasDockerComposeCompatibleFrontend as S, hasTauriCompatibleFrontend as T, isToolingOverlayPart as V, toolingOverlayStackParts as _, getAddonStackPartBinding as a, UI_LIBRARY_COMPATIBILITY as b, getStackPartOptions as c, hasVitePlusWorkspaceRoot as d, legacyProjectConfigToStackParts as f, stackPartsToLegacyProjectConfigPartial as g, stackGraphToLegacyProjectConfigForEcosystem as h, formatStackPartSpec as i, TOOLING_CATEGORIES as j, requiresChatSdkVercelAIForExamples as k, getStackToolDefinitions as l, parseStackPartSpecs as m, STACK_TOOL_DEFINITIONS as n, getRoleTargetPath as o, materializeProvidedStackParts as p, createStackPart as r, getStackPartCompatibilityIssueForPart as s, ELIXIR_UNSUPPORTED_GRAPH_TOOLS as t, hasJavaScriptWorkspaceRoot as u, validateStackParts as v, hasTanStackAICompatibleFrontend as w, getUnsupportedWebDeployFrontend as x, BACKEND_UTILS_COMPATIBLE_BACKENDS as y, getToolingSelectionOptions as z };
3239
+ //# sourceMappingURL=stack-graph-AWbdUrN3.mjs.map