@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
@@ -1,208 +0,0 @@
1
- import { WELL_KNOWN_FILES } from "../../models/const.js";
2
- import type { KeyedModel, SourceFile } from "../../models/source-file.js";
3
-
4
- export type TypeOrmImport = { className: string; importPath: string };
5
- export type ServiceImport = TypeOrmImport & { scope?: "singleton" | "request" };
6
-
7
- /** Tracks named imports from a single module path, split into values and types. */
8
- class ImportGroup {
9
- private readonly _values: string[] = [];
10
- private readonly _types: string[] = [];
11
-
12
- public add(name: string, typeOnly = false): this {
13
- if (typeOnly) {
14
- // Value import already present — value wins, skip
15
- if (!this._values.includes(name) && !this._types.includes(name)) {
16
- this._types.push(name);
17
- }
18
- } else {
19
- // Promote: remove from types if it was previously type-only
20
- const typeIdx = this._types.indexOf(name);
21
- if (typeIdx !== -1) this._types.splice(typeIdx, 1);
22
- if (!this._values.includes(name)) this._values.push(name);
23
- }
24
- return this;
25
- }
26
-
27
- public render(from: string): string {
28
- const values = [...this._values].sort();
29
- const types = [...this._types].sort().map((n) => `type ${n}`);
30
- return `import { ${[...values, ...types].join(", ")} } from "${from}";`;
31
- }
32
- }
33
-
34
- /**
35
- * Model for `projects/api/src/main.ts`.
36
- *
37
- * Starts with the minimal HTTP + YAML config setup. Features mutate the model
38
- * by calling addSideEffectImport / addImport / addBuilderCall / addEntity /
39
- * addRepository. files() renders whatever has been accumulated.
40
- */
41
- export class MainSourceFile implements KeyedModel {
42
- public readonly key = WELL_KNOWN_FILES.MAIN_TS;
43
-
44
- private readonly _sideEffectImports: string[] = [];
45
- private readonly _extraImports = new Map<string, ImportGroup>();
46
- private readonly _builderCalls: string[] = [];
47
- private readonly _entities: TypeOrmImport[] = [];
48
- private readonly _repositories: TypeOrmImport[] = [];
49
- private readonly _services: ServiceImport[] = [];
50
- private _dataSourceName: string | undefined;
51
- private _typeOrmSeed: string | undefined;
52
-
53
- public constructor(private readonly appName: string) {}
54
-
55
- /** Adds a bare side-effect import, e.g. `@decohere/cortex/auth`. */
56
- public addSideEffectImport(path: string): this {
57
- if (!this._sideEffectImports.includes(path)) this._sideEffectImports.push(path);
58
- return this;
59
- }
60
-
61
- /** Adds a named import, e.g. `import { Foo } from "bar"`. */
62
- public addImport(name: string, from: string, typeOnly = false): this {
63
- const group = this._extraImports.get(from) ?? new ImportGroup();
64
- group.add(name, typeOnly);
65
- this._extraImports.set(from, group);
66
- return this;
67
- }
68
-
69
- /**
70
- * Adds a builder call inserted after `.withConfiguration()` and before
71
- * `.withServices()`. Pass the call without leading dot, e.g. `.withAuth()`.
72
- */
73
- public addBuilderCall(call: string): this {
74
- this._builderCalls.push(call);
75
- return this;
76
- }
77
-
78
- /** Adds a TypeORM entity — import and entry in `withTypeOrm({ entities })`. */
79
- public addEntity(entity: TypeOrmImport): this {
80
- this._entities.push(entity);
81
- return this;
82
- }
83
-
84
- /** Adds a TypeORM repository — import and entry in `withTypeOrm({ repositories })`. */
85
- public addRepository(repo: TypeOrmImport): this {
86
- this._repositories.push(repo);
87
- return this;
88
- }
89
-
90
- /** Adds a service registered in `.withServices()`. */
91
- public addService(service: ServiceImport): this {
92
- this._services.push(service);
93
- return this;
94
- }
95
-
96
- /**
97
- * Datasource name passed to `withTypeOrm({ name })`. Must match
98
- * `database.dataSources[].name` in config.yaml.
99
- */
100
- public setDataSourceName(name: string): this {
101
- this._dataSourceName = name;
102
- return this;
103
- }
104
-
105
- /**
106
- * Inline seed callback body for `withTypeOrm({ seed })`.
107
- * Receives `dataSource` in scope. Example: `await dataSource.getRepository(User).save(...)`.
108
- */
109
- public setTypeOrmSeed(seedBody: string): this {
110
- this._typeOrmSeed = seedBody;
111
- return this;
112
- }
113
-
114
- public files(): SourceFile[] {
115
- const imports: string[] = [
116
- 'import "@decohere/scribe-pino";',
117
- 'import "@decohere/cortex/http";',
118
- ];
119
-
120
- for (const path of this._sideEffectImports) {
121
- imports.push(`import "${path}";`);
122
- }
123
-
124
- imports.push(
125
- 'import "@decohere/protocol";',
126
- 'import { YamlDataSource } from "@decohere/protocol";',
127
- 'import { Builder } from "@decohere/cortex";',
128
- 'import { FastifyProvider } from "@decohere/cortex/providers";',
129
- 'import { scribe } from "@decohere/scribe";',
130
- 'import { SampleService } from "./services/sample.service.js";',
131
- );
132
-
133
- for (const e of this._entities) {
134
- imports.push(`import { ${e.className} } from "${e.importPath}";`);
135
- }
136
-
137
- for (const r of this._repositories) {
138
- imports.push(`import { ${r.className} } from "${r.importPath}";`);
139
- }
140
-
141
- for (const s of this._services) {
142
- imports.push(`import { ${s.className} } from "${s.importPath}";`);
143
- }
144
-
145
- for (const [from, group] of this._extraImports) {
146
- imports.push(group.render(from));
147
- }
148
-
149
- imports.push('import { HelloController } from "./controllers/hello.controller.js";');
150
-
151
- const serviceRegs = [
152
- "cb.registerSingleton(SampleService);",
153
- ...this._services.map((s) =>
154
- s.scope === "request"
155
- ? `cb.registerRequestScoped(${s.className});`
156
- : `cb.registerSingleton(${s.className});`,
157
- ),
158
- ].join(" ");
159
-
160
- const builderCalls: string[] = [
161
- `\t.withHttp(FastifyProvider)`,
162
- `\t.withConfiguration(YamlDataSource)`,
163
- `\t.withHealth()`,
164
- `\t.withOpenApi({ info: { title: "${this.appName}", version: "1.0.0" } })`,
165
- ...this._builderCalls.map((c) => `\t${c}`),
166
- `\t.withServices((cb) => { ${serviceRegs} })`,
167
- ];
168
-
169
- if (this._entities.length > 0) {
170
- const entities = this._entities.map((e) => e.className).join(", ");
171
- const repos = this._repositories.map((r) => r.className).join(", ");
172
- const named = this._dataSourceName != null ? `name: "${this._dataSourceName}", ` : "";
173
- if (this._typeOrmSeed) {
174
- builderCalls.push(
175
- `\t.withTypeOrm({ ${named}entities: [${entities}], repositories: [${repos}], seed: async (dataSource) => { ${this._typeOrmSeed} } })`,
176
- );
177
- } else {
178
- builderCalls.push(
179
- `\t.withTypeOrm({ ${named}entities: [${entities}], repositories: [${repos}] })`,
180
- );
181
- }
182
- }
183
-
184
- builderCalls.push(`\t.withControllers(HelloController)`);
185
-
186
- const content = [
187
- ...imports,
188
- "",
189
- `scribe.withPinoPretty().and.withPinoRoll({ file: "logs/${this.appName}.log" });`,
190
- "",
191
- `const app = await Builder.create({ applicationName: "${this.appName}" })`,
192
- ...builderCalls,
193
- `\t.build();`,
194
- "",
195
- "await app.start({ port: Number(process.env.PORT ?? 3000) });",
196
- "",
197
- "export default app;",
198
- "",
199
- ].join("\n");
200
-
201
- return [
202
- {
203
- filePath: "src/main.ts",
204
- content,
205
- },
206
- ];
207
- }
208
- }
@@ -1,162 +0,0 @@
1
- import { WELL_KNOWN_FILES } from "../../models/const.js";
2
- import type { KeyedModel, SourceFile } from "../../models/source-file.js";
3
- import { DEFAULT_WEBAPI_NAME, webapiProjectDir, webapiReleaseEntry } from "../const.js";
4
-
5
- export type ProductionDockerfileOptions = {
6
- /** When true, only write `Dockerfile.<name>` (existing workspace — do not replace root Dockerfile). */
7
- namedOnly?: boolean;
8
- };
9
-
10
- /**
11
- * Production Dockerfiles for PaaS (Railway/Fly) and local `docker build`.
12
- *
13
- * Context is always the **repo root** (`docker build -f Dockerfile.billing .`).
14
- * Root `Dockerfile` is the first API (`pnpm start`). Extra or custom-named APIs
15
- * also get `Dockerfile.<name>` so each service can be deployed on its own.
16
- *
17
- * Runtime `node_modules` come from a **prod-deps** stage (`pnpm install --prod`),
18
- * not the full build install (Forge, TypeScript, Biome, Vitest).
19
- */
20
- export class WebapiProductionDockerfile implements KeyedModel {
21
- public readonly key = WELL_KNOWN_FILES.PRODUCTION_DOCKERFILE;
22
-
23
- public constructor(
24
- private readonly apiNames: readonly string[] = [DEFAULT_WEBAPI_NAME],
25
- private readonly options: ProductionDockerfileOptions = {},
26
- ) {}
27
-
28
- public files(): SourceFile[] {
29
- const names = this.apiNames.length > 0 ? this.apiNames : [DEFAULT_WEBAPI_NAME];
30
- const first = names[0] ?? DEFAULT_WEBAPI_NAME;
31
- const files: SourceFile[] = [];
32
-
33
- if (!this.options.namedOnly) {
34
- files.push({
35
- filePath: ".dockerignore",
36
- content: dockerignoreContent(),
37
- });
38
- files.push({
39
- filePath: "Dockerfile",
40
- content: renderProductionDockerfile(first, "start"),
41
- });
42
- }
43
-
44
- const emitNamed =
45
- this.options.namedOnly || names.length > 1 || first !== DEFAULT_WEBAPI_NAME;
46
- if (emitNamed) {
47
- for (const name of names) {
48
- files.push({
49
- filePath: `Dockerfile.${name}`,
50
- content: renderProductionDockerfile(name, `start:${name}`),
51
- });
52
- }
53
- }
54
-
55
- return files;
56
- }
57
- }
58
-
59
- export function renderProductionDockerfile(apiName: string, startScript: string): string {
60
- const projectDir = webapiProjectDir(apiName);
61
- const startCmd =
62
- startScript === "start"
63
- ? 'CMD ["pnpm", "start"]'
64
- : `CMD ["pnpm", "run", "${startScript}"]`;
65
- const startEntry = webapiReleaseEntry(apiName);
66
- const dockerfileFlag = startScript === "start" ? "" : `.${apiName}`;
67
-
68
- return `# syntax=docker/dockerfile:1
69
- # Self-contained production image: install + forge build happen inside docker build.
70
- # Hosts (Railway/Fly/etc.) clone git and run \`docker build\` — they do not receive CI dist/.
71
- # Build from the repo root: docker build -f Dockerfile${dockerfileFlag} .
72
- #
73
- # Runtime node_modules are a production-only install (prod-deps). The build stage's
74
- # full install (Forge, TypeScript, Biome, Vitest) is not copied into the run image.
75
- #
76
- # Private @decohere/* packages: pass a BuildKit secret named npm_token (npm auth token), e.g.
77
- # docker build --secret id=npm_token,env=NODE_AUTH_TOKEN .
78
- # Or set NODE_AUTH_TOKEN as a build-arg on hosts that only support build-args.
79
-
80
- FROM node:24-bookworm AS build
81
- WORKDIR /app
82
- RUN corepack enable && corepack prepare pnpm@10 --activate
83
-
84
- COPY package.json ./
85
- COPY pnpm-lock.yaml* ./
86
- COPY tsconfig.json biome.json ./
87
- COPY config ./config
88
- COPY ${projectDir} ./${projectDir}
89
-
90
- ${renderPnpmInstall("all")}
91
-
92
- RUN pnpm run build
93
-
94
- FROM node:24-bookworm AS prod-deps
95
- WORKDIR /app
96
- ENV NODE_ENV=production
97
- RUN corepack enable && corepack prepare pnpm@10 --activate
98
-
99
- COPY package.json ./
100
- COPY pnpm-lock.yaml* ./
101
- COPY ${projectDir}/package.json ./${projectDir}/package.json
102
-
103
- ${renderPnpmInstall("prod")}
104
-
105
- FROM node:24-bookworm-slim AS run
106
- WORKDIR /app
107
- ENV NODE_ENV=production
108
- RUN corepack enable && corepack prepare pnpm@10 --activate
109
-
110
- COPY --from=prod-deps /app/package.json ./
111
- COPY --from=prod-deps /app/pnpm-lock.yaml* ./
112
- COPY --from=build /app/config ./config
113
- COPY --from=build /app/dist ./dist
114
- COPY --from=prod-deps /app/node_modules ./node_modules
115
- COPY --from=prod-deps /app/${projectDir}/package.json ./${projectDir}/package.json
116
-
117
- EXPOSE 3000
118
- ${startCmd}
119
- # Entry resolved by package.json "${startScript}" → node ${startEntry}
120
- `;
121
- }
122
-
123
- /** npm auth + pnpm install. Token is required again in prod-deps (separate stage, no store). */
124
- function renderPnpmInstall(mode: "all" | "prod"): string {
125
- const flags = mode === "prod" ? " --prod" : "";
126
- return `ARG NODE_AUTH_TOKEN=
127
- RUN --mount=type=secret,id=npm_token,required=false \\
128
- if [ -f /run/secrets/npm_token ]; then \\
129
- TOKEN="$(cat /run/secrets/npm_token)"; \\
130
- else \\
131
- TOKEN="\${NODE_AUTH_TOKEN}"; \\
132
- fi; \\
133
- if [ -n "$TOKEN" ]; then \\
134
- echo "//registry.npmjs.org/:_authToken=\${TOKEN}" > .npmrc; \\
135
- fi; \\
136
- if [ -f pnpm-lock.yaml ]; then \\
137
- pnpm install --frozen-lockfile${flags}; \\
138
- else \\
139
- pnpm install${flags}; \\
140
- fi; \\
141
- rm -f .npmrc`;
142
- }
143
-
144
- function dockerignoreContent(): string {
145
- return [
146
- "node_modules",
147
- "dist",
148
- ".git",
149
- ".devcontainer",
150
- ".vscode",
151
- ".github",
152
- "postman",
153
- ".postman",
154
- ".env",
155
- ".env.*",
156
- "!.env.example",
157
- "coverage",
158
- "**/*.spec.ts",
159
- "**/*.test.ts",
160
- "",
161
- ].join("\n");
162
- }
@@ -1,45 +0,0 @@
1
- import type { SourceFile, SourceFileProvider } from "../../models/source-file.js";
2
-
3
- const SAMPLE_CONFIG_CONTENT = `export class SampleConfig {\n\tpublic greeting: string = "";\n}\n`;
4
-
5
- const SAMPLE_SERVICE_CONTENT = [
6
- `import { inject, injectable } from "@decohere/crucible/decorators";`,
7
- `import { ConfigurationServiceToken } from "@decohere/nexus/configuration";`,
8
- `import type { IConfigurationService } from "@decohere/nexus/configuration";`,
9
- `import { SampleConfig } from "../sample.config.js";`,
10
- ``,
11
- `@injectable()`,
12
- `export class SampleService {`,
13
- `\tpublic constructor(`,
14
- `\t\t@inject(ConfigurationServiceToken) private readonly config: IConfigurationService,`,
15
- `\t) {}`,
16
- ``,
17
- `\tpublic async getGreeting(): Promise<string> {`,
18
- `\t\tconst config = await this.config.get(SampleConfig);`,
19
- `\t\treturn config.greeting;`,
20
- `\t}`,
21
- `}`,
22
- ``,
23
- ].join("\n");
24
-
25
- export class SampleConfigModel implements SourceFileProvider {
26
- public files(): SourceFile[] {
27
- return [
28
- {
29
- filePath: "src/sample.config.ts",
30
- content: SAMPLE_CONFIG_CONTENT,
31
- },
32
- ];
33
- }
34
- }
35
-
36
- export class SampleServiceModel implements SourceFileProvider {
37
- public files(): SourceFile[] {
38
- return [
39
- {
40
- filePath: "src/services/sample.service.ts",
41
- content: SAMPLE_SERVICE_CONTENT,
42
- },
43
- ];
44
- }
45
- }
@@ -1,21 +0,0 @@
1
- import { DockerfileModel } from "../../models/docker-image.model.js";
2
- import { CREATE_VERSION, DEVCONTAINER_IMAGE_REPOSITORY } from "../../version.js";
3
-
4
- /**
5
- * Scaffolded app `.devcontainer/Dockerfile` — thin `FROM` the published base image.
6
- * Optional feature layers (e.g. postgresql-client) append via `addLayer`.
7
- */
8
- export class WebapiDevContainer extends DockerfileModel {
9
- public constructor(baseImage = `${DEVCONTAINER_IMAGE_REPOSITORY}:${CREATE_VERSION}`) {
10
- super(baseImage);
11
- }
12
-
13
- public files() {
14
- return [
15
- {
16
- filePath: ".devcontainer/Dockerfile",
17
- content: this.build(),
18
- },
19
- ];
20
- }
21
- }
@@ -1,92 +0,0 @@
1
- /** Minimal OpenAPI 3 document shape used for Postman conversion. */
2
- export type ScaffoldOpenApiDocument = {
3
- openapi: "3.0.3";
4
- info: { title: string; version: string; description?: string };
5
- servers?: Array<{ url: string; description?: string }>;
6
- paths: Record<string, Record<string, unknown>>;
7
- };
8
-
9
- /**
10
- * Canonical Hello WebAPI OpenAPI document used to generate Postman requests.
11
- * Keep paths aligned with scaffolded HelloController (`/hello`, `/hello/{id}`).
12
- */
13
- export function buildHelloOpenApiDocument(projectName: string): ScaffoldOpenApiDocument {
14
- return {
15
- openapi: "3.0.3",
16
- info: {
17
- title: `${projectName} WebAPI`,
18
- version: "1.0.0",
19
- description:
20
- "Generated from OpenAPI for Postman. Auth/env overlays are applied separately.",
21
- },
22
- servers: [{ url: "{{baseUrl}}" }],
23
- paths: {
24
- "/hello": {
25
- get: {
26
- operationId: "HelloController.getAll",
27
- summary: "List (anonymous)",
28
- tags: ["Hello"],
29
- parameters: [
30
- { name: "filter", in: "query", required: false, schema: { type: "string" } },
31
- ],
32
- responses: { "200": { description: "Successful response" } },
33
- },
34
- post: {
35
- operationId: "HelloController.create",
36
- summary: "Create",
37
- tags: ["Hello"],
38
- requestBody: {
39
- required: true,
40
- content: {
41
- "application/json": {
42
- schema: {
43
- type: "object",
44
- properties: { message: { type: "string" } },
45
- required: ["message"],
46
- example: { message: "hello" },
47
- },
48
- },
49
- },
50
- },
51
- responses: { "200": { description: "Successful response" } },
52
- },
53
- },
54
- "/hello/{id}": {
55
- get: {
56
- operationId: "HelloController.getOne",
57
- summary: "Get by id",
58
- tags: ["Hello"],
59
- parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
60
- responses: { "200": { description: "Successful response" } },
61
- },
62
- put: {
63
- operationId: "HelloController.update",
64
- summary: "Update",
65
- tags: ["Hello"],
66
- parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
67
- requestBody: {
68
- required: true,
69
- content: {
70
- "application/json": {
71
- schema: {
72
- type: "object",
73
- properties: { message: { type: "string" } },
74
- required: ["message"],
75
- example: { message: "updated" },
76
- },
77
- },
78
- },
79
- },
80
- responses: { "200": { description: "Successful response" } },
81
- },
82
- delete: {
83
- operationId: "HelloController.remove",
84
- summary: "Delete",
85
- tags: ["Hello"],
86
- parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
87
- responses: { "200": { description: "Successful response" } },
88
- },
89
- },
90
- },
91
- };
92
- }