workos 0.17.1 → 0.19.0

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 (366) hide show
  1. package/README.md +11 -149
  2. package/dist/bin.js +155 -23
  3. package/dist/bin.js.map +1 -1
  4. package/dist/cli.config.d.ts +0 -2
  5. package/dist/cli.config.js +3 -3
  6. package/dist/cli.config.js.map +1 -1
  7. package/dist/commands/api/index.js +18 -10
  8. package/dist/commands/api/index.js.map +1 -1
  9. package/dist/commands/api/interactive.js +11 -11
  10. package/dist/commands/api/interactive.js.map +1 -1
  11. package/dist/commands/claim.js +28 -16
  12. package/dist/commands/claim.js.map +1 -1
  13. package/dist/commands/connection.js +3 -3
  14. package/dist/commands/connection.js.map +1 -1
  15. package/dist/commands/debug.d.ts +11 -0
  16. package/dist/commands/debug.js +30 -14
  17. package/dist/commands/debug.js.map +1 -1
  18. package/dist/commands/dev.d.ts +7 -8
  19. package/dist/commands/dev.js +14 -14
  20. package/dist/commands/dev.js.map +1 -1
  21. package/dist/commands/directory.js +3 -3
  22. package/dist/commands/directory.js.map +1 -1
  23. package/dist/commands/doctor.js +2 -2
  24. package/dist/commands/doctor.js.map +1 -1
  25. package/dist/commands/emulate.d.ts +1 -0
  26. package/dist/commands/emulate.js +3 -2
  27. package/dist/commands/emulate.js.map +1 -1
  28. package/dist/commands/env.d.ts +10 -0
  29. package/dist/commands/env.js +94 -18
  30. package/dist/commands/env.js.map +1 -1
  31. package/dist/commands/install.js +9 -10
  32. package/dist/commands/install.js.map +1 -1
  33. package/dist/commands/login.d.ts +25 -15
  34. package/dist/commands/login.js +126 -71
  35. package/dist/commands/login.js.map +1 -1
  36. package/dist/commands/logout.js +4 -4
  37. package/dist/commands/logout.js.map +1 -1
  38. package/dist/commands/mcp.d.ts +14 -0
  39. package/dist/commands/mcp.js +99 -0
  40. package/dist/commands/mcp.js.map +1 -0
  41. package/dist/commands/migrations.js +2 -1
  42. package/dist/commands/migrations.js.map +1 -1
  43. package/dist/commands/seed.js +5 -4
  44. package/dist/commands/seed.js.map +1 -1
  45. package/dist/commands/setup.d.ts +50 -0
  46. package/dist/commands/setup.js +237 -0
  47. package/dist/commands/setup.js.map +1 -0
  48. package/dist/commands/vault-run.js +3 -2
  49. package/dist/commands/vault-run.js.map +1 -1
  50. package/dist/commands/verify-login.d.ts +25 -0
  51. package/dist/commands/verify-login.js +158 -0
  52. package/dist/commands/verify-login.js.map +1 -0
  53. package/dist/doctor/checks/ai-analysis.js +2 -1
  54. package/dist/doctor/checks/ai-analysis.js.map +1 -1
  55. package/dist/doctor/checks/mcp.d.ts +15 -0
  56. package/dist/doctor/checks/mcp.js +34 -0
  57. package/dist/doctor/checks/mcp.js.map +1 -0
  58. package/dist/doctor/index.js +7 -1
  59. package/dist/doctor/index.js.map +1 -1
  60. package/dist/doctor/issues.js +18 -1
  61. package/dist/doctor/issues.js.map +1 -1
  62. package/dist/doctor/output.js +18 -3
  63. package/dist/doctor/output.js.map +1 -1
  64. package/dist/doctor/types.d.ts +17 -0
  65. package/dist/doctor/types.js.map +1 -1
  66. package/dist/integrations/dotnet/index.js +1 -1
  67. package/dist/integrations/dotnet/index.js.map +1 -1
  68. package/dist/integrations/elixir/index.js +1 -1
  69. package/dist/integrations/elixir/index.js.map +1 -1
  70. package/dist/integrations/go/index.js +1 -1
  71. package/dist/integrations/go/index.js.map +1 -1
  72. package/dist/integrations/nextjs/index.js +8 -5
  73. package/dist/integrations/nextjs/index.js.map +1 -1
  74. package/dist/integrations/nextjs/utils.d.ts +1 -1
  75. package/dist/integrations/nextjs/utils.js +21 -6
  76. package/dist/integrations/nextjs/utils.js.map +1 -1
  77. package/dist/integrations/react-router/index.js +5 -5
  78. package/dist/integrations/react-router/index.js.map +1 -1
  79. package/dist/integrations/react-router/utils.js +28 -12
  80. package/dist/integrations/react-router/utils.js.map +1 -1
  81. package/dist/integrations/ruby/index.js +1 -1
  82. package/dist/integrations/ruby/index.js.map +1 -1
  83. package/dist/lib/adapters/cli-adapter.d.ts +33 -4
  84. package/dist/lib/adapters/cli-adapter.js +251 -135
  85. package/dist/lib/adapters/cli-adapter.js.map +1 -1
  86. package/dist/lib/adapters/dashboard-adapter.js +3 -3
  87. package/dist/lib/adapters/dashboard-adapter.js.map +1 -1
  88. package/dist/lib/adapters/headless-adapter.d.ts +7 -0
  89. package/dist/lib/adapters/headless-adapter.js +42 -5
  90. package/dist/lib/adapters/headless-adapter.js.map +1 -1
  91. package/dist/lib/agent-interface.js +19 -5
  92. package/dist/lib/agent-interface.js.map +1 -1
  93. package/dist/lib/agent-runner.js +1 -1
  94. package/dist/lib/agent-runner.js.map +1 -1
  95. package/dist/lib/ai-content.js +2 -1
  96. package/dist/lib/ai-content.js.map +1 -1
  97. package/dist/lib/api-key.js +2 -1
  98. package/dist/lib/api-key.js.map +1 -1
  99. package/dist/lib/completion-data.d.ts +33 -0
  100. package/dist/lib/completion-data.js +33 -0
  101. package/dist/lib/completion-data.js.map +1 -0
  102. package/dist/lib/config-store.d.ts +13 -0
  103. package/dist/lib/config-store.js +24 -0
  104. package/dist/lib/config-store.js.map +1 -1
  105. package/dist/lib/config.d.ts +1 -1
  106. package/dist/lib/config.js +1 -1
  107. package/dist/lib/config.js.map +1 -1
  108. package/dist/lib/constants.d.ts +17 -0
  109. package/dist/lib/constants.js +17 -0
  110. package/dist/lib/constants.js.map +1 -1
  111. package/dist/lib/events.d.ts +34 -1
  112. package/dist/lib/events.js.map +1 -1
  113. package/dist/lib/framework-config.d.ts +6 -0
  114. package/dist/lib/framework-config.js.map +1 -1
  115. package/dist/lib/installer-core.d.ts +34 -21
  116. package/dist/lib/installer-core.js +24 -5
  117. package/dist/lib/installer-core.js.map +1 -1
  118. package/dist/lib/installer-core.types.d.ts +3 -1
  119. package/dist/lib/installer-core.types.js.map +1 -1
  120. package/dist/lib/mcp-clients.d.ts +45 -0
  121. package/dist/lib/mcp-clients.js +297 -0
  122. package/dist/lib/mcp-clients.js.map +1 -0
  123. package/dist/lib/port-detection.js +19 -13
  124. package/dist/lib/port-detection.js.map +1 -1
  125. package/dist/lib/preferences.d.ts +42 -0
  126. package/dist/lib/preferences.js +34 -5
  127. package/dist/lib/preferences.js.map +1 -1
  128. package/dist/lib/run-with-core.js +42 -4
  129. package/dist/lib/run-with-core.js.map +1 -1
  130. package/dist/lib/settings.d.ts +5 -14
  131. package/dist/lib/settings.js +5 -16
  132. package/dist/lib/settings.js.map +1 -1
  133. package/dist/lib/startup-notice-gate.d.ts +21 -0
  134. package/dist/lib/startup-notice-gate.js +29 -0
  135. package/dist/lib/startup-notice-gate.js.map +1 -0
  136. package/dist/lib/telemetry-notice.d.ts +5 -5
  137. package/dist/lib/telemetry-notice.js +10 -8
  138. package/dist/lib/telemetry-notice.js.map +1 -1
  139. package/dist/lib/unclaimed-env-provision.js +15 -12
  140. package/dist/lib/unclaimed-env-provision.js.map +1 -1
  141. package/dist/lib/unclaimed-warning.js +7 -3
  142. package/dist/lib/unclaimed-warning.js.map +1 -1
  143. package/dist/lib/validation/security-checks.js +2 -1
  144. package/dist/lib/validation/security-checks.js.map +1 -1
  145. package/dist/lib/validation/validator.js +30 -3
  146. package/dist/lib/validation/validator.js.map +1 -1
  147. package/dist/lib/workos-management.js +26 -19
  148. package/dist/lib/workos-management.js.map +1 -1
  149. package/dist/run.d.ts +1 -0
  150. package/dist/run.js +5 -1
  151. package/dist/run.js.map +1 -1
  152. package/dist/steps/add-or-update-environment-variables.js +10 -10
  153. package/dist/steps/add-or-update-environment-variables.js.map +1 -1
  154. package/dist/steps/run-prettier.js +3 -3
  155. package/dist/steps/run-prettier.js.map +1 -1
  156. package/dist/steps/upload-environment-variables/index.js +15 -3
  157. package/dist/steps/upload-environment-variables/index.js.map +1 -1
  158. package/dist/steps/upload-environment-variables/providers/vercel.js +2 -2
  159. package/dist/steps/upload-environment-variables/providers/vercel.js.map +1 -1
  160. package/dist/utils/analytics.d.ts +1 -0
  161. package/dist/utils/analytics.js +5 -1
  162. package/dist/utils/analytics.js.map +1 -1
  163. package/dist/utils/box.d.ts +6 -30
  164. package/dist/utils/box.js +8 -95
  165. package/dist/utils/box.js.map +1 -1
  166. package/dist/utils/cli-symbols.d.ts +14 -2
  167. package/dist/utils/cli-symbols.js +12 -0
  168. package/dist/utils/cli-symbols.js.map +1 -1
  169. package/dist/utils/debug.js +3 -3
  170. package/dist/utils/debug.js.map +1 -1
  171. package/dist/utils/environment.js +1 -1
  172. package/dist/utils/environment.js.map +1 -1
  173. package/dist/utils/help-json.js +153 -4
  174. package/dist/utils/help-json.js.map +1 -1
  175. package/dist/utils/interaction-mode.d.ts +1 -1
  176. package/dist/utils/interaction-mode.js +0 -3
  177. package/dist/utils/interaction-mode.js.map +1 -1
  178. package/dist/utils/output.d.ts +5 -3
  179. package/dist/utils/output.js +5 -5
  180. package/dist/utils/output.js.map +1 -1
  181. package/dist/utils/package-manager.js +1 -1
  182. package/dist/utils/package-manager.js.map +1 -1
  183. package/dist/utils/recovery-hints.d.ts +5 -0
  184. package/dist/utils/recovery-hints.js +12 -0
  185. package/dist/utils/recovery-hints.js.map +1 -1
  186. package/dist/utils/summary-box.d.ts +8 -1
  187. package/dist/utils/summary-box.js +67 -4
  188. package/dist/utils/summary-box.js.map +1 -1
  189. package/dist/utils/telemetry-types.d.ts +1 -1
  190. package/dist/utils/telemetry-types.js.map +1 -1
  191. package/dist/utils/types.d.ts +10 -0
  192. package/dist/utils/types.js.map +1 -1
  193. package/dist/utils/{clack-utils.d.ts → ui-utils.d.ts} +1 -1
  194. package/dist/utils/{clack-utils.js → ui-utils.js} +64 -37
  195. package/dist/utils/ui-utils.js.map +1 -0
  196. package/dist/utils/ui.d.ts +148 -0
  197. package/dist/utils/ui.js +368 -0
  198. package/dist/utils/ui.js.map +1 -0
  199. package/dist/utils/urls.d.ts +9 -3
  200. package/dist/utils/urls.js +10 -4
  201. package/dist/utils/urls.js.map +1 -1
  202. package/package.json +13 -20
  203. package/dist/check-coverage.ts +0 -237
  204. package/dist/emulate/core/id.d.ts +0 -48
  205. package/dist/emulate/core/id.js +0 -73
  206. package/dist/emulate/core/id.js.map +0 -1
  207. package/dist/emulate/core/index.d.ts +0 -8
  208. package/dist/emulate/core/index.js +0 -8
  209. package/dist/emulate/core/index.js.map +0 -1
  210. package/dist/emulate/core/jwt.d.ts +0 -28
  211. package/dist/emulate/core/jwt.js +0 -78
  212. package/dist/emulate/core/jwt.js.map +0 -1
  213. package/dist/emulate/core/middleware/auth.d.ts +0 -15
  214. package/dist/emulate/core/middleware/auth.js +0 -17
  215. package/dist/emulate/core/middleware/auth.js.map +0 -1
  216. package/dist/emulate/core/middleware/error-handler.d.ts +0 -22
  217. package/dist/emulate/core/middleware/error-handler.js +0 -72
  218. package/dist/emulate/core/middleware/error-handler.js.map +0 -1
  219. package/dist/emulate/core/pagination.d.ts +0 -27
  220. package/dist/emulate/core/pagination.js +0 -43
  221. package/dist/emulate/core/pagination.js.map +0 -1
  222. package/dist/emulate/core/plugin.d.ts +0 -15
  223. package/dist/emulate/core/plugin.js +0 -2
  224. package/dist/emulate/core/plugin.js.map +0 -1
  225. package/dist/emulate/core/server.d.ts +0 -17
  226. package/dist/emulate/core/server.js +0 -90
  227. package/dist/emulate/core/server.js.map +0 -1
  228. package/dist/emulate/core/store.d.ts +0 -44
  229. package/dist/emulate/core/store.js +0 -169
  230. package/dist/emulate/core/store.js.map +0 -1
  231. package/dist/emulate/index.d.ts +0 -25
  232. package/dist/emulate/index.js +0 -47
  233. package/dist/emulate/index.js.map +0 -1
  234. package/dist/emulate/workos/constants.d.ts +0 -56
  235. package/dist/emulate/workos/constants.js +0 -56
  236. package/dist/emulate/workos/constants.js.map +0 -1
  237. package/dist/emulate/workos/entities.d.ts +0 -360
  238. package/dist/emulate/workos/entities.js +0 -2
  239. package/dist/emulate/workos/entities.js.map +0 -1
  240. package/dist/emulate/workos/event-bus.d.ts +0 -17
  241. package/dist/emulate/workos/event-bus.js +0 -70
  242. package/dist/emulate/workos/event-bus.js.map +0 -1
  243. package/dist/emulate/workos/helpers.d.ts +0 -72
  244. package/dist/emulate/workos/helpers.js +0 -211
  245. package/dist/emulate/workos/helpers.js.map +0 -1
  246. package/dist/emulate/workos/index.d.ts +0 -93
  247. package/dist/emulate/workos/index.js +0 -326
  248. package/dist/emulate/workos/index.js.map +0 -1
  249. package/dist/emulate/workos/role-helpers.d.ts +0 -21
  250. package/dist/emulate/workos/role-helpers.js +0 -130
  251. package/dist/emulate/workos/role-helpers.js.map +0 -1
  252. package/dist/emulate/workos/routes/api-keys.d.ts +0 -2
  253. package/dist/emulate/workos/routes/api-keys.js +0 -32
  254. package/dist/emulate/workos/routes/api-keys.js.map +0 -1
  255. package/dist/emulate/workos/routes/audit-logs.d.ts +0 -2
  256. package/dist/emulate/workos/routes/audit-logs.js +0 -104
  257. package/dist/emulate/workos/routes/audit-logs.js.map +0 -1
  258. package/dist/emulate/workos/routes/auth-challenges.d.ts +0 -2
  259. package/dist/emulate/workos/routes/auth-challenges.js +0 -51
  260. package/dist/emulate/workos/routes/auth-challenges.js.map +0 -1
  261. package/dist/emulate/workos/routes/auth-factors.d.ts +0 -2
  262. package/dist/emulate/workos/routes/auth-factors.js +0 -51
  263. package/dist/emulate/workos/routes/auth-factors.js.map +0 -1
  264. package/dist/emulate/workos/routes/auth.d.ts +0 -2
  265. package/dist/emulate/workos/routes/auth.js +0 -350
  266. package/dist/emulate/workos/routes/auth.js.map +0 -1
  267. package/dist/emulate/workos/routes/authorization-checks.d.ts +0 -10
  268. package/dist/emulate/workos/routes/authorization-checks.js +0 -123
  269. package/dist/emulate/workos/routes/authorization-checks.js.map +0 -1
  270. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +0 -2
  271. package/dist/emulate/workos/routes/authorization-org-roles.js +0 -64
  272. package/dist/emulate/workos/routes/authorization-org-roles.js.map +0 -1
  273. package/dist/emulate/workos/routes/authorization-permissions.d.ts +0 -2
  274. package/dist/emulate/workos/routes/authorization-permissions.js +0 -67
  275. package/dist/emulate/workos/routes/authorization-permissions.js.map +0 -1
  276. package/dist/emulate/workos/routes/authorization-resources.d.ts +0 -2
  277. package/dist/emulate/workos/routes/authorization-resources.js +0 -117
  278. package/dist/emulate/workos/routes/authorization-resources.js.map +0 -1
  279. package/dist/emulate/workos/routes/authorization-roles.d.ts +0 -2
  280. package/dist/emulate/workos/routes/authorization-roles.js +0 -13
  281. package/dist/emulate/workos/routes/authorization-roles.js.map +0 -1
  282. package/dist/emulate/workos/routes/config.d.ts +0 -2
  283. package/dist/emulate/workos/routes/config.js +0 -57
  284. package/dist/emulate/workos/routes/config.js.map +0 -1
  285. package/dist/emulate/workos/routes/connect.d.ts +0 -2
  286. package/dist/emulate/workos/routes/connect.js +0 -65
  287. package/dist/emulate/workos/routes/connect.js.map +0 -1
  288. package/dist/emulate/workos/routes/connections.d.ts +0 -2
  289. package/dist/emulate/workos/routes/connections.js +0 -73
  290. package/dist/emulate/workos/routes/connections.js.map +0 -1
  291. package/dist/emulate/workos/routes/data-integrations.d.ts +0 -2
  292. package/dist/emulate/workos/routes/data-integrations.js +0 -55
  293. package/dist/emulate/workos/routes/data-integrations.js.map +0 -1
  294. package/dist/emulate/workos/routes/directories.d.ts +0 -2
  295. package/dist/emulate/workos/routes/directories.js +0 -90
  296. package/dist/emulate/workos/routes/directories.js.map +0 -1
  297. package/dist/emulate/workos/routes/email-verification.d.ts +0 -2
  298. package/dist/emulate/workos/routes/email-verification.js +0 -49
  299. package/dist/emulate/workos/routes/email-verification.js.map +0 -1
  300. package/dist/emulate/workos/routes/events.d.ts +0 -2
  301. package/dist/emulate/workos/routes/events.js +0 -18
  302. package/dist/emulate/workos/routes/events.js.map +0 -1
  303. package/dist/emulate/workos/routes/feature-flags.d.ts +0 -2
  304. package/dist/emulate/workos/routes/feature-flags.js +0 -103
  305. package/dist/emulate/workos/routes/feature-flags.js.map +0 -1
  306. package/dist/emulate/workos/routes/invitations.d.ts +0 -2
  307. package/dist/emulate/workos/routes/invitations.js +0 -122
  308. package/dist/emulate/workos/routes/invitations.js.map +0 -1
  309. package/dist/emulate/workos/routes/legacy-mfa.d.ts +0 -2
  310. package/dist/emulate/workos/routes/legacy-mfa.js +0 -75
  311. package/dist/emulate/workos/routes/legacy-mfa.js.map +0 -1
  312. package/dist/emulate/workos/routes/magic-auth.d.ts +0 -2
  313. package/dist/emulate/workos/routes/magic-auth.js +0 -32
  314. package/dist/emulate/workos/routes/magic-auth.js.map +0 -1
  315. package/dist/emulate/workos/routes/memberships.d.ts +0 -2
  316. package/dist/emulate/workos/routes/memberships.js +0 -114
  317. package/dist/emulate/workos/routes/memberships.js.map +0 -1
  318. package/dist/emulate/workos/routes/organization-domains.d.ts +0 -2
  319. package/dist/emulate/workos/routes/organization-domains.js +0 -58
  320. package/dist/emulate/workos/routes/organization-domains.js.map +0 -1
  321. package/dist/emulate/workos/routes/organizations.d.ts +0 -2
  322. package/dist/emulate/workos/routes/organizations.js +0 -131
  323. package/dist/emulate/workos/routes/organizations.js.map +0 -1
  324. package/dist/emulate/workos/routes/password-reset.d.ts +0 -2
  325. package/dist/emulate/workos/routes/password-reset.js +0 -61
  326. package/dist/emulate/workos/routes/password-reset.js.map +0 -1
  327. package/dist/emulate/workos/routes/pipes.d.ts +0 -2
  328. package/dist/emulate/workos/routes/pipes.js +0 -82
  329. package/dist/emulate/workos/routes/pipes.js.map +0 -1
  330. package/dist/emulate/workos/routes/portal.d.ts +0 -2
  331. package/dist/emulate/workos/routes/portal.js +0 -18
  332. package/dist/emulate/workos/routes/portal.js.map +0 -1
  333. package/dist/emulate/workos/routes/radar.d.ts +0 -2
  334. package/dist/emulate/workos/routes/radar.js +0 -41
  335. package/dist/emulate/workos/routes/radar.js.map +0 -1
  336. package/dist/emulate/workos/routes/sessions.d.ts +0 -2
  337. package/dist/emulate/workos/routes/sessions.js +0 -51
  338. package/dist/emulate/workos/routes/sessions.js.map +0 -1
  339. package/dist/emulate/workos/routes/sso.d.ts +0 -2
  340. package/dist/emulate/workos/routes/sso.js +0 -161
  341. package/dist/emulate/workos/routes/sso.js.map +0 -1
  342. package/dist/emulate/workos/routes/user-features.d.ts +0 -2
  343. package/dist/emulate/workos/routes/user-features.js +0 -50
  344. package/dist/emulate/workos/routes/user-features.js.map +0 -1
  345. package/dist/emulate/workos/routes/users.d.ts +0 -2
  346. package/dist/emulate/workos/routes/users.js +0 -129
  347. package/dist/emulate/workos/routes/users.js.map +0 -1
  348. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +0 -2
  349. package/dist/emulate/workos/routes/webhook-endpoints.js +0 -67
  350. package/dist/emulate/workos/routes/webhook-endpoints.js.map +0 -1
  351. package/dist/emulate/workos/routes/widgets.d.ts +0 -2
  352. package/dist/emulate/workos/routes/widgets.js +0 -27
  353. package/dist/emulate/workos/routes/widgets.js.map +0 -1
  354. package/dist/emulate/workos/store.d.ts +0 -48
  355. package/dist/emulate/workos/store.js +0 -102
  356. package/dist/emulate/workos/store.js.map +0 -1
  357. package/dist/emulate/workos/webhook-signer.d.ts +0 -1
  358. package/dist/emulate/workos/webhook-signer.js +0 -8
  359. package/dist/emulate/workos/webhook-signer.js.map +0 -1
  360. package/dist/gen-routes-lib.spec.ts +0 -659
  361. package/dist/gen-routes-lib.ts +0 -647
  362. package/dist/gen-routes.ts +0 -96
  363. package/dist/utils/clack-utils.js.map +0 -1
  364. package/dist/utils/clack.d.ts +0 -5
  365. package/dist/utils/clack.js +0 -34
  366. package/dist/utils/clack.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-notice.js","sourceRoot":"","sources":["../../src/lib/telemetry-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvF,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,IAAI,gBAAgB;YAAE,OAAO;QAC7B,IAAI,UAAU,EAAE;YAAE,OAAO,CAAC,mEAAmE;QAC7F,IAAI,mBAAmB,EAAE;YAAE,OAAO,CAAC,wCAAwC;QAC3E,IAAI,aAAa,EAAE;YAAE,OAAO,CAAC,2BAA2B;QAExD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,uDAAuD,MAAM,kBAAkB,CAAC;QACjH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,yBAAyB;IACvC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * First-run telemetry notice.\n *\n * Prints a one-time, stderr-only box telling the user that anonymous CLI usage\n * telemetry is being collected and how to turn it off. Shown at most once ever\n * (backed by the persisted `noticeShownAt` timestamp in preferences.json), only\n * in interactive human mode, and never on the machine-readable path.\n *\n * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,\n * a `!isJsonMode()` gate, the shared renderStderrBox helper, and a never-throws\n * contract so it can never block command execution. The one structural\n * difference is persistence — this notice writes `noticeShownAt` the first time\n * it actually displays so it never re-shows across runs.\n */\n\nimport chalk from 'chalk';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\nimport { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';\n\nlet shownThisSession = false;\n\n/**\n * Show the first-run telemetry notice if it has never been displayed.\n *\n * Gate order is load-bearing: every suppression check runs BEFORE\n * markNoticeShown(), so a non-human first run (--json / piped / CI) never\n * consumes the one-time display. The flag is set only when the box is actually\n * rendered, so a real human eventually sees it. Never throws.\n */\nexport function maybeShowTelemetryNotice(): void {\n try {\n if (shownThisSession) return;\n if (isJsonMode()) return; // suppress in --json / non-TTY / CI (output auto-switches to json)\n if (isTelemetryOptedOut()) return; // already opted out — nothing to inform\n if (isNoticeShown()) return; // already shown once, ever\n\n const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));\n const inner = ` ${chalk.cyan('ℹ')} WorkOS collects anonymous CLI usage telemetry. Run ${optOut} to disable it. `;\n renderStderrBox(inner, chalk.cyan);\n // Set the per-session guard and persist ONLY after a successful render, so a\n // render failure (caught below) lets a later command in this process retry\n // rather than silently suppressing the notice for the rest of the session.\n shownThisSession = true;\n markNoticeShown();\n } catch {\n // Never block command execution.\n }\n}\n\n/** Reset session state (for testing). */\nexport function resetTelemetryNoticeState(): void {\n shownThisSession = false;\n}\n"]}
1
+ {"version":3,"file":"telemetry-notice.js","sourceRoot":"","sources":["../../src/lib/telemetry-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,IAAI,gBAAgB;YAAE,OAAO;QAC7B,IAAI,UAAU,EAAE;YAAE,OAAO,CAAC,mEAAmE;QAC7F,IAAI,mBAAmB,EAAE;YAAE,OAAO,CAAC,wCAAwC;QAC3E,IAAI,aAAa,EAAE;YAAE,OAAO,CAAC,2BAA2B;QAExD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACpE,kBAAkB,CAChB,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,sCAAsC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,EAAE,CAC7G,CAAC;QACF,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;QAClB,6EAA6E;QAC7E,sBAAsB,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,yBAAyB;IACvC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * First-run telemetry notice.\n *\n * Prints a one-time, stderr-only notice telling the user that anonymous CLI\n * usage telemetry is being collected and how to turn it off. Shown at most once\n * ever (backed by the persisted `noticeShownAt` timestamp in preferences.json),\n * only in interactive human mode, and never on the machine-readable path.\n *\n * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,\n * a `!isJsonMode()` gate, the shared renderStderrNotice helper, and a never-throws\n * contract so it can never block command execution. The one structural\n * difference is persistence — this notice writes `noticeShownAt` the first time\n * it actually displays so it never re-shows across runs.\n */\n\nimport chalk from 'chalk';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrNotice } from '../utils/box.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\nimport { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\n\nlet shownThisSession = false;\n\n/**\n * Show the first-run telemetry notice if it has never been displayed.\n *\n * Gate order is load-bearing: every suppression check runs BEFORE\n * markNoticeShown(), so a non-human first run (--json / piped / CI) never\n * consumes the one-time display. The flag is set only when the box is actually\n * rendered, so a real human eventually sees it. Never throws.\n */\nexport function maybeShowTelemetryNotice(): void {\n try {\n if (shownThisSession) return;\n if (isJsonMode()) return; // suppress in --json / non-TTY / CI (output auto-switches to json)\n if (isTelemetryOptedOut()) return; // already opted out — nothing to inform\n if (isNoticeShown()) return; // already shown once, ever\n\n const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));\n renderStderrNotice(\n `${chalk.cyan('ℹ')} ${chalk.dim('Anonymous CLI usage telemetry is on.')} ${chalk.dim('Disable:')} ${optOut}`,\n );\n // Set the per-session guard and persist ONLY after a successful render, so a\n // render failure (caught below) lets a later command in this process retry\n // rather than silently suppressing the notice for the rest of the session.\n shownThisSession = true;\n markNoticeShown();\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n } catch {\n // Never block command execution.\n }\n}\n\n/** Reset session state (for testing). */\nexport function resetTelemetryNoticeState(): void {\n shownThisSession = false;\n}\n"]}
@@ -7,11 +7,12 @@
7
7
  */
8
8
  import chalk from 'chalk';
9
9
  import { provisionUnclaimedEnvironment, UnclaimedEnvApiError } from './unclaimed-env-api.js';
10
- import { getConfig, saveConfig, getActiveEnvironment } from './config-store.js';
10
+ import { getConfig, saveConfig, getActiveEnvironment, freshEnvKey } from './config-store.js';
11
11
  import { writeCredentialsEnv } from './env-writer.js';
12
12
  import { logInfo, logError } from '../utils/debug.js';
13
- import { renderStderrBox } from '../utils/box.js';
14
- import clack from '../utils/clack.js';
13
+ import { renderStderrNotice } from '../utils/box.js';
14
+ import ui from '../utils/ui.js';
15
+ import { formatWorkOSCommand } from '../utils/command-invocation.js';
15
16
  /**
16
17
  * Try to provision an unclaimed environment. Non-fatal — returns true on success,
17
18
  * false on any failure.
@@ -36,27 +37,29 @@ export async function tryProvisionUnclaimedEnv(options) {
36
37
  envVars[key] = options.redirectUri;
37
38
  }
38
39
  writeCredentialsEnv(options.installDir, envVars);
39
- // Save to config store (after .env.local succeeds)
40
+ // Save to config store (after .env.local succeeds). A fresh key so a repeated
41
+ // provision never clobbers an earlier env's claim token (unrecoverable — it
42
+ // lives only in this config).
40
43
  const config = getConfig() ?? { environments: {} };
41
- config.environments['unclaimed'] = {
42
- name: 'unclaimed',
44
+ const key = freshEnvKey(config, 'unclaimed');
45
+ config.environments[key] = {
46
+ name: key,
43
47
  type: 'unclaimed',
44
48
  apiKey: result.apiKey,
45
49
  clientId: result.clientId,
46
50
  claimToken: result.claimToken,
47
51
  };
48
- config.activeEnvironment = 'unclaimed';
52
+ config.activeEnvironment = key;
49
53
  saveConfig(config);
50
54
  // Verify config persisted — critical for `workos env claim` in a later process
51
55
  const readBack = getActiveEnvironment();
52
56
  if (!readBack || readBack.type !== 'unclaimed') {
53
57
  logError('[unclaimed-env-provision] Config read-back failed after save — claim token may not persist');
54
- clack.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');
58
+ ui.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');
55
59
  return false;
56
60
  }
57
61
  logInfo('[unclaimed-env-provision] Unclaimed environment provisioned and saved');
58
- const inner = ` ✓ ${chalk.green('Environment provisioned')} — Run ${chalk.cyan('workos env claim')} to keep it. `;
59
- renderStderrBox(inner, chalk.green);
62
+ renderStderrNotice(`${chalk.green('')} ${chalk.bold('Environment provisioned')} ${chalk.dim('credentials saved to your project')}`, `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to link it to your account.')}`);
60
63
  return true;
61
64
  }
62
65
  catch (error) {
@@ -64,12 +67,12 @@ export async function tryProvisionUnclaimedEnv(options) {
64
67
  logError('[unclaimed-env-provision] Failed:', message);
65
68
  if (error instanceof UnclaimedEnvApiError) {
66
69
  if (error.statusCode === 429) {
67
- clack.log.warn('WorkOS is busy, falling back to login...');
70
+ ui.log.warn('WorkOS is busy, falling back to login...');
68
71
  }
69
72
  }
70
73
  else {
71
74
  // Non-API errors (filesystem, keyring) are unexpected — surface to user
72
- clack.log.warn(`Could not set up environment: ${message}. Falling back to login...`);
75
+ ui.log.warn(`Could not set up environment: ${message}. Falling back to login...`);
73
76
  }
74
77
  return false;
75
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"unclaimed-env-provision.js","sourceRoot":"","sources":["../../src/lib/unclaimed-env-provision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unclaimed environment provisioning helper.\n *\n * Calls the unclaimed env API, saves credentials to config store as type 'unclaimed',\n * and returns whether provisioning succeeded. Non-fatal — wraps everything in\n * try/catch so install flow can fall back to login.\n */\n\nimport chalk from 'chalk';\nimport { provisionUnclaimedEnvironment, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { getConfig, saveConfig, getActiveEnvironment } from './config-store.js';\nimport type { CliConfig } from './config-store.js';\nimport { writeCredentialsEnv } from './env-writer.js';\nimport { logInfo, logError } from '../utils/debug.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport clack from '../utils/clack.js';\n\nexport interface UnclaimedEnvProvisionOptions {\n installDir: string;\n /** Redirect URI key name varies by framework */\n redirectUriKey?: string;\n /** Redirect URI value */\n redirectUri?: string;\n}\n\n/**\n * Try to provision an unclaimed environment. Non-fatal — returns true on success,\n * false on any failure.\n *\n * On success:\n * - Saves environment to config store as type 'unclaimed'\n * - Sets it as active environment\n * - Writes credentials (including cookie password and claim token) to .env.local\n */\nexport async function tryProvisionUnclaimedEnv(options: UnclaimedEnvProvisionOptions): Promise<boolean> {\n try {\n logInfo('[unclaimed-env-provision] Attempting unclaimed environment provisioning');\n\n const result = await provisionUnclaimedEnvironment();\n\n // Write .env.local first — if this fails, config stays clean (no orphan entries)\n const envVars: Record<string, string> = {\n WORKOS_API_KEY: result.apiKey,\n WORKOS_CLIENT_ID: result.clientId,\n WORKOS_CLAIM_TOKEN: result.claimToken,\n };\n\n if (options.redirectUri) {\n const key = options.redirectUriKey ?? 'WORKOS_REDIRECT_URI';\n envVars[key] = options.redirectUri;\n }\n\n writeCredentialsEnv(options.installDir, envVars);\n\n // Save to config store (after .env.local succeeds)\n const config: CliConfig = getConfig() ?? { environments: {} };\n config.environments['unclaimed'] = {\n name: 'unclaimed',\n type: 'unclaimed',\n apiKey: result.apiKey,\n clientId: result.clientId,\n claimToken: result.claimToken,\n };\n config.activeEnvironment = 'unclaimed';\n saveConfig(config);\n\n // Verify config persisted — critical for `workos env claim` in a later process\n const readBack = getActiveEnvironment();\n if (!readBack || readBack.type !== 'unclaimed') {\n logError('[unclaimed-env-provision] Config read-back failed after save — claim token may not persist');\n clack.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');\n return false;\n }\n\n logInfo('[unclaimed-env-provision] Unclaimed environment provisioned and saved');\n const inner = ` ✓ ${chalk.green('Environment provisioned')} — Run ${chalk.cyan('workos env claim')} to keep it. `;\n renderStderrBox(inner, chalk.green);\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n logError('[unclaimed-env-provision] Failed:', message);\n\n if (error instanceof UnclaimedEnvApiError) {\n if (error.statusCode === 429) {\n clack.log.warn('WorkOS is busy, falling back to login...');\n }\n } else {\n // Non-API errors (filesystem, keyring) are unexpected — surface to user\n clack.log.warn(`Could not set up environment: ${message}. Falling back to login...`);\n }\n\n return false;\n }\n}\n"]}
1
+ {"version":3,"file":"unclaimed-env-provision.js","sourceRoot":"","sources":["../../src/lib/unclaimed-env-provision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unclaimed environment provisioning helper.\n *\n * Calls the unclaimed env API, saves credentials to config store as type 'unclaimed',\n * and returns whether provisioning succeeded. Non-fatal — wraps everything in\n * try/catch so install flow can fall back to login.\n */\n\nimport chalk from 'chalk';\nimport { provisionUnclaimedEnvironment, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { getConfig, saveConfig, getActiveEnvironment, freshEnvKey } from './config-store.js';\nimport type { CliConfig } from './config-store.js';\nimport { writeCredentialsEnv } from './env-writer.js';\nimport { logInfo, logError } from '../utils/debug.js';\nimport { renderStderrNotice } from '../utils/box.js';\nimport ui from '../utils/ui.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\n\nexport interface UnclaimedEnvProvisionOptions {\n installDir: string;\n /** Redirect URI key name varies by framework */\n redirectUriKey?: string;\n /** Redirect URI value */\n redirectUri?: string;\n}\n\n/**\n * Try to provision an unclaimed environment. Non-fatal — returns true on success,\n * false on any failure.\n *\n * On success:\n * - Saves environment to config store as type 'unclaimed'\n * - Sets it as active environment\n * - Writes credentials (including cookie password and claim token) to .env.local\n */\nexport async function tryProvisionUnclaimedEnv(options: UnclaimedEnvProvisionOptions): Promise<boolean> {\n try {\n logInfo('[unclaimed-env-provision] Attempting unclaimed environment provisioning');\n\n const result = await provisionUnclaimedEnvironment();\n\n // Write .env.local first — if this fails, config stays clean (no orphan entries)\n const envVars: Record<string, string> = {\n WORKOS_API_KEY: result.apiKey,\n WORKOS_CLIENT_ID: result.clientId,\n WORKOS_CLAIM_TOKEN: result.claimToken,\n };\n\n if (options.redirectUri) {\n const key = options.redirectUriKey ?? 'WORKOS_REDIRECT_URI';\n envVars[key] = options.redirectUri;\n }\n\n writeCredentialsEnv(options.installDir, envVars);\n\n // Save to config store (after .env.local succeeds). A fresh key so a repeated\n // provision never clobbers an earlier env's claim token (unrecoverable — it\n // lives only in this config).\n const config: CliConfig = getConfig() ?? { environments: {} };\n const key = freshEnvKey(config, 'unclaimed');\n config.environments[key] = {\n name: key,\n type: 'unclaimed',\n apiKey: result.apiKey,\n clientId: result.clientId,\n claimToken: result.claimToken,\n };\n config.activeEnvironment = key;\n saveConfig(config);\n\n // Verify config persisted — critical for `workos env claim` in a later process\n const readBack = getActiveEnvironment();\n if (!readBack || readBack.type !== 'unclaimed') {\n logError('[unclaimed-env-provision] Config read-back failed after save — claim token may not persist');\n ui.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');\n return false;\n }\n\n logInfo('[unclaimed-env-provision] Unclaimed environment provisioned and saved');\n renderStderrNotice(\n `${chalk.green('')} ${chalk.bold('Environment provisioned')} ${chalk.dim('credentials saved to your project')}`,\n `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to link it to your account.')}`,\n );\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n logError('[unclaimed-env-provision] Failed:', message);\n\n if (error instanceof UnclaimedEnvApiError) {\n if (error.statusCode === 429) {\n ui.log.warn('WorkOS is busy, falling back to login...');\n }\n } else {\n // Non-API errors (filesystem, keyring) are unexpected — surface to user\n ui.log.warn(`Could not set up environment: ${message}. Falling back to login...`);\n }\n\n return false;\n }\n}\n"]}
@@ -11,7 +11,10 @@ import { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed }
11
11
  import { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';
12
12
  import { logError, logInfo } from '../utils/debug.js';
13
13
  import { isJsonMode } from '../utils/output.js';
14
- import { renderStderrBox } from '../utils/box.js';
14
+ import { renderStderrNotice } from '../utils/box.js';
15
+ import { pill } from '../utils/ui.js';
16
+ import { markStartupNoticeShown } from './startup-notice-gate.js';
17
+ import { formatWorkOSCommand } from '../utils/command-invocation.js';
15
18
  let warningShownThisSession = false;
16
19
  let claimCheckDoneThisSession = false;
17
20
  /**
@@ -57,8 +60,9 @@ export async function warnIfUnclaimed() {
57
60
  return;
58
61
  warningShownThisSession = true;
59
62
  if (!isJsonMode()) {
60
- const inner = ` ${chalk.yellow('Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;
61
- renderStderrBox(inner, chalk.yellow);
63
+ renderStderrNotice(`${pill('WARN', 'warn')} ${chalk.bold('Unclaimed environment')} ${chalk.dim('not linked to your account')}`, `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to save it — its credentials live only on this machine.')}`);
64
+ // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.
65
+ markStartupNoticeShown();
62
66
  }
63
67
  }
64
68
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"unclaimed-warning.js","sourceRoot":"","sources":["../../src/lib/unclaimed-warning.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,IAAI,uBAAuB,GAAG,KAAK,CAAC;AACpC,IAAI,yBAAyB,GAAG,KAAK,CAAC;AAEtC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO;QAEjD,2DAA2D;QAC3D,2EAA2E;QAC3E,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAC/B,yBAAyB,GAAG,IAAI,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACpE,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;oBAC1B,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,wEAAwE,CAAC,CAAC;oBAClF,OAAO;gBACT,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,oBAAoB,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBACtE,yEAAyE;oBACzE,yDAAyD;oBACzD,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,0DAA0D,CAAC,CAAC;oBACpE,OAAO;gBACT,CAAC;gBACD,wEAAwE;gBACxE,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBAC1C,QAAQ,CAAC,yCAAyC,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBACvF,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,yCAAyC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,IAAI,uBAAuB;YAAE,OAAO;QACpC,uBAAuB,GAAG,IAAI,CAAC;QAE/B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,sBAAsB,CAAC;YACxH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yDAAyD;QACzD,QAAQ,CAAC,uCAAuC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,0BAA0B;IACxC,uBAAuB,GAAG,KAAK,CAAC;IAChC,yBAAyB,GAAG,KAAK,CAAC;AACpC,CAAC","sourcesContent":["/**\n * Unclaimed environment warning module.\n *\n * Shows a one-line stderr warning when the active environment is unclaimed.\n * On first run, checks if the environment was claimed externally (e.g. via\n * browser) and updates the local config if so.\n * Never throws — all errors are caught to avoid blocking management commands.\n */\n\nimport chalk from 'chalk';\nimport { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed } from './config-store.js';\nimport { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { logError, logInfo } from '../utils/debug.js';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\n\nlet warningShownThisSession = false;\nlet claimCheckDoneThisSession = false;\n\n/**\n * Show a warning if the active environment is unclaimed.\n * Non-blocking — never throws.\n */\nexport async function warnIfUnclaimed(): Promise<void> {\n try {\n const env = getActiveEnvironment();\n if (!env || !isUnclaimedEnvironment(env)) return;\n\n // Check once per session if the env was claimed externally\n // claimToken and clientId guaranteed present by UnclaimedEnvironmentConfig\n if (!claimCheckDoneThisSession) {\n claimCheckDoneThisSession = true;\n try {\n const result = await createClaimNonce(env.clientId, env.claimToken);\n if (result.alreadyClaimed) {\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Environment was claimed externally, config updated');\n return;\n }\n } catch (error) {\n if (error instanceof UnclaimedEnvApiError && error.statusCode === 401) {\n // 401 likely means the claim token was invalidated after the environment\n // was claimed. We assume claimed and promote to sandbox.\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Claim token invalid/expired, removed');\n return;\n }\n // Log non-401 errors for diagnostics, then fall through to show warning\n if (error instanceof UnclaimedEnvApiError) {\n logError('[unclaimed-warning] Claim check failed:', error.statusCode, error.message);\n } else {\n logError('[unclaimed-warning] Claim check failed:', error instanceof Error ? error.message : String(error));\n }\n }\n }\n\n // Show warning once per session\n if (warningShownThisSession) return;\n warningShownThisSession = true;\n\n if (!isJsonMode()) {\n const inner = ` ${chalk.yellow('Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;\n renderStderrBox(inner, chalk.yellow);\n }\n } catch (error) {\n // Never block command execution, but log for diagnostics\n logError('[unclaimed-warning] Unexpected error:', error instanceof Error ? error.message : String(error));\n }\n}\n\n/** Reset session state (for testing) */\nexport function resetUnclaimedWarningState(): void {\n warningShownThisSession = false;\n claimCheckDoneThisSession = false;\n}\n"]}
1
+ {"version":3,"file":"unclaimed-warning.js","sourceRoot":"","sources":["../../src/lib/unclaimed-warning.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unclaimed environment warning module.\n *\n * Shows a one-line stderr warning when the active environment is unclaimed.\n * On first run, checks if the environment was claimed externally (e.g. via\n * browser) and updates the local config if so.\n * Never throws — all errors are caught to avoid blocking management commands.\n */\n\nimport chalk from 'chalk';\nimport { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed } from './config-store.js';\nimport { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { logError, logInfo } from '../utils/debug.js';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrNotice } from '../utils/box.js';\nimport { pill } from '../utils/ui.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\n\nlet warningShownThisSession = false;\nlet claimCheckDoneThisSession = false;\n\n/**\n * Show a warning if the active environment is unclaimed.\n * Non-blocking — never throws.\n */\nexport async function warnIfUnclaimed(): Promise<void> {\n try {\n const env = getActiveEnvironment();\n if (!env || !isUnclaimedEnvironment(env)) return;\n\n // Check once per session if the env was claimed externally\n // claimToken and clientId guaranteed present by UnclaimedEnvironmentConfig\n if (!claimCheckDoneThisSession) {\n claimCheckDoneThisSession = true;\n try {\n const result = await createClaimNonce(env.clientId, env.claimToken);\n if (result.alreadyClaimed) {\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Environment was claimed externally, config updated');\n return;\n }\n } catch (error) {\n if (error instanceof UnclaimedEnvApiError && error.statusCode === 401) {\n // 401 likely means the claim token was invalidated after the environment\n // was claimed. We assume claimed and promote to sandbox.\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Claim token invalid/expired, removed');\n return;\n }\n // Log non-401 errors for diagnostics, then fall through to show warning\n if (error instanceof UnclaimedEnvApiError) {\n logError('[unclaimed-warning] Claim check failed:', error.statusCode, error.message);\n } else {\n logError('[unclaimed-warning] Claim check failed:', error instanceof Error ? error.message : String(error));\n }\n }\n }\n\n // Show warning once per session\n if (warningShownThisSession) return;\n warningShownThisSession = true;\n\n if (!isJsonMode()) {\n renderStderrNotice(\n `${pill('WARN', 'warn')} ${chalk.bold('Unclaimed environment')} ${chalk.dim('not linked to your account')}`,\n `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to save it — its credentials live only on this machine.')}`,\n );\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n }\n } catch (error) {\n // Never block command execution, but log for diagnostics\n logError('[unclaimed-warning] Unexpected error:', error instanceof Error ? error.message : String(error));\n }\n}\n\n/** Reset session state (for testing) */\nexport function resetUnclaimedWarningState(): void {\n warningShownThisSession = false;\n claimCheckDoneThisSession = false;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';
2
+ import { formatWorkOSCommand } from '../../utils/command-invocation.js';
2
3
  /**
3
4
  * The "security subset" of `workos doctor`'s auth-pattern checks that the
4
5
  * installer enforces. These are the patterns that are *unsafe* or *leak secrets*
@@ -99,7 +100,7 @@ export function formatBlockingSecurityError(blocking) {
99
100
  '',
100
101
  ...lines,
101
102
  '',
102
- 'Fix the issues above (or run `workos doctor` for details and remediation) and re-run the installer.',
103
+ `Fix the issues above (or run \`${formatWorkOSCommand('doctor')}\` for details and remediation) and re-run the installer.`,
103
104
  ].join('\n');
104
105
  }
105
106
  //# sourceMappingURL=security-checks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"security-checks.js","sourceRoot":"","sources":["../../../src/lib/validation/security-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';\nimport type { AuthPatternFinding, FrameworkInfo, EnvironmentInfo, SdkInfo } from '../../doctor/types.js';\nimport type { ValidationIssue } from './types.js';\n\n/**\n * The \"security subset\" of `workos doctor`'s auth-pattern checks that the\n * installer enforces. These are the patterns that are *unsafe* or *leak secrets*\n * (an unsafe GET sign-out, an API key in client env/source, an ungitignored\n * .env). Completeness checks (missing middleware/callback/provider) are\n * intentionally excluded here — `validateInstallation` already covers those, and\n * they carry higher false-positive risk than we want gating install success.\n *\n * Keep in sync with the codes emitted by `src/doctor/checks/auth-patterns.ts`.\n */\nconst SECURITY_FINDING_CODES = new Set<string>([\n 'SIGNOUT_GET_HANDLER',\n 'SIGNOUT_LINK_PREFETCH',\n 'API_KEY_LEAKED_TO_CLIENT',\n 'API_KEY_IN_SOURCE',\n 'ENV_FILE_NOT_GITIGNORED',\n 'MIXED_ENVIRONMENT',\n]);\n\n/** Map an installer integration id to the framework name doctor's checks expect. */\nconst INTEGRATION_FRAMEWORK_NAME: Record<string, string> = {\n nextjs: 'Next.js',\n 'react-router': 'React Router',\n 'tanstack-start': 'TanStack Start',\n};\n\nexport interface SecurityCheckResult {\n /** All security-class findings for this install (errors + warnings). */\n findings: AuthPatternFinding[];\n /**\n * Error-severity findings that must block a successful install. Empty when the\n * install is secure; a non-empty list means install should not report success.\n */\n blocking: AuthPatternFinding[];\n}\n\n/**\n * Run the security subset of doctor's auth-pattern checks against an install\n * directory. Pure file inspection — no network — so it is safe to call both\n * inside the installer's self-correction loop and as the final pre-success gate.\n *\n * This closes the install-validate ↔ doctor gap: previously install could report\n * `success: true` while `workos doctor` immediately found a security hole,\n * because neither the retry loop nor `validateInstallation` ran these checks.\n */\nexport async function runInstallSecurityChecks(integration: string, installDir: string): Promise<SecurityCheckResult> {\n const framework: FrameworkInfo = {\n name: INTEGRATION_FRAMEWORK_NAME[integration] ?? null,\n version: null,\n };\n // checkAuthPatterns loads .env files itself; these structs only satisfy the\n // shape its non-Next.js checks read from.\n const environment: EnvironmentInfo = {\n apiKeyConfigured: false,\n apiKeyType: null,\n clientId: null,\n redirectUri: null,\n cookieDomain: null,\n baseUrl: null,\n };\n const sdk: SdkInfo = {\n name: null,\n version: null,\n latest: null,\n outdated: false,\n isAuthKit: false,\n language: 'javascript',\n };\n\n const result = await checkAuthPatterns({ installDir }, framework, environment, sdk);\n const findings = result.findings.filter((f) => SECURITY_FINDING_CODES.has(f.code));\n const blocking = findings.filter((f) => f.severity === 'error');\n return { findings, blocking };\n}\n\n/** Convert security findings into ValidationIssues for the emitter/report surfaces. */\nexport function securityFindingsToIssues(findings: AuthPatternFinding[]): ValidationIssue[] {\n return findings.map((f) => ({\n type: 'pattern',\n severity: f.severity,\n message: f.filePath ? `${f.message} (${f.filePath})` : f.message,\n hint: f.remediation,\n }));\n}\n\n/**\n * Build an agent correction prompt from security findings so the installer's\n * self-correction loop fixes them before declaring success. Returns an empty\n * string when there is nothing to correct.\n */\nexport function formatSecurityFindingsForAgent(findings: AuthPatternFinding[]): string {\n if (findings.length === 0) return '';\n const lines = findings.map((f) => {\n const loc = f.filePath ? ` in ${f.filePath}` : '';\n const fix = f.remediation ? ` Fix: ${f.remediation}` : '';\n return `- [${f.severity}] ${f.message}${loc}.${fix}`;\n });\n return `Security checks found issues that must be fixed:\\n\\n${lines.join('\\n')}\\n\\nApply the fixes above, then make sure the project still builds.`;\n}\n\n/**\n * Build the error message thrown when error-severity security findings survive\n * the installer's retries — the message that turns a silent insecure \"success\"\n * into a visible failure.\n */\nexport function formatBlockingSecurityError(blocking: AuthPatternFinding[]): string {\n const lines = blocking.map((f) => {\n const loc = f.filePath ? ` (${f.filePath})` : '';\n return ` • ${f.code}: ${f.message}${loc}`;\n });\n return [\n 'Installation produced insecure code that could not be auto-corrected:',\n '',\n ...lines,\n '',\n 'Fix the issues above (or run `workos doctor` for details and remediation) and re-run the installer.',\n ].join('\\n');\n}\n"]}
1
+ {"version":3,"file":"security-checks.js","sourceRoot":"","sources":["../../../src/lib/validation/security-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';\nimport type { AuthPatternFinding, FrameworkInfo, EnvironmentInfo, SdkInfo } from '../../doctor/types.js';\nimport type { ValidationIssue } from './types.js';\nimport { formatWorkOSCommand } from '../../utils/command-invocation.js';\n\n/**\n * The \"security subset\" of `workos doctor`'s auth-pattern checks that the\n * installer enforces. These are the patterns that are *unsafe* or *leak secrets*\n * (an unsafe GET sign-out, an API key in client env/source, an ungitignored\n * .env). Completeness checks (missing middleware/callback/provider) are\n * intentionally excluded here — `validateInstallation` already covers those, and\n * they carry higher false-positive risk than we want gating install success.\n *\n * Keep in sync with the codes emitted by `src/doctor/checks/auth-patterns.ts`.\n */\nconst SECURITY_FINDING_CODES = new Set<string>([\n 'SIGNOUT_GET_HANDLER',\n 'SIGNOUT_LINK_PREFETCH',\n 'API_KEY_LEAKED_TO_CLIENT',\n 'API_KEY_IN_SOURCE',\n 'ENV_FILE_NOT_GITIGNORED',\n 'MIXED_ENVIRONMENT',\n]);\n\n/** Map an installer integration id to the framework name doctor's checks expect. */\nconst INTEGRATION_FRAMEWORK_NAME: Record<string, string> = {\n nextjs: 'Next.js',\n 'react-router': 'React Router',\n 'tanstack-start': 'TanStack Start',\n};\n\nexport interface SecurityCheckResult {\n /** All security-class findings for this install (errors + warnings). */\n findings: AuthPatternFinding[];\n /**\n * Error-severity findings that must block a successful install. Empty when the\n * install is secure; a non-empty list means install should not report success.\n */\n blocking: AuthPatternFinding[];\n}\n\n/**\n * Run the security subset of doctor's auth-pattern checks against an install\n * directory. Pure file inspection — no network — so it is safe to call both\n * inside the installer's self-correction loop and as the final pre-success gate.\n *\n * This closes the install-validate ↔ doctor gap: previously install could report\n * `success: true` while `workos doctor` immediately found a security hole,\n * because neither the retry loop nor `validateInstallation` ran these checks.\n */\nexport async function runInstallSecurityChecks(integration: string, installDir: string): Promise<SecurityCheckResult> {\n const framework: FrameworkInfo = {\n name: INTEGRATION_FRAMEWORK_NAME[integration] ?? null,\n version: null,\n };\n // checkAuthPatterns loads .env files itself; these structs only satisfy the\n // shape its non-Next.js checks read from.\n const environment: EnvironmentInfo = {\n apiKeyConfigured: false,\n apiKeyType: null,\n clientId: null,\n redirectUri: null,\n cookieDomain: null,\n baseUrl: null,\n };\n const sdk: SdkInfo = {\n name: null,\n version: null,\n latest: null,\n outdated: false,\n isAuthKit: false,\n language: 'javascript',\n };\n\n const result = await checkAuthPatterns({ installDir }, framework, environment, sdk);\n const findings = result.findings.filter((f) => SECURITY_FINDING_CODES.has(f.code));\n const blocking = findings.filter((f) => f.severity === 'error');\n return { findings, blocking };\n}\n\n/** Convert security findings into ValidationIssues for the emitter/report surfaces. */\nexport function securityFindingsToIssues(findings: AuthPatternFinding[]): ValidationIssue[] {\n return findings.map((f) => ({\n type: 'pattern',\n severity: f.severity,\n message: f.filePath ? `${f.message} (${f.filePath})` : f.message,\n hint: f.remediation,\n }));\n}\n\n/**\n * Build an agent correction prompt from security findings so the installer's\n * self-correction loop fixes them before declaring success. Returns an empty\n * string when there is nothing to correct.\n */\nexport function formatSecurityFindingsForAgent(findings: AuthPatternFinding[]): string {\n if (findings.length === 0) return '';\n const lines = findings.map((f) => {\n const loc = f.filePath ? ` in ${f.filePath}` : '';\n const fix = f.remediation ? ` Fix: ${f.remediation}` : '';\n return `- [${f.severity}] ${f.message}${loc}.${fix}`;\n });\n return `Security checks found issues that must be fixed:\\n\\n${lines.join('\\n')}\\n\\nApply the fixes above, then make sure the project still builds.`;\n}\n\n/**\n * Build the error message thrown when error-severity security findings survive\n * the installer's retries — the message that turns a silent insecure \"success\"\n * into a visible failure.\n */\nexport function formatBlockingSecurityError(blocking: AuthPatternFinding[]): string {\n const lines = blocking.map((f) => {\n const loc = f.filePath ? ` (${f.filePath})` : '';\n return ` • ${f.code}: ${f.message}${loc}`;\n });\n return [\n 'Installation produced insecure code that could not be auto-corrected:',\n '',\n ...lines,\n '',\n `Fix the issues above (or run \\`${formatWorkOSCommand('doctor')}\\` for details and remediation) and re-run the installer.`,\n ].join('\\n');\n}\n"]}
@@ -3,6 +3,7 @@ import { existsSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import fg from 'fast-glob';
5
5
  import { runBuildValidation } from './build-validator.js';
6
+ import { detectPort } from '../port-detection.js';
6
7
  export async function validateInstallation(framework, projectDir, options = {}) {
7
8
  const startTime = Date.now();
8
9
  const issues = [];
@@ -213,6 +214,29 @@ export async function validateFrameworkSpecific(framework, projectDir) {
213
214
  }
214
215
  return issues;
215
216
  }
217
+ /**
218
+ * Compares the redirect URI's effective port to the detected dev server port,
219
+ * for local dev hosts only, and records an error-severity issue on mismatch.
220
+ * Skips production URIs and placeholder hosts like http://test.
221
+ */
222
+ function validateRedirectUriPort(url, framework, projectDir, issues) {
223
+ // Only enforce for local dev hosts; skip production URIs and placeholder
224
+ // hosts like http://test. Node parses IPv6 hostnames with brackets: [::1].
225
+ const localHosts = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
226
+ if (!localHosts.has(url.hostname))
227
+ return;
228
+ const effectivePort = url.port || (url.protocol === 'https:' ? '443' : '80');
229
+ const detected = detectPort(framework, projectDir);
230
+ if (Number(effectivePort) !== detected) {
231
+ issues.push({
232
+ type: 'env',
233
+ severity: 'error',
234
+ message: `Redirect URI port :${effectivePort} does not match the detected dev server port :${detected}`,
235
+ hint: `Update the redirect URI to use port :${detected} in .env.local and the WorkOS dashboard ` +
236
+ `(redirect URI, CORS origin, homepage URL), or change your dev server to run on port ${effectivePort}.`,
237
+ });
238
+ }
239
+ }
216
240
  /**
217
241
  * Validates that the Next.js redirect URI matches an existing callback route.
218
242
  *
@@ -237,6 +261,7 @@ async function validateNextjsRedirectUri(projectDir, issues) {
237
261
  try {
238
262
  const url = new URL(redirectUri);
239
263
  callbackPath = url.pathname;
264
+ validateRedirectUriPort(url, 'nextjs', projectDir, issues);
240
265
  }
241
266
  catch {
242
267
  issues.push({
@@ -272,7 +297,7 @@ async function validateNextjsRedirectUri(projectDir, issues) {
272
297
  const actualPath = '/' + existingRoutes[0].replace(/^(src\/)?app\//, '').replace(/\/route\.(ts|tsx|js|jsx)$/, '');
273
298
  hint =
274
299
  `Found callback route at ${existingRoutes[0]} but redirect URI points to ${callbackPath}. Either:\n` +
275
- ` 1. Change NEXT_PUBLIC_WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\n` +
300
+ ` 1. Change NEXT_PUBLIC_WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\n` +
276
301
  ` 2. Move the route to app/${routePath}/route.ts`;
277
302
  }
278
303
  issues.push({
@@ -308,6 +333,7 @@ async function validateReactRouterRedirectUri(projectDir, issues) {
308
333
  try {
309
334
  const url = new URL(redirectUri);
310
335
  callbackPath = url.pathname;
336
+ validateRedirectUriPort(url, 'react-router', projectDir, issues);
311
337
  }
312
338
  catch {
313
339
  issues.push({
@@ -361,7 +387,7 @@ async function validateReactRouterRedirectUri(projectDir, issues) {
361
387
  .replace(/\./g, '/');
362
388
  hint =
363
389
  `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\n` +
364
- ` 1. Change WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\n` +
390
+ ` 1. Change WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\n` +
365
391
  ` 2. Move the route to app/routes/${dotPath}.tsx`;
366
392
  }
367
393
  issues.push({
@@ -396,6 +422,7 @@ async function validateTanstackStartRedirectUri(projectDir, issues) {
396
422
  try {
397
423
  const url = new URL(redirectUri);
398
424
  callbackPath = url.pathname;
425
+ validateRedirectUriPort(url, 'tanstack-start', projectDir, issues);
399
426
  }
400
427
  catch {
401
428
  issues.push({
@@ -433,7 +460,7 @@ async function validateTanstackStartRedirectUri(projectDir, issues) {
433
460
  .replace(/\/index$/, '');
434
461
  hint =
435
462
  `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\n` +
436
- ` 1. Change WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\n` +
463
+ ` 1. Change WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\n` +
437
464
  ` 2. Move the route to app/routes/${routePath}.tsx`;
438
465
  }
439
466
  issues.push({
@@ -1 +1 @@
1
- {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/lib/validation/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,WAAW,CAAC;AAE3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAO1D,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,UAAkB,EAClB,UAA2B,EAAE;IAE7B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,mDAAmD;IACnD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,SAAS;YACT,MAAM,EAAE,EAAE;YACV,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACnC,CAAC;IACJ,CAAC;IAED,kBAAkB;IAClB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAEzD,2CAA2C;IAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,yBAAyB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAEzE,kCAAkC;IAClC,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC/B,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC;QACjE,SAAS;QACT,MAAM;QACN,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;KACnC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,OAAgB;IAC1D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,SAAS,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;QAErD,mCAAmC;QACnC,IAAI,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC7C,OAAO;gBACL,GAAG,KAAK;gBACR,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;gBACtD,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;gBAC/D,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,YAAY,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;aAC7D,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,kCAAkC;IACjD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAsB,EAAE,UAAkB;IAC/E,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC;IAExC,IAAI,GAA4B,CAAC;IACjC,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;QACnD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAA2B,CAAC;IAChE,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;IACtE,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IAExC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;QACxC,MAAM,QAAQ,GAAG,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QAE7F,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE;gBACxC,IAAI,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE;aACtC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAsB,EAAE,UAAkB;IAC9E,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAU,GAAG,EAAE,CAAC;IAEpB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,yBAAyB;gBAClC,IAAI,EAAE,uDAAuD;aAC9D,CAAC,CAAC;QACL,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACjC,wCAAwC;QACxC,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACzC,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,OAAO,KAAK,EAAE,GAAG,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU;gBAC1B,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,gBAAgB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB;gBAC7E,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,2BAA2B,CAAC;YAEhD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;gBACvD,OAAO,EAAE,iCAAiC,IAAI,CAAC,IAAI,EAAE;gBACrD,IAAI;aACL,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAsB,EAAE,UAAkB;IAC5E,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;YAC9B,SAAS;QACX,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,iBAAiB,IAAI,CAAC,IAAI,EAAE;gBACrC,IAAI,EAAE,UAAU,IAAI,CAAC,IAAI,EAAE;aAC5B,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,yBAAyB;QACzB,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;YAAC,MAAM,CAAC;gBACP,wCAAwC;gBACxC,SAAS;YACX,CAAC;YAED,sBAAsB;YACtB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACvC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC/B,MAAM,CAAC,IAAI,CAAC;4BACV,IAAI,EAAE,SAAS;4BACf,QAAQ,EAAE,SAAS;4BACnB,OAAO,EAAE,QAAQ,OAAO,CAAC,CAAC,CAAC,+BAA+B,OAAO,GAAG;4BACpE,IAAI,EAAE,UAAU,OAAO,CAAC,CAAC,CAAC,cAAc,OAAO,EAAE;yBAClD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;YAED,+BAA+B;YAC/B,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpE,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,QAAQ,OAAO,CAAC,CAAC,CAAC,oBAAoB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;wBAC/E,IAAI,EAAE,UAAU,OAAO,CAAC,CAAC,CAAC,iCAAiC;qBAC5D,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,SAAiB,EAAE,UAAkB;IACnF,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,8BAA8B;IAC9B,MAAM,yBAAyB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,wBAAwB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEnD,iCAAiC;IACjC,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,MAAM,yBAAyB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,iCAAiC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC5D,MAAM,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC;YACjF,MAAM;QACR,KAAK,OAAO;YACV,MAAM,6BAA6B,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACxD,MAAM;QACR,KAAK,cAAc;YACjB,MAAM,8BAA8B,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACzD,MAAM,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC;YACjF,MAAM;QACR,KAAK,gBAAgB;YACnB,MAAM,gCAAgC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC3D,MAAM,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC;YACjF,MAAM;IACV,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,yBAAyB,CAAC,UAAkB,EAAE,MAAyB;IACpF,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,+CAA+C;IACzD,CAAC;IAED,6BAA6B;IAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC1E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,iDAAiD;IAC3D,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,YAAoB,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;QACjC,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,yBAAyB,WAAW,EAAE;YAC/C,IAAI,EAAE,qDAAqD;SAC5D,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,yCAAyC;IACzC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAElD,uEAAuE;IACvE,MAAM,aAAa,GAAG;QACpB,OAAO,SAAS,WAAW;QAC3B,OAAO,SAAS,YAAY;QAC5B,OAAO,SAAS,WAAW;QAC3B,OAAO,SAAS,YAAY;QAC5B,WAAW,SAAS,WAAW;QAC/B,WAAW,SAAS,YAAY;QAChC,WAAW,SAAS,WAAW;QAC/B,WAAW,SAAS,YAAY;KACjC,CAAC;IAEF,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAE3F,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,mDAAmD;QACnD,MAAM,cAAc,GAAG,MAAM,EAAE,CAC7B,CAAC,0CAA0C,EAAE,8CAA8C,CAAC,EAC5F;YACE,GAAG,EAAE,UAAU;SAChB,CACF,CAAC;QAEF,IAAI,IAAI,GAAG,iCAAiC,SAAS,WAAW,CAAC;QACjE,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,0DAA0D;YAC1D,MAAM,UAAU,GAAG,GAAG,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;YAClH,IAAI;gBACF,2BAA2B,cAAc,CAAC,CAAC,CAAC,+BAA+B,YAAY,aAAa;oBACpG,uEAAuE,UAAU,IAAI;oBACrF,8BAA8B,SAAS,WAAW,CAAC;QACvD,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,sBAAsB,YAAY,8BAA8B;YACzE,IAAI;SACL,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,8BAA8B,CAAC,UAAkB,EAAE,MAAyB;IACzF,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,YAAoB,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;QACjC,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,yBAAyB,WAAW,EAAE;YAC/C,IAAI,EAAE,yCAAyC;SAChD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAClD,iEAAiE;IACjE,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE9C,sCAAsC;IACtC,MAAM,aAAa,GAAG;QACpB,oDAAoD;QACpD,cAAc,OAAO,MAAM;QAC3B,cAAc,OAAO,KAAK;QAC1B,cAAc,OAAO,MAAM;QAC3B,cAAc,OAAO,KAAK;QAC1B,sDAAsD;QACtD,cAAc,SAAS,MAAM;QAC7B,cAAc,SAAS,KAAK;QAC5B,cAAc,SAAS,MAAM;QAC7B,cAAc,SAAS,KAAK;QAC5B,kEAAkE;QAClE,cAAc,SAAS,YAAY;QACnC,cAAc,SAAS,WAAW;QAClC,cAAc,SAAS,YAAY;QACnC,cAAc,SAAS,WAAW;QAClC,kEAAkE;QAClE,cAAc,SAAS,YAAY;QACnC,cAAc,SAAS,WAAW;QAClC,cAAc,SAAS,YAAY;QACnC,cAAc,SAAS,WAAW;KACnC,CAAC;IAEF,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAE3F,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,CAAC,0CAA0C,CAAC,EAAE;YAC5E,GAAG,EAAE,UAAU;SAChB,CAAC,CAAC;QAEH,IAAI,IAAI,GAAG,gCAAgC,OAAO,MAAM,CAAC;QACzD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YACrC,qCAAqC;YACrC,MAAM,UAAU,GACd,GAAG;gBACH,UAAU;qBACP,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;qBAC7B,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;qBAC7B,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;qBAC/B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACzB,IAAI;gBACF,2BAA2B,UAAU,+BAA+B,YAAY,aAAa;oBAC7F,2DAA2D,UAAU,IAAI;oBACzE,qCAAqC,OAAO,MAAM,CAAC;QACvD,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,sBAAsB,YAAY,8BAA8B;YACzE,IAAI;SACL,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,gCAAgC,CAAC,UAAkB,EAAE,MAAyB;IAC3F,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,YAAoB,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;QACjC,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,yBAAyB,WAAW,EAAE;YAC/C,IAAI,EAAE,yCAAyC;SAChD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAElD,gCAAgC;IAChC,MAAM,aAAa,GAAG;QACpB,cAAc,SAAS,MAAM;QAC7B,cAAc,SAAS,KAAK;QAC5B,cAAc,SAAS,MAAM;QAC7B,cAAc,SAAS,KAAK;QAC5B,cAAc,SAAS,YAAY;QACnC,cAAc,SAAS,WAAW;QAClC,cAAc,SAAS,YAAY;QACnC,cAAc,SAAS,WAAW;KACnC,CAAC;IAEF,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAE3F,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,CAAC,0CAA0C,CAAC,EAAE;YAC5E,GAAG,EAAE,UAAU;SAChB,CAAC,CAAC;QAEH,IAAI,IAAI,GAAG,gCAAgC,SAAS,MAAM,CAAC;QAC3D,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,UAAU,GACd,GAAG;gBACH,UAAU;qBACP,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;qBAC7B,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;qBAC7B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAC7B,IAAI;gBACF,2BAA2B,UAAU,+BAA+B,YAAY,aAAa;oBAC7F,2DAA2D,UAAU,IAAI;oBACzE,qCAAqC,SAAS,MAAM,CAAC;QACzD,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,sBAAsB,YAAY,8BAA8B;YACzE,IAAI;SACL,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,4BAA4B,CACzC,UAAkB,EAClB,MAAyB,EACzB,UAAkB;IAElB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,+CAA+C;IACzD,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,UAAU,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IACxE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,iDAAiD;IAC3D,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,GAAG,UAAU,8CAA8C,QAAQ,CAAC,MAAM,GAAG;YACtF,IAAI,EAAE,qDAAqD;SAC5D,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,yBAAyB,CAAC,UAAkB,EAAE,MAAyB;IACpF,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC/C,IAAI,UAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,8CAA8C;IAC9C,MAAM,cAAc,GAAG,CAAC,wBAAwB,EAAE,oCAAoC,CAAC,CAAC;IAExF,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtC,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,KAAK;oBACX,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,4BAA4B,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM;oBACjE,IAAI,EAAE,oFAAoF;iBAC3F,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,gBAAgB,GAAG,CAAC,0BAA0B,EAAE,sCAAsC,CAAC,CAAC;IAE9F,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,KAAK;oBACX,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,8BAA8B,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM;oBACnE,IAAI,EAAE,yFAAyF;iBAChG,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,iCAAiC,CAAC,UAAkB,EAAE,MAAyB;IAC5F,sEAAsE;IACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3C,MAAM,YAAY,GAAG;QACnB,GAAG,WAAW,eAAe;QAC7B,GAAG,WAAW,eAAe;QAC7B,GAAG,WAAW,UAAU;QACxB,GAAG,WAAW,UAAU;KACzB,CAAC;IAEF,MAAM,mBAAmB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IAED,gDAAgD;IAChD,MAAM,WAAW,GAAG;QAClB,eAAe;QACf,eAAe;QACf,mBAAmB;QACnB,mBAAmB;QACnB,UAAU;QACV,UAAU;QACV,cAAc;QACd,cAAc;KACf,CAAC;IACF,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzG,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,GAAG,UAAU,uCAAuC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE;YACzF,IAAI,EAAE,cAAc,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,0GAA0G;SAC7K,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,wCAAwC;IACxC,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,CAAC,+BAA+B,CAAC,EAAE;QAC5D,GAAG,EAAE,UAAU;QACf,MAAM,EAAE,CAAC,iBAAiB,CAAC;KAC5B,CAAC,CAAC;IAEH,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,6CAA6C,SAAS,CAAC,CAAC,CAAC,EAAE;YACpE,IAAI,EAAE,cAAc,WAAW,oBAAoB,WAAW,sCAAsC;SACrG,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,6BAA6B,CAAC,UAAkB,EAAE,MAAyB;IACxF,qCAAqC;IACrC,MAAM,aAAa,GAAG;QACpB,cAAc;QACd,cAAc;QACd,eAAe;QACf,eAAe;QACf,aAAa;QACb,aAAa;QACb,gBAAgB;QAChB,gBAAgB;KACjB,CAAC;IAEF,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACxC,aAAa,GAAG,IAAI,CAAC;gBACrB,MAAM;YACR,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,qEAAqE;QACrE,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QACjD,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;gBACzD,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;gBAC7D,IAAI,IAAI,CAAC,2BAA2B,CAAC,EAAE,CAAC;oBACtC,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,kDAAkD;wBAC3D,IAAI,EAAE,yHAAyH;qBAChI,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,wBAAwB,CAAC,UAAkB,EAAE,MAAyB;IACnF,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAEpD,IAAI,UAAkB,CAAC;IACvB,IAAI,eAAuB,CAAC;IAE5B,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,sCAAsC;IAChD,CAAC;IAED,IAAI,CAAC;QACH,eAAe,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,uCAAuC;IACjD,CAAC;IAED,sCAAsC;IACtC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAuB,EAAE;QACxD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YACtD,IAAI,KAAK,EAAE,CAAC;gBACV,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,YAAY,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;IAE/C,4CAA4C;IAC5C,MAAM,UAAU,GAAG;QACjB,gBAAgB;QAChB,kBAAkB;QAClB,qBAAqB;QACrB,wBAAwB;QACxB,8BAA8B;QAC9B,iCAAiC;KAClC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE7C,IAAI,QAAQ,IAAI,UAAU,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,GAAG,OAAO,8CAA8C;gBACjE,IAAI,EAAE,yFAAyF;aAChG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport fg from 'fast-glob';\nimport type { ValidationResult, ValidationRules, ValidationIssue } from './types.js';\nimport { runBuildValidation } from './build-validator.js';\n\nexport interface ValidateOptions {\n variant?: string;\n runBuild?: boolean;\n}\n\nexport async function validateInstallation(\n framework: string,\n projectDir: string,\n options: ValidateOptions = {},\n): Promise<ValidationResult> {\n const startTime = Date.now();\n const issues: ValidationIssue[] = [];\n\n // Load rules for framework (with optional variant)\n const rules = await loadRules(framework, options.variant);\n if (!rules) {\n return {\n passed: true,\n framework,\n issues: [],\n durationMs: Date.now() - startTime,\n };\n }\n\n // Run validations\n issues.push(...(await validatePackages(rules, projectDir)));\n issues.push(...(await validateEnvVars(rules, projectDir)));\n issues.push(...(await validateFiles(rules, projectDir)));\n\n // Run framework-specific cross-validations\n issues.push(...(await validateFrameworkSpecific(framework, projectDir)));\n\n // Run build validation if enabled\n if (options.runBuild !== false) {\n const buildResult = await runBuildValidation(projectDir);\n issues.push(...buildResult.issues);\n }\n\n return {\n passed: issues.filter((i) => i.severity === 'error').length === 0,\n framework,\n issues,\n durationMs: Date.now() - startTime,\n };\n}\n\nasync function loadRules(framework: string, variant?: string): Promise<ValidationRules | null> {\n const rulesPath = new URL(`./rules/${framework}.json`, import.meta.url);\n try {\n const content = await readFile(rulesPath, 'utf-8');\n const rules = JSON.parse(content) as ValidationRules;\n\n // Merge variant rules if specified\n if (variant && rules.variants?.[variant]) {\n const variantRules = rules.variants[variant];\n return {\n ...rules,\n files: [...rules.files, ...(variantRules.files || [])],\n packages: [...rules.packages, ...(variantRules.packages || [])],\n envVars: [...rules.envVars, ...(variantRules.envVars || [])],\n };\n }\n\n return rules;\n } catch {\n return null; // No rules for this framework yet\n }\n}\n\nexport async function validatePackages(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n const pkgPath = join(projectDir, 'package.json');\n if (!existsSync(pkgPath)) return issues;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n } catch {\n // Malformed package.json - skip package validation\n return issues;\n }\n\n const deps = (pkg.dependencies || {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies || {}) as Record<string, string>;\n const allDeps = { ...devDeps, ...deps };\n\n for (const rule of rules.packages) {\n const location = rule.location || 'any';\n const searchIn = location === 'any' ? allDeps : location === 'dependencies' ? deps : devDeps;\n\n if (!searchIn[rule.name]) {\n issues.push({\n type: 'package',\n severity: 'error',\n message: `Missing package: ${rule.name}`,\n hint: `Run: npm install ${rule.name}`,\n });\n }\n }\n\n return issues;\n}\n\nexport async function validateEnvVars(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n const envPath = join(projectDir, '.env.local');\n let envContent = '';\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n if (rules.envVars.length > 0) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: 'Missing .env.local file',\n hint: 'Create .env.local with required environment variables',\n });\n }\n return issues;\n }\n\n for (const rule of rules.envVars) {\n // Check primary name and any alternates\n const varsToCheck = [rule.name, ...(rule.alternates || [])];\n const found = varsToCheck.some((varName) => {\n const pattern = new RegExp(`^${varName}=.+`, 'm');\n return pattern.test(envContent);\n });\n\n if (!found) {\n const hint = rule.alternates\n ? `Add ${rule.name} (or one of: ${rule.alternates.join(', ')}) to .env.local`\n : `Add ${rule.name}=your_value to .env.local`;\n\n issues.push({\n type: 'env',\n severity: rule.required === false ? 'warning' : 'error',\n message: `Missing environment variable: ${rule.name}`,\n hint,\n });\n }\n }\n\n return issues;\n}\n\nexport async function validateFiles(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n\n for (const rule of rules.files) {\n let matches: string[];\n try {\n matches = await fg(rule.path, { cwd: projectDir });\n } catch {\n // Invalid glob pattern - skip\n continue;\n }\n\n if (matches.length === 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Missing file: ${rule.path}`,\n hint: `Create ${rule.path}`,\n });\n continue;\n }\n\n // Check content patterns\n if (rule.mustContain || rule.mustContainAny) {\n const filePath = join(projectDir, matches[0]);\n let content: string;\n try {\n content = await readFile(filePath, 'utf-8');\n } catch {\n // File read error - skip content checks\n continue;\n }\n\n // All must be present\n if (rule.mustContain) {\n for (const pattern of rule.mustContain) {\n if (!content.includes(pattern)) {\n issues.push({\n type: 'pattern',\n severity: 'warning',\n message: `File ${matches[0]} missing expected pattern: \"${pattern}\"`,\n hint: `Ensure ${matches[0]} contains: ${pattern}`,\n });\n }\n }\n }\n\n // At least one must be present\n if (rule.mustContainAny) {\n const hasAny = rule.mustContainAny.some((p) => content.includes(p));\n if (!hasAny) {\n issues.push({\n type: 'pattern',\n severity: 'warning',\n message: `File ${matches[0]} missing one of: ${rule.mustContainAny.join(', ')}`,\n hint: `Ensure ${matches[0]} contains one of these patterns`,\n });\n }\n }\n }\n }\n\n return issues;\n}\n\n/**\n * Framework-specific cross-validations that require reading multiple sources.\n */\nexport async function validateFrameworkSpecific(framework: string, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n\n // Universal cross-validations\n await validateCredentialFormats(projectDir, issues);\n await validateDuplicateEnvVars(projectDir, issues);\n\n // Framework-specific validations\n switch (framework) {\n case 'nextjs':\n await validateNextjsRedirectUri(projectDir, issues);\n await validateNextjsMiddlewarePlacement(projectDir, issues);\n await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');\n break;\n case 'react':\n await validateReactProviderWrapping(projectDir, issues);\n break;\n case 'react-router':\n await validateReactRouterRedirectUri(projectDir, issues);\n await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');\n break;\n case 'tanstack-start':\n await validateTanstackStartRedirectUri(projectDir, issues);\n await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');\n break;\n }\n\n return issues;\n}\n\n/**\n * Validates that the Next.js redirect URI matches an existing callback route.\n *\n * Common failure: .env.local has /auth/callback but route is at /api/auth/callback\n */\nasync function validateNextjsRedirectUri(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return; // No .env.local - other validators handle this\n }\n\n // Extract redirect URI value\n const match = envContent.match(/^NEXT_PUBLIC_WORKOS_REDIRECT_URI=(.+)$/m);\n if (!match) {\n return; // Missing env var - other validators handle this\n }\n\n const redirectUri = match[1].trim();\n let callbackPath: string;\n\n try {\n const url = new URL(redirectUri);\n callbackPath = url.pathname;\n } catch {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid redirect URI: ${redirectUri}`,\n hint: 'NEXT_PUBLIC_WORKOS_REDIRECT_URI must be a valid URL',\n });\n return;\n }\n\n // Remove leading slash for path matching\n const routePath = callbackPath.replace(/^\\//, '');\n\n // Check if route file exists at expected location (Next.js App Router)\n const routePatterns = [\n `app/${routePath}/route.ts`,\n `app/${routePath}/route.tsx`,\n `app/${routePath}/route.js`,\n `app/${routePath}/route.jsx`,\n `src/app/${routePath}/route.ts`,\n `src/app/${routePath}/route.tsx`,\n `src/app/${routePath}/route.js`,\n `src/app/${routePath}/route.jsx`,\n ];\n\n const routeExists = routePatterns.some((pattern) => existsSync(join(projectDir, pattern)));\n\n if (!routeExists) {\n // Check what routes DO exist to give a better hint\n const existingRoutes = await fg(\n ['app/**/callback/**/route.{ts,tsx,js,jsx}', 'src/app/**/callback/**/route.{ts,tsx,js,jsx}'],\n {\n cwd: projectDir,\n },\n );\n\n let hint = `Create a route handler at app/${routePath}/route.ts`;\n if (existingRoutes.length > 0) {\n // Found a route at a different path - likely the mismatch\n const actualPath = '/' + existingRoutes[0].replace(/^(src\\/)?app\\//, '').replace(/\\/route\\.(ts|tsx|js|jsx)$/, '');\n hint =\n `Found callback route at ${existingRoutes[0]} but redirect URI points to ${callbackPath}. Either:\\n` +\n ` 1. Change NEXT_PUBLIC_WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\\n` +\n ` 2. Move the route to app/${routePath}/route.ts`;\n }\n\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Redirect URI path \"${callbackPath}\" has no matching route file`,\n hint,\n });\n }\n}\n\n/**\n * Validates that the React Router redirect URI matches an existing callback route.\n *\n * React Router v7 framework mode uses file-based routing:\n * - /auth/callback → app/routes/auth.callback.tsx (dot notation)\n * - /auth/callback → app/routes/auth/callback.tsx (nested folders)\n */\nasync function validateReactRouterRedirectUri(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return;\n }\n\n const match = envContent.match(/^WORKOS_REDIRECT_URI=(.+)$/m);\n if (!match) {\n return;\n }\n\n const redirectUri = match[1].trim();\n let callbackPath: string;\n\n try {\n const url = new URL(redirectUri);\n callbackPath = url.pathname;\n } catch {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid redirect URI: ${redirectUri}`,\n hint: 'WORKOS_REDIRECT_URI must be a valid URL',\n });\n return;\n }\n\n const routePath = callbackPath.replace(/^\\//, '');\n // React Router uses dot notation: /auth/callback → auth.callback\n const dotPath = routePath.replace(/\\//g, '.');\n\n // Check possible route file locations\n const routePatterns = [\n // Dot notation (e.g., app/routes/auth.callback.tsx)\n `app/routes/${dotPath}.tsx`,\n `app/routes/${dotPath}.ts`,\n `app/routes/${dotPath}.jsx`,\n `app/routes/${dotPath}.js`,\n // Nested folders (e.g., app/routes/auth/callback.tsx)\n `app/routes/${routePath}.tsx`,\n `app/routes/${routePath}.ts`,\n `app/routes/${routePath}.jsx`,\n `app/routes/${routePath}.js`,\n // Index file in folder (e.g., app/routes/auth/callback/index.tsx)\n `app/routes/${routePath}/index.tsx`,\n `app/routes/${routePath}/index.ts`,\n `app/routes/${routePath}/index.jsx`,\n `app/routes/${routePath}/index.js`,\n // Route file in folder (e.g., app/routes/auth/callback/route.tsx)\n `app/routes/${routePath}/route.tsx`,\n `app/routes/${routePath}/route.ts`,\n `app/routes/${routePath}/route.jsx`,\n `app/routes/${routePath}/route.js`,\n ];\n\n const routeExists = routePatterns.some((pattern) => existsSync(join(projectDir, pattern)));\n\n if (!routeExists) {\n const existingRoutes = await fg(['app/routes/**/*callback*.{ts,tsx,js,jsx}'], {\n cwd: projectDir,\n });\n\n let hint = `Create a route at app/routes/${dotPath}.tsx`;\n if (existingRoutes.length > 0) {\n const actualFile = existingRoutes[0];\n // Convert file path back to URL path\n const actualPath =\n '/' +\n actualFile\n .replace(/^app\\/routes\\//, '')\n .replace(/\\.(tsx?|jsx?)$/, '')\n .replace(/\\/(index|route)$/, '')\n .replace(/\\./g, '/');\n hint =\n `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\\n` +\n ` 1. Change WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\\n` +\n ` 2. Move the route to app/routes/${dotPath}.tsx`;\n }\n\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Redirect URI path \"${callbackPath}\" has no matching route file`,\n hint,\n });\n }\n}\n\n/**\n * Validates that the TanStack Start redirect URI matches an existing callback route.\n *\n * TanStack Start uses file-based routing:\n * - /auth/callback → app/routes/auth/callback.tsx\n */\nasync function validateTanstackStartRedirectUri(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return;\n }\n\n const match = envContent.match(/^WORKOS_REDIRECT_URI=(.+)$/m);\n if (!match) {\n return;\n }\n\n const redirectUri = match[1].trim();\n let callbackPath: string;\n\n try {\n const url = new URL(redirectUri);\n callbackPath = url.pathname;\n } catch {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid redirect URI: ${redirectUri}`,\n hint: 'WORKOS_REDIRECT_URI must be a valid URL',\n });\n return;\n }\n\n const routePath = callbackPath.replace(/^\\//, '');\n\n // TanStack Start route patterns\n const routePatterns = [\n `app/routes/${routePath}.tsx`,\n `app/routes/${routePath}.ts`,\n `app/routes/${routePath}.jsx`,\n `app/routes/${routePath}.js`,\n `app/routes/${routePath}/index.tsx`,\n `app/routes/${routePath}/index.ts`,\n `app/routes/${routePath}/index.jsx`,\n `app/routes/${routePath}/index.js`,\n ];\n\n const routeExists = routePatterns.some((pattern) => existsSync(join(projectDir, pattern)));\n\n if (!routeExists) {\n const existingRoutes = await fg(['app/routes/**/*callback*.{ts,tsx,js,jsx}'], {\n cwd: projectDir,\n });\n\n let hint = `Create a route at app/routes/${routePath}.tsx`;\n if (existingRoutes.length > 0) {\n const actualFile = existingRoutes[0];\n const actualPath =\n '/' +\n actualFile\n .replace(/^app\\/routes\\//, '')\n .replace(/\\.(tsx?|jsx?)$/, '')\n .replace(/\\/index$/, '');\n hint =\n `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\\n` +\n ` 1. Change WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\\n` +\n ` 2. Move the route to app/routes/${routePath}.tsx`;\n }\n\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Redirect URI path \"${callbackPath}\" has no matching route file`,\n hint,\n });\n }\n}\n\n/**\n * Validates cookie password is at least 32 characters.\n * WorkOS requires this for secure session encryption.\n */\nasync function validateCookiePasswordLength(\n projectDir: string,\n issues: ValidationIssue[],\n envVarName: string,\n): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return; // No .env.local - other validators handle this\n }\n\n const match = envContent.match(new RegExp(`^${envVarName}=(.*)$`, 'm'));\n if (!match) {\n return; // Missing env var - other validators handle this\n }\n\n const password = match[1].trim();\n if (password.length < 32) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `${envVarName} must be at least 32 characters (currently ${password.length})`,\n hint: `Generate a secure password: openssl rand -base64 32`,\n });\n }\n}\n\n/**\n * Validates credential formats:\n * - API key should start with sk_\n * - Client ID should start with client_\n */\nasync function validateCredentialFormats(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return;\n }\n\n // Check API key format (any common variation)\n const apiKeyPatterns = [/^WORKOS_API_KEY=(.*)$/m, /^NEXT_PUBLIC_WORKOS_API_KEY=(.*)$/m];\n\n for (const pattern of apiKeyPatterns) {\n const match = envContent.match(pattern);\n if (match) {\n const value = match[1].trim();\n if (value && !value.startsWith('sk_')) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid API key format: \"${value.substring(0, 10)}...\"`,\n hint: 'WorkOS API keys start with \"sk_\". Check your WorkOS Dashboard for the correct key.',\n });\n }\n }\n }\n\n // Check Client ID format\n const clientIdPatterns = [/^WORKOS_CLIENT_ID=(.*)$/m, /^NEXT_PUBLIC_WORKOS_CLIENT_ID=(.*)$/m];\n\n for (const pattern of clientIdPatterns) {\n const match = envContent.match(pattern);\n if (match) {\n const value = match[1].trim();\n if (value && !value.startsWith('client_')) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid Client ID format: \"${value.substring(0, 15)}...\"`,\n hint: 'WorkOS Client IDs start with \"client_\". Check your WorkOS Dashboard for the correct ID.',\n });\n }\n }\n }\n}\n\n/**\n * Validates Next.js middleware/proxy is at the correct location.\n * Must be alongside the app/ directory — Next.js only watches for these files\n * in the parent directory of app/.\n */\nasync function validateNextjsMiddlewarePlacement(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n // Determine where app/ lives to know where middleware/proxy should be\n const appInSrc = existsSync(join(projectDir, 'src', 'app'));\n const expectedDir = appInSrc ? 'src/' : '';\n\n const correctPaths = [\n `${expectedDir}middleware.ts`,\n `${expectedDir}middleware.js`,\n `${expectedDir}proxy.ts`,\n `${expectedDir}proxy.js`,\n ];\n\n const hasCorrectPlacement = correctPaths.some((p) => existsSync(join(projectDir, p)));\n if (hasCorrectPlacement) {\n return;\n }\n\n // Check for middleware/proxy at the wrong level\n const allPossible = [\n 'middleware.ts',\n 'middleware.js',\n 'src/middleware.ts',\n 'src/middleware.js',\n 'proxy.ts',\n 'proxy.js',\n 'src/proxy.ts',\n 'src/proxy.js',\n ];\n const wrongLevel = allPossible.find((p) => existsSync(join(projectDir, p)) && !correctPaths.includes(p));\n\n if (wrongLevel) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `${wrongLevel} is at the wrong level — app/ is in ${appInSrc ? 'src/' : 'root'}`,\n hint: `Move it to ${expectedDir}${wrongLevel.replace(/^src\\//, '')} (must be alongside app/ directory). Next.js silently ignores middleware/proxy files at the wrong level.`,\n });\n return;\n }\n\n // Check for deeply misplaced middleware\n const misplaced = await fg(['**/{middleware,proxy}.{ts,js}'], {\n cwd: projectDir,\n ignore: ['node_modules/**'],\n });\n\n if (misplaced.length > 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `middleware/proxy found at wrong location: ${misplaced[0]}`,\n hint: `Must be at ${expectedDir}middleware.ts or ${expectedDir}proxy.ts (alongside app/ directory).`,\n });\n }\n}\n\n/**\n * Validates React SPA has AuthKitProvider wrapping the app.\n */\nasync function validateReactProviderWrapping(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n // Common entry points for React apps\n const entryPatterns = [\n 'src/main.tsx',\n 'src/main.jsx',\n 'src/index.tsx',\n 'src/index.jsx',\n 'src/App.tsx',\n 'src/App.jsx',\n 'app/layout.tsx',\n 'app/layout.jsx',\n ];\n\n let foundProvider = false;\n\n for (const pattern of entryPatterns) {\n const filePath = join(projectDir, pattern);\n if (!existsSync(filePath)) continue;\n\n try {\n const content = await readFile(filePath, 'utf-8');\n if (content.includes('AuthKitProvider')) {\n foundProvider = true;\n break;\n }\n } catch {\n continue;\n }\n }\n\n if (!foundProvider) {\n // Check if package is installed (if not, other validators handle it)\n const pkgPath = join(projectDir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps['@workos-inc/authkit-react']) {\n issues.push({\n type: 'pattern',\n severity: 'warning',\n message: 'AuthKitProvider not found in common entry points',\n hint: 'Wrap your app with <AuthKitProvider> in main.tsx or App.tsx. See: https://workos.com/docs/user-management/react/authkit',\n });\n }\n } catch {\n // Malformed package.json - skip\n }\n }\n }\n}\n\n/**\n * Detects duplicate env vars between .env and .env.local with different values.\n * This can cause confusing behavior where wrong values are used.\n */\nasync function validateDuplicateEnvVars(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env');\n const envLocalPath = join(projectDir, '.env.local');\n\n let envContent: string;\n let envLocalContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return; // No .env file - no conflict possible\n }\n\n try {\n envLocalContent = await readFile(envLocalPath, 'utf-8');\n } catch {\n return; // No .env.local - no conflict possible\n }\n\n // Parse env files into key-value maps\n const parseEnv = (content: string): Map<string, string> => {\n const map = new Map<string, string>();\n for (const line of content.split(/\\r?\\n/)) {\n const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);\n if (match) {\n map.set(match[1], match[2].trim());\n }\n }\n return map;\n };\n\n const envVars = parseEnv(envContent);\n const envLocalVars = parseEnv(envLocalContent);\n\n // Check for WorkOS-related vars that differ\n const workosVars = [\n 'WORKOS_API_KEY',\n 'WORKOS_CLIENT_ID',\n 'WORKOS_REDIRECT_URI',\n 'WORKOS_COOKIE_PASSWORD',\n 'NEXT_PUBLIC_WORKOS_CLIENT_ID',\n 'NEXT_PUBLIC_WORKOS_REDIRECT_URI',\n ];\n\n for (const varName of workosVars) {\n const envValue = envVars.get(varName);\n const localValue = envLocalVars.get(varName);\n\n if (envValue && localValue && envValue !== localValue) {\n issues.push({\n type: 'env',\n severity: 'warning',\n message: `${varName} has different values in .env and .env.local`,\n hint: `.env.local takes precedence. Remove from .env to avoid confusion, or ensure they match.`,\n });\n }\n }\n}\n"]}
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/lib/validation/validator.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport fg from 'fast-glob';\nimport type { ValidationResult, ValidationRules, ValidationIssue } from './types.js';\nimport { runBuildValidation } from './build-validator.js';\nimport { detectPort } from '../port-detection.js';\n\nexport interface ValidateOptions {\n variant?: string;\n runBuild?: boolean;\n}\n\nexport async function validateInstallation(\n framework: string,\n projectDir: string,\n options: ValidateOptions = {},\n): Promise<ValidationResult> {\n const startTime = Date.now();\n const issues: ValidationIssue[] = [];\n\n // Load rules for framework (with optional variant)\n const rules = await loadRules(framework, options.variant);\n if (!rules) {\n return {\n passed: true,\n framework,\n issues: [],\n durationMs: Date.now() - startTime,\n };\n }\n\n // Run validations\n issues.push(...(await validatePackages(rules, projectDir)));\n issues.push(...(await validateEnvVars(rules, projectDir)));\n issues.push(...(await validateFiles(rules, projectDir)));\n\n // Run framework-specific cross-validations\n issues.push(...(await validateFrameworkSpecific(framework, projectDir)));\n\n // Run build validation if enabled\n if (options.runBuild !== false) {\n const buildResult = await runBuildValidation(projectDir);\n issues.push(...buildResult.issues);\n }\n\n return {\n passed: issues.filter((i) => i.severity === 'error').length === 0,\n framework,\n issues,\n durationMs: Date.now() - startTime,\n };\n}\n\nasync function loadRules(framework: string, variant?: string): Promise<ValidationRules | null> {\n const rulesPath = new URL(`./rules/${framework}.json`, import.meta.url);\n try {\n const content = await readFile(rulesPath, 'utf-8');\n const rules = JSON.parse(content) as ValidationRules;\n\n // Merge variant rules if specified\n if (variant && rules.variants?.[variant]) {\n const variantRules = rules.variants[variant];\n return {\n ...rules,\n files: [...rules.files, ...(variantRules.files || [])],\n packages: [...rules.packages, ...(variantRules.packages || [])],\n envVars: [...rules.envVars, ...(variantRules.envVars || [])],\n };\n }\n\n return rules;\n } catch {\n return null; // No rules for this framework yet\n }\n}\n\nexport async function validatePackages(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n const pkgPath = join(projectDir, 'package.json');\n if (!existsSync(pkgPath)) return issues;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n } catch {\n // Malformed package.json - skip package validation\n return issues;\n }\n\n const deps = (pkg.dependencies || {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies || {}) as Record<string, string>;\n const allDeps = { ...devDeps, ...deps };\n\n for (const rule of rules.packages) {\n const location = rule.location || 'any';\n const searchIn = location === 'any' ? allDeps : location === 'dependencies' ? deps : devDeps;\n\n if (!searchIn[rule.name]) {\n issues.push({\n type: 'package',\n severity: 'error',\n message: `Missing package: ${rule.name}`,\n hint: `Run: npm install ${rule.name}`,\n });\n }\n }\n\n return issues;\n}\n\nexport async function validateEnvVars(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n const envPath = join(projectDir, '.env.local');\n let envContent = '';\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n if (rules.envVars.length > 0) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: 'Missing .env.local file',\n hint: 'Create .env.local with required environment variables',\n });\n }\n return issues;\n }\n\n for (const rule of rules.envVars) {\n // Check primary name and any alternates\n const varsToCheck = [rule.name, ...(rule.alternates || [])];\n const found = varsToCheck.some((varName) => {\n const pattern = new RegExp(`^${varName}=.+`, 'm');\n return pattern.test(envContent);\n });\n\n if (!found) {\n const hint = rule.alternates\n ? `Add ${rule.name} (or one of: ${rule.alternates.join(', ')}) to .env.local`\n : `Add ${rule.name}=your_value to .env.local`;\n\n issues.push({\n type: 'env',\n severity: rule.required === false ? 'warning' : 'error',\n message: `Missing environment variable: ${rule.name}`,\n hint,\n });\n }\n }\n\n return issues;\n}\n\nexport async function validateFiles(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n\n for (const rule of rules.files) {\n let matches: string[];\n try {\n matches = await fg(rule.path, { cwd: projectDir });\n } catch {\n // Invalid glob pattern - skip\n continue;\n }\n\n if (matches.length === 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Missing file: ${rule.path}`,\n hint: `Create ${rule.path}`,\n });\n continue;\n }\n\n // Check content patterns\n if (rule.mustContain || rule.mustContainAny) {\n const filePath = join(projectDir, matches[0]);\n let content: string;\n try {\n content = await readFile(filePath, 'utf-8');\n } catch {\n // File read error - skip content checks\n continue;\n }\n\n // All must be present\n if (rule.mustContain) {\n for (const pattern of rule.mustContain) {\n if (!content.includes(pattern)) {\n issues.push({\n type: 'pattern',\n severity: 'warning',\n message: `File ${matches[0]} missing expected pattern: \"${pattern}\"`,\n hint: `Ensure ${matches[0]} contains: ${pattern}`,\n });\n }\n }\n }\n\n // At least one must be present\n if (rule.mustContainAny) {\n const hasAny = rule.mustContainAny.some((p) => content.includes(p));\n if (!hasAny) {\n issues.push({\n type: 'pattern',\n severity: 'warning',\n message: `File ${matches[0]} missing one of: ${rule.mustContainAny.join(', ')}`,\n hint: `Ensure ${matches[0]} contains one of these patterns`,\n });\n }\n }\n }\n }\n\n return issues;\n}\n\n/**\n * Framework-specific cross-validations that require reading multiple sources.\n */\nexport async function validateFrameworkSpecific(framework: string, projectDir: string): Promise<ValidationIssue[]> {\n const issues: ValidationIssue[] = [];\n\n // Universal cross-validations\n await validateCredentialFormats(projectDir, issues);\n await validateDuplicateEnvVars(projectDir, issues);\n\n // Framework-specific validations\n switch (framework) {\n case 'nextjs':\n await validateNextjsRedirectUri(projectDir, issues);\n await validateNextjsMiddlewarePlacement(projectDir, issues);\n await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');\n break;\n case 'react':\n await validateReactProviderWrapping(projectDir, issues);\n break;\n case 'react-router':\n await validateReactRouterRedirectUri(projectDir, issues);\n await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');\n break;\n case 'tanstack-start':\n await validateTanstackStartRedirectUri(projectDir, issues);\n await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');\n break;\n }\n\n return issues;\n}\n\n/**\n * Compares the redirect URI's effective port to the detected dev server port,\n * for local dev hosts only, and records an error-severity issue on mismatch.\n * Skips production URIs and placeholder hosts like http://test.\n */\nfunction validateRedirectUriPort(url: URL, framework: string, projectDir: string, issues: ValidationIssue[]): void {\n // Only enforce for local dev hosts; skip production URIs and placeholder\n // hosts like http://test. Node parses IPv6 hostnames with brackets: [::1].\n const localHosts = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n if (!localHosts.has(url.hostname)) return;\n\n const effectivePort = url.port || (url.protocol === 'https:' ? '443' : '80');\n const detected = detectPort(framework, projectDir);\n\n if (Number(effectivePort) !== detected) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Redirect URI port :${effectivePort} does not match the detected dev server port :${detected}`,\n hint:\n `Update the redirect URI to use port :${detected} in .env.local and the WorkOS dashboard ` +\n `(redirect URI, CORS origin, homepage URL), or change your dev server to run on port ${effectivePort}.`,\n });\n }\n}\n\n/**\n * Validates that the Next.js redirect URI matches an existing callback route.\n *\n * Common failure: .env.local has /auth/callback but route is at /api/auth/callback\n */\nasync function validateNextjsRedirectUri(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return; // No .env.local - other validators handle this\n }\n\n // Extract redirect URI value\n const match = envContent.match(/^NEXT_PUBLIC_WORKOS_REDIRECT_URI=(.+)$/m);\n if (!match) {\n return; // Missing env var - other validators handle this\n }\n\n const redirectUri = match[1].trim();\n let callbackPath: string;\n\n try {\n const url = new URL(redirectUri);\n callbackPath = url.pathname;\n validateRedirectUriPort(url, 'nextjs', projectDir, issues);\n } catch {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid redirect URI: ${redirectUri}`,\n hint: 'NEXT_PUBLIC_WORKOS_REDIRECT_URI must be a valid URL',\n });\n return;\n }\n\n // Remove leading slash for path matching\n const routePath = callbackPath.replace(/^\\//, '');\n\n // Check if route file exists at expected location (Next.js App Router)\n const routePatterns = [\n `app/${routePath}/route.ts`,\n `app/${routePath}/route.tsx`,\n `app/${routePath}/route.js`,\n `app/${routePath}/route.jsx`,\n `src/app/${routePath}/route.ts`,\n `src/app/${routePath}/route.tsx`,\n `src/app/${routePath}/route.js`,\n `src/app/${routePath}/route.jsx`,\n ];\n\n const routeExists = routePatterns.some((pattern) => existsSync(join(projectDir, pattern)));\n\n if (!routeExists) {\n // Check what routes DO exist to give a better hint\n const existingRoutes = await fg(\n ['app/**/callback/**/route.{ts,tsx,js,jsx}', 'src/app/**/callback/**/route.{ts,tsx,js,jsx}'],\n {\n cwd: projectDir,\n },\n );\n\n let hint = `Create a route handler at app/${routePath}/route.ts`;\n if (existingRoutes.length > 0) {\n // Found a route at a different path - likely the mismatch\n const actualPath = '/' + existingRoutes[0].replace(/^(src\\/)?app\\//, '').replace(/\\/route\\.(ts|tsx|js|jsx)$/, '');\n hint =\n `Found callback route at ${existingRoutes[0]} but redirect URI points to ${callbackPath}. Either:\\n` +\n ` 1. Change NEXT_PUBLIC_WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\\n` +\n ` 2. Move the route to app/${routePath}/route.ts`;\n }\n\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Redirect URI path \"${callbackPath}\" has no matching route file`,\n hint,\n });\n }\n}\n\n/**\n * Validates that the React Router redirect URI matches an existing callback route.\n *\n * React Router v7 framework mode uses file-based routing:\n * - /auth/callback → app/routes/auth.callback.tsx (dot notation)\n * - /auth/callback → app/routes/auth/callback.tsx (nested folders)\n */\nasync function validateReactRouterRedirectUri(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return;\n }\n\n const match = envContent.match(/^WORKOS_REDIRECT_URI=(.+)$/m);\n if (!match) {\n return;\n }\n\n const redirectUri = match[1].trim();\n let callbackPath: string;\n\n try {\n const url = new URL(redirectUri);\n callbackPath = url.pathname;\n validateRedirectUriPort(url, 'react-router', projectDir, issues);\n } catch {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid redirect URI: ${redirectUri}`,\n hint: 'WORKOS_REDIRECT_URI must be a valid URL',\n });\n return;\n }\n\n const routePath = callbackPath.replace(/^\\//, '');\n // React Router uses dot notation: /auth/callback → auth.callback\n const dotPath = routePath.replace(/\\//g, '.');\n\n // Check possible route file locations\n const routePatterns = [\n // Dot notation (e.g., app/routes/auth.callback.tsx)\n `app/routes/${dotPath}.tsx`,\n `app/routes/${dotPath}.ts`,\n `app/routes/${dotPath}.jsx`,\n `app/routes/${dotPath}.js`,\n // Nested folders (e.g., app/routes/auth/callback.tsx)\n `app/routes/${routePath}.tsx`,\n `app/routes/${routePath}.ts`,\n `app/routes/${routePath}.jsx`,\n `app/routes/${routePath}.js`,\n // Index file in folder (e.g., app/routes/auth/callback/index.tsx)\n `app/routes/${routePath}/index.tsx`,\n `app/routes/${routePath}/index.ts`,\n `app/routes/${routePath}/index.jsx`,\n `app/routes/${routePath}/index.js`,\n // Route file in folder (e.g., app/routes/auth/callback/route.tsx)\n `app/routes/${routePath}/route.tsx`,\n `app/routes/${routePath}/route.ts`,\n `app/routes/${routePath}/route.jsx`,\n `app/routes/${routePath}/route.js`,\n ];\n\n const routeExists = routePatterns.some((pattern) => existsSync(join(projectDir, pattern)));\n\n if (!routeExists) {\n const existingRoutes = await fg(['app/routes/**/*callback*.{ts,tsx,js,jsx}'], {\n cwd: projectDir,\n });\n\n let hint = `Create a route at app/routes/${dotPath}.tsx`;\n if (existingRoutes.length > 0) {\n const actualFile = existingRoutes[0];\n // Convert file path back to URL path\n const actualPath =\n '/' +\n actualFile\n .replace(/^app\\/routes\\//, '')\n .replace(/\\.(tsx?|jsx?)$/, '')\n .replace(/\\/(index|route)$/, '')\n .replace(/\\./g, '/');\n hint =\n `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\\n` +\n ` 1. Change WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\\n` +\n ` 2. Move the route to app/routes/${dotPath}.tsx`;\n }\n\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Redirect URI path \"${callbackPath}\" has no matching route file`,\n hint,\n });\n }\n}\n\n/**\n * Validates that the TanStack Start redirect URI matches an existing callback route.\n *\n * TanStack Start uses file-based routing:\n * - /auth/callback → app/routes/auth/callback.tsx\n */\nasync function validateTanstackStartRedirectUri(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return;\n }\n\n const match = envContent.match(/^WORKOS_REDIRECT_URI=(.+)$/m);\n if (!match) {\n return;\n }\n\n const redirectUri = match[1].trim();\n let callbackPath: string;\n\n try {\n const url = new URL(redirectUri);\n callbackPath = url.pathname;\n validateRedirectUriPort(url, 'tanstack-start', projectDir, issues);\n } catch {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid redirect URI: ${redirectUri}`,\n hint: 'WORKOS_REDIRECT_URI must be a valid URL',\n });\n return;\n }\n\n const routePath = callbackPath.replace(/^\\//, '');\n\n // TanStack Start route patterns\n const routePatterns = [\n `app/routes/${routePath}.tsx`,\n `app/routes/${routePath}.ts`,\n `app/routes/${routePath}.jsx`,\n `app/routes/${routePath}.js`,\n `app/routes/${routePath}/index.tsx`,\n `app/routes/${routePath}/index.ts`,\n `app/routes/${routePath}/index.jsx`,\n `app/routes/${routePath}/index.js`,\n ];\n\n const routeExists = routePatterns.some((pattern) => existsSync(join(projectDir, pattern)));\n\n if (!routeExists) {\n const existingRoutes = await fg(['app/routes/**/*callback*.{ts,tsx,js,jsx}'], {\n cwd: projectDir,\n });\n\n let hint = `Create a route at app/routes/${routePath}.tsx`;\n if (existingRoutes.length > 0) {\n const actualFile = existingRoutes[0];\n const actualPath =\n '/' +\n actualFile\n .replace(/^app\\/routes\\//, '')\n .replace(/\\.(tsx?|jsx?)$/, '')\n .replace(/\\/index$/, '');\n hint =\n `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\\n` +\n ` 1. Change WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\\n` +\n ` 2. Move the route to app/routes/${routePath}.tsx`;\n }\n\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Redirect URI path \"${callbackPath}\" has no matching route file`,\n hint,\n });\n }\n}\n\n/**\n * Validates cookie password is at least 32 characters.\n * WorkOS requires this for secure session encryption.\n */\nasync function validateCookiePasswordLength(\n projectDir: string,\n issues: ValidationIssue[],\n envVarName: string,\n): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return; // No .env.local - other validators handle this\n }\n\n const match = envContent.match(new RegExp(`^${envVarName}=(.*)$`, 'm'));\n if (!match) {\n return; // Missing env var - other validators handle this\n }\n\n const password = match[1].trim();\n if (password.length < 32) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `${envVarName} must be at least 32 characters (currently ${password.length})`,\n hint: `Generate a secure password: openssl rand -base64 32`,\n });\n }\n}\n\n/**\n * Validates credential formats:\n * - API key should start with sk_\n * - Client ID should start with client_\n */\nasync function validateCredentialFormats(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env.local');\n let envContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return;\n }\n\n // Check API key format (any common variation)\n const apiKeyPatterns = [/^WORKOS_API_KEY=(.*)$/m, /^NEXT_PUBLIC_WORKOS_API_KEY=(.*)$/m];\n\n for (const pattern of apiKeyPatterns) {\n const match = envContent.match(pattern);\n if (match) {\n const value = match[1].trim();\n if (value && !value.startsWith('sk_')) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid API key format: \"${value.substring(0, 10)}...\"`,\n hint: 'WorkOS API keys start with \"sk_\". Check your WorkOS Dashboard for the correct key.',\n });\n }\n }\n }\n\n // Check Client ID format\n const clientIdPatterns = [/^WORKOS_CLIENT_ID=(.*)$/m, /^NEXT_PUBLIC_WORKOS_CLIENT_ID=(.*)$/m];\n\n for (const pattern of clientIdPatterns) {\n const match = envContent.match(pattern);\n if (match) {\n const value = match[1].trim();\n if (value && !value.startsWith('client_')) {\n issues.push({\n type: 'env',\n severity: 'error',\n message: `Invalid Client ID format: \"${value.substring(0, 15)}...\"`,\n hint: 'WorkOS Client IDs start with \"client_\". Check your WorkOS Dashboard for the correct ID.',\n });\n }\n }\n }\n}\n\n/**\n * Validates Next.js middleware/proxy is at the correct location.\n * Must be alongside the app/ directory — Next.js only watches for these files\n * in the parent directory of app/.\n */\nasync function validateNextjsMiddlewarePlacement(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n // Determine where app/ lives to know where middleware/proxy should be\n const appInSrc = existsSync(join(projectDir, 'src', 'app'));\n const expectedDir = appInSrc ? 'src/' : '';\n\n const correctPaths = [\n `${expectedDir}middleware.ts`,\n `${expectedDir}middleware.js`,\n `${expectedDir}proxy.ts`,\n `${expectedDir}proxy.js`,\n ];\n\n const hasCorrectPlacement = correctPaths.some((p) => existsSync(join(projectDir, p)));\n if (hasCorrectPlacement) {\n return;\n }\n\n // Check for middleware/proxy at the wrong level\n const allPossible = [\n 'middleware.ts',\n 'middleware.js',\n 'src/middleware.ts',\n 'src/middleware.js',\n 'proxy.ts',\n 'proxy.js',\n 'src/proxy.ts',\n 'src/proxy.js',\n ];\n const wrongLevel = allPossible.find((p) => existsSync(join(projectDir, p)) && !correctPaths.includes(p));\n\n if (wrongLevel) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `${wrongLevel} is at the wrong level — app/ is in ${appInSrc ? 'src/' : 'root'}`,\n hint: `Move it to ${expectedDir}${wrongLevel.replace(/^src\\//, '')} (must be alongside app/ directory). Next.js silently ignores middleware/proxy files at the wrong level.`,\n });\n return;\n }\n\n // Check for deeply misplaced middleware\n const misplaced = await fg(['**/{middleware,proxy}.{ts,js}'], {\n cwd: projectDir,\n ignore: ['node_modules/**'],\n });\n\n if (misplaced.length > 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `middleware/proxy found at wrong location: ${misplaced[0]}`,\n hint: `Must be at ${expectedDir}middleware.ts or ${expectedDir}proxy.ts (alongside app/ directory).`,\n });\n }\n}\n\n/**\n * Validates React SPA has AuthKitProvider wrapping the app.\n */\nasync function validateReactProviderWrapping(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n // Common entry points for React apps\n const entryPatterns = [\n 'src/main.tsx',\n 'src/main.jsx',\n 'src/index.tsx',\n 'src/index.jsx',\n 'src/App.tsx',\n 'src/App.jsx',\n 'app/layout.tsx',\n 'app/layout.jsx',\n ];\n\n let foundProvider = false;\n\n for (const pattern of entryPatterns) {\n const filePath = join(projectDir, pattern);\n if (!existsSync(filePath)) continue;\n\n try {\n const content = await readFile(filePath, 'utf-8');\n if (content.includes('AuthKitProvider')) {\n foundProvider = true;\n break;\n }\n } catch {\n continue;\n }\n }\n\n if (!foundProvider) {\n // Check if package is installed (if not, other validators handle it)\n const pkgPath = join(projectDir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps['@workos-inc/authkit-react']) {\n issues.push({\n type: 'pattern',\n severity: 'warning',\n message: 'AuthKitProvider not found in common entry points',\n hint: 'Wrap your app with <AuthKitProvider> in main.tsx or App.tsx. See: https://workos.com/docs/user-management/react/authkit',\n });\n }\n } catch {\n // Malformed package.json - skip\n }\n }\n }\n}\n\n/**\n * Detects duplicate env vars between .env and .env.local with different values.\n * This can cause confusing behavior where wrong values are used.\n */\nasync function validateDuplicateEnvVars(projectDir: string, issues: ValidationIssue[]): Promise<void> {\n const envPath = join(projectDir, '.env');\n const envLocalPath = join(projectDir, '.env.local');\n\n let envContent: string;\n let envLocalContent: string;\n\n try {\n envContent = await readFile(envPath, 'utf-8');\n } catch {\n return; // No .env file - no conflict possible\n }\n\n try {\n envLocalContent = await readFile(envLocalPath, 'utf-8');\n } catch {\n return; // No .env.local - no conflict possible\n }\n\n // Parse env files into key-value maps\n const parseEnv = (content: string): Map<string, string> => {\n const map = new Map<string, string>();\n for (const line of content.split(/\\r?\\n/)) {\n const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);\n if (match) {\n map.set(match[1], match[2].trim());\n }\n }\n return map;\n };\n\n const envVars = parseEnv(envContent);\n const envLocalVars = parseEnv(envLocalContent);\n\n // Check for WorkOS-related vars that differ\n const workosVars = [\n 'WORKOS_API_KEY',\n 'WORKOS_CLIENT_ID',\n 'WORKOS_REDIRECT_URI',\n 'WORKOS_COOKIE_PASSWORD',\n 'NEXT_PUBLIC_WORKOS_CLIENT_ID',\n 'NEXT_PUBLIC_WORKOS_REDIRECT_URI',\n ];\n\n for (const varName of workosVars) {\n const envValue = envVars.get(varName);\n const localValue = envLocalVars.get(varName);\n\n if (envValue && localValue && envValue !== localValue) {\n issues.push({\n type: 'env',\n severity: 'warning',\n message: `${varName} has different values in .env and .env.local`,\n hint: `.env.local takes precedence. Remove from .env to avoid confusion, or ensure they match.`,\n });\n }\n }\n}\n"]}