@decohere/scaffold 0.0.1-dev.18 → 0.0.1-dev.20261105.54

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 (264) hide show
  1. package/LICENSE +15 -0
  2. package/commonjs/src/add-api.action.js +37 -0
  3. package/commonjs/src/add-api.command.js +39 -0
  4. package/commonjs/src/add.command.js +22 -0
  5. package/commonjs/src/add.module.js +24 -0
  6. package/commonjs/src/auth0-configure.action.js +28 -0
  7. package/commonjs/src/auth0-configure.command.js +24 -0
  8. package/commonjs/src/auth0-login.action.js +28 -0
  9. package/commonjs/src/auth0-login.command.js +23 -0
  10. package/commonjs/src/auth0.command.js +22 -0
  11. package/commonjs/src/auth0.module.js +28 -0
  12. package/commonjs/src/cli.js +8 -0
  13. package/commonjs/src/create.command.js +22 -0
  14. package/commonjs/src/create.module.js +24 -0
  15. package/commonjs/src/domain/prompt.js +62 -0
  16. package/commonjs/src/index.js +48 -0
  17. package/commonjs/src/models/compose.model.js +258 -0
  18. package/commonjs/src/models/config-yaml.model.js +86 -0
  19. package/commonjs/src/models/const.js +30 -0
  20. package/commonjs/src/models/dev-container.model.js +172 -0
  21. package/commonjs/src/models/docker-image.model.js +277 -0
  22. package/commonjs/src/models/env-file.model.js +48 -0
  23. package/commonjs/src/models/gitignore.model.js +48 -0
  24. package/commonjs/src/models/launch-json.model.js +70 -0
  25. package/commonjs/src/models/package-json.model.js +157 -0
  26. package/commonjs/src/models/project.model.js +132 -0
  27. package/commonjs/src/models/source-file.js +4 -0
  28. package/commonjs/src/models/tsconfig.model.js +32 -0
  29. package/commonjs/src/models/vitest-config.model.js +33 -0
  30. package/commonjs/src/models/vscode-settings.model.js +29 -0
  31. package/commonjs/src/program.js +64 -0
  32. package/commonjs/src/resolve-target-dir.js +17 -0
  33. package/commonjs/src/services/scaffold.service.js +60 -0
  34. package/commonjs/src/version.js +55 -0
  35. package/commonjs/src/webapi/api-name.js +93 -0
  36. package/commonjs/src/webapi/auth0-commands.js +250 -0
  37. package/commonjs/src/webapi/authorize-scopes.js +79 -0
  38. package/commonjs/src/webapi/cli-options.js +99 -0
  39. package/commonjs/src/webapi/const.js +54 -0
  40. package/commonjs/src/webapi/create-api.js +275 -0
  41. package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +164 -0
  42. package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
  43. package/commonjs/src/webapi/devcontainer/host-scripts.js +50 -0
  44. package/commonjs/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
  45. package/commonjs/src/webapi/devcontainer/templates/initialize.sh +169 -0
  46. package/commonjs/src/webapi/devcontainer/templates/post-create.sh +35 -0
  47. package/commonjs/src/webapi/devcontainer/templates/post-start.sh +65 -0
  48. package/commonjs/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
  49. package/commonjs/src/webapi/features/auth.feature.js +408 -0
  50. package/commonjs/src/webapi/features/auth0-cli.js +472 -0
  51. package/commonjs/src/webapi/features/biome-format.js +47 -0
  52. package/commonjs/src/webapi/features/database.feature.js +433 -0
  53. package/commonjs/src/webapi/features/finalize.feature.js +93 -0
  54. package/commonjs/src/webapi/features/postman.feature.js +322 -0
  55. package/commonjs/src/webapi/models/api-package-json.model.js +58 -0
  56. package/commonjs/src/webapi/models/auth-config.model.js +24 -0
  57. package/commonjs/src/webapi/models/controller-source-file.model.js +190 -0
  58. package/commonjs/src/webapi/models/github-ci.model.js +193 -0
  59. package/commonjs/src/webapi/models/main-source-file.model.js +165 -0
  60. package/commonjs/src/webapi/models/production-dockerfile.model.js +75 -0
  61. package/commonjs/src/webapi/models/sample-files.model.js +58 -0
  62. package/commonjs/src/webapi/models/webapi-image.model.js +25 -0
  63. package/commonjs/src/webapi/postman/hello-openapi.js +173 -0
  64. package/commonjs/src/webapi/postman/local-view.js +182 -0
  65. package/commonjs/src/webapi/postman/openapi-to-postman.js +238 -0
  66. package/commonjs/src/webapi/workspace-readme.js +109 -0
  67. package/commonjs/src/webapi.action.js +28 -0
  68. package/commonjs/src/webapi.command.js +43 -0
  69. package/es6/src/add-api.action.js +27 -0
  70. package/es6/src/add-api.command.js +29 -0
  71. package/es6/src/add.command.js +12 -0
  72. package/es6/src/add.module.js +14 -0
  73. package/es6/src/auth0-configure.action.js +18 -0
  74. package/es6/src/auth0-configure.command.js +14 -0
  75. package/es6/src/auth0-login.action.js +18 -0
  76. package/es6/src/auth0-login.command.js +13 -0
  77. package/es6/src/auth0.command.js +12 -0
  78. package/es6/src/auth0.module.js +18 -0
  79. package/es6/src/cli.js +4 -0
  80. package/es6/src/create.command.js +12 -0
  81. package/es6/src/create.module.js +14 -0
  82. package/es6/src/domain/prompt.js +41 -0
  83. package/es6/src/index.js +9 -0
  84. package/es6/src/models/compose.model.js +254 -0
  85. package/es6/src/models/config-yaml.model.js +74 -0
  86. package/es6/src/models/const.js +20 -0
  87. package/es6/src/models/dev-container.model.js +162 -0
  88. package/es6/src/models/docker-image.model.js +235 -0
  89. package/es6/src/models/env-file.model.js +44 -0
  90. package/es6/src/models/gitignore.model.js +38 -0
  91. package/es6/src/models/launch-json.model.js +60 -0
  92. package/es6/src/models/package-json.model.js +142 -0
  93. package/es6/src/models/project.model.js +117 -0
  94. package/es6/src/models/source-file.js +1 -0
  95. package/es6/src/models/tsconfig.model.js +22 -0
  96. package/es6/src/models/vitest-config.model.js +23 -0
  97. package/es6/src/models/vscode-settings.model.js +19 -0
  98. package/es6/src/program.js +50 -0
  99. package/es6/src/resolve-target-dir.js +7 -0
  100. package/es6/src/services/scaffold.service.js +50 -0
  101. package/es6/src/version.js +34 -0
  102. package/es6/src/webapi/api-name.js +57 -0
  103. package/es6/src/webapi/auth0-commands.js +185 -0
  104. package/es6/src/webapi/authorize-scopes.js +55 -0
  105. package/es6/src/webapi/cli-options.js +34 -0
  106. package/es6/src/webapi/const.js +21 -0
  107. package/es6/src/webapi/create-api.js +255 -0
  108. package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +154 -0
  109. package/es6/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
  110. package/es6/src/webapi/devcontainer/host-scripts.js +29 -0
  111. package/es6/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
  112. package/es6/src/webapi/devcontainer/templates/initialize.sh +169 -0
  113. package/es6/src/webapi/devcontainer/templates/post-create.sh +35 -0
  114. package/es6/src/webapi/devcontainer/templates/post-start.sh +65 -0
  115. package/es6/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
  116. package/es6/src/webapi/features/auth.feature.js +357 -0
  117. package/es6/src/webapi/features/auth0-cli.js +406 -0
  118. package/es6/src/webapi/features/biome-format.js +40 -0
  119. package/es6/src/webapi/features/database.feature.js +423 -0
  120. package/es6/src/webapi/features/finalize.feature.js +83 -0
  121. package/es6/src/webapi/features/postman.feature.js +256 -0
  122. package/es6/src/webapi/models/api-package-json.model.js +53 -0
  123. package/es6/src/webapi/models/auth-config.model.js +20 -0
  124. package/es6/src/webapi/models/controller-source-file.model.js +181 -0
  125. package/{src/webapi/models/github-ci.model.ts → es6/src/webapi/models/github-ci.model.js} +27 -29
  126. package/es6/src/webapi/models/main-source-file.model.js +161 -0
  127. package/es6/src/webapi/models/production-dockerfile.model.js +66 -0
  128. package/es6/src/webapi/models/sample-files.model.js +40 -0
  129. package/es6/src/webapi/models/webapi-image.model.js +18 -0
  130. package/es6/src/webapi/postman/hello-openapi.js +166 -0
  131. package/es6/src/webapi/postman/local-view.js +167 -0
  132. package/es6/src/webapi/postman/openapi-to-postman.js +179 -0
  133. package/es6/src/webapi/workspace-readme.js +88 -0
  134. package/es6/src/webapi.action.js +18 -0
  135. package/es6/src/webapi.command.js +33 -0
  136. package/package.json +19 -9
  137. package/types/src/add-api.action.d.ts +13 -0
  138. package/types/src/add-api.command.d.ts +7 -0
  139. package/types/src/add.command.d.ts +6 -0
  140. package/types/src/add.module.d.ts +14 -0
  141. package/types/src/auth0-configure.action.d.ts +7 -0
  142. package/types/src/auth0-configure.command.d.ts +6 -0
  143. package/types/src/auth0-login.action.d.ts +4 -0
  144. package/types/src/auth0-login.command.d.ts +6 -0
  145. package/types/src/auth0.command.d.ts +6 -0
  146. package/types/src/auth0.module.d.ts +18 -0
  147. package/types/src/cli.d.ts +2 -0
  148. package/types/src/create.command.d.ts +6 -0
  149. package/types/src/create.module.d.ts +14 -0
  150. package/types/src/domain/prompt.d.ts +4 -0
  151. package/{src/index.ts → types/src/index.d.ts} +4 -0
  152. package/types/src/models/compose.model.d.ts +73 -0
  153. package/types/src/models/config-yaml.model.d.ts +27 -0
  154. package/types/src/models/const.d.ts +20 -0
  155. package/types/src/models/dev-container.model.d.ts +49 -0
  156. package/types/src/models/docker-image.model.d.ts +90 -0
  157. package/types/src/models/env-file.model.d.ts +16 -0
  158. package/types/src/models/gitignore.model.d.ts +9 -0
  159. package/types/src/models/launch-json.model.d.ts +7 -0
  160. package/types/src/models/package-json.model.d.ts +36 -0
  161. package/types/src/models/project.model.d.ts +60 -0
  162. package/{src/models/source-file.ts → types/src/models/source-file.d.ts} +4 -6
  163. package/types/src/models/tsconfig.model.d.ts +5 -0
  164. package/types/src/models/vitest-config.model.d.ts +5 -0
  165. package/types/src/models/vscode-settings.model.d.ts +7 -0
  166. package/types/src/program.d.ts +8 -0
  167. package/{src/resolve-target-dir.ts → types/src/resolve-target-dir.d.ts} +1 -4
  168. package/types/src/services/scaffold.service.d.ts +22 -0
  169. package/types/src/version.d.ts +6 -0
  170. package/types/src/webapi/api-name.d.ts +14 -0
  171. package/types/src/webapi/auth0-commands.d.ts +11 -0
  172. package/types/src/webapi/authorize-scopes.d.ts +7 -0
  173. package/types/src/webapi/cli-options.d.ts +6 -0
  174. package/{src/webapi/const.ts → types/src/webapi/const.d.ts} +9 -21
  175. package/types/src/webapi/create-api.d.ts +15 -0
  176. package/types/src/webapi/devcontainer/codebase-rag-mcp.d.ts +32 -0
  177. package/types/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
  178. package/types/src/webapi/devcontainer/host-scripts.d.ts +7 -0
  179. package/types/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
  180. package/types/src/webapi/devcontainer/templates/initialize.sh +169 -0
  181. package/types/src/webapi/devcontainer/templates/post-create.sh +35 -0
  182. package/types/src/webapi/devcontainer/templates/post-start.sh +65 -0
  183. package/types/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
  184. package/types/src/webapi/features/auth.feature.d.ts +10 -0
  185. package/types/src/webapi/features/auth0-cli.d.ts +84 -0
  186. package/types/src/webapi/features/biome-format.d.ts +5 -0
  187. package/types/src/webapi/features/database.feature.d.ts +7 -0
  188. package/types/src/webapi/features/finalize.feature.d.ts +2 -0
  189. package/types/src/webapi/features/postman.feature.d.ts +16 -0
  190. package/types/src/webapi/models/api-package-json.model.d.ts +18 -0
  191. package/types/src/webapi/models/auth-config.model.d.ts +26 -0
  192. package/types/src/webapi/models/controller-source-file.model.d.ts +46 -0
  193. package/types/src/webapi/models/github-ci.model.d.ts +9 -0
  194. package/types/src/webapi/models/main-source-file.model.d.ts +54 -0
  195. package/types/src/webapi/models/production-dockerfile.model.d.ts +18 -0
  196. package/types/src/webapi/models/sample-files.model.d.ts +7 -0
  197. package/types/src/webapi/models/webapi-image.model.d.ts +12 -0
  198. package/types/src/webapi/postman/hello-openapi.d.ts +19 -0
  199. package/types/src/webapi/postman/local-view.d.ts +17 -0
  200. package/types/src/webapi/postman/openapi-to-postman.d.ts +62 -0
  201. package/types/src/webapi/workspace-readme.d.ts +18 -0
  202. package/types/src/webapi.action.d.ts +14 -0
  203. package/types/src/webapi.command.d.ts +11 -0
  204. package/src/add-api.action.ts +0 -25
  205. package/src/add-api.command.ts +0 -31
  206. package/src/add.command.ts +0 -20
  207. package/src/add.module.ts +0 -11
  208. package/src/cli.ts +0 -4
  209. package/src/create.command.ts +0 -20
  210. package/src/create.module.ts +0 -11
  211. package/src/domain/prompt.ts +0 -50
  212. package/src/models/compose.model.ts +0 -205
  213. package/src/models/config-yaml.model.ts +0 -83
  214. package/src/models/const.ts +0 -20
  215. package/src/models/dev-container.model.ts +0 -136
  216. package/src/models/docker-image.model.ts +0 -343
  217. package/src/models/env-file.model.ts +0 -45
  218. package/src/models/gitignore.model.ts +0 -35
  219. package/src/models/launch-json.model.ts +0 -60
  220. package/src/models/package-json.model.ts +0 -169
  221. package/src/models/project.model.ts +0 -141
  222. package/src/models/tsconfig.model.ts +0 -29
  223. package/src/models/vitest-config.model.ts +0 -26
  224. package/src/models/vscode-settings.model.ts +0 -24
  225. package/src/program.ts +0 -63
  226. package/src/services/scaffold.service.ts +0 -89
  227. package/src/version.ts +0 -56
  228. package/src/webapi/api-name.ts +0 -73
  229. package/src/webapi/cli-options.ts +0 -56
  230. package/src/webapi/create-api.ts +0 -275
  231. package/src/webapi/devcontainer/host-scripts.ts +0 -35
  232. package/src/webapi/features/auth.feature.ts +0 -454
  233. package/src/webapi/features/auth0-cli.ts +0 -421
  234. package/src/webapi/features/biome-format.ts +0 -34
  235. package/src/webapi/features/database.feature.ts +0 -522
  236. package/src/webapi/features/finalize.feature.ts +0 -112
  237. package/src/webapi/features/postman.feature.ts +0 -287
  238. package/src/webapi/models/api-package-json.model.ts +0 -60
  239. package/src/webapi/models/auth-config.model.ts +0 -36
  240. package/src/webapi/models/controller-source-file.model.ts +0 -232
  241. package/src/webapi/models/main-source-file.model.ts +0 -208
  242. package/src/webapi/models/production-dockerfile.model.ts +0 -162
  243. package/src/webapi/models/sample-files.model.ts +0 -45
  244. package/src/webapi/models/webapi-image.model.ts +0 -21
  245. package/src/webapi/postman/hello-openapi.ts +0 -92
  246. package/src/webapi/postman/local-view.ts +0 -195
  247. package/src/webapi/postman/openapi-to-postman.ts +0 -208
  248. package/src/webapi/workspace-readme.ts +0 -192
  249. package/src/webapi.action.ts +0 -22
  250. package/src/webapi.command.ts +0 -41
  251. package/tests/auth-postman.wiring.spec.ts +0 -263
  252. package/tests/auth0-cli.spec.ts +0 -232
  253. package/tests/biome-format.spec.ts +0 -49
  254. package/tests/cli.integration.spec.ts +0 -658
  255. package/tests/compose.model.spec.ts +0 -51
  256. package/tests/container-image.model.spec.ts +0 -162
  257. package/tests/dev-container.model.spec.ts +0 -29
  258. package/tests/devcontainer-base-image.spec.ts +0 -32
  259. package/tests/hello-di-inject.scaffold.spec.ts +0 -18
  260. package/tests/hello-route.contract.spec.ts +0 -94
  261. package/tests/package-json.order.spec.ts +0 -59
  262. package/tests/postman-from-openapi.spec.ts +0 -197
  263. package/tests/seed-devcontainer-templates.ts +0 -40
  264. package/tests/webapi/devcontainer/codebase-rag-mcp.spec.ts +0 -96
