@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,408 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "AuthFeature", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return AuthFeature;
9
+ }
10
+ });
11
+ const _prompt = require("../../domain/prompt.js");
12
+ const _const = require("../../models/const.js");
13
+ const _auth0commands = require("../auth0-commands.js");
14
+ const _authconfigmodel = require("../models/auth-config.model.js");
15
+ const _controllersourcefilemodel = require("../models/controller-source-file.model.js");
16
+ const _auth0cli = /*#__PURE__*/ _interop_require_wildcard(require("./auth0-cli.js"));
17
+ function _getRequireWildcardCache(nodeInterop) {
18
+ if (typeof WeakMap !== "function") return null;
19
+ var cacheBabelInterop = new WeakMap();
20
+ var cacheNodeInterop = new WeakMap();
21
+ return (_getRequireWildcardCache = function(nodeInterop) {
22
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
23
+ })(nodeInterop);
24
+ }
25
+ function _interop_require_wildcard(obj, nodeInterop) {
26
+ if (!nodeInterop && obj && obj.__esModule) {
27
+ return obj;
28
+ }
29
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
30
+ return {
31
+ default: obj
32
+ };
33
+ }
34
+ var cache = _getRequireWildcardCache(nodeInterop);
35
+ if (cache && cache.has(obj)) {
36
+ return cache.get(obj);
37
+ }
38
+ var newObj = {
39
+ __proto__: null
40
+ };
41
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
42
+ for(var key in obj){
43
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
44
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
45
+ if (desc && (desc.get || desc.set)) {
46
+ Object.defineProperty(newObj, key, desc);
47
+ } else {
48
+ newObj[key] = obj[key];
49
+ }
50
+ }
51
+ }
52
+ newObj.default = obj;
53
+ if (cache) {
54
+ cache.set(obj, newObj);
55
+ }
56
+ return newObj;
57
+ }
58
+ const ADMIN_ROLE = "admin";
59
+ const USER_ENTITY_TS = `import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
60
+ import type { Entity as IEntity } from "@decohere/data";
61
+
62
+ @Entity()
63
+ export class UserEntity implements IEntity {
64
+ \t@PrimaryGeneratedColumn()
65
+ \tpublic id!: number;
66
+
67
+ \t@Column({ unique: true })
68
+ \tpublic sub!: string;
69
+
70
+ \t@Column({ type: "varchar", nullable: true })
71
+ \tpublic email!: string | null;
72
+
73
+ \t@Column()
74
+ \tpublic displayName!: string;
75
+
76
+ \t/** App-owned roles. The \`admin\` role grants CASL \`manage\`/\`all\` (wildcard). */
77
+ \t@Column({ type: "simple-json", default: "[]" })
78
+ \tpublic roles!: string[];
79
+ }
80
+ `;
81
+ const USER_REPOSITORY_TS = `import { injectable } from "@decohere/crucible/decorators";
82
+ import { injectUnitOfWork } from "@decohere/data";
83
+ import { TypeOrmRepository, TypeOrmUnitOfWork } from "@decohere/data-typeorm";
84
+ import { UserEntity } from "../entities/user.entity.js";
85
+
86
+ @injectable()
87
+ export class UserEntityRepository extends TypeOrmRepository<UserEntity> {
88
+ \tpublic constructor(@injectUnitOfWork() unitOfWork: TypeOrmUnitOfWork) {
89
+ \t\tsuper(unitOfWork, UserEntity);
90
+ \t}
91
+
92
+ \tpublic findBySub(sub: string): Promise<UserEntity | null> {
93
+ \t\treturn this.firstOrDefault({ where: { sub } });
94
+ \t}
95
+ }
96
+ `;
97
+ const USER_SERVICE_TS = `import { injectable } from "@decohere/crucible/decorators";
98
+ import type { Principal } from "@decohere/http";
99
+ import { UserEntity } from "../entities/user.entity.js";
100
+ import { UserEntityRepository } from "../repositories/user.repository.js";
101
+
102
+ function claimString(principal: Principal | undefined, key: string): string | undefined {
103
+ \tconst value = principal?.[key];
104
+ \treturn typeof value === "string" && value.length > 0 ? value : undefined;
105
+ }
106
+
107
+ @injectable()
108
+ export class UserService {
109
+ \tpublic constructor(private readonly users: UserEntityRepository) {}
110
+
111
+ \t/**
112
+ \t * Upsert a local profile keyed by JWT \`sub\`.
113
+ \t * Profile fields from the token fill gaps; seeded rows keep displayName/email/roles.
114
+ \t */
115
+ \tpublic async ensureFromPrincipal(principal: Principal | undefined): Promise<UserEntity> {
116
+ \t\tconst sub = principal?.sub;
117
+ \t\tif (!sub) {
118
+ \t\t\tthrow new Error("Authenticated request is missing principal.sub");
119
+ \t\t}
120
+
121
+ \t\tconst email =
122
+ \t\t\tclaimString(principal, "email") ?? claimString(principal, "preferred_username");
123
+ \t\tconst displayFromToken =
124
+ \t\t\tclaimString(principal, "name") ??
125
+ \t\t\tclaimString(principal, "nickname") ??
126
+ \t\t\temail;
127
+
128
+ \t\tconst existing = await this.users.findBySub(sub);
129
+ \t\tif (existing) {
130
+ \t\t\tlet dirty = false;
131
+ \t\t\tif (!existing.email && email) {
132
+ \t\t\t\texisting.email = email;
133
+ \t\t\t\tdirty = true;
134
+ \t\t\t}
135
+ \t\t\tif (existing.displayName === existing.sub && displayFromToken) {
136
+ \t\t\t\texisting.displayName = displayFromToken;
137
+ \t\t\t\tdirty = true;
138
+ \t\t\t}
139
+ \t\t\treturn dirty ? this.users.save(existing) : existing;
140
+ \t\t}
141
+
142
+ \t\tconst created = new UserEntity();
143
+ \t\tcreated.sub = sub;
144
+ \t\tcreated.email = email ?? null;
145
+ \t\tcreated.displayName = displayFromToken ?? sub;
146
+ \t\tcreated.roles = Array.isArray(principal.roles)
147
+ \t\t\t? principal.roles.filter((r): r is string => typeof r === "string")
148
+ \t\t\t: [];
149
+ \t\treturn this.users.save(created);
150
+ \t}
151
+ }
152
+ `;
153
+ const ABILITY_FACTORY_TS = `import type { Principal } from "@decohere/http";
154
+ import {
155
+ \tADMIN_ROLE,
156
+ \tcreatePrincipalFromPayload,
157
+ \tdefineAbilitiesFor as defaultDefineAbilitiesFor,
158
+ } from "@decohere/http";
159
+
160
+ /**
161
+ * Customize CASL authorization here (product-specific rules).
162
+ *
163
+ * Default delegates to \`@decohere/http\`: \`admin\` role → \`manage\`/\`all\`; IdP scopes
164
+ * (\`action:Subject\`) → abilities. Replace the body when you need app rules
165
+ * beyond that — this file is the intentional extension point, not framework
166
+ * infrastructure to copy.
167
+ */
168
+ export function defineAbilitiesFor(principal: Principal) {
169
+ \treturn defaultDefineAbilitiesFor(principal);
170
+ }
171
+
172
+ /**
173
+ * Customize JWT → Principal mapping here (IdP-specific claims).
174
+ *
175
+ * Elevates \`AUTH0_ADMIN_USER_SUB\` to the admin role so the seeded admin works
176
+ * before/without roles on the access token.
177
+ */
178
+ export function mapPrincipal(payload: Record<string, unknown>): Principal {
179
+ \tconst principal = createPrincipalFromPayload(payload);
180
+ \tconst sub = principal.sub;
181
+ \tif (sub && sub === process.env.AUTH0_ADMIN_USER_SUB) {
182
+ \t\treturn {
183
+ \t\t\t...principal,
184
+ \t\t\troles: [...new Set([ADMIN_ROLE, ...(principal.roles ?? [])])],
185
+ \t\t};
186
+ \t}
187
+ \treturn principal;
188
+ }
189
+ `;
190
+ async function getOptions(workspace, cliOptions) {
191
+ if (cliOptions.authProvider === "auth0" && !cliOptions.issuerBaseUrl) {
192
+ return getAuth0Options(workspace, cliOptions);
193
+ }
194
+ if (cliOptions.authProvider != null) return {};
195
+ const choices = [
196
+ {
197
+ label: "none"
198
+ },
199
+ {
200
+ label: "auth0"
201
+ }
202
+ ];
203
+ const selected = await (0, _prompt.pick)("\n Auth provider: ", choices, (c)=>c.label);
204
+ const provider = selected?.label ?? "none";
205
+ if (provider !== "auth0") return {
206
+ authProvider: "none"
207
+ };
208
+ return getAuth0Options(workspace, cliOptions);
209
+ }
210
+ async function getAuth0Options(workspace, cliOptions) {
211
+ const active = (0, _auth0commands.ensureLoggedIn)();
212
+ const issuerBaseUrl = `https://${active.name}`;
213
+ console.log(`\n ✓ Auth0 tenant: ${issuerBaseUrl}`);
214
+ process.stdout.write("\n Fetching Auth0 APIs...");
215
+ const userApis = _auth0cli.listApis().filter((a)=>!a.name.includes("Management API"));
216
+ process.stdout.write(` found ${userApis.length}.\n`);
217
+ const projectName = workspace.name;
218
+ const apiChoices = [
219
+ {
220
+ label: `<new> https://api.${projectName}`,
221
+ existing: null
222
+ },
223
+ ...userApis.map((a)=>({
224
+ label: `${a.name} — ${a.identifier}`,
225
+ existing: a.identifier
226
+ }))
227
+ ];
228
+ const apiSelected = await (0, _prompt.pick)("\n Select Auth0 API: ", apiChoices, (c)=>c.label);
229
+ let audience;
230
+ if (!apiSelected || apiSelected.existing === null) {
231
+ const defaultId = `https://api.${projectName}`;
232
+ audience = await (0, _prompt.ask)(` API Identifier [${defaultId}]: `, defaultId);
233
+ console.log("\n Creating Auth0 API...");
234
+ audience = _auth0cli.createApi(`${projectName} API`, audience).identifier;
235
+ console.log(` \u2713 API created: ${audience}`);
236
+ } else {
237
+ audience = apiSelected.existing;
238
+ console.log(` \u2713 Selected: ${audience}`);
239
+ try {
240
+ _auth0cli.updateApiScopes(audience);
241
+ console.log(` \u2713 API scopes ensured (${_auth0cli.HELLO_API_SCOPES.join(", ")})`);
242
+ } catch {
243
+ console.error(" ✗ Failed to update API scopes — add Hello scopes in the Auth0 dashboard.");
244
+ }
245
+ }
246
+ const withLocalUsers = cliOptions.databaseProvider === "postgres/typeorm";
247
+ let adminUsername;
248
+ let adminPassword;
249
+ if (withLocalUsers) {
250
+ adminUsername = cliOptions.adminUsername ?? await (0, _prompt.ask)("\n Admin username [admin]: ", "admin");
251
+ adminPassword = cliOptions.adminPassword;
252
+ if (adminPassword == null) {
253
+ adminPassword = await (0, _prompt.ask)(" Admin password (leave blank to generate): ");
254
+ if (!adminPassword) {
255
+ adminPassword = _auth0cli.generateAdminPassword();
256
+ console.log(` ✓ Generated admin password (written to .env): ${adminPassword}`);
257
+ }
258
+ }
259
+ }
260
+ return {
261
+ authProvider: "auth0",
262
+ issuerBaseUrl,
263
+ audience,
264
+ ...withLocalUsers ? {
265
+ adminUsername,
266
+ adminPassword
267
+ } : {}
268
+ };
269
+ }
270
+ function adminEmail(workspaceName, username) {
271
+ if (username.includes("@")) return username;
272
+ const slug = workspaceName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
273
+ return `${username}@${slug}.example.com`;
274
+ }
275
+ function createAuth0AdminUser(workspace, options, authConfig) {
276
+ const username = options.adminUsername ?? "admin";
277
+ const password = options.adminPassword;
278
+ if (!password) return;
279
+ const email = adminEmail(workspace.name, username);
280
+ try {
281
+ const admin = _auth0cli.createAdminDatabaseUser({
282
+ email,
283
+ password,
284
+ username
285
+ });
286
+ authConfig.setAdminUser({
287
+ sub: admin.user_id,
288
+ email: admin.email,
289
+ password: admin.password,
290
+ username: admin.username
291
+ });
292
+ workspace.getModel(_const.WELL_KNOWN_FILES.ENV_FILE)?.addEntry("AUTH0_ADMIN_USER_SUB", admin.user_id).addEntry("AUTH0_ADMIN_USER_EMAIL", admin.email).addEntry("AUTH0_ADMIN_USER_PASSWORD", admin.password).addEntry("AUTH0_ADMIN_USERNAME", admin.username);
293
+ console.log(` ✓ Auth0 admin user ready: ${admin.email} (role: ${ADMIN_ROLE})`);
294
+ console.log(` Login with EMAIL (not username): ${admin.email}`);
295
+ console.log(` Password: see AUTH0_ADMIN_USER_PASSWORD in .env`);
296
+ } catch (err) {
297
+ console.error(` ✗ Failed to ensure Auth0 admin user (${email}): ${err instanceof Error ? err.message : err}`);
298
+ }
299
+ }
300
+ function scaffoldLocalUserProfile(workspace) {
301
+ for (const project of workspace.getProjects()){
302
+ project.getModel(_const.WELL_KNOWN_FILES.MAIN_TS)?.addEntity({
303
+ className: "UserEntity",
304
+ importPath: "./entities/user.entity.js"
305
+ }).addRepository({
306
+ className: "UserEntityRepository",
307
+ importPath: "./repositories/user.repository.js"
308
+ }).addService({
309
+ className: "UserService",
310
+ importPath: "./services/user.service.js",
311
+ scope: "request"
312
+ }).addImport("defineAbilitiesFor", "./auth/ability.factory.js").addImport("mapPrincipal", "./auth/ability.factory.js").setTypeOrmSeed([
313
+ "const sub = process.env.AUTH0_ADMIN_USER_SUB;",
314
+ "if (!sub) return;",
315
+ "const repo = dataSource.getRepository(UserEntity);",
316
+ "const existing = await repo.findOne({ where: { sub } });",
317
+ "if (existing) return;",
318
+ "await repo.save(repo.create({",
319
+ " sub,",
320
+ " email: process.env.AUTH0_ADMIN_USER_EMAIL ?? null,",
321
+ ' displayName: process.env.AUTH0_ADMIN_USERNAME ?? "admin",',
322
+ ' roles: ["admin"],',
323
+ "}));"
324
+ ].join(" "));
325
+ project.addFile({
326
+ files: ()=>[
327
+ {
328
+ filePath: "src/entities/user.entity.ts",
329
+ content: USER_ENTITY_TS
330
+ },
331
+ {
332
+ filePath: "src/repositories/user.repository.ts",
333
+ content: USER_REPOSITORY_TS
334
+ },
335
+ {
336
+ filePath: "src/services/user.service.ts",
337
+ content: USER_SERVICE_TS
338
+ },
339
+ {
340
+ filePath: "src/auth/ability.factory.ts",
341
+ content: ABILITY_FACTORY_TS
342
+ }
343
+ ]
344
+ });
345
+ }
346
+ for (const controller of workspace.getModels(_controllersourcefilemodel.ControllerSourceFile)){
347
+ controller.addImport("UserService", "../services/user.service.js").addImport("inject", "@decohere/crucible/decorators").addConstructorParam("@inject(UserService) private readonly users: UserService");
348
+ controller.getMethod("getOne").setAsync(true).setBody([
349
+ "const user = await this.users.ensureFromPrincipal(this.principalProvider.currentPrincipal);",
350
+ "return { id, message: `Hello ${user.displayName}!`, username: user.displayName };"
351
+ ]);
352
+ }
353
+ }
354
+ function scaffold(workspace, options) {
355
+ if (options.authProvider !== "auth0") return;
356
+ const resource = "Hello";
357
+ const withLocalUsers = options.databaseProvider === "postgres/typeorm";
358
+ for (const controller of workspace.getModels(_controllersourcefilemodel.ControllerSourceFile)){
359
+ controller.addDecorator(`@Authorize("read", "${resource}")`).addCortexImport("AllowAnonymous").addCortexImport("Authorize").addCortexImport("IPrincipalProviderToken").addCortexImport("IPrincipalProvider", true).addImport("inject", "@decohere/crucible/decorators").addConstructorParam(`@inject(IPrincipalProviderToken) private readonly principalProvider: IPrincipalProvider`);
360
+ controller.getMethod("getAll").addDecorator("@AllowAnonymous()");
361
+ if (!withLocalUsers) {
362
+ controller.getMethod("getOne").setBody([
363
+ "const principal = this.principalProvider.currentPrincipal;",
364
+ "const username =",
365
+ '\t(typeof principal?.name === "string" && principal.name) ||',
366
+ '\t(typeof principal?.nickname === "string" && principal.nickname) ||',
367
+ '\t(typeof principal?.email === "string" && principal.email) ||',
368
+ '\t(typeof principal?.preferred_username === "string" && principal.preferred_username) ||',
369
+ "\tprincipal?.sub ||",
370
+ '\t"unknown";',
371
+ "return { id, message: `Hello ${username}!`, username };"
372
+ ]);
373
+ }
374
+ controller.getMethod("create").addDecorator(`@Authorize("create", "${resource}")`);
375
+ controller.getMethod("update").addDecorator(`@Authorize("update", "${resource}")`);
376
+ controller.getMethod("remove").addDecorator(`@Authorize("delete", "${resource}")`);
377
+ }
378
+ for (const project of workspace.getProjects()){
379
+ project.getModel(_const.WELL_KNOWN_FILES.MAIN_TS)?.addSideEffectImport("@decohere/http/auth");
380
+ }
381
+ if (withLocalUsers) {
382
+ scaffoldLocalUserProfile(workspace);
383
+ for (const project of workspace.getProjects()){
384
+ project.getModel(_const.WELL_KNOWN_FILES.MAIN_TS)?.addBuilderCall(".withAuth(defineAbilitiesFor, { principalMapper: mapPrincipal })");
385
+ }
386
+ } else {
387
+ for (const project of workspace.getProjects()){
388
+ project.getModel(_const.WELL_KNOWN_FILES.MAIN_TS)?.addBuilderCall(".withAuth()");
389
+ }
390
+ }
391
+ const issuerPlaceholder = "${AUTH0_ISSUER_BASE_URL}";
392
+ const audPlaceholder = "${AUTH0_AUDIENCE}";
393
+ const configSection = `auth:\n discoveryUrl: ${issuerPlaceholder}\n audience: ${audPlaceholder}\n`;
394
+ workspace.getModel(_const.WELL_KNOWN_FILES.CONFIG_YAML)?.addSection(configSection);
395
+ workspace.getModel(_const.WELL_KNOWN_FILES.ENV_FILE)?.addEntry("AUTH0_ISSUER_BASE_URL", options.issuerBaseUrl ?? "").addEntry("AUTH0_AUDIENCE", options.audience ?? "");
396
+ const authConfig = new _authconfigmodel.AuthConfigModel({
397
+ issuerBaseUrl: options.issuerBaseUrl ?? "",
398
+ audience: options.audience ?? ""
399
+ });
400
+ workspace.addModel(authConfig);
401
+ if (withLocalUsers && options.adminPassword) {
402
+ createAuth0AdminUser(workspace, options, authConfig);
403
+ }
404
+ }
405
+ const AuthFeature = {
406
+ getOptions,
407
+ scaffold
408
+ };