zudojs-cli 2.0.0 → 2.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 (249) hide show
  1. package/README.md +69 -9
  2. package/dist/src/adapters/frontend/angular.adapter.d.ts +0 -1
  3. package/dist/src/adapters/frontend/angular.adapter.js +14 -97
  4. package/dist/src/adapters/frontend/astro.adapter.d.ts +0 -1
  5. package/dist/src/adapters/frontend/astro.adapter.js +4 -64
  6. package/dist/src/adapters/frontend/fallback/fallback.helper.d.ts +41 -0
  7. package/dist/src/adapters/frontend/fallback/fallback.helper.js +57 -0
  8. package/dist/src/adapters/frontend/fallback/framework/angular/angular.fallback.d.ts +18 -0
  9. package/dist/src/adapters/frontend/fallback/framework/angular/angular.fallback.js +107 -0
  10. package/dist/src/adapters/frontend/fallback/framework/angular/angularWorkspace.fallback.d.ts +23 -0
  11. package/dist/src/adapters/frontend/fallback/framework/angular/angularWorkspace.fallback.js +125 -0
  12. package/dist/src/adapters/frontend/fallback/framework/angular/index.d.ts +8 -0
  13. package/dist/src/adapters/frontend/fallback/framework/angular/index.js +8 -0
  14. package/dist/src/adapters/frontend/fallback/framework/astro.fallback.d.ts +14 -0
  15. package/dist/src/adapters/frontend/fallback/framework/astro.fallback.js +76 -0
  16. package/dist/src/adapters/frontend/fallback/framework/index.d.ts +13 -0
  17. package/dist/src/adapters/frontend/fallback/framework/index.js +13 -0
  18. package/dist/src/adapters/frontend/fallback/framework/next.fallback.d.ts +13 -0
  19. package/dist/src/adapters/frontend/fallback/framework/next.fallback.js +105 -0
  20. package/dist/src/adapters/frontend/fallback/framework/nuxt.fallback.d.ts +14 -0
  21. package/dist/src/adapters/frontend/fallback/framework/nuxt.fallback.js +56 -0
  22. package/dist/src/adapters/frontend/fallback/framework/sveltekit.fallback.d.ts +15 -0
  23. package/dist/src/adapters/frontend/fallback/framework/sveltekit.fallback.js +94 -0
  24. package/dist/src/adapters/frontend/fallback/index.d.ts +12 -0
  25. package/dist/src/adapters/frontend/fallback/index.js +12 -0
  26. package/dist/src/adapters/frontend/fallback/vite/index.d.ts +11 -0
  27. package/dist/src/adapters/frontend/fallback/vite/index.js +11 -0
  28. package/dist/src/adapters/frontend/fallback/vite/react.fallback.d.ts +13 -0
  29. package/dist/src/adapters/frontend/fallback/vite/react.fallback.js +77 -0
  30. package/dist/src/adapters/frontend/fallback/vite/svelte.fallback.d.ts +14 -0
  31. package/dist/src/adapters/frontend/fallback/vite/svelte.fallback.js +95 -0
  32. package/dist/src/adapters/frontend/fallback/vite/vanilla.fallback.d.ts +13 -0
  33. package/dist/src/adapters/frontend/fallback/vite/vanilla.fallback.js +71 -0
  34. package/dist/src/adapters/frontend/fallback/vite/vue.fallback.d.ts +14 -0
  35. package/dist/src/adapters/frontend/fallback/vite/vue.fallback.js +78 -0
  36. package/dist/src/adapters/frontend/next.adapter.d.ts +0 -1
  37. package/dist/src/adapters/frontend/next.adapter.js +16 -95
  38. package/dist/src/adapters/frontend/nuxt.adapter.d.ts +1 -2
  39. package/dist/src/adapters/frontend/nuxt.adapter.js +32 -43
  40. package/dist/src/adapters/frontend/react-native.adapter.js +1 -1
  41. package/dist/src/adapters/frontend/react.adapter.d.ts +0 -1
  42. package/dist/src/adapters/frontend/react.adapter.js +7 -89
  43. package/dist/src/adapters/frontend/svelte.adapter.d.ts +0 -1
  44. package/dist/src/adapters/frontend/svelte.adapter.js +4 -86
  45. package/dist/src/adapters/frontend/sveltekit.adapter.d.ts +0 -1
  46. package/dist/src/adapters/frontend/sveltekit.adapter.js +4 -57
  47. package/dist/src/adapters/frontend/vanilla.adapter.d.ts +0 -1
  48. package/dist/src/adapters/frontend/vanilla.adapter.js +4 -47
  49. package/dist/src/adapters/frontend/vue.adapter.d.ts +0 -1
  50. package/dist/src/adapters/frontend/vue.adapter.js +4 -76
  51. package/dist/src/bin/bin.menu.d.ts +22 -0
  52. package/dist/src/bin/bin.menu.js +40 -0
  53. package/dist/src/bin/bin.runner.d.ts +38 -0
  54. package/dist/src/bin/bin.runner.js +51 -0
  55. package/dist/src/bin/index.d.ts +9 -0
  56. package/dist/src/bin/index.js +9 -0
  57. package/dist/src/bin/zudojs.js +15 -4
  58. package/dist/src/cliApplication/cliApplication.core.d.ts +4 -2
  59. package/dist/src/cliApplication/cliApplication.core.js +48 -36
  60. package/dist/src/cliApplication/cliApplication.logger.d.ts +5 -0
  61. package/dist/src/cliApplication/cliApplication.logger.js +5 -3
  62. package/dist/src/cliApplication/invocation/index.d.ts +12 -0
  63. package/dist/src/cliApplication/invocation/index.js +12 -0
  64. package/dist/src/cliApplication/invocation/invocation.hint.d.ts +30 -0
  65. package/dist/src/cliApplication/invocation/invocation.hint.js +74 -0
  66. package/dist/src/cliApplication/invocation/invocation.locate.d.ts +34 -0
  67. package/dist/src/cliApplication/invocation/invocation.locate.js +53 -0
  68. package/dist/src/cliApplication/invocation/invocation.name.d.ts +37 -0
  69. package/dist/src/cliApplication/invocation/invocation.name.js +69 -0
  70. package/dist/src/cliApplication/invocation/invocation.sanitize.d.ts +19 -0
  71. package/dist/src/cliApplication/invocation/invocation.sanitize.js +32 -0
  72. package/dist/src/cliParser/cliParser.core.js +3 -1
  73. package/dist/src/cliParser/cliParser.helper.d.ts +7 -0
  74. package/dist/src/cliParser/cliParser.helper.js +19 -2
  75. package/dist/src/cliParser/cliParser.longOption.js +2 -2
  76. package/dist/src/commands/add.command.d.ts +7 -3
  77. package/dist/src/commands/add.command.js +96 -57
  78. package/dist/src/commands/create.command.js +29 -42
  79. package/dist/src/commands/doctor.command.d.ts +2 -1
  80. package/dist/src/commands/doctor.command.js +52 -4
  81. package/dist/src/commands/generate.command.js +31 -16
  82. package/dist/src/commands/info.command.js +11 -1
  83. package/dist/src/constants/index.d.ts +21 -3
  84. package/dist/src/constants/index.js +40 -5
  85. package/dist/src/constants/zudojsVersions.generated.d.ts +9 -0
  86. package/dist/src/constants/zudojsVersions.generated.js +48 -0
  87. package/dist/src/constants/zudojsVersions.helper.d.ts +21 -0
  88. package/dist/src/constants/zudojsVersions.helper.js +29 -0
  89. package/dist/src/generators/frontend/frontendGenerator.core.js +9 -10
  90. package/dist/src/generators/frontend/frontendPipeline.d.ts +6 -1
  91. package/dist/src/generators/frontend/frontendPipeline.js +9 -1
  92. package/dist/src/generators/fullstack/fullstackComposer.core.js +7 -1
  93. package/dist/src/generators/index.d.ts +1 -2
  94. package/dist/src/generators/index.js +1 -2
  95. package/dist/src/generators/infrastructure/infrastructure.generator.js +13 -5
  96. package/dist/src/generators/integration/integrationGenerator.core.d.ts +2 -0
  97. package/dist/src/generators/integration/integrationGenerator.core.js +31 -2
  98. package/dist/src/generators/module/module.generator.js +36 -4
  99. package/dist/src/generators/resource/index.d.ts +8 -0
  100. package/dist/src/generators/resource/index.js +8 -0
  101. package/dist/src/generators/resource/resource.generator.d.ts +27 -0
  102. package/dist/src/generators/resource/resource.generator.js +87 -0
  103. package/dist/src/generators/resource/resource.layout.d.ts +26 -0
  104. package/dist/src/generators/resource/resource.layout.js +46 -0
  105. package/dist/src/generators/resource/resource.plan.d.ts +27 -0
  106. package/dist/src/generators/resource/resource.plan.js +46 -0
  107. package/dist/src/generators/resource/resource.run.d.ts +25 -0
  108. package/dist/src/generators/resource/resource.run.js +52 -0
  109. package/dist/src/prompts/cancel.prompt.d.ts +5 -1
  110. package/dist/src/prompts/cancel.prompt.js +4 -0
  111. package/dist/src/prompts/menu/index.d.ts +13 -0
  112. package/dist/src/prompts/menu/index.js +13 -0
  113. package/dist/src/prompts/menu/menu.adapter.d.ts +16 -0
  114. package/dist/src/prompts/menu/menu.adapter.js +56 -0
  115. package/dist/src/prompts/menu/menu.constant.d.ts +20 -0
  116. package/dist/src/prompts/menu/menu.constant.js +29 -0
  117. package/dist/src/prompts/menu/menu.flow.d.ts +18 -0
  118. package/dist/src/prompts/menu/menu.flow.js +82 -0
  119. package/dist/src/prompts/menu/menu.select.d.ts +26 -0
  120. package/dist/src/prompts/menu/menu.select.js +118 -0
  121. package/dist/src/prompts/menu/menu.type.d.ts +66 -0
  122. package/dist/src/prompts/menu/menu.type.js +8 -0
  123. package/dist/src/recipes/apply/index.d.ts +9 -0
  124. package/dist/src/recipes/apply/index.js +9 -0
  125. package/dist/src/recipes/apply/recipeApply.app.d.ts +20 -0
  126. package/dist/src/recipes/apply/recipeApply.app.js +79 -0
  127. package/dist/src/recipes/apply/recipeApply.context.d.ts +14 -0
  128. package/dist/src/recipes/apply/recipeApply.context.js +76 -0
  129. package/dist/src/recipes/apply/recipeApply.files.d.ts +39 -0
  130. package/dist/src/recipes/apply/recipeApply.files.js +84 -0
  131. package/dist/src/recipes/apply/recipeApply.project.d.ts +11 -0
  132. package/dist/src/recipes/apply/recipeApply.project.js +50 -0
  133. package/dist/src/recipes/apply/recipeApply.report.d.ts +20 -0
  134. package/dist/src/recipes/apply/recipeApply.report.js +51 -0
  135. package/dist/src/recipes/catalog/database/database.files.d.ts +15 -0
  136. package/dist/src/recipes/catalog/database/database.files.js +98 -0
  137. package/dist/src/recipes/catalog/database/database.recipe.d.ts +9 -0
  138. package/dist/src/recipes/catalog/database/database.recipe.js +50 -0
  139. package/dist/src/recipes/catalog/database/index.d.ts +6 -0
  140. package/dist/src/recipes/catalog/database/index.js +6 -0
  141. package/dist/src/recipes/catalog/email.recipe.d.ts +9 -0
  142. package/dist/src/recipes/catalog/email.recipe.js +122 -0
  143. package/dist/src/recipes/catalog/index.d.ts +11 -0
  144. package/dist/src/recipes/catalog/index.js +11 -0
  145. package/dist/src/recipes/catalog/openapi.recipe.d.ts +8 -0
  146. package/dist/src/recipes/catalog/openapi.recipe.js +25 -0
  147. package/dist/src/recipes/catalog/platform/cache.recipe.d.ts +7 -0
  148. package/dist/src/recipes/catalog/platform/cache.recipe.js +55 -0
  149. package/dist/src/recipes/catalog/platform/index.d.ts +9 -0
  150. package/dist/src/recipes/catalog/platform/index.js +9 -0
  151. package/dist/src/recipes/catalog/platform/messaging.recipe.d.ts +7 -0
  152. package/dist/src/recipes/catalog/platform/messaging.recipe.js +46 -0
  153. package/dist/src/recipes/catalog/platform/observability.recipe.d.ts +8 -0
  154. package/dist/src/recipes/catalog/platform/observability.recipe.js +49 -0
  155. package/dist/src/recipes/catalog/platform/queue.recipe.d.ts +7 -0
  156. package/dist/src/recipes/catalog/platform/queue.recipe.js +61 -0
  157. package/dist/src/recipes/catalog/platform/scheduler.recipe.d.ts +7 -0
  158. package/dist/src/recipes/catalog/platform/scheduler.recipe.js +56 -0
  159. package/dist/src/recipes/catalog/redis.recipe.d.ts +8 -0
  160. package/dist/src/recipes/catalog/redis.recipe.js +79 -0
  161. package/dist/src/recipes/catalog/storage.recipe.d.ts +7 -0
  162. package/dist/src/recipes/catalog/storage.recipe.js +49 -0
  163. package/dist/src/recipes/catalog/websockets.recipe.d.ts +10 -0
  164. package/dist/src/recipes/catalog/websockets.recipe.js +84 -0
  165. package/dist/src/recipes/docker/docker.compose.d.ts +19 -0
  166. package/dist/src/recipes/docker/docker.compose.js +90 -0
  167. package/dist/src/recipes/docker/docker.recipe.d.ts +10 -0
  168. package/dist/src/recipes/docker/docker.recipe.js +51 -0
  169. package/dist/src/recipes/docker/docker.refresh.d.ts +13 -0
  170. package/dist/src/recipes/docker/docker.refresh.js +37 -0
  171. package/dist/src/recipes/docker/index.d.ts +7 -0
  172. package/dist/src/recipes/docker/index.js +7 -0
  173. package/dist/src/recipes/index.d.ts +8 -0
  174. package/dist/src/recipes/index.js +8 -0
  175. package/dist/src/recipes/recipe.registry.d.ts +17 -0
  176. package/dist/src/recipes/recipe.registry.js +46 -0
  177. package/dist/src/recipes/recipe.type.d.ts +93 -0
  178. package/dist/src/recipes/recipe.type.js +12 -0
  179. package/dist/src/resolvers/dependency/dependencyVersions.constant.d.ts +100 -5
  180. package/dist/src/resolvers/dependency/dependencyVersions.constant.js +103 -34
  181. package/dist/src/resolvers/dependency/index.d.ts +1 -1
  182. package/dist/src/resolvers/dependency/index.js +1 -1
  183. package/dist/src/resolvers/project.resolver.d.ts +14 -1
  184. package/dist/src/resolvers/project.resolver.js +11 -3
  185. package/dist/src/scaffolders/scaffolder.helper.js +12 -1
  186. package/dist/src/templates/backendApp/backendApp.core.d.ts +45 -0
  187. package/dist/src/templates/backendApp/backendApp.core.js +66 -0
  188. package/dist/src/templates/backendApp/backendApp.modules.d.ts +26 -0
  189. package/dist/src/templates/backendApp/backendApp.modules.js +25 -0
  190. package/dist/src/templates/backendApp/backendApp.project.d.ts +26 -0
  191. package/dist/src/templates/backendApp/backendApp.project.js +87 -0
  192. package/dist/src/templates/backendApp/index.d.ts +8 -0
  193. package/dist/src/templates/backendApp/index.js +8 -0
  194. package/dist/src/templates/backendApp/parts/config.template.d.ts +28 -0
  195. package/dist/src/templates/backendApp/parts/config.template.js +118 -0
  196. package/dist/src/templates/backendApp/parts/httpUtils.template.d.ts +10 -0
  197. package/dist/src/templates/backendApp/parts/httpUtils.template.js +103 -0
  198. package/dist/src/templates/backendApp/parts/index.d.ts +9 -0
  199. package/dist/src/templates/backendApp/parts/index.js +9 -0
  200. package/dist/src/templates/backendApp/parts/integrations.template.d.ts +16 -0
  201. package/dist/src/templates/backendApp/parts/integrations.template.js +115 -0
  202. package/dist/src/templates/backendApp/parts/wiring.template.d.ts +26 -0
  203. package/dist/src/templates/backendApp/parts/wiring.template.js +111 -0
  204. package/dist/src/templates/microservice/microservice.template.d.ts +4 -1
  205. package/dist/src/templates/microservice/microservice.template.js +74 -90
  206. package/dist/src/templates/modular-monolith/modularMonolith.template.js +25 -77
  207. package/dist/src/templates/monolith/monolith.template.d.ts +11 -26
  208. package/dist/src/templates/monolith/monolith.template.js +49 -146
  209. package/dist/src/templates/resource/index.d.ts +9 -0
  210. package/dist/src/templates/resource/index.js +9 -0
  211. package/dist/src/templates/resource/layers/index.d.ts +9 -0
  212. package/dist/src/templates/resource/layers/index.js +9 -0
  213. package/dist/src/templates/resource/layers/resourceData.template.d.ts +9 -0
  214. package/dist/src/templates/resource/layers/resourceData.template.js +108 -0
  215. package/dist/src/templates/resource/layers/resourcePrisma.template.d.ts +15 -0
  216. package/dist/src/templates/resource/layers/resourcePrisma.template.js +89 -0
  217. package/dist/src/templates/resource/layers/resourceRoutes.template.d.ts +13 -0
  218. package/dist/src/templates/resource/layers/resourceRoutes.template.js +128 -0
  219. package/dist/src/templates/resource/layers/resourceService.template.d.ts +13 -0
  220. package/dist/src/templates/resource/layers/resourceService.template.js +95 -0
  221. package/dist/src/templates/resource/resource.files.d.ts +33 -0
  222. package/dist/src/templates/resource/resource.files.js +99 -0
  223. package/dist/src/templates/resource/resource.names.d.ts +38 -0
  224. package/dist/src/templates/resource/resource.names.js +60 -0
  225. package/dist/src/templates/resource/resource.type.d.ts +19 -0
  226. package/dist/src/templates/resource/resource.type.js +15 -0
  227. package/dist/src/templates/shared/appRuntime.template.js +28 -11
  228. package/dist/src/templates/shared/capability.template.d.ts +13 -4
  229. package/dist/src/templates/shared/capability.template.js +21 -5
  230. package/dist/src/templates/shared/dockerfile.template.d.ts +12 -4
  231. package/dist/src/templates/shared/dockerfile.template.js +68 -29
  232. package/dist/src/templates/shared/pnpm.template.d.ts +8 -2
  233. package/dist/src/templates/shared/pnpm.template.js +9 -1
  234. package/dist/src/templates/shared/server.template.d.ts +23 -22
  235. package/dist/src/templates/shared/server.template.js +79 -53
  236. package/dist/src/types/scaffold.type.d.ts +6 -0
  237. package/dist/src/types/scaffold.type.js +2 -1
  238. package/dist/src/utils/utils.name.js +6 -0
  239. package/dist/src/utils/utils.writeGuard.d.ts +5 -0
  240. package/dist/src/utils/utils.writeGuard.js +23 -0
  241. package/dist/src/wiring/index.d.ts +10 -0
  242. package/dist/src/wiring/index.js +10 -0
  243. package/dist/src/wiring/wiring.dependencies.d.ts +29 -0
  244. package/dist/src/wiring/wiring.dependencies.js +83 -0
  245. package/dist/src/wiring/wiring.edits.d.ts +51 -0
  246. package/dist/src/wiring/wiring.edits.js +90 -0
  247. package/dist/src/wiring/wiring.markers.d.ts +53 -0
  248. package/dist/src/wiring/wiring.markers.js +87 -0
  249. package/package.json +20 -15