@@ -0,0 +1,14 @@
1
+ import { ModuleManifest } from "@decohere/devkit/cli";
2
+ import { AddCommand } from "./add.command.js";
3
+ import { AddApiAction } from "./add-api.action.js";
4
+ import { AddApiCommand } from "./add-api.command.js";
5
+ export class AddModule extends ModuleManifest {
6
+ id = "add";
7
+ command = AddCommand;
8
+ commands = {
9
+ api: AddApiCommand
10
+ };
11
+ actions = {
12
+ api: AddApiAction
13
+ };
14
+ }
@@ -0,0 +1,18 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { injectable } from "@decohere/crucible/decorators";
8
+ import { ActionResponse } from "@decohere/devkit/cli";
9
+ import { runAuth0Configure } from "./webapi/auth0-commands.js";
10
+ export class Auth0ConfigureAction {
11
+ async run(options) {
12
+ runAuth0Configure(typeof options.d === "string" ? options.d : ".");
13
+ return ActionResponse.Success("configure");
14
+ }
15
+ }
16
+ Auth0ConfigureAction = _ts_decorate([
17
+ injectable()
18
+ ], Auth0ConfigureAction);
@@ -0,0 +1,14 @@
1
+ import { DecohereCommand } from "@decohere/devkit/cli";
2
+ import { Auth0ConfigureAction } from "./auth0-configure.action.js";
3
+ export class Auth0ConfigureCommand extends DecohereCommand {
4
+ get actions() {
5
+ return [
6
+ Auth0ConfigureAction
7
+ ];
8
+ }
9
+ constructor(name){
10
+ super(name);
11
+ this.description("Update Auth0 API permissions and Postman SPA from @Authorize in this workspace");
12
+ this.option("-d <directory>", "Workspace directory (default: current directory)", ".");
13
+ }
14
+ }
@@ -0,0 +1,18 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { injectable } from "@decohere/crucible/decorators";
8
+ import { ActionResponse } from "@decohere/devkit/cli";
9
+ import { runAuth0Login } from "./webapi/auth0-commands.js";
10
+ export class Auth0LoginAction {
11
+ async run() {
12
+ runAuth0Login();
13
+ return ActionResponse.Success("login");
14
+ }
15
+ }
16
+ Auth0LoginAction = _ts_decorate([
17
+ injectable()
18
+ ], Auth0LoginAction);
@@ -0,0 +1,13 @@
1
+ import { DecohereCommand } from "@decohere/devkit/cli";
2
+ import { Auth0LoginAction } from "./auth0-login.action.js";
3
+ export class Auth0LoginCommand extends DecohereCommand {
4
+ get actions() {
5
+ return [
6
+ Auth0LoginAction
7
+ ];
8
+ }
9
+ constructor(name){
10
+ super(name);
11
+ this.description("Authenticate the Auth0 CLI (device URL; tokens persist on the host)");
12
+ }
13
+ }
@@ -0,0 +1,12 @@
1
+ import { DecohereCommand } from "@decohere/devkit/cli";
2
+ export class Auth0Command extends DecohereCommand {
3
+ constructor(commands, handler){
4
+ super(Auth0Command);
5
+ this.description("Log in to Auth0 and update a scaffolded client");
6
+ this.exitOverride();
7
+ this.registerSubcommand(commands, handler);
8
+ }
9
+ run(_actions, _options) {
10
+ return this.help();
11
+ }
12
+ }
@@ -0,0 +1,18 @@
1
+ import { ModuleManifest } from "@decohere/devkit/cli";
2
+ import { Auth0Command } from "./auth0.command.js";
3
+ import { Auth0ConfigureAction } from "./auth0-configure.action.js";
4
+ import { Auth0ConfigureCommand } from "./auth0-configure.command.js";
5
+ import { Auth0LoginAction } from "./auth0-login.action.js";
6
+ import { Auth0LoginCommand } from "./auth0-login.command.js";
7
+ export class Auth0Module extends ModuleManifest {
8
+ id = "auth0";
9
+ command = Auth0Command;
10
+ commands = {
11
+ login: Auth0LoginCommand,
12
+ configure: Auth0ConfigureCommand
13
+ };
14
+ actions = {
15
+ login: Auth0LoginAction,
16
+ configure: Auth0ConfigureAction
17
+ };
18
+ }
package/es6/src/cli.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./program.js";
3
+ // Exit as soon as the command is done, without waiting for handles it left open.
4
+ process.exit(await runCli());
@@ -0,0 +1,12 @@
1
+ import { DecohereCommand } from "@decohere/devkit/cli";
2
+ export class CreateCommand extends DecohereCommand {
3
+ constructor(commands, handler){
4
+ super(CreateCommand);
5
+ this.description("Create a new Decohere workspace");
6
+ this.exitOverride();
7
+ this.registerSubcommand(commands, handler);
8
+ }
9
+ run(_actions, _options) {
10
+ return this.help();
11
+ }
12
+ }
@@ -0,0 +1,14 @@
1
+ import { ModuleManifest } from "@decohere/devkit/cli";
2
+ import { CreateCommand } from "./create.command.js";
3
+ import { WebapiAction } from "./webapi.action.js";
4
+ import { WebapiCommand } from "./webapi.command.js";
5
+ export class CreateModule extends ModuleManifest {
6
+ id = "create";
7
+ command = CreateCommand;
8
+ commands = {
9
+ webapi: WebapiCommand
10
+ };
11
+ actions = {
12
+ webapi: WebapiAction
13
+ };
14
+ }
@@ -0,0 +1,41 @@
1
+ import { createInterface } from "node:readline";
2
+ // readline/promises rl.question() has a race condition with piped (non-TTY) stdin
3
+ // in Node ≥ 22: after the first line is read, stdin emits 'end', readline closes,
4
+ // and all subsequent question() calls resolve to "".
5
+ // Fix: use the async-iterator API, which buffers all lines before the close event fires.
6
+ let _rl = null;
7
+ let _lines = null;
8
+ function getLines() {
9
+ if (_lines == null) {
10
+ _rl = createInterface({
11
+ input: process.stdin,
12
+ terminal: false
13
+ });
14
+ _lines = _rl[Symbol.asyncIterator]();
15
+ }
16
+ return _lines;
17
+ }
18
+ /** Release stdin so the process can exit after interactive prompts. */ export function closePrompt() {
19
+ _rl?.close();
20
+ _rl = null;
21
+ _lines = null;
22
+ }
23
+ async function readLine(prompt) {
24
+ process.stdout.write(prompt);
25
+ const { value, done } = await getLines().next();
26
+ return done ? "" : value.trim();
27
+ }
28
+ export async function ask(question, fallback = "") {
29
+ const answer = await readLine(question);
30
+ return answer || fallback;
31
+ }
32
+ export async function pick(label, items, display) {
33
+ console.log(label.trimEnd());
34
+ for (const [i, item] of items.entries()){
35
+ console.log(` ${i + 1}. ${display(item)}`);
36
+ }
37
+ const answer = await readLine(" > ");
38
+ const idx = Number.parseInt(answer, 10);
39
+ if (Number.isNaN(idx) || idx < 1 || idx > items.length) return null;
40
+ return items[idx - 1];
41
+ }
@@ -0,0 +1,9 @@
1
+ export { AddModule } from "./add.module.js";
2
+ export { AddApiAction } from "./add-api.action.js";
3
+ export { Auth0Module } from "./auth0.module.js";
4
+ export { Auth0ConfigureAction } from "./auth0-configure.action.js";
5
+ export { Auth0LoginAction } from "./auth0-login.action.js";
6
+ export { CreateModule } from "./create.module.js";
7
+ export { runCli } from "./program.js";
8
+ export { scaffoldApi } from "./webapi/create-api.js";
9
+ export { WebapiAction } from "./webapi.action.js";
@@ -0,0 +1,254 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { isMap, isSeq, parseDocument } from "yaml";
3
+ import { WELL_KNOWN_FILES } from "./const.js";
4
+ /**
5
+ * Live builder for `docker-compose.yml`.
6
+ *
7
+ * Features call `addService` / `addVolume` / `addNetwork` in their scaffold phases.
8
+ * Loaded with `fromFile` (`decohere add api`), additions merge into the existing file:
9
+ * services, networks and volumes already there are left untouched, comments kept.
10
+ */ export class DockerCompose {
11
+ key = WELL_KNOWN_FILES.DOCKER_COMPOSE;
12
+ _projectName;
13
+ _headerComments = [];
14
+ _services = new Map();
15
+ _volumes = new Set();
16
+ _networks = new Map();
17
+ _envFiles = [];
18
+ _existing;
19
+ /** Load an existing compose file so `decohere add api` can merge into it. */ static fromFile(absolutePath) {
20
+ const compose = new DockerCompose();
21
+ compose._existing = parseDocument(readFileSync(absolutePath, "utf8"));
22
+ return compose;
23
+ }
24
+ /** True when the service is defined (in the loaded file or added since). */ hasService(name) {
25
+ return this._services.has(name) || this._existing?.hasIn([
26
+ "services",
27
+ name
28
+ ]) === true;
29
+ }
30
+ /** Compose project name (`name:` top-level) — isolates containers/volumes. */ setProjectName(name) {
31
+ this._projectName = name;
32
+ return this;
33
+ }
34
+ /** File-level `#` comments rendered above `name:` / `services:`. */ addHeaderComment(...lines) {
35
+ this._headerComments.push(...lines);
36
+ return this;
37
+ }
38
+ addService(name, config) {
39
+ this._services.set(name, config);
40
+ return this;
41
+ }
42
+ files() {
43
+ return [
44
+ {
45
+ filePath: ".devcontainer/docker-compose.yml",
46
+ content: this.getContent()
47
+ }
48
+ ];
49
+ }
50
+ addVolume(name) {
51
+ this._volumes.add(name);
52
+ return this;
53
+ }
54
+ addNetwork(key, network) {
55
+ this._networks.set(key, network);
56
+ return this;
57
+ }
58
+ /** Append an env_file path to an existing service. */ addEnvFile(serviceName, filePath) {
59
+ // A service already in the loaded file wins over an `addService` of the same name.
60
+ const svc = this._existing?.hasIn([
61
+ "services",
62
+ serviceName
63
+ ]) ? undefined : this._services.get(serviceName);
64
+ if (svc) svc.env_file = [
65
+ ...svc.env_file ?? [],
66
+ filePath
67
+ ];
68
+ else this._envFiles.push([
69
+ serviceName,
70
+ filePath
71
+ ]);
72
+ return this;
73
+ }
74
+ getContent() {
75
+ if (this._existing) return this.mergeIntoExisting(this._existing);
76
+ const lines = [];
77
+ for (const comment of this._headerComments){
78
+ lines.push(comment.startsWith("#") ? comment : `# ${comment}`);
79
+ }
80
+ if (this._headerComments.length > 0) {
81
+ lines.push("");
82
+ }
83
+ if (this._projectName) {
84
+ lines.push(`name: ${this._projectName}`, "");
85
+ }
86
+ lines.push("services:");
87
+ for (const [name, svc] of this._services){
88
+ lines.push(` ${name}:`);
89
+ if (svc.build) lines.push(` build: ${svc.build}`);
90
+ if (svc.image) lines.push(` image: ${svc.image}`);
91
+ if (svc.container_name) lines.push(` container_name: ${svc.container_name}`);
92
+ if (svc.command) lines.push(` command: ${svc.command}`);
93
+ if (svc.user) lines.push(` user: "${svc.user}"`);
94
+ if (svc.restart) lines.push(` restart: ${svc.restart}`);
95
+ if (svc.profiles && svc.profiles.length > 0) {
96
+ lines.push(" profiles:");
97
+ for (const p of svc.profiles)lines.push(` - ${p}`);
98
+ }
99
+ if (svc.env_file && svc.env_file.length > 0) {
100
+ lines.push(" env_file:");
101
+ for (const f of svc.env_file)lines.push(` - ${f}`);
102
+ }
103
+ if (svc.environment && Object.keys(svc.environment).length > 0) {
104
+ lines.push(" environment:");
105
+ for (const [k, v] of Object.entries(svc.environment)){
106
+ lines.push(` ${k}: ${v}`);
107
+ }
108
+ }
109
+ if (svc.ports && svc.ports.length > 0) {
110
+ lines.push(" ports:");
111
+ for (const p of svc.ports)lines.push(` - "${p}"`);
112
+ }
113
+ if (svc.volumes && svc.volumes.length > 0) {
114
+ lines.push(" volumes:");
115
+ for (const v of svc.volumes)lines.push(` - ${v}`);
116
+ }
117
+ if (svc.networks && svc.networks.length > 0) {
118
+ const onlyStrings = svc.networks.every((n)=>typeof n === "string");
119
+ if (onlyStrings) {
120
+ lines.push(" networks:");
121
+ for (const n of svc.networks)lines.push(` - ${n}`);
122
+ } else {
123
+ lines.push(" networks:");
124
+ for (const n of svc.networks){
125
+ if (typeof n === "string") {
126
+ lines.push(` ${n}:`);
127
+ } else {
128
+ for (const [netKey, cfg] of Object.entries(n)){
129
+ lines.push(` ${netKey}:`);
130
+ if (cfg.aliases && cfg.aliases.length > 0) {
131
+ lines.push(" aliases:");
132
+ for (const a of cfg.aliases)lines.push(` - ${a}`);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ if (svc.depends_on && svc.depends_on.length > 0) {
140
+ lines.push(" depends_on:");
141
+ for (const d of svc.depends_on)lines.push(` - ${d}`);
142
+ }
143
+ if (svc.healthcheck) {
144
+ const hc = svc.healthcheck;
145
+ lines.push(" healthcheck:");
146
+ lines.push(` test: ${JSON.stringify(hc.test)}`);
147
+ if (hc.interval) lines.push(` interval: ${hc.interval}`);
148
+ if (hc.timeout) lines.push(` timeout: ${hc.timeout}`);
149
+ if (hc.retries != null) lines.push(` retries: ${hc.retries}`);
150
+ }
151
+ }
152
+ if (this._networks.size > 0) {
153
+ lines.push("");
154
+ lines.push("networks:");
155
+ for (const [key, net] of this._networks){
156
+ lines.push(` ${key}:`);
157
+ lines.push(` name: ${net.name}`);
158
+ if (net.external) lines.push(" external: true");
159
+ }
160
+ }
161
+ if (this._volumes.size > 0) {
162
+ lines.push("");
163
+ lines.push("volumes:");
164
+ for (const v of this._volumes)lines.push(` ${v}:`);
165
+ }
166
+ return `${lines.join("\n")}\n`;
167
+ }
168
+ mergeIntoExisting(doc) {
169
+ if (!isMap(doc.contents)) throw new Error("docker-compose.yml is not a YAML mapping");
170
+ for (const line of this._headerComments){
171
+ const text = line.replace(/^#\s?/, "");
172
+ const before = doc.commentBefore ?? "";
173
+ if (!before.includes(text)) doc.commentBefore = before ? `${before}\n ${text}` : ` ${text}`;
174
+ }
175
+ for (const [name, svc] of this._services){
176
+ if (!doc.hasIn([
177
+ "services",
178
+ name
179
+ ])) doc.setIn([
180
+ "services",
181
+ name
182
+ ], doc.createNode(toComposeYaml(svc)));
183
+ }
184
+ for (const [service, path] of this._envFiles){
185
+ if (!doc.hasIn([
186
+ "services",
187
+ service
188
+ ])) continue;
189
+ const envFiles = doc.getIn([
190
+ "services",
191
+ service,
192
+ "env_file"
193
+ ]);
194
+ if (isSeq(envFiles)) {
195
+ if (!envFiles.items.some((item)=>String(item) === path)) envFiles.add(path);
196
+ } else if (envFiles == null) {
197
+ doc.setIn([
198
+ "services",
199
+ service,
200
+ "env_file"
201
+ ], doc.createNode([
202
+ path
203
+ ]));
204
+ } else if (String(envFiles) !== path) {
205
+ doc.setIn([
206
+ "services",
207
+ service,
208
+ "env_file"
209
+ ], doc.createNode([
210
+ String(envFiles),
211
+ path
212
+ ]));
213
+ }
214
+ }
215
+ for (const [key, network] of this._networks){
216
+ if (!doc.hasIn([
217
+ "networks",
218
+ key
219
+ ])) doc.setIn([
220
+ "networks",
221
+ key
222
+ ], doc.createNode(network));
223
+ }
224
+ for (const volume of this._volumes){
225
+ if (!doc.hasIn([
226
+ "volumes",
227
+ volume
228
+ ])) doc.setIn([
229
+ "volumes",
230
+ volume
231
+ ], null);
232
+ }
233
+ return doc.toString({
234
+ lineWidth: 0
235
+ });
236
+ }
237
+ }
238
+ /** Plain compose YAML shape for a service (networks with aliases become a map). */ function toComposeYaml(svc) {
239
+ const { networks, ...rest } = svc;
240
+ if (!networks || networks.length === 0) return rest;
241
+ if (networks.every((n)=>typeof n === "string")) return {
242
+ ...rest,
243
+ networks
244
+ };
245
+ const networkMap = {};
246
+ for (const network of networks){
247
+ if (typeof network === "string") networkMap[network] = null;
248
+ else Object.assign(networkMap, network);
249
+ }
250
+ return {
251
+ ...rest,
252
+ networks: networkMap
253
+ };
254
+ }
@@ -0,0 +1,74 @@
1
+ import { readFileSync } from "node:fs";
2
+ /**
3
+ * Builder for a YAML config file (e.g. config/config.yaml or config/config.dev.yaml).
4
+ *
5
+ * Starts with a base section passed to the constructor. Features call
6
+ * `addSection(yaml)` during their scaffold phase to append their own block.
7
+ * `files()` joins all sections with a blank line separator.
8
+ */ export class ConfigYaml {
9
+ key;
10
+ filePath;
11
+ _sections = [];
12
+ constructor(key, filePath, baseContent){
13
+ this.key = key;
14
+ this.filePath = filePath;
15
+ this._sections.push(baseContent);
16
+ }
17
+ addSection(yaml) {
18
+ this._sections.push(yaml);
19
+ return this;
20
+ }
21
+ /** True when a datasource with this `name:` already appears in the file. */ hasDataSource(name) {
22
+ return this._sections.join("\n").includes(`name: ${name}\n`);
23
+ }
24
+ hasDataSources() {
25
+ return this._sections.join("\n").includes("dataSources:");
26
+ }
27
+ /**
28
+ * Insert a `dataSources` list item. Used by `webapi add` so we do not write a
29
+ * second top-level `database:` key (YAML last-key-wins).
30
+ */ appendDataSource(item) {
31
+ const current = this._sections.join("\n");
32
+ this._sections.length = 0;
33
+ this._sections.push(injectYamlDataSource(current, item));
34
+ return this;
35
+ }
36
+ static fromFile(key, relativePath, absolutePath) {
37
+ const content = readFileSync(absolutePath, "utf8");
38
+ return new ConfigYaml(key, relativePath, content);
39
+ }
40
+ files() {
41
+ return [
42
+ {
43
+ filePath: this.filePath,
44
+ content: this._sections.join("\n")
45
+ }
46
+ ];
47
+ }
48
+ }
49
+ /** Insert a YAML list item under `dataSources:`; append a new `database:` block if missing. */ export function injectYamlDataSource(yaml, item) {
50
+ const block = item.replace(/\n$/, "");
51
+ if (!yaml.includes("dataSources:")) {
52
+ const prefix = yaml.trimEnd();
53
+ const section = `database:\n dataSources:\n${block}\n`;
54
+ return prefix.length > 0 ? `${prefix}\n\n${section}` : section;
55
+ }
56
+ const lines = yaml.split("\n");
57
+ const dsIndex = lines.findIndex((line)=>/^\s*dataSources:\s*$/.test(line));
58
+ if (dsIndex < 0) {
59
+ return `${yaml.trimEnd()}\n\ndatabase:\n dataSources:\n${block}\n`;
60
+ }
61
+ let insertAt = lines.length;
62
+ for(let i = dsIndex + 1; i < lines.length; i++){
63
+ if (/^[A-Za-z]/.test(lines[i] ?? "")) {
64
+ insertAt = i;
65
+ break;
66
+ }
67
+ }
68
+ const insertLines = block.split("\n");
69
+ if (insertAt < lines.length && lines[insertAt - 1] !== "") {
70
+ insertLines.push("");
71
+ }
72
+ lines.splice(insertAt, 0, ...insertLines);
73
+ return lines.join("\n");
74
+ }
@@ -0,0 +1,20 @@
1
+ export const WELL_KNOWN_FILES = {
2
+ VSCODE_SETTINGS: "VSCODE_SETTINGS",
3
+ LAUNCH_JSON: "LAUNCH_JSON",
4
+ DOCKER_COMPOSE: "DOCKER_COMPOSE",
5
+ DEVCONTAINER_JSON: "DEVCONTAINER_JSON",
6
+ DOCKERFILE: "DOCKERFILE",
7
+ PRODUCTION_DOCKERFILE: "PRODUCTION_DOCKERFILE",
8
+ GITHUB_CI: "GITHUB_CI",
9
+ PACKAGE_JSON: "PACKAGE_JSON",
10
+ API_PACKAGE_JSON: "API_PACKAGE_JSON",
11
+ TSCONFIG: "TSCONFIG",
12
+ VITEST_CONFIG: "VITEST_CONFIG",
13
+ MAIN_TS: "MAIN_TS",
14
+ CONTROLLER_TS: "CONTROLLER_TS",
15
+ CONFIG_YAML: "CONFIG_YAML",
16
+ CONFIG_DEV_YAML: "CONFIG_DEV_YAML",
17
+ GITIGNORE: "GITIGNORE",
18
+ AUTH_CONFIG: "AUTH_CONFIG",
19
+ ENV_FILE: "ENV_FILE"
20
+ };