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

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 +156 -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 +265 -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 +141 -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 +245 -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,263 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { resetMemfs } from "@decohere/nexus/testing";
4
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
- import { seedDevcontainerTemplates } from "./seed-devcontainer-templates.js";
6
-
7
- /**
8
- * Feature-level coverage for Auth0 + Postman wiring.
9
- *
10
- * webapi.spec.ts only asserts install behaviour with authProvider:"none" and
11
- * stubs auth0-cli as isInstalled:false — so missing .env / empty auth0ClientId
12
- * on re-scaffold never failed the suite.
13
- */
14
-
15
- const {
16
- mockAsk,
17
- mockPick,
18
- mockInstall,
19
- mockCreateAdminDatabaseUser,
20
- mockEnsureSpaApp,
21
- mockEnableConnectionForClient,
22
- mockIsInstalled,
23
- } = vi.hoisted(() => ({
24
- mockAsk: vi.fn((_q: string, fallback = "") => Promise.resolve(fallback)),
25
- mockPick: vi.fn(
26
- <T>(_label: string, items: T[], _display: (item: T) => string): Promise<T | null> =>
27
- Promise.resolve(items[0] ?? null),
28
- ),
29
- mockInstall: vi.fn().mockResolvedValue({ message: "", data: [] }),
30
- mockCreateAdminDatabaseUser: vi.fn(),
31
- mockEnsureSpaApp: vi.fn(),
32
- mockEnableConnectionForClient: vi.fn(),
33
- mockIsInstalled: vi.fn(() => true),
34
- }));
35
-
36
- const mockPm = {
37
- name: "pnpm" as const,
38
- install: mockInstall,
39
- syncLockFile: vi.fn().mockResolvedValue({ message: "", data: [] }),
40
- };
41
-
42
- vi.mock("../src/domain/prompt.js", async () => {
43
- const actual = await import("../src/domain/prompt.js");
44
- return { ...actual, ask: mockAsk, pick: mockPick };
45
- });
46
-
47
- vi.mock(import("../../nexus/src/interop/index.js"), async (importOriginal) => {
48
- const actual = await importOriginal();
49
- return {
50
- ...actual,
51
- detectPackageManager: () => mockPm,
52
- promptForPackageManager: () => Promise.resolve(mockPm),
53
- };
54
- });
55
-
56
- vi.mock(import("../src/webapi/features/auth0-cli.js"), async (importOriginal) => {
57
- const actual = await importOriginal();
58
- return {
59
- ...actual,
60
- isInstalled: mockIsInstalled,
61
- login: () => true,
62
- listTenants: () => [{ name: "dev-tenant.us.auth0.com", active: true }],
63
- listApis: () => [{ name: "webapi API", identifier: "https://api.webapi" }],
64
- updateApiScopes: vi.fn(),
65
- createAdminDatabaseUser: mockCreateAdminDatabaseUser,
66
- ensureSpaApp: mockEnsureSpaApp,
67
- createSpaApp: vi.fn(() => {
68
- throw new Error("createSpaApp should not be called — use ensureSpaApp");
69
- }),
70
- enableConnectionForClient: mockEnableConnectionForClient,
71
- generateAdminPassword: () => "Generated1!",
72
- };
73
- });
74
-
75
- vi.mock("../src/webapi/features/biome-format.js", () => ({
76
- formatScaffoldedFiles: vi.fn(),
77
- }));
78
-
79
- const { scaffoldApi } = await import("../src/webapi/create-api.js");
80
-
81
- const TARGET = "/my-api";
82
-
83
- /** Extra Auth0 fields accepted via the shared options bag (AuthFeature). */
84
- type ScaffoldOpts = Parameters<typeof scaffoldApi>[0] & {
85
- issuerBaseUrl?: string;
86
- audience?: string;
87
- adminUsername?: string;
88
- adminPassword?: string;
89
- };
90
-
91
- function read(rel: string): string {
92
- return readFileSync(join(TARGET, rel), "utf8");
93
- }
94
-
95
- const baseOpts = (): ScaffoldOpts => ({
96
- d: TARGET,
97
- databaseProvider: "postgres/typeorm",
98
- authProvider: "auth0",
99
- issuerBaseUrl: "https://dev-tenant.us.auth0.com",
100
- audience: "https://api.webapi",
101
- adminUsername: "admin",
102
- adminPassword: "Generated1!",
103
- postmanProvider: "local",
104
- installDependencies: "no",
105
- });
106
-
107
- beforeEach(() => {
108
- vi.spyOn(process, "cwd").mockReturnValue("/");
109
- process.env.DECOHERE_BUILDER = "1";
110
- resetMemfs();
111
- seedDevcontainerTemplates();
112
- mockInstall.mockClear();
113
- mockAsk.mockClear();
114
- mockCreateAdminDatabaseUser.mockReset();
115
- mockEnsureSpaApp.mockReset();
116
- mockEnableConnectionForClient.mockReset();
117
-
118
- mockCreateAdminDatabaseUser.mockReturnValue({
119
- user_id: "auth0|admin",
120
- email: "admin@my-api.example.com",
121
- password: "Generated1!",
122
- username: "admin",
123
- });
124
- mockEnsureSpaApp.mockReturnValue({ client_id: "spa_client_123", reused: false });
125
- });
126
-
127
- afterEach(() => {
128
- resetMemfs();
129
- vi.restoreAllMocks();
130
- delete process.env.DECOHERE_BUILDER;
131
- });
132
-
133
- describe("scaffoldApi auth0 + postman wiring", () => {
134
- it("writes AUTH0_ADMIN_* to .env and auth0ClientId into the Postman environment", async () => {
135
- await scaffoldApi(baseOpts());
136
-
137
- const env = read(".env");
138
- expect(env).toContain("AUTH0_ADMIN_USER_SUB=auth0|admin");
139
- expect(env).toContain("AUTH0_ADMIN_USER_EMAIL=admin@my-api.example.com");
140
- expect(env).toContain("AUTH0_ADMIN_USER_PASSWORD=Generated1!");
141
- expect(env).toContain("AUTH0_ADMIN_USERNAME=admin");
142
-
143
- const postmanEnv = read("postman/environments/my-api-Local.environment.yaml");
144
- expect(postmanEnv).toMatch(/key: auth0ClientId\n\s+value: spa_client_123/);
145
- expect(mockEnsureSpaApp).toHaveBeenCalledWith(
146
- "my-api Postman",
147
- expect.arrayContaining(["https://oauth.pstmn.io/v1/callback"]),
148
- );
149
- expect(mockCreateAdminDatabaseUser).toHaveBeenCalledOnce();
150
-
151
- const readme = read("README.md");
152
- expect(readme).toContain("## Auth0");
153
- expect(readme).toContain("AUTH0_ISSUER_BASE_URL");
154
- expect(readme).toContain("## Postman");
155
- expect(readme).toContain("Local View");
156
- });
157
-
158
- it("still writes admin env when Auth0 returns an existing user identity", async () => {
159
- mockCreateAdminDatabaseUser.mockReturnValue({
160
- user_id: "auth0|existing",
161
- email: "admin@my-api.example.com",
162
- password: "Generated1!",
163
- username: "admin",
164
- });
165
-
166
- await scaffoldApi(baseOpts());
167
-
168
- const env = read(".env");
169
- expect(env).toContain("AUTH0_ADMIN_USER_SUB=auth0|existing");
170
- expect(env).toContain("AUTH0_ADMIN_USER_PASSWORD=Generated1!");
171
- });
172
-
173
- it("writes a non-empty auth0ClientId when the SPA app is reused", async () => {
174
- mockEnsureSpaApp.mockReturnValue({ client_id: "spa_reused", reused: true });
175
-
176
- await scaffoldApi(baseOpts());
177
-
178
- const postmanEnv = read("postman/environments/my-api-Local.environment.yaml");
179
- expect(postmanEnv).toMatch(/key: auth0ClientId\n\s+value: spa_reused/);
180
- expect(postmanEnv).not.toMatch(/key: auth0ClientId\n\s+value: ""/);
181
-
182
- const collectionAuth = read(
183
- "postman/collections/my-api WebAPI/.resources/definition.yaml",
184
- );
185
- expect(collectionAuth).toMatch(/clientId:\s*spa_reused/);
186
- });
187
-
188
- it("preserves existing auth0ClientId when ensureSpaApp fails on re-scaffold", async () => {
189
- await scaffoldApi(baseOpts());
190
-
191
- mockEnsureSpaApp.mockImplementation(() => {
192
- throw new Error("auth0 apps create failed: Unauthorized");
193
- });
194
-
195
- await scaffoldApi(baseOpts());
196
-
197
- const postmanEnv = read("postman/environments/my-api-Local.environment.yaml");
198
- expect(postmanEnv).toMatch(/key: auth0ClientId\n\s+value: spa_client_123/);
199
- expect(postmanEnv).not.toMatch(/key: auth0ClientId\n\s+value: ""/);
200
- });
201
-
202
- it("fails the scaffold instead of writing an empty auth0ClientId", async () => {
203
- mockEnsureSpaApp.mockImplementation(() => {
204
- throw new Error("auth0 apps create failed: Unauthorized");
205
- });
206
-
207
- await expect(scaffoldApi(baseOpts())).rejects.toThrow(/auth0ClientId would be empty/);
208
- });
209
-
210
- it("surfaces Auth0 application-limit guidance without writing an empty auth0ClientId", async () => {
211
- mockEnsureSpaApp.mockImplementation(() => {
212
- throw new Error(
213
- [
214
- 'Auth0 refused to create SPA "my-api Postman": tenant application limit reached.',
215
- "This tenant already has 3 application(s):",
216
- " - ledger Postman (spa_a)",
217
- "",
218
- "Fix: Auth0 Dashboard → Applications → Applications → delete unused apps,",
219
- 'then re-run the scaffold (or choose Postman "none" to skip SPA creation).',
220
- ].join("\n"),
221
- );
222
- });
223
-
224
- let thrown: unknown;
225
- try {
226
- await scaffoldApi(baseOpts());
227
- } catch (err) {
228
- thrown = err;
229
- }
230
- expect(thrown).toBeInstanceOf(Error);
231
- const message = (thrown as Error).message;
232
- expect(message).toMatch(/tenant application limit reached/);
233
- expect(message).toMatch(/delete unused apps/i);
234
- expect(message).not.toMatch(/auth0ClientId would be empty/);
235
- });
236
-
237
- it("scaffolds a thin ability.factory extension point (not CASL infrastructure)", async () => {
238
- await scaffoldApi(baseOpts());
239
-
240
- const ability = read("projects/api/src/auth/ability.factory.ts");
241
- expect(ability).toContain("export function defineAbilitiesFor");
242
- expect(ability).toContain("export function mapPrincipal");
243
- expect(ability).toContain("defaultDefineAbilitiesFor");
244
- expect(ability).toContain("createPrincipalFromPayload");
245
- expect(ability).toContain("AUTH0_ADMIN_USER_SUB");
246
- expect(ability).not.toContain("@casl/ability");
247
- expect(ability).not.toContain("AbilityBuilder");
248
- expect(ability).not.toContain("createMongoAbility");
249
-
250
- const main = read("projects/api/src/main.ts");
251
- expect(main).toContain(".withAuth(defineAbilitiesFor, { principalMapper: mapPrincipal })");
252
- expect(main).not.toContain("createAbilityFactory");
253
-
254
- const rootPkg = JSON.parse(read("package.json")) as {
255
- dependencies?: Record<string, string>;
256
- };
257
- const apiPkg = JSON.parse(read("projects/api/package.json")) as {
258
- dependencies?: Record<string, string>;
259
- };
260
- expect(rootPkg.dependencies?.["@casl/ability"]).toBeUndefined();
261
- expect(apiPkg.dependencies?.["@casl/ability"]).toBeUndefined();
262
- });
263
- });
@@ -1,232 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const { mockSpawnSync } = vi.hoisted(() => ({
4
- mockSpawnSync: vi.fn(),
5
- }));
6
-
7
- vi.mock("node:child_process", () => ({
8
- execFileSync: vi.fn(),
9
- spawnSync: mockSpawnSync,
10
- }));
11
-
12
- const {
13
- createAdminDatabaseUser,
14
- createApi,
15
- DB_CONNECTION_NAME,
16
- ensureSpaApp,
17
- updateApiScopes,
18
- } = await import("../src/webapi/features/auth0-cli.js");
19
-
20
- beforeEach(() => {
21
- mockSpawnSync.mockReset();
22
- });
23
-
24
- afterEach(() => {
25
- vi.clearAllMocks();
26
- });
27
-
28
- describe("createApi / updateApiScopes RBAC", () => {
29
- it("creates APIs with --enforce-policies and access_token_authz dialect", () => {
30
- mockSpawnSync.mockReturnValueOnce({
31
- status: 0,
32
- stdout: JSON.stringify({ identifier: "https://api.webapi" }),
33
- stderr: "",
34
- error: undefined,
35
- });
36
-
37
- const api = createApi("webapi API", "https://api.webapi");
38
- expect(api.identifier).toBe("https://api.webapi");
39
-
40
- const args = mockSpawnSync.mock.calls[0]?.[1] as string[];
41
- expect(args).toEqual(
42
- expect.arrayContaining([
43
- "apis",
44
- "create",
45
- "--enforce-policies",
46
- "--token-dialect",
47
- "access_token_authz",
48
- ]),
49
- );
50
- });
51
-
52
- it("updates existing APIs with --enforce-policies so re-scaffold is not fail-open", () => {
53
- mockSpawnSync.mockReturnValueOnce({
54
- status: 0,
55
- stdout: "",
56
- stderr: "",
57
- error: undefined,
58
- });
59
-
60
- updateApiScopes("https://api.webapi");
61
-
62
- const args = mockSpawnSync.mock.calls[0]?.[1] as string[];
63
- expect(args).toEqual(
64
- expect.arrayContaining([
65
- "apis",
66
- "update",
67
- "https://api.webapi",
68
- "--enforce-policies",
69
- "--token-dialect",
70
- "access_token_authz",
71
- ]),
72
- );
73
- });
74
- });
75
-
76
- describe("createAdminDatabaseUser", () => {
77
- it("returns the created user on success", () => {
78
- mockSpawnSync.mockReturnValueOnce({
79
- status: 0,
80
- stdout: JSON.stringify({ user_id: "auth0|new", email: "admin@demo.example.com" }),
81
- stderr: "",
82
- error: undefined,
83
- });
84
-
85
- const admin = createAdminDatabaseUser({
86
- email: "admin@demo.example.com",
87
- password: "Secret1!",
88
- username: "admin",
89
- });
90
-
91
- expect(admin).toEqual({
92
- user_id: "auth0|new",
93
- email: "admin@demo.example.com",
94
- password: "Secret1!",
95
- username: "admin",
96
- });
97
- expect(mockSpawnSync).toHaveBeenCalledOnce();
98
- });
99
-
100
- it("looks up an existing user, resets password, and still returns credentials", () => {
101
- mockSpawnSync
102
- .mockReturnValueOnce({
103
- status: 1,
104
- stdout: "",
105
- stderr: 'The user already exists. {"statusCode":409}',
106
- error: undefined,
107
- })
108
- .mockReturnValueOnce({
109
- status: 0,
110
- stdout: JSON.stringify([
111
- { user_id: "auth0|existing", email: "admin@demo.example.com" },
112
- ]),
113
- stderr: "",
114
- error: undefined,
115
- })
116
- .mockReturnValueOnce({
117
- status: 0,
118
- stdout: "{}",
119
- stderr: "",
120
- error: undefined,
121
- });
122
-
123
- const admin = createAdminDatabaseUser({
124
- email: "admin@demo.example.com",
125
- password: "NewSecret1!",
126
- username: "admin",
127
- });
128
-
129
- expect(admin).toEqual({
130
- user_id: "auth0|existing",
131
- email: "admin@demo.example.com",
132
- password: "NewSecret1!",
133
- username: "admin",
134
- });
135
-
136
- const patchCall = mockSpawnSync.mock.calls[2]?.[1] as string[];
137
- expect(patchCall).toEqual(
138
- expect.arrayContaining([
139
- "api",
140
- "patch",
141
- `users/${encodeURIComponent("auth0|existing")}`,
142
- ]),
143
- );
144
- const dataIdx = patchCall.indexOf("--data");
145
- expect(JSON.parse(patchCall[dataIdx + 1] ?? "{}")).toEqual({
146
- password: "NewSecret1!",
147
- connection: DB_CONNECTION_NAME,
148
- });
149
- });
150
- });
151
-
152
- describe("ensureSpaApp", () => {
153
- it("reuses an existing app by name and refreshes callbacks", () => {
154
- mockSpawnSync
155
- .mockReturnValueOnce({
156
- status: 0,
157
- stdout: JSON.stringify([
158
- { client_id: "spa_existing", name: "webapi Postman" },
159
- { client_id: "other", name: "Other App" },
160
- ]),
161
- stderr: "",
162
- error: undefined,
163
- })
164
- .mockReturnValueOnce({
165
- status: 0,
166
- stdout: "",
167
- stderr: "",
168
- error: undefined,
169
- });
170
-
171
- const app = ensureSpaApp("webapi Postman", [
172
- "https://oauth.pstmn.io/v1/callback",
173
- "https://oauth.pstmn.io/v1/browser-callback",
174
- ]);
175
-
176
- expect(app).toEqual({ client_id: "spa_existing", reused: true });
177
- const updateArgs = mockSpawnSync.mock.calls[1]?.[1] as string[];
178
- expect(updateArgs).toEqual(
179
- expect.arrayContaining([
180
- "apps",
181
- "update",
182
- "spa_existing",
183
- "--callbacks",
184
- "https://oauth.pstmn.io/v1/callback,https://oauth.pstmn.io/v1/browser-callback",
185
- ]),
186
- );
187
- });
188
-
189
- it("creates a SPA when no matching app exists", () => {
190
- mockSpawnSync
191
- .mockReturnValueOnce({
192
- status: 0,
193
- stdout: JSON.stringify([{ client_id: "other", name: "Other App" }]),
194
- stderr: "",
195
- error: undefined,
196
- })
197
- .mockReturnValueOnce({
198
- status: 0,
199
- stdout: JSON.stringify({ client_id: "spa_new", name: "webapi Postman" }),
200
- stderr: "",
201
- error: undefined,
202
- });
203
-
204
- const app = ensureSpaApp("webapi Postman", ["https://oauth.pstmn.io/v1/callback"]);
205
- expect(app).toEqual({ client_id: "spa_new", reused: false });
206
- });
207
-
208
- it("fails with a delete-apps message when Auth0 hits the application entity limit", () => {
209
- mockSpawnSync
210
- .mockReturnValueOnce({
211
- status: 0,
212
- stdout: JSON.stringify([
213
- { client_id: "spa_a", name: "ledger Postman" },
214
- { client_id: "spa_b", name: "workspace Postman" },
215
- { client_id: "m2m", name: "Default App" },
216
- ]),
217
- stderr: "",
218
- error: undefined,
219
- })
220
- .mockReturnValueOnce({
221
- status: 1,
222
- stdout: "",
223
- stderr:
224
- "Failed to create application: 403 Forbidden: You reached the limit of entities of this type for this tenant.",
225
- error: undefined,
226
- });
227
-
228
- expect(() =>
229
- ensureSpaApp("crypto Postman", ["https://oauth.pstmn.io/v1/callback"]),
230
- ).toThrow(/tenant application limit reached[\s\S]*delete unused apps/i);
231
- });
232
- });
@@ -1,49 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const { mockSpawnSync } = vi.hoisted(() => ({
4
- mockSpawnSync: vi.fn(),
5
- }));
6
-
7
- vi.mock("node:child_process", () => ({
8
- spawnSync: mockSpawnSync,
9
- }));
10
-
11
- const { formatScaffoldedFiles } = await import("../src/webapi/features/biome-format.js");
12
-
13
- beforeEach(() => {
14
- mockSpawnSync.mockReset();
15
- });
16
-
17
- afterEach(() => {
18
- vi.clearAllMocks();
19
- });
20
-
21
- describe("formatScaffoldedFiles", () => {
22
- it("runs biome check --write in the target directory", () => {
23
- mockSpawnSync.mockReturnValue({ status: 0, stdout: "", stderr: "", error: undefined });
24
-
25
- formatScaffoldedFiles("/tmp/scaffold");
26
-
27
- expect(mockSpawnSync).toHaveBeenCalledWith(
28
- "biome",
29
- ["check", "--write", "."],
30
- expect.objectContaining({ cwd: "/tmp/scaffold" }),
31
- );
32
- });
33
-
34
- it("falls back to npx when biome is not on PATH", () => {
35
- const enoent = Object.assign(new Error("not found"), { code: "ENOENT" });
36
- mockSpawnSync
37
- .mockReturnValueOnce({ status: 1, stdout: "", stderr: "", error: enoent })
38
- .mockReturnValueOnce({ status: 0, stdout: "", stderr: "", error: undefined });
39
-
40
- formatScaffoldedFiles("/tmp/scaffold");
41
-
42
- expect(mockSpawnSync).toHaveBeenNthCalledWith(
43
- 2,
44
- "npx",
45
- ["--yes", "@biomejs/biome", "check", "--write", "."],
46
- expect.objectContaining({ cwd: "/tmp/scaffold" }),
47
- );
48
- });
49
- });