@@ -0,0 +1,29 @@
1
+ /**
2
+ * zudojs-cli — Menu Entries
3
+ *
4
+ * The numbered entries of the interactive menu, in display order.
5
+ */
6
+ /** Menu entries; `key` is the digit that selects each one. */
7
+ export const MENU_ITEMS = Object.freeze([
8
+ { id: "create", key: "1", label: "Create a new project" },
9
+ { id: "dev", key: "2", label: "Start dev server", project: "cwd" },
10
+ { id: "generate", key: "3", label: "Generate code", project: "ancestor" },
11
+ { id: "add", key: "4", label: "Add a feature", project: "cwd" },
12
+ { id: "build", key: "5", label: "Build", project: "ancestor" },
13
+ { id: "doctor", key: "6", label: "Doctor", hint: "check the project" },
14
+ { id: "info", key: "7", label: "Info", hint: "CLI and project details" },
15
+ { id: "help", key: "8", label: "Help", hint: "list every command" },
16
+ { id: "exit", key: "0", label: "Exit" },
17
+ ]);
18
+ /** The question the menu asks. */
19
+ export const MENU_MESSAGE = "What would you like to do?";
20
+ /** Printed when the menu is left with Esc or Ctrl+C. */
21
+ export const MENU_CANCELLED = "Operation cancelled.";
22
+ /**
23
+ * Names accepted by the "Generate code" follow-up.
24
+ *
25
+ * The same shape the project-name prompt allows: a leading "-" would be
26
+ * parsed as an option once the name is handed to `generate`.
27
+ */
28
+ export const MENU_NAME_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
29
+ //# sourceMappingURL=menu.constant.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * zudojs-cli — Menu Flow
3
+ *
4
+ * Turns a menu choice into the argument list of an existing command,
5
+ * prompting for whatever that command requires.
6
+ */
7
+ import { type MenuFlowOptions, type MenuItem, type MenuOutcome } from "./menu.type.js";
8
+ /** Explains why a project-only entry cannot run in `cwd`. */
9
+ export declare function notInProjectMessage(item: MenuItem, cwd: string, name: string): string;
10
+ /**
11
+ * Runs the interactive menu until the user picks something runnable,
12
+ * exits, or cancels.
13
+ *
14
+ * A project-only entry chosen outside a project says so and shows the
15
+ * menu again rather than failing after its follow-up questions.
16
+ */
17
+ export declare function runMenuFlow(options: MenuFlowOptions): Promise<MenuOutcome>;
18
+ //# sourceMappingURL=menu.flow.d.ts.map
@@ -0,0 +1,82 @@
1
+ /**
2
+ * zudojs-cli — Menu Flow
3
+ *
4
+ * Turns a menu choice into the argument list of an existing command,
5
+ * prompting for whatever that command requires.
6
+ */
7
+ import { MENU_CANCELLED, MENU_ITEMS, MENU_NAME_PATTERN } from "./menu.constant.js";
8
+ import { MENU_CANCEL, } from "./menu.type.js";
9
+ /** Explains why a project-only entry cannot run in `cwd`. */
10
+ export function notInProjectMessage(item, cwd, name) {
11
+ return (`"${item.label}" needs a Zudojs project, and ${cwd} is not inside one ` +
12
+ "(no .zudojs/manifest.json, zudojs.config.ts or zudojs block in package.json). " +
13
+ `Choose 1 to create a project, or cd into one and run "${name}" again.`);
14
+ }
15
+ /** Validates the resource name asked for by "Generate code". */
16
+ function validateResourceName(value) {
17
+ const trimmed = value.trim();
18
+ if (trimmed.length === 0)
19
+ return "A name is required.";
20
+ if (!MENU_NAME_PATTERN.test(trimmed)) {
21
+ return "Must start with a letter or digit; only letters, digits, hyphens and underscores are allowed.";
22
+ }
23
+ return undefined;
24
+ }
25
+ /** Collects the arguments a chosen entry's command requires. */
26
+ async function argumentsFor(item, options) {
27
+ const { prompter } = options;
28
+ switch (item.id) {
29
+ case "generate": {
30
+ const schematic = await prompter.select("What do you want to generate?", options.schematics);
31
+ if (schematic === MENU_CANCEL)
32
+ return MENU_CANCEL;
33
+ const name = await prompter.text(`Name of the ${schematic}?`, "user", validateResourceName);
34
+ if (name === MENU_CANCEL)
35
+ return MENU_CANCEL;
36
+ return ["generate", schematic, name.trim()];
37
+ }
38
+ case "add": {
39
+ const feature = await prompter.select("Which feature do you want to add?", options.features);
40
+ if (feature === MENU_CANCEL)
41
+ return MENU_CANCEL;
42
+ return ["add", feature];
43
+ }
44
+ case "help":
45
+ return ["--help"];
46
+ default:
47
+ // `create` prompts for its own name and options when run on a TTY;
48
+ // dev, build, doctor and info take no required arguments.
49
+ return [item.id];
50
+ }
51
+ }
52
+ /**
53
+ * Runs the interactive menu until the user picks something runnable,
54
+ * exits, or cancels.
55
+ *
56
+ * A project-only entry chosen outside a project says so and shows the
57
+ * menu again rather than failing after its follow-up questions.
58
+ */
59
+ export async function runMenuFlow(options) {
60
+ const { prompter } = options;
61
+ for (;;) {
62
+ const id = await prompter.choose(MENU_ITEMS);
63
+ if (id === MENU_CANCEL) {
64
+ prompter.cancel(MENU_CANCELLED);
65
+ return { kind: "cancel" };
66
+ }
67
+ const item = MENU_ITEMS.find((entry) => entry.id === id);
68
+ if (!item || item.id === "exit")
69
+ return { kind: "exit" };
70
+ if (item.project && !options.isProject(options.cwd, item.project)) {
71
+ prompter.warn(notInProjectMessage(item, options.cwd, options.name));
72
+ continue;
73
+ }
74
+ const args = await argumentsFor(item, options);
75
+ if (args === MENU_CANCEL) {
76
+ prompter.cancel(MENU_CANCELLED);
77
+ return { kind: "cancel" };
78
+ }
79
+ return { kind: "run", args };
80
+ }
81
+ }
82
+ //# sourceMappingURL=menu.flow.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * zudojs-cli — Numbered Select
3
+ *
4
+ * A select prompt that takes either a digit or the arrow keys.
5
+ *
6
+ * `@clack/prompts`' `select` has no hook for "press 3 to pick the third
7
+ * entry", so this drives the keypress stream itself, with clack's glyphs and
8
+ * readline setup so it hands the terminal cleanly to the prompts after it.
9
+ */
10
+ import { type MenuCancel, type MenuItem, type MenuItemId } from "./menu.type.js";
11
+ /** The streams the prompt reads keys from and draws on. */
12
+ export interface NumberedSelectIO {
13
+ readonly input: NodeJS.ReadableStream & {
14
+ readonly isTTY?: boolean;
15
+ setRawMode?(mode: boolean): unknown;
16
+ };
17
+ readonly output: NodeJS.WritableStream;
18
+ }
19
+ /**
20
+ * Shows the numbered menu and resolves with the chosen entry's id, or
21
+ * `MENU_CANCEL` on Esc / Ctrl+C.
22
+ *
23
+ * A digit picks its entry at once; ↑/↓ (or k/j) move and Enter confirms.
24
+ */
25
+ export declare function numberedSelect(items: readonly MenuItem[], io: NumberedSelectIO): Promise<MenuItemId | MenuCancel>;
26
+ //# sourceMappingURL=menu.select.d.ts.map
@@ -0,0 +1,118 @@
1
+ /**
2
+ * zudojs-cli — Numbered Select
3
+ *
4
+ * A select prompt that takes either a digit or the arrow keys.
5
+ *
6
+ * `@clack/prompts`' `select` has no hook for "press 3 to pick the third
7
+ * entry", so this drives the keypress stream itself, with clack's glyphs and
8
+ * readline setup so it hands the terminal cleanly to the prompts after it.
9
+ */
10
+ import * as readline from "node:readline";
11
+ import { styleText } from "node:util";
12
+ import * as p from "@clack/prompts";
13
+ import { MENU_MESSAGE } from "./menu.constant.js";
14
+ import { MENU_CANCEL, } from "./menu.type.js";
15
+ const HIDE_CURSOR = "\u001B[?25l";
16
+ const SHOW_CURSOR = "\u001B[?25h";
17
+ /** Renders the frame for the current cursor position or final state. */
18
+ function renderFrame(items, cursor, state, paint) {
19
+ const bar = paint("gray", p.S_BAR);
20
+ const current = items[cursor];
21
+ if (state !== "active") {
22
+ const submitted = state === "submit";
23
+ const symbol = submitted
24
+ ? paint("green", p.S_STEP_SUBMIT)
25
+ : paint("red", p.S_STEP_CANCEL);
26
+ const label = `${current.key}. ${current.label}`;
27
+ return [
28
+ bar,
29
+ `${symbol} ${MENU_MESSAGE}`,
30
+ `${bar} ${paint(submitted ? "dim" : ["strikethrough", "dim"], label)}`,
31
+ ];
32
+ }
33
+ const digits = items.map((item) => Number(item.key)).filter(Number.isFinite);
34
+ const keys = `${Math.min(...digits)}-${Math.max(...digits)}`;
35
+ return [
36
+ bar,
37
+ `${paint("cyan", p.S_STEP_ACTIVE)} ${MENU_MESSAGE}`,
38
+ ...items.map((item, index) => {
39
+ const active = index === cursor;
40
+ const radio = active
41
+ ? paint("green", p.S_RADIO_ACTIVE)
42
+ : paint("dim", p.S_RADIO_INACTIVE);
43
+ const text = `${item.key}. ${item.label}`;
44
+ const hint = active && item.hint ? paint("dim", ` (${item.hint})`) : "";
45
+ return `${paint("cyan", p.S_BAR)} ${radio} ${active ? text : paint("dim", text)}${hint}`;
46
+ }),
47
+ `${paint("cyan", p.S_BAR_END)} ${paint("dim", `↑/↓ move · ${keys} pick · Enter select · Esc exit`)}`,
48
+ ];
49
+ }
50
+ /**
51
+ * Shows the numbered menu and resolves with the chosen entry's id, or
52
+ * `MENU_CANCEL` on Esc / Ctrl+C.
53
+ *
54
+ * A digit picks its entry at once; ↑/↓ (or k/j) move and Enter confirms.
55
+ */
56
+ export function numberedSelect(items, io) {
57
+ const { input, output } = io;
58
+ const paint = (style, text) => styleText(style, text, { stream: output });
59
+ return new Promise((resolve) => {
60
+ let cursor = 0;
61
+ let drawn = 0;
62
+ const rl = readline.createInterface({
63
+ input,
64
+ tabSize: 2,
65
+ prompt: "",
66
+ escapeCodeTimeout: 50,
67
+ terminal: true,
68
+ });
69
+ readline.emitKeypressEvents(input, rl);
70
+ const draw = (state) => {
71
+ if (drawn > 0) {
72
+ readline.moveCursor(output, 0, -(drawn - 1));
73
+ readline.cursorTo(output, 0);
74
+ readline.clearScreenDown(output);
75
+ }
76
+ const lines = renderFrame(items, cursor, state, paint);
77
+ output.write(lines.join("\n"));
78
+ drawn = lines.length;
79
+ };
80
+ const finish = (result) => {
81
+ input.removeListener("keypress", onKeypress);
82
+ draw(result === MENU_CANCEL ? "cancel" : "submit");
83
+ output.write(`\n${SHOW_CURSOR}`);
84
+ if (input.isTTY)
85
+ input.setRawMode?.(false);
86
+ rl.close();
87
+ resolve(result);
88
+ };
89
+ const onKeypress = (text, key = {}) => {
90
+ if ((key.ctrl && key.name === "c") || key.name === "escape") {
91
+ return finish(MENU_CANCEL);
92
+ }
93
+ if (key.name === "return" || key.name === "enter") {
94
+ return finish(items[cursor].id);
95
+ }
96
+ if (key.name === "up" || key.name === "k") {
97
+ cursor = (cursor - 1 + items.length) % items.length;
98
+ }
99
+ else if (key.name === "down" || key.name === "j") {
100
+ cursor = (cursor + 1) % items.length;
101
+ }
102
+ else {
103
+ const index = items.findIndex((item) => item.key === text);
104
+ if (index < 0)
105
+ return;
106
+ cursor = index;
107
+ return finish(items[index].id);
108
+ }
109
+ draw("active");
110
+ };
111
+ input.on("keypress", onKeypress);
112
+ if (input.isTTY)
113
+ input.setRawMode?.(true);
114
+ output.write(HIDE_CURSOR);
115
+ draw("active");
116
+ });
117
+ }
118
+ //# sourceMappingURL=menu.select.js.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * zudojs-cli — Menu Types
3
+ *
4
+ * Contracts for the interactive menu shown by a bare `zudojs` / `zudo`.
5
+ */
6
+ import type { CLIChoiceOption } from "../../constants/index.js";
7
+ /** Returned by a prompt when the user pressed Esc or Ctrl+C. */
8
+ export declare const MENU_CANCEL: unique symbol;
9
+ /** The cancel sentinel's type. */
10
+ export type MenuCancel = typeof MENU_CANCEL;
11
+ /** Identifier of one menu entry. */
12
+ export type MenuItemId = "create" | "dev" | "generate" | "add" | "build" | "doctor" | "info" | "help" | "exit";
13
+ /**
14
+ * How an entry checks that it is inside a project.
15
+ *
16
+ * `cwd` mirrors `dev` and `add`, which only look at the current directory;
17
+ * `ancestor` mirrors `build` and `generate`, which walk up to the root.
18
+ */
19
+ export type MenuProjectScope = "cwd" | "ancestor";
20
+ /** One numbered entry of the menu. */
21
+ export interface MenuItem {
22
+ readonly id: MenuItemId;
23
+ /** The digit that selects the entry. */
24
+ readonly key: string;
25
+ readonly label: string;
26
+ readonly hint?: string;
27
+ /** Set on entries that only work inside a Zudojs project. */
28
+ readonly project?: MenuProjectScope;
29
+ }
30
+ /**
31
+ * The prompts the menu needs, injectable so the flow can be tested
32
+ * without a terminal.
33
+ */
34
+ export interface MenuPrompter {
35
+ /** Shows the numbered menu and resolves with the chosen entry. */
36
+ choose(items: readonly MenuItem[]): Promise<MenuItemId | MenuCancel>;
37
+ /** Asks the user to pick one of several values. */
38
+ select(message: string, choices: readonly CLIChoiceOption[]): Promise<string | MenuCancel>;
39
+ /** Asks for free text; `validate` returns an error message or nothing. */
40
+ text(message: string, placeholder: string, validate: (value: string) => string | undefined): Promise<string | MenuCancel>;
41
+ /** Tells the user an entry cannot run here. */
42
+ warn(message: string): void;
43
+ /** Reports that the menu was cancelled. */
44
+ cancel(message: string): void;
45
+ }
46
+ /** What the menu decided. */
47
+ export type MenuOutcome = {
48
+ readonly kind: "run";
49
+ readonly args: readonly string[];
50
+ } | {
51
+ readonly kind: "exit";
52
+ } | {
53
+ readonly kind: "cancel";
54
+ };
55
+ /** Dependencies of the menu flow. */
56
+ export interface MenuFlowOptions {
57
+ readonly prompter: MenuPrompter;
58
+ readonly cwd: string;
59
+ /** The name the user typed, for messages (`zudo` or `zudojs`). */
60
+ readonly name: string;
61
+ /** Whether `cwd` is inside a Zudojs project for the given scope. */
62
+ readonly isProject: (cwd: string, scope: MenuProjectScope) => boolean;
63
+ readonly schematics: readonly CLIChoiceOption[];
64
+ readonly features: readonly CLIChoiceOption[];
65
+ }
66
+ //# sourceMappingURL=menu.type.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * zudojs-cli — Menu Types
3
+ *
4
+ * Contracts for the interactive menu shown by a bare `zudojs` / `zudo`.
5
+ */
6
+ /** Returned by a prompt when the user pressed Esc or Ctrl+C. */
7
+ export const MENU_CANCEL = Symbol("zudojs.menu.cancel");
8
+ //# sourceMappingURL=menu.type.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * zudojs-cli — Applying recipes to a project on disk.
3
+ */
4
+ export * from "./recipeApply.app.js";
5
+ export * from "./recipeApply.files.js";
6
+ export * from "./recipeApply.project.js";
7
+ export * from "./recipeApply.context.js";
8
+ export * from "./recipeApply.report.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * zudojs-cli — Applying recipes to a project on disk.
3
+ */
4
+ export * from "./recipeApply.app.js";
5
+ export * from "./recipeApply.files.js";
6
+ export * from "./recipeApply.project.js";
7
+ export * from "./recipeApply.context.js";
8
+ export * from "./recipeApply.report.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * zudojs-cli — Applying an app recipe to one backend app.
3
+ *
4
+ * Writes the integration file and the recipe's extra files when they are
5
+ * missing, registers the integration, config section and server lines
6
+ * between their markers, and merges `.env.example` and `package.json`.
7
+ * Files that exist are kept, never overwritten; a missing marker becomes a
8
+ * manual step instead of an edit.
9
+ */
10
+ import type { AppRecipe, RecipeContext } from "../recipe.type.js";
11
+ /** What applying a recipe to one app did. */
12
+ export interface RecipeOutcome {
13
+ readonly written: readonly string[];
14
+ readonly kept: readonly string[];
15
+ readonly edited: readonly string[];
16
+ readonly manualSteps: readonly string[];
17
+ }
18
+ /** Applies `recipe` to the app at `context.appRoot` of the project at `root`. */
19
+ export declare function applyAppRecipe(recipe: AppRecipe, context: RecipeContext, root: string): Promise<RecipeOutcome>;
20
+ //# sourceMappingURL=recipeApply.app.d.ts.map
@@ -0,0 +1,79 @@
1
+ /**
2
+ * zudojs-cli — Applying an app recipe to one backend app.
3
+ *
4
+ * Writes the integration file and the recipe's extra files when they are
5
+ * missing, registers the integration, config section and server lines
6
+ * between their markers, and merges `.env.example` and `package.json`.
7
+ * Files that exist are kept, never overwritten; a missing marker becomes a
8
+ * manual step instead of an edit.
9
+ */
10
+ import { existsSync, readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { writeFile } from "../../utils/utils.fileSystem.js";
13
+ import { MARKERS, applyMarkerEdits } from "../../wiring/index.js";
14
+ import { mergePackageJson, withEnvVariables } from "./recipeApply.files.js";
15
+ /** Joins a root-relative app dir and a path inside it. */
16
+ function inApp(appRoot, path) {
17
+ return appRoot === "" ? path : `${appRoot}/${path}`;
18
+ }
19
+ /** Applies `recipe` to the app at `context.appRoot` of the project at `root`. */
20
+ export async function applyAppRecipe(recipe, context, root) {
21
+ recipe.validate?.(context);
22
+ const at = (path) => inApp(context.appRoot, path);
23
+ const written = [];
24
+ const kept = [];
25
+ const edits = [];
26
+ const files = { ...(recipe.files?.(context) ?? {}) };
27
+ if (recipe.integration !== undefined) {
28
+ const { file, exportName } = recipe.integration;
29
+ files[`src/integrations/${file}`] = recipe.integration.source(context);
30
+ const index = at("src/integrations/index.ts");
31
+ const module = file.replace(/\.ts$/, ".js");
32
+ edits.push({ file: index, marker: MARKERS.integrationImports, line: `import { ${exportName} } from "./${module}";` }, { file: index, marker: MARKERS.integrations, line: `${exportName},` });
33
+ }
34
+ for (const [path, content] of Object.entries(files)) {
35
+ if (existsSync(join(root, at(path)))) {
36
+ kept.push(at(path));
37
+ }
38
+ else {
39
+ await writeFile(root, at(path), content);
40
+ written.push(at(path));
41
+ }
42
+ }
43
+ if (recipe.configSection !== undefined) {
44
+ edits.push({ file: at("src/configs/index.ts"), marker: MARKERS.config, line: recipe.configSection });
45
+ }
46
+ for (const { marker, line } of recipe.serverLines?.(context) ?? []) {
47
+ edits.push({ file: at("src/server.ts"), marker, line });
48
+ }
49
+ const configFile = join(root, at("src/configs/index.ts"));
50
+ const configKey = recipe.configSection?.split(":")[0];
51
+ const configured = configKey !== undefined &&
52
+ existsSync(configFile) &&
53
+ new RegExp(`^\\s*${configKey}:`, "m").test(readFileSync(configFile, "utf-8"));
54
+ const outcome = await applyMarkerEdits(root, configured ? edits.filter((edit) => edit.marker !== MARKERS.config) : edits);
55
+ const edited = [...outcome.edited];
56
+ const envPath = at(".env.example");
57
+ const env = recipe.env?.(context) ?? [];
58
+ if (env.length > 0) {
59
+ const current = existsSync(join(root, envPath)) ? readFileSync(join(root, envPath), "utf-8") : "";
60
+ const next = withEnvVariables(current, env);
61
+ if (next !== undefined) {
62
+ await writeFile(root, envPath, next);
63
+ edited.push(envPath);
64
+ }
65
+ }
66
+ const pkgPath = at("package.json");
67
+ const pkg = JSON.parse(readFileSync(join(root, pkgPath), "utf-8"));
68
+ mergePackageJson(pkg, {
69
+ feature: recipe.feature,
70
+ dependencies: recipe.dependencies,
71
+ ...(recipe.devDependencies ? { devDependencies: recipe.devDependencies } : {}),
72
+ ...(recipe.scripts ? { scripts: recipe.scripts } : {}),
73
+ ...(recipe.replaceScripts ? { replaceScripts: recipe.replaceScripts } : {}),
74
+ });
75
+ await writeFile(root, pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
76
+ edited.push(pkgPath);
77
+ return { written, kept, edited, manualSteps: outcome.manualSteps };
78
+ }
79
+ //# sourceMappingURL=recipeApply.app.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * zudojs-cli — What recipes need to know about a project on disk.
3
+ */
4
+ import type { ProjectLayout } from "../../resolvers/layout/projectLayout.core.js";
5
+ import type { ProjectRecipeContext, RecipeContext } from "../recipe.type.js";
6
+ /** The project name as a slug (from the root package.json). */
7
+ export declare function projectSlug(root: string): string;
8
+ /** The database engine recorded in `.zudojs/manifest.json` (default postgresql). */
9
+ export declare function recordedDatabase(root: string): string;
10
+ /** The recipe context of one backend app. */
11
+ export declare function recipeContextFor(root: string, dir: string): RecipeContext;
12
+ /** The context of a project recipe (docker) for `layout`. */
13
+ export declare function projectRecipeContext(layout: ProjectLayout, feature: string, capabilities: readonly string[]): ProjectRecipeContext;
14
+ //# sourceMappingURL=recipeApply.context.d.ts.map
@@ -0,0 +1,76 @@
1
+ /**
2
+ * zudojs-cli — What recipes need to know about a project on disk.
3
+ */
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { basename, join, relative } from "node:path";
6
+ function readJson(path) {
7
+ try {
8
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
9
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
10
+ }
11
+ catch {
12
+ return {};
13
+ }
14
+ }
15
+ /** The project name as a slug (from the root package.json). */
16
+ export function projectSlug(root) {
17
+ const name = readJson(join(root, "package.json"))["name"];
18
+ const raw = typeof name === "string" && name !== "" ? name : basename(root);
19
+ return raw.replace(/^@[^/]+\//, "").replace(/[^a-z0-9-]+/gi, "-").toLowerCase() || "app";
20
+ }
21
+ /** The database engine recorded in `.zudojs/manifest.json` (default postgresql). */
22
+ export function recordedDatabase(root) {
23
+ const database = readJson(join(root, ".zudojs", "manifest.json"))["database"];
24
+ return typeof database?.provider === "string" ? database.provider : "postgresql";
25
+ }
26
+ /** The app's name: `app` for the project root, else its directory name. */
27
+ function appName(root, dir) {
28
+ return dir === root ? "app" : basename(dir);
29
+ }
30
+ /** The recipe context of one backend app. */
31
+ export function recipeContextFor(root, dir) {
32
+ return {
33
+ projectSlug: projectSlug(root),
34
+ appName: appName(root, dir),
35
+ database: recordedDatabase(root),
36
+ appRoot: relative(root, dir).split("\\").join("/"),
37
+ };
38
+ }
39
+ /** PORT from an app's `.env.example`, else `fallback`. */
40
+ function recordedPort(dir, fallback) {
41
+ const path = join(dir, ".env.example");
42
+ const match = existsSync(path) ? /^PORT=(\d+)\s*$/m.exec(readFileSync(path, "utf-8")) : null;
43
+ const port = Number(match?.[1]);
44
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : fallback;
45
+ }
46
+ /** Features declared in an app's package.json. */
47
+ function declaredFeatures(dir) {
48
+ const block = readJson(join(dir, "package.json"))["zudojs"];
49
+ return Array.isArray(block?.features)
50
+ ? block.features.filter((f) => typeof f === "string")
51
+ : [];
52
+ }
53
+ /** The context of a project recipe (docker) for `layout`. */
54
+ export function projectRecipeContext(layout, feature, capabilities) {
55
+ const apps = layout.backendDirs.map((dir, index) => ({
56
+ dir: relative(layout.root, dir).split("\\").join("/"),
57
+ name: appName(layout.root, dir),
58
+ port: recordedPort(dir, 3000 + index),
59
+ prisma: existsSync(join(dir, "prisma", "schema.prisma")),
60
+ }));
61
+ const features = new Set([feature, ...capabilities]);
62
+ for (const dir of layout.backendDirs) {
63
+ for (const declared of declaredFeatures(dir))
64
+ features.add(declared);
65
+ }
66
+ return {
67
+ root: layout.root,
68
+ projectSlug: projectSlug(layout.root),
69
+ architecture: layout.architecture,
70
+ packageManager: layout.packageManager,
71
+ database: recordedDatabase(layout.root),
72
+ features,
73
+ apps,
74
+ };
75
+ }
76
+ //# sourceMappingURL=recipeApply.context.js.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * zudojs-cli — Edits `add` makes to plain project files: `.env.example`,
3
+ * `.gitignore`, `package.json` and `pnpm-workspace.yaml`. Every edit is
4
+ * additive and idempotent; existing values are never replaced (except the
5
+ * scripts a recipe names explicitly, and only from their generated value).
6
+ */
7
+ import type { EnvVariable } from "../../templates/backendApp/index.js";
8
+ /** `.env.example` with the variables it does not declare yet appended. */
9
+ export declare function withEnvVariables(current: string, variables: readonly EnvVariable[]): string | undefined;
10
+ /** `.gitignore` with the lines it lacks appended. */
11
+ export declare function withGitignoreLines(current: string, lines: readonly string[]): string | undefined;
12
+ /** A package.json shape `add` edits. */
13
+ export interface EditablePackageJson {
14
+ dependencies?: Record<string, string>;
15
+ devDependencies?: Record<string, string>;
16
+ scripts?: Record<string, string>;
17
+ zudojs?: Record<string, unknown>;
18
+ [key: string]: unknown;
19
+ }
20
+ /** What to merge into a package.json. */
21
+ export interface PackageJsonChanges {
22
+ readonly feature: string;
23
+ readonly dependencies: Readonly<Record<string, string>>;
24
+ readonly devDependencies?: Readonly<Record<string, string>>;
25
+ readonly scripts?: Readonly<Record<string, string>>;
26
+ readonly replaceScripts?: Readonly<Record<string, {
27
+ from: string;
28
+ to: string;
29
+ }>>;
30
+ }
31
+ /** Merges `changes` into `pkg` in place. */
32
+ export declare function mergePackageJson(pkg: EditablePackageJson, changes: PackageJsonChanges): void;
33
+ /**
34
+ * `pnpm-workspace.yaml` with `names` added to both build-script allow-lists
35
+ * (pnpm 10 `onlyBuiltDependencies`, pnpm 11 `allowBuilds`). Returns
36
+ * `undefined` when there is no such file or nothing to add.
37
+ */
38
+ export declare function withAllowedBuilds(root: string, names: readonly string[]): string | undefined;
39
+ //# sourceMappingURL=recipeApply.files.d.ts.map