@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,421 +0,0 @@
1
- import { execFileSync, spawnSync } from "node:child_process";
2
- import { randomBytes } from "node:crypto";
3
-
4
- /** Management API scopes needed for API, SPA, connections, and admin users. */
5
- const REQUIRED_SCOPES = [
6
- "create:clients",
7
- "read:clients",
8
- "update:clients",
9
- "create:resource_servers",
10
- "read:resource_servers",
11
- "update:resource_servers",
12
- "read:connections",
13
- "update:connections",
14
- "create:users",
15
- "read:users",
16
- "update:users",
17
- ].join(",");
18
-
19
- /** Hard timeout for any Management API call. Prevents indefinite hangs on token re-auth. */
20
- const API_TIMEOUT_MS = 8_000;
21
-
22
- /** Default API scopes matching Hello CASL Authorize decorators. */
23
- export const HELLO_API_SCOPES = [
24
- "read:Hello",
25
- "create:Hello",
26
- "update:Hello",
27
- "delete:Hello",
28
- ] as const;
29
-
30
- export const DB_CONNECTION_NAME = "Username-Password-Authentication";
31
-
32
- export class Auth0AuthError extends Error {
33
- public constructor(message: string) {
34
- super(message);
35
- this.name = "Auth0AuthError";
36
- }
37
- }
38
-
39
- /** Returns true if an error or stderr output looks like an expired/missing token. */
40
- function isAuthError(text: string): boolean {
41
- return (
42
- text.includes("renew access token") ||
43
- text.includes("log in") ||
44
- text.includes("Unauthorized")
45
- );
46
- }
47
-
48
- /** Strip Auth0 CLI banner lines before JSON. */
49
- function parseJsonOutput<T>(stdout: string): T {
50
- const start = stdout.indexOf("{");
51
- const arrayStart = stdout.indexOf("[");
52
- let idx = -1;
53
- if (start === -1) idx = arrayStart;
54
- else if (arrayStart === -1) idx = start;
55
- else idx = Math.min(start, arrayStart);
56
- if (idx < 0) throw new Error(`Expected JSON from auth0 CLI, got: ${stdout.slice(0, 200)}`);
57
- return JSON.parse(stdout.slice(idx)) as T;
58
- }
59
-
60
- /** Run an auth0 CLI command with a hard timeout. Returns stdout on success, throws on failure. */
61
- function runAuth0(args: string[]): string {
62
- const result = spawnSync("auth0", args, {
63
- encoding: "utf8",
64
- timeout: API_TIMEOUT_MS,
65
- input: "",
66
- stdio: "pipe",
67
- });
68
-
69
- if (result.error) {
70
- const msg = result.error.message;
71
- if (isAuthError(msg) || (result.error as NodeJS.ErrnoException).code === "ETIMEDOUT") {
72
- throw new Auth0AuthError(msg);
73
- }
74
- throw result.error;
75
- }
76
-
77
- if (result.status !== 0) {
78
- const stderr = result.stderr ?? "";
79
- if (isAuthError(stderr)) throw new Auth0AuthError(stderr);
80
- throw new Error(stderr || `auth0 exited with code ${result.status}`);
81
- }
82
-
83
- return result.stdout;
84
- }
85
-
86
- export interface Auth0Tenant {
87
- name: string;
88
- active?: boolean;
89
- }
90
-
91
- export function isInstalled(): boolean {
92
- try {
93
- execFileSync("auth0", ["--version"], { stdio: "pipe" });
94
- return true;
95
- } catch {
96
- return false;
97
- }
98
- }
99
-
100
- export function listTenants(): Auth0Tenant[] {
101
- try {
102
- const out = execFileSync("auth0", ["tenants", "list", "--json"], {
103
- encoding: "utf8",
104
- stdio: "pipe",
105
- });
106
- return JSON.parse(out) as Auth0Tenant[];
107
- } catch {
108
- return [];
109
- }
110
- }
111
-
112
- export function login(): boolean {
113
- const result = spawnSync("auth0", ["login", "--scopes", REQUIRED_SCOPES], {
114
- stdio: "inherit",
115
- });
116
- return result.status === 0;
117
- }
118
-
119
- export function listApis(): Array<{ identifier: string; name: string }> {
120
- try {
121
- const out = runAuth0(["apis", "list", "--json", "--no-input"]);
122
- return parseJsonOutput(out);
123
- } catch (err) {
124
- if (err instanceof Auth0AuthError) throw err;
125
- return [];
126
- }
127
- }
128
-
129
- export function createApi(
130
- name: string,
131
- identifier: string,
132
- scopes: readonly string[] = HELLO_API_SCOPES,
133
- ): { identifier: string } {
134
- const out = runAuth0([
135
- "apis",
136
- "create",
137
- "--name",
138
- name,
139
- "--identifier",
140
- identifier,
141
- "--scopes",
142
- scopes.join(","),
143
- // RBAC: only permissions assigned to the user/role appear on the token.
144
- "--enforce-policies",
145
- // Include Auth0 `permissions[]` claim (consumed by sample principalMapper).
146
- "--token-dialect",
147
- "access_token_authz",
148
- "--json",
149
- "--no-input",
150
- ]);
151
- return parseJsonOutput(out);
152
- }
153
-
154
- /**
155
- * Replace an API's scopes and enable RBAC policy enforcement.
156
- * Auth0 CLI `--scopes` replaces the full set.
157
- */
158
- export function updateApiScopes(
159
- identifier: string,
160
- scopes: readonly string[] = HELLO_API_SCOPES,
161
- ): void {
162
- runAuth0([
163
- "apis",
164
- "update",
165
- identifier,
166
- "--scopes",
167
- scopes.join(","),
168
- "--enforce-policies",
169
- "--token-dialect",
170
- "access_token_authz",
171
- "--no-input",
172
- ]);
173
- }
174
-
175
- /** True when Auth0 refuses create because the tenant hit its Applications quota. */
176
- export function isAuth0EntityLimitError(err: unknown): boolean {
177
- const text = err instanceof Error ? err.message : String(err);
178
- return /limit of entities|reached the limit|tenant application limit reached/i.test(text);
179
- }
180
-
181
- /**
182
- * Actionable message when Auth0 will not create another application.
183
- * Does not suggest reusing unrelated apps — free tenants must delete unused ones.
184
- */
185
- export function formatSpaEntityLimitError(appName: string, apps: Auth0App[]): string {
186
- const listed =
187
- apps.length === 0
188
- ? "Could not list existing applications."
189
- : `This tenant already has ${apps.length} application(s):\n${apps
190
- .map((a) => ` - ${a.name} (${a.client_id})`)
191
- .join("\n")}`;
192
-
193
- return [
194
- `Auth0 refused to create SPA "${appName}": tenant application limit reached.`,
195
- listed,
196
- "",
197
- "Fix: Auth0 Dashboard → Applications → Applications → delete unused apps,",
198
- `then re-run the scaffold (or choose Postman "none" to skip SPA creation).`,
199
- "Re-scaffold of the same project name reuses an exact-name match when it still exists.",
200
- ].join("\n");
201
- }
202
-
203
- export function createSpaApp(
204
- name: string,
205
- callbackUrls: string | readonly string[],
206
- ): { client_id: string } {
207
- const callbacks = typeof callbackUrls === "string" ? callbackUrls : callbackUrls.join(",");
208
- const out = runAuth0([
209
- "apps",
210
- "create",
211
- "--name",
212
- name,
213
- "--type",
214
- "spa",
215
- "--callbacks",
216
- callbacks,
217
- "--json",
218
- "--no-input",
219
- ]);
220
- const app = parseJsonOutput<{ client_id?: string; clientId?: string }>(out);
221
- const clientId = app.client_id ?? app.clientId;
222
- if (!clientId) {
223
- throw new Error(
224
- `Auth0 create SPA response missing client_id. stdout: ${out.slice(0, 300)}`,
225
- );
226
- }
227
- return { client_id: clientId };
228
- }
229
-
230
- export type Auth0App = { client_id: string; name: string };
231
-
232
- export function listApps(): Auth0App[] {
233
- try {
234
- const out = runAuth0(["apps", "list", "--json", "--no-input"]);
235
- const apps =
236
- parseJsonOutput<Array<{ client_id?: string; clientId?: string; name?: string }>>(out);
237
- return apps
238
- .map((a) => ({
239
- client_id: a.client_id ?? a.clientId ?? "",
240
- name: a.name ?? "",
241
- }))
242
- .filter((a) => a.client_id && a.name);
243
- } catch (err) {
244
- if (err instanceof Auth0AuthError) throw err;
245
- return [];
246
- }
247
- }
248
-
249
- export function updateSpaCallbacks(
250
- clientId: string,
251
- callbackUrls: string | readonly string[],
252
- ): void {
253
- const callbacks = typeof callbackUrls === "string" ? callbackUrls : callbackUrls.join(",");
254
- runAuth0(["apps", "update", clientId, "--callbacks", callbacks, "--no-input"]);
255
- }
256
-
257
- /**
258
- * Create a Postman SPA, or reuse an existing app with the same name (re-scaffold).
259
- * Always returns a client_id suitable for Postman auth0ClientId.
260
- * On Auth0 application-quota errors, throws a clear delete-apps message (no cross-app reuse).
261
- */
262
- export function ensureSpaApp(
263
- name: string,
264
- callbackUrls: string | readonly string[],
265
- ): { client_id: string; reused: boolean } {
266
- const apps = listApps();
267
- const existing = apps.find((a) => a.name === name);
268
- if (existing) {
269
- try {
270
- updateSpaCallbacks(existing.client_id, callbackUrls);
271
- } catch {
272
- // Callbacks may already match; still usable for Postman.
273
- }
274
- return { client_id: existing.client_id, reused: true };
275
- }
276
- try {
277
- return { ...createSpaApp(name, callbackUrls), reused: false };
278
- } catch (err) {
279
- if (isAuth0EntityLimitError(err)) {
280
- throw new Error(formatSpaEntityLimitError(name, apps));
281
- }
282
- throw err;
283
- }
284
- }
285
-
286
- type Auth0Connection = { id: string; name: string; strategy: string };
287
-
288
- /** Enable a database connection for an application (required for username/password login). */
289
- export function enableConnectionForClient(connectionName: string, clientId: string): void {
290
- const listOut = runAuth0(["api", "get", "connections", "--no-input"]);
291
- const connections = parseJsonOutput<Auth0Connection[]>(listOut);
292
- const connection = connections.find((c) => c.name === connectionName);
293
- if (!connection) {
294
- throw new Error(`Auth0 connection not found: ${connectionName}`);
295
- }
296
-
297
- const clientsOut = runAuth0([
298
- "api",
299
- "get",
300
- `connections/${connection.id}/clients`,
301
- "--no-input",
302
- ]);
303
- const { clients } = parseJsonOutput<{ clients: Array<{ client_id: string }> }>(clientsOut);
304
- if (clients.some((c) => c.client_id === clientId)) return;
305
-
306
- // Dedicated enabled-clients endpoint (enabled_clients on connection PATCH is deprecated).
307
- runAuth0([
308
- "api",
309
- "patch",
310
- `connections/${connection.id}/clients`,
311
- "--data",
312
- JSON.stringify([{ client_id: clientId, status: true }]),
313
- "--no-input",
314
- ]);
315
- }
316
-
317
- export type AdminDatabaseUser = {
318
- user_id: string;
319
- email: string;
320
- password: string;
321
- username: string;
322
- };
323
-
324
- /** Strong default password when the scaffolder caller leaves password blank. */
325
- export function generateAdminPassword(): string {
326
- return `${randomBytes(12).toString("base64url")}aA1!`;
327
- }
328
-
329
- function isUserAlreadyExistsError(err: unknown): boolean {
330
- const text = err instanceof Error ? err.message : String(err);
331
- return /already exists|user exists|Conflict|409/i.test(text);
332
- }
333
-
334
- /** Look up a user by email (Management API users-by-email). */
335
- export function findUserByEmail(email: string): { user_id: string; email: string } | null {
336
- const out = runAuth0([
337
- "api",
338
- "get",
339
- `users-by-email?email=${encodeURIComponent(email)}`,
340
- "--no-input",
341
- ]);
342
- const users = parseJsonOutput<Array<{ user_id?: string; email?: string }>>(out);
343
- const user = users.find((u) => u.user_id && u.email);
344
- if (!user?.user_id || !user.email) return null;
345
- return { user_id: user.user_id, email: user.email };
346
- }
347
-
348
- /** Reset password so a re-scaffold can write a known password to .env. */
349
- export function updateUserPassword(userId: string, password: string): void {
350
- runAuth0([
351
- "api",
352
- "patch",
353
- `users/${encodeURIComponent(userId)}`,
354
- "--data",
355
- JSON.stringify({ password, connection: DB_CONNECTION_NAME }),
356
- "--no-input",
357
- ]);
358
- }
359
-
360
- /**
361
- * Create a DB connection root-admin user for local / Postman login.
362
- * If the user already exists, look them up, reset password to `input.password`,
363
- * and return that identity so callers can still write .env credentials.
364
- */
365
- export function createAdminDatabaseUser(input: {
366
- email: string;
367
- password: string;
368
- username: string;
369
- }): AdminDatabaseUser {
370
- const body = {
371
- email: input.email,
372
- connection: DB_CONNECTION_NAME,
373
- password: input.password,
374
- name: input.username,
375
- email_verified: true,
376
- verify_email: false,
377
- app_metadata: { roles: ["admin"] },
378
- };
379
-
380
- try {
381
- const out = runAuth0([
382
- "api",
383
- "post",
384
- "users",
385
- "--data",
386
- JSON.stringify(body),
387
- "--no-input",
388
- ]);
389
- const user = parseJsonOutput<{ user_id?: string; email?: string }>(out);
390
- if (!user.user_id || !user.email) {
391
- throw new Error(
392
- `Auth0 create user response missing user_id/email. stdout: ${out.slice(0, 300)}`,
393
- );
394
- }
395
- return {
396
- user_id: user.user_id,
397
- email: user.email,
398
- password: input.password,
399
- username: input.username,
400
- };
401
- } catch (err) {
402
- if (!isUserAlreadyExistsError(err)) throw err;
403
-
404
- const existing = findUserByEmail(input.email);
405
- if (!existing) {
406
- throw new Error(
407
- `Auth0 user already exists for ${input.email}, but lookup by email returned no user.`,
408
- );
409
- }
410
-
411
- updateUserPassword(existing.user_id, input.password);
412
- return {
413
- user_id: existing.user_id,
414
- email: existing.email,
415
- password: input.password,
416
- username: input.username,
417
- };
418
- }
419
- }
420
-
421
- export { REQUIRED_SCOPES };
@@ -1,34 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
-
3
- /**
4
- * Format/lint-fix all files in the scaffolded workspace with Biome.
5
- * Prefer a global `biome` binary (builder image); fall back to `npx`.
6
- */
7
- export function formatScaffoldedFiles(targetDir: string, paths: string[] = ["."]): void {
8
- process.stdout.write("\n Formatting scaffolded files with Biome...");
9
-
10
- let result = spawnSync("biome", ["check", "--write", ...paths], {
11
- cwd: targetDir,
12
- encoding: "utf8",
13
- stdio: "pipe",
14
- });
15
-
16
- if (result.error && (result.error as NodeJS.ErrnoException).code === "ENOENT") {
17
- result = spawnSync("npx", ["--yes", "@biomejs/biome", "check", "--write", ...paths], {
18
- cwd: targetDir,
19
- encoding: "utf8",
20
- stdio: "pipe",
21
- });
22
- }
23
-
24
- if (result.status === 0) {
25
- process.stdout.write(" done.\n");
26
- return;
27
- }
28
-
29
- const detail = (result.stderr || result.stdout || result.error?.message || "").trim();
30
- process.stdout.write(" failed.\n");
31
- if (detail) {
32
- console.error(` ✗ Biome: ${detail.slice(0, 400)}`);
33
- }
34
- }