workos 0.18.0 → 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 (333) hide show
  1. package/README.md +8 -145
  2. package/dist/bin.js +129 -50
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/api/index.js +18 -10
  5. package/dist/commands/api/index.js.map +1 -1
  6. package/dist/commands/api/interactive.js +11 -11
  7. package/dist/commands/api/interactive.js.map +1 -1
  8. package/dist/commands/claim.js +28 -16
  9. package/dist/commands/claim.js.map +1 -1
  10. package/dist/commands/connection.js +3 -3
  11. package/dist/commands/connection.js.map +1 -1
  12. package/dist/commands/debug.js +6 -6
  13. package/dist/commands/debug.js.map +1 -1
  14. package/dist/commands/dev.d.ts +7 -8
  15. package/dist/commands/dev.js +14 -14
  16. package/dist/commands/dev.js.map +1 -1
  17. package/dist/commands/directory.js +3 -3
  18. package/dist/commands/directory.js.map +1 -1
  19. package/dist/commands/doctor.js +2 -2
  20. package/dist/commands/doctor.js.map +1 -1
  21. package/dist/commands/emulate.d.ts +1 -0
  22. package/dist/commands/emulate.js +3 -2
  23. package/dist/commands/emulate.js.map +1 -1
  24. package/dist/commands/env.d.ts +10 -0
  25. package/dist/commands/env.js +94 -18
  26. package/dist/commands/env.js.map +1 -1
  27. package/dist/commands/install.js +8 -13
  28. package/dist/commands/install.js.map +1 -1
  29. package/dist/commands/login.d.ts +25 -15
  30. package/dist/commands/login.js +126 -71
  31. package/dist/commands/login.js.map +1 -1
  32. package/dist/commands/logout.js +4 -4
  33. package/dist/commands/logout.js.map +1 -1
  34. package/dist/commands/mcp.js +7 -16
  35. package/dist/commands/mcp.js.map +1 -1
  36. package/dist/commands/migrations.js +2 -1
  37. package/dist/commands/migrations.js.map +1 -1
  38. package/dist/commands/seed.js +5 -4
  39. package/dist/commands/seed.js.map +1 -1
  40. package/dist/commands/setup.d.ts +50 -0
  41. package/dist/commands/setup.js +237 -0
  42. package/dist/commands/setup.js.map +1 -0
  43. package/dist/commands/vault-run.js +3 -2
  44. package/dist/commands/vault-run.js.map +1 -1
  45. package/dist/commands/verify-login.d.ts +25 -0
  46. package/dist/commands/verify-login.js +158 -0
  47. package/dist/commands/verify-login.js.map +1 -0
  48. package/dist/doctor/issues.js +3 -2
  49. package/dist/doctor/issues.js.map +1 -1
  50. package/dist/doctor/output.js +2 -1
  51. package/dist/doctor/output.js.map +1 -1
  52. package/dist/integrations/dotnet/index.js +1 -1
  53. package/dist/integrations/dotnet/index.js.map +1 -1
  54. package/dist/integrations/elixir/index.js +1 -1
  55. package/dist/integrations/elixir/index.js.map +1 -1
  56. package/dist/integrations/go/index.js +1 -1
  57. package/dist/integrations/go/index.js.map +1 -1
  58. package/dist/integrations/nextjs/index.js +8 -5
  59. package/dist/integrations/nextjs/index.js.map +1 -1
  60. package/dist/integrations/nextjs/utils.d.ts +1 -1
  61. package/dist/integrations/nextjs/utils.js +21 -6
  62. package/dist/integrations/nextjs/utils.js.map +1 -1
  63. package/dist/integrations/react-router/index.js +5 -5
  64. package/dist/integrations/react-router/index.js.map +1 -1
  65. package/dist/integrations/react-router/utils.js +28 -12
  66. package/dist/integrations/react-router/utils.js.map +1 -1
  67. package/dist/integrations/ruby/index.js +1 -1
  68. package/dist/integrations/ruby/index.js.map +1 -1
  69. package/dist/lib/adapters/cli-adapter.d.ts +33 -4
  70. package/dist/lib/adapters/cli-adapter.js +251 -135
  71. package/dist/lib/adapters/cli-adapter.js.map +1 -1
  72. package/dist/lib/adapters/dashboard-adapter.js +3 -3
  73. package/dist/lib/adapters/dashboard-adapter.js.map +1 -1
  74. package/dist/lib/adapters/headless-adapter.d.ts +7 -0
  75. package/dist/lib/adapters/headless-adapter.js +42 -5
  76. package/dist/lib/adapters/headless-adapter.js.map +1 -1
  77. package/dist/lib/agent-interface.js +15 -1
  78. package/dist/lib/agent-interface.js.map +1 -1
  79. package/dist/lib/agent-runner.js +1 -1
  80. package/dist/lib/agent-runner.js.map +1 -1
  81. package/dist/lib/api-key.js +2 -1
  82. package/dist/lib/api-key.js.map +1 -1
  83. package/dist/lib/completion-data.d.ts +33 -0
  84. package/dist/lib/completion-data.js +33 -0
  85. package/dist/lib/completion-data.js.map +1 -0
  86. package/dist/lib/config-store.d.ts +13 -0
  87. package/dist/lib/config-store.js +24 -0
  88. package/dist/lib/config-store.js.map +1 -1
  89. package/dist/lib/config.d.ts +1 -1
  90. package/dist/lib/config.js +1 -1
  91. package/dist/lib/config.js.map +1 -1
  92. package/dist/lib/constants.d.ts +8 -0
  93. package/dist/lib/constants.js +8 -0
  94. package/dist/lib/constants.js.map +1 -1
  95. package/dist/lib/events.d.ts +34 -1
  96. package/dist/lib/events.js.map +1 -1
  97. package/dist/lib/framework-config.d.ts +6 -0
  98. package/dist/lib/framework-config.js.map +1 -1
  99. package/dist/lib/installer-core.d.ts +14 -1
  100. package/dist/lib/installer-core.js +24 -5
  101. package/dist/lib/installer-core.js.map +1 -1
  102. package/dist/lib/installer-core.types.d.ts +3 -1
  103. package/dist/lib/installer-core.types.js.map +1 -1
  104. package/dist/lib/mcp-clients.d.ts +6 -0
  105. package/dist/lib/mcp-clients.js +13 -0
  106. package/dist/lib/mcp-clients.js.map +1 -1
  107. package/dist/lib/port-detection.js +19 -13
  108. package/dist/lib/port-detection.js.map +1 -1
  109. package/dist/lib/preferences.d.ts +30 -2
  110. package/dist/lib/preferences.js +34 -8
  111. package/dist/lib/preferences.js.map +1 -1
  112. package/dist/lib/run-with-core.js +40 -3
  113. package/dist/lib/run-with-core.js.map +1 -1
  114. package/dist/lib/telemetry-notice.d.ts +5 -5
  115. package/dist/lib/telemetry-notice.js +7 -8
  116. package/dist/lib/telemetry-notice.js.map +1 -1
  117. package/dist/lib/unclaimed-env-provision.js +15 -12
  118. package/dist/lib/unclaimed-env-provision.js.map +1 -1
  119. package/dist/lib/unclaimed-warning.js +4 -3
  120. package/dist/lib/unclaimed-warning.js.map +1 -1
  121. package/dist/lib/validation/security-checks.js +2 -1
  122. package/dist/lib/validation/security-checks.js.map +1 -1
  123. package/dist/lib/validation/validator.js +30 -3
  124. package/dist/lib/validation/validator.js.map +1 -1
  125. package/dist/lib/workos-management.js +26 -19
  126. package/dist/lib/workos-management.js.map +1 -1
  127. package/dist/run.d.ts +1 -0
  128. package/dist/run.js +5 -1
  129. package/dist/run.js.map +1 -1
  130. package/dist/steps/add-or-update-environment-variables.js +10 -10
  131. package/dist/steps/add-or-update-environment-variables.js.map +1 -1
  132. package/dist/steps/run-prettier.js +3 -3
  133. package/dist/steps/run-prettier.js.map +1 -1
  134. package/dist/steps/upload-environment-variables/index.js +15 -3
  135. package/dist/steps/upload-environment-variables/index.js.map +1 -1
  136. package/dist/steps/upload-environment-variables/providers/vercel.js +2 -2
  137. package/dist/steps/upload-environment-variables/providers/vercel.js.map +1 -1
  138. package/dist/utils/box.d.ts +6 -30
  139. package/dist/utils/box.js +8 -95
  140. package/dist/utils/box.js.map +1 -1
  141. package/dist/utils/cli-symbols.d.ts +14 -2
  142. package/dist/utils/cli-symbols.js +12 -0
  143. package/dist/utils/cli-symbols.js.map +1 -1
  144. package/dist/utils/debug.js +3 -3
  145. package/dist/utils/debug.js.map +1 -1
  146. package/dist/utils/environment.js +1 -1
  147. package/dist/utils/environment.js.map +1 -1
  148. package/dist/utils/help-json.js +115 -4
  149. package/dist/utils/help-json.js.map +1 -1
  150. package/dist/utils/package-manager.js +1 -1
  151. package/dist/utils/package-manager.js.map +1 -1
  152. package/dist/utils/recovery-hints.d.ts +5 -0
  153. package/dist/utils/recovery-hints.js +12 -0
  154. package/dist/utils/recovery-hints.js.map +1 -1
  155. package/dist/utils/summary-box.d.ts +8 -1
  156. package/dist/utils/summary-box.js +67 -4
  157. package/dist/utils/summary-box.js.map +1 -1
  158. package/dist/utils/types.d.ts +10 -0
  159. package/dist/utils/types.js.map +1 -1
  160. package/dist/utils/{clack-utils.d.ts → ui-utils.d.ts} +1 -1
  161. package/dist/utils/{clack-utils.js → ui-utils.js} +64 -37
  162. package/dist/utils/ui-utils.js.map +1 -0
  163. package/dist/utils/ui.d.ts +148 -0
  164. package/dist/utils/ui.js +368 -0
  165. package/dist/utils/ui.js.map +1 -0
  166. package/package.json +5 -13
  167. package/dist/check-coverage.ts +0 -237
  168. package/dist/emulate/core/id.d.ts +0 -48
  169. package/dist/emulate/core/id.js +0 -73
  170. package/dist/emulate/core/id.js.map +0 -1
  171. package/dist/emulate/core/index.d.ts +0 -8
  172. package/dist/emulate/core/index.js +0 -8
  173. package/dist/emulate/core/index.js.map +0 -1
  174. package/dist/emulate/core/jwt.d.ts +0 -28
  175. package/dist/emulate/core/jwt.js +0 -78
  176. package/dist/emulate/core/jwt.js.map +0 -1
  177. package/dist/emulate/core/middleware/auth.d.ts +0 -15
  178. package/dist/emulate/core/middleware/auth.js +0 -17
  179. package/dist/emulate/core/middleware/auth.js.map +0 -1
  180. package/dist/emulate/core/middleware/error-handler.d.ts +0 -22
  181. package/dist/emulate/core/middleware/error-handler.js +0 -72
  182. package/dist/emulate/core/middleware/error-handler.js.map +0 -1
  183. package/dist/emulate/core/pagination.d.ts +0 -27
  184. package/dist/emulate/core/pagination.js +0 -43
  185. package/dist/emulate/core/pagination.js.map +0 -1
  186. package/dist/emulate/core/plugin.d.ts +0 -15
  187. package/dist/emulate/core/plugin.js +0 -2
  188. package/dist/emulate/core/plugin.js.map +0 -1
  189. package/dist/emulate/core/server.d.ts +0 -17
  190. package/dist/emulate/core/server.js +0 -90
  191. package/dist/emulate/core/server.js.map +0 -1
  192. package/dist/emulate/core/store.d.ts +0 -44
  193. package/dist/emulate/core/store.js +0 -169
  194. package/dist/emulate/core/store.js.map +0 -1
  195. package/dist/emulate/index.d.ts +0 -25
  196. package/dist/emulate/index.js +0 -47
  197. package/dist/emulate/index.js.map +0 -1
  198. package/dist/emulate/workos/constants.d.ts +0 -56
  199. package/dist/emulate/workos/constants.js +0 -56
  200. package/dist/emulate/workos/constants.js.map +0 -1
  201. package/dist/emulate/workos/entities.d.ts +0 -360
  202. package/dist/emulate/workos/entities.js +0 -2
  203. package/dist/emulate/workos/entities.js.map +0 -1
  204. package/dist/emulate/workos/event-bus.d.ts +0 -17
  205. package/dist/emulate/workos/event-bus.js +0 -70
  206. package/dist/emulate/workos/event-bus.js.map +0 -1
  207. package/dist/emulate/workos/helpers.d.ts +0 -72
  208. package/dist/emulate/workos/helpers.js +0 -211
  209. package/dist/emulate/workos/helpers.js.map +0 -1
  210. package/dist/emulate/workos/index.d.ts +0 -93
  211. package/dist/emulate/workos/index.js +0 -326
  212. package/dist/emulate/workos/index.js.map +0 -1
  213. package/dist/emulate/workos/role-helpers.d.ts +0 -21
  214. package/dist/emulate/workos/role-helpers.js +0 -130
  215. package/dist/emulate/workos/role-helpers.js.map +0 -1
  216. package/dist/emulate/workos/routes/api-keys.d.ts +0 -2
  217. package/dist/emulate/workos/routes/api-keys.js +0 -32
  218. package/dist/emulate/workos/routes/api-keys.js.map +0 -1
  219. package/dist/emulate/workos/routes/audit-logs.d.ts +0 -2
  220. package/dist/emulate/workos/routes/audit-logs.js +0 -104
  221. package/dist/emulate/workos/routes/audit-logs.js.map +0 -1
  222. package/dist/emulate/workos/routes/auth-challenges.d.ts +0 -2
  223. package/dist/emulate/workos/routes/auth-challenges.js +0 -51
  224. package/dist/emulate/workos/routes/auth-challenges.js.map +0 -1
  225. package/dist/emulate/workos/routes/auth-factors.d.ts +0 -2
  226. package/dist/emulate/workos/routes/auth-factors.js +0 -51
  227. package/dist/emulate/workos/routes/auth-factors.js.map +0 -1
  228. package/dist/emulate/workos/routes/auth.d.ts +0 -2
  229. package/dist/emulate/workos/routes/auth.js +0 -350
  230. package/dist/emulate/workos/routes/auth.js.map +0 -1
  231. package/dist/emulate/workos/routes/authorization-checks.d.ts +0 -10
  232. package/dist/emulate/workos/routes/authorization-checks.js +0 -123
  233. package/dist/emulate/workos/routes/authorization-checks.js.map +0 -1
  234. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +0 -2
  235. package/dist/emulate/workos/routes/authorization-org-roles.js +0 -64
  236. package/dist/emulate/workos/routes/authorization-org-roles.js.map +0 -1
  237. package/dist/emulate/workos/routes/authorization-permissions.d.ts +0 -2
  238. package/dist/emulate/workos/routes/authorization-permissions.js +0 -67
  239. package/dist/emulate/workos/routes/authorization-permissions.js.map +0 -1
  240. package/dist/emulate/workos/routes/authorization-resources.d.ts +0 -2
  241. package/dist/emulate/workos/routes/authorization-resources.js +0 -117
  242. package/dist/emulate/workos/routes/authorization-resources.js.map +0 -1
  243. package/dist/emulate/workos/routes/authorization-roles.d.ts +0 -2
  244. package/dist/emulate/workos/routes/authorization-roles.js +0 -13
  245. package/dist/emulate/workos/routes/authorization-roles.js.map +0 -1
  246. package/dist/emulate/workos/routes/config.d.ts +0 -2
  247. package/dist/emulate/workos/routes/config.js +0 -57
  248. package/dist/emulate/workos/routes/config.js.map +0 -1
  249. package/dist/emulate/workos/routes/connect.d.ts +0 -2
  250. package/dist/emulate/workos/routes/connect.js +0 -65
  251. package/dist/emulate/workos/routes/connect.js.map +0 -1
  252. package/dist/emulate/workos/routes/connections.d.ts +0 -2
  253. package/dist/emulate/workos/routes/connections.js +0 -73
  254. package/dist/emulate/workos/routes/connections.js.map +0 -1
  255. package/dist/emulate/workos/routes/data-integrations.d.ts +0 -2
  256. package/dist/emulate/workos/routes/data-integrations.js +0 -55
  257. package/dist/emulate/workos/routes/data-integrations.js.map +0 -1
  258. package/dist/emulate/workos/routes/directories.d.ts +0 -2
  259. package/dist/emulate/workos/routes/directories.js +0 -90
  260. package/dist/emulate/workos/routes/directories.js.map +0 -1
  261. package/dist/emulate/workos/routes/email-verification.d.ts +0 -2
  262. package/dist/emulate/workos/routes/email-verification.js +0 -49
  263. package/dist/emulate/workos/routes/email-verification.js.map +0 -1
  264. package/dist/emulate/workos/routes/events.d.ts +0 -2
  265. package/dist/emulate/workos/routes/events.js +0 -18
  266. package/dist/emulate/workos/routes/events.js.map +0 -1
  267. package/dist/emulate/workos/routes/feature-flags.d.ts +0 -2
  268. package/dist/emulate/workos/routes/feature-flags.js +0 -103
  269. package/dist/emulate/workos/routes/feature-flags.js.map +0 -1
  270. package/dist/emulate/workos/routes/invitations.d.ts +0 -2
  271. package/dist/emulate/workos/routes/invitations.js +0 -122
  272. package/dist/emulate/workos/routes/invitations.js.map +0 -1
  273. package/dist/emulate/workos/routes/legacy-mfa.d.ts +0 -2
  274. package/dist/emulate/workos/routes/legacy-mfa.js +0 -75
  275. package/dist/emulate/workos/routes/legacy-mfa.js.map +0 -1
  276. package/dist/emulate/workos/routes/magic-auth.d.ts +0 -2
  277. package/dist/emulate/workos/routes/magic-auth.js +0 -32
  278. package/dist/emulate/workos/routes/magic-auth.js.map +0 -1
  279. package/dist/emulate/workos/routes/memberships.d.ts +0 -2
  280. package/dist/emulate/workos/routes/memberships.js +0 -114
  281. package/dist/emulate/workos/routes/memberships.js.map +0 -1
  282. package/dist/emulate/workos/routes/organization-domains.d.ts +0 -2
  283. package/dist/emulate/workos/routes/organization-domains.js +0 -58
  284. package/dist/emulate/workos/routes/organization-domains.js.map +0 -1
  285. package/dist/emulate/workos/routes/organizations.d.ts +0 -2
  286. package/dist/emulate/workos/routes/organizations.js +0 -131
  287. package/dist/emulate/workos/routes/organizations.js.map +0 -1
  288. package/dist/emulate/workos/routes/password-reset.d.ts +0 -2
  289. package/dist/emulate/workos/routes/password-reset.js +0 -61
  290. package/dist/emulate/workos/routes/password-reset.js.map +0 -1
  291. package/dist/emulate/workos/routes/pipes.d.ts +0 -2
  292. package/dist/emulate/workos/routes/pipes.js +0 -82
  293. package/dist/emulate/workos/routes/pipes.js.map +0 -1
  294. package/dist/emulate/workos/routes/portal.d.ts +0 -2
  295. package/dist/emulate/workos/routes/portal.js +0 -18
  296. package/dist/emulate/workos/routes/portal.js.map +0 -1
  297. package/dist/emulate/workos/routes/radar.d.ts +0 -2
  298. package/dist/emulate/workos/routes/radar.js +0 -41
  299. package/dist/emulate/workos/routes/radar.js.map +0 -1
  300. package/dist/emulate/workos/routes/sessions.d.ts +0 -2
  301. package/dist/emulate/workos/routes/sessions.js +0 -51
  302. package/dist/emulate/workos/routes/sessions.js.map +0 -1
  303. package/dist/emulate/workos/routes/sso.d.ts +0 -2
  304. package/dist/emulate/workos/routes/sso.js +0 -161
  305. package/dist/emulate/workos/routes/sso.js.map +0 -1
  306. package/dist/emulate/workos/routes/user-features.d.ts +0 -2
  307. package/dist/emulate/workos/routes/user-features.js +0 -50
  308. package/dist/emulate/workos/routes/user-features.js.map +0 -1
  309. package/dist/emulate/workos/routes/users.d.ts +0 -2
  310. package/dist/emulate/workos/routes/users.js +0 -129
  311. package/dist/emulate/workos/routes/users.js.map +0 -1
  312. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +0 -2
  313. package/dist/emulate/workos/routes/webhook-endpoints.js +0 -67
  314. package/dist/emulate/workos/routes/webhook-endpoints.js.map +0 -1
  315. package/dist/emulate/workos/routes/widgets.d.ts +0 -2
  316. package/dist/emulate/workos/routes/widgets.js +0 -27
  317. package/dist/emulate/workos/routes/widgets.js.map +0 -1
  318. package/dist/emulate/workos/store.d.ts +0 -48
  319. package/dist/emulate/workos/store.js +0 -102
  320. package/dist/emulate/workos/store.js.map +0 -1
  321. package/dist/emulate/workos/webhook-signer.d.ts +0 -1
  322. package/dist/emulate/workos/webhook-signer.js +0 -8
  323. package/dist/emulate/workos/webhook-signer.js.map +0 -1
  324. package/dist/gen-routes-lib.spec.ts +0 -659
  325. package/dist/gen-routes-lib.ts +0 -647
  326. package/dist/gen-routes.ts +0 -96
  327. package/dist/lib/mcp-notice.d.ts +0 -72
  328. package/dist/lib/mcp-notice.js +0 -275
  329. package/dist/lib/mcp-notice.js.map +0 -1
  330. package/dist/utils/clack-utils.js.map +0 -1
  331. package/dist/utils/clack.d.ts +0 -5
  332. package/dist/utils/clack.js +0 -34
  333. package/dist/utils/clack.js.map +0 -1
@@ -1,237 +0,0 @@
1
- #!/usr/bin/env tsx
2
- /**
3
- * Coverage checker: compares the WorkOS OpenAPI spec against the emulator's
4
- * registered routes to find missing or extra endpoints.
5
- *
6
- * Usage:
7
- * pnpm check:coverage path/to/openapi.yaml
8
- * pnpm check:coverage ~/Developer/workos/packages/api/open-api-spec.yaml
9
- *
10
- * Reports:
11
- * - Spec endpoints missing from the emulator
12
- * - Emulator endpoints not in the spec (custom/internal)
13
- * - Coverage percentage
14
- */
15
-
16
- import { readFileSync, existsSync, readdirSync } from 'node:fs';
17
- import { resolve, extname, join } from 'node:path';
18
- import YAML from 'yaml';
19
-
20
- // ---------------------------------------------------------------------------
21
- // Parse OpenAPI spec endpoints
22
- // ---------------------------------------------------------------------------
23
-
24
- interface SpecEndpoint {
25
- method: string;
26
- path: string;
27
- operationId?: string;
28
- summary?: string;
29
- tags: string[];
30
- }
31
-
32
- function parseOpenApiEndpoints(specPath: string): SpecEndpoint[] {
33
- const raw = readFileSync(specPath, 'utf-8');
34
- const ext = extname(specPath).toLowerCase();
35
- const spec = ext === '.yaml' || ext === '.yml' ? YAML.parse(raw) : JSON.parse(raw);
36
-
37
- const endpoints: SpecEndpoint[] = [];
38
- const methods = ['get', 'post', 'put', 'patch', 'delete'] as const;
39
-
40
- for (const [path, item] of Object.entries(spec.paths ?? {}) as [string, any][]) {
41
- for (const method of methods) {
42
- const op = item[method];
43
- if (!op) continue;
44
-
45
- // Normalize OpenAPI path params {id} → :id
46
- const normalizedPath = path.replace(/\{([^}]+)\}/g, ':$1');
47
-
48
- endpoints.push({
49
- method: method.toUpperCase(),
50
- path: normalizedPath,
51
- operationId: op.operationId,
52
- summary: op.summary,
53
- tags: op.tags ?? [],
54
- });
55
- }
56
- }
57
-
58
- return endpoints;
59
- }
60
-
61
- // ---------------------------------------------------------------------------
62
- // Parse emulator registered routes from source files
63
- // ---------------------------------------------------------------------------
64
-
65
- interface EmulatorEndpoint {
66
- method: string;
67
- path: string;
68
- file: string;
69
- line: number;
70
- }
71
-
72
- function parseEmulatorEndpoints(): EmulatorEndpoint[] {
73
- const routesDir = resolve('src/emulate/workos/routes');
74
- const serverFile = resolve('src/emulate/core/server.ts');
75
- const endpoints: EmulatorEndpoint[] = [];
76
-
77
- const routePattern = /app\.(get|post|put|patch|delete)\('([^']+)'/g;
78
-
79
- const filesToScan: string[] = [];
80
-
81
- // Collect route files
82
- if (existsSync(routesDir)) {
83
- for (const file of readdirSync(routesDir)) {
84
- if (file.endsWith('.ts') && !file.endsWith('.spec.ts')) {
85
- filesToScan.push(join(routesDir, file));
86
- }
87
- }
88
- }
89
-
90
- // Also scan server.ts for JWKS and other direct routes
91
- if (existsSync(serverFile)) {
92
- filesToScan.push(serverFile);
93
- }
94
-
95
- for (const filePath of filesToScan) {
96
- const content = readFileSync(filePath, 'utf-8');
97
- const lines = content.split('\n');
98
-
99
- for (let i = 0; i < lines.length; i++) {
100
- routePattern.lastIndex = 0;
101
- let match;
102
- while ((match = routePattern.exec(lines[i])) !== null) {
103
- endpoints.push({
104
- method: match[1].toUpperCase(),
105
- path: match[2],
106
- file: filePath.replace(resolve('.') + '/', ''),
107
- line: i + 1,
108
- });
109
- }
110
- }
111
- }
112
-
113
- return endpoints;
114
- }
115
-
116
- // ---------------------------------------------------------------------------
117
- // Normalize paths for comparison
118
- // ---------------------------------------------------------------------------
119
-
120
- /** Normalize path params to a canonical form for matching.
121
- * e.g., :id, :orgId, :organization_id all become :param in the same position */
122
- function normalizePath(path: string): string {
123
- return path
124
- .replace(/:[a-zA-Z_]+/g, ':param')
125
- .replace(/\/+$/, '')
126
- .toLowerCase();
127
- }
128
-
129
- function routeKey(method: string, path: string): string {
130
- return `${method} ${normalizePath(path)}`;
131
- }
132
-
133
- // ---------------------------------------------------------------------------
134
- // Main
135
- // ---------------------------------------------------------------------------
136
-
137
- function main(): void {
138
- const specPath = process.argv[2];
139
- if (!specPath) {
140
- console.error('Usage: check-coverage <openapi-spec-path>');
141
- console.error(' e.g.: pnpm check:coverage ~/Developer/workos/packages/api/open-api-spec.yaml');
142
- process.exit(1);
143
- }
144
-
145
- const resolvedSpec = resolve(specPath);
146
- if (!existsSync(resolvedSpec)) {
147
- console.error(`Spec file not found: ${resolvedSpec}`);
148
- process.exit(1);
149
- }
150
-
151
- const specEndpoints = parseOpenApiEndpoints(resolvedSpec);
152
- const emulatorEndpoints = parseEmulatorEndpoints();
153
-
154
- // Build lookup maps
155
- const specMap = new Map<string, SpecEndpoint>();
156
- for (const ep of specEndpoints) {
157
- specMap.set(routeKey(ep.method, ep.path), ep);
158
- }
159
-
160
- const emulatorMap = new Map<string, EmulatorEndpoint>();
161
- for (const ep of emulatorEndpoints) {
162
- emulatorMap.set(routeKey(ep.method, ep.path), ep);
163
- }
164
-
165
- // Find gaps
166
- const missing: SpecEndpoint[] = [];
167
- const covered: SpecEndpoint[] = [];
168
- for (const [key, ep] of specMap) {
169
- if (emulatorMap.has(key)) {
170
- covered.push(ep);
171
- } else {
172
- missing.push(ep);
173
- }
174
- }
175
-
176
- const extra: EmulatorEndpoint[] = [];
177
- for (const [key, ep] of emulatorMap) {
178
- if (!specMap.has(key)) {
179
- extra.push(ep);
180
- }
181
- }
182
-
183
- // Group missing by tag
184
- const missingByTag = new Map<string, SpecEndpoint[]>();
185
- for (const ep of missing) {
186
- const tag = ep.tags[0] ?? 'untagged';
187
- if (!missingByTag.has(tag)) missingByTag.set(tag, []);
188
- missingByTag.get(tag)!.push(ep);
189
- }
190
-
191
- // Report
192
- const total = specEndpoints.length;
193
- const coveredCount = covered.length;
194
- const pct = total > 0 ? ((coveredCount / total) * 100).toFixed(1) : '0';
195
-
196
- console.log('');
197
- console.log('=== Emulator API Coverage Report ===');
198
- console.log('');
199
- console.log(` Spec endpoints: ${total}`);
200
- console.log(` Emulator endpoints: ${emulatorEndpoints.length}`);
201
- console.log(` Covered: ${coveredCount}/${total} (${pct}%)`);
202
- console.log(` Missing: ${missing.length}`);
203
- console.log(` Extra (emulator-only): ${extra.length}`);
204
- console.log('');
205
-
206
- if (missing.length > 0) {
207
- console.log('--- Missing from emulator ---');
208
- console.log('');
209
- for (const [tag, eps] of [...missingByTag.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
210
- console.log(` [${tag}]`);
211
- for (const ep of eps) {
212
- const desc = ep.summary ? ` — ${ep.summary}` : '';
213
- console.log(` ${ep.method.padEnd(6)} ${ep.path}${desc}`);
214
- }
215
- console.log('');
216
- }
217
- }
218
-
219
- if (extra.length > 0) {
220
- console.log('--- Emulator-only (not in spec) ---');
221
- console.log('');
222
- for (const ep of extra.sort((a, b) => a.path.localeCompare(b.path))) {
223
- console.log(` ${ep.method.padEnd(6)} ${ep.path} (${ep.file}:${ep.line})`);
224
- }
225
- console.log('');
226
- }
227
-
228
- if (missing.length === 0) {
229
- console.log('Full coverage — all spec endpoints are implemented.');
230
- console.log('');
231
- }
232
-
233
- // Exit 1 if there are missing endpoints (useful for CI later)
234
- process.exit(missing.length > 0 ? 1 : 0);
235
- }
236
-
237
- main();
@@ -1,48 +0,0 @@
1
- export declare function generateId(prefix: string): string;
2
- export declare function resetIdState(): void;
3
- export declare const ID_PREFIXES: {
4
- readonly user: "user";
5
- readonly organization: "org";
6
- readonly organization_membership: "om";
7
- readonly organization_domain: "org_domain";
8
- readonly connection: "conn";
9
- readonly connection_domain: "conn_domain";
10
- readonly directory: "directory";
11
- readonly directory_user: "directory_user";
12
- readonly directory_group: "directory_grp";
13
- readonly event: "evt";
14
- readonly invitation: "inv";
15
- readonly session: "session";
16
- readonly email_verification: "email_verification";
17
- readonly password_reset: "password_reset";
18
- readonly magic_auth: "magic_auth";
19
- readonly authentication_factor: "auth_factor";
20
- readonly authentication_challenge: "auth_challenge";
21
- readonly authorization_code: "auth_code";
22
- readonly identity: "identity";
23
- readonly sso_authorization: "sso_auth";
24
- readonly refresh_token: "ref";
25
- readonly device_authorization: "dev_auth";
26
- readonly api_key: "api_key";
27
- readonly profile: "prof";
28
- readonly pipe_connection: "pipe_conn";
29
- readonly redirect_uri: "redir";
30
- readonly cors_origin: "cors";
31
- readonly authorized_application: "auth_app";
32
- readonly connected_account: "conn_acct";
33
- readonly role: "role";
34
- readonly permission: "perm";
35
- readonly role_permission: "rp";
36
- readonly authorization_resource: "auth_res";
37
- readonly role_assignment: "ra";
38
- readonly audit_log_action: "audit_action";
39
- readonly audit_log_event: "audit_event";
40
- readonly audit_log_export: "audit_export";
41
- readonly feature_flag: "ff";
42
- readonly flag_target: "ff_target";
43
- readonly connect_application: "connect_app";
44
- readonly client_secret: "client_secret";
45
- readonly data_integration_auth: "di_auth";
46
- readonly radar_attempt: "radar_attempt";
47
- readonly webhook_endpoint: "we";
48
- };
@@ -1,73 +0,0 @@
1
- const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32
2
- const ENCODING_LEN = ENCODING.length; // 32
3
- const TIME_LEN = 10; // 10 chars encodes 48-bit ms timestamp
4
- const RANDOM_LEN = 16; // 16 chars of randomness
5
- let lastTime = 0;
6
- export function generateId(prefix) {
7
- let now = Date.now();
8
- if (now <= lastTime) {
9
- now = lastTime + 1;
10
- }
11
- lastTime = now;
12
- let timeStr = '';
13
- let t = now;
14
- for (let i = TIME_LEN - 1; i >= 0; i--) {
15
- timeStr = ENCODING[t % ENCODING_LEN] + timeStr;
16
- t = Math.floor(t / ENCODING_LEN);
17
- }
18
- let randStr = '';
19
- for (let i = 0; i < RANDOM_LEN; i++) {
20
- randStr += ENCODING[Math.floor(Math.random() * ENCODING_LEN)];
21
- }
22
- return `${prefix}_${timeStr}${randStr}`;
23
- }
24
- export function resetIdState() {
25
- lastTime = 0;
26
- }
27
- export const ID_PREFIXES = {
28
- user: 'user',
29
- organization: 'org',
30
- organization_membership: 'om',
31
- organization_domain: 'org_domain',
32
- connection: 'conn',
33
- connection_domain: 'conn_domain',
34
- directory: 'directory',
35
- directory_user: 'directory_user',
36
- directory_group: 'directory_grp',
37
- event: 'evt',
38
- invitation: 'inv',
39
- session: 'session',
40
- email_verification: 'email_verification',
41
- password_reset: 'password_reset',
42
- magic_auth: 'magic_auth',
43
- authentication_factor: 'auth_factor',
44
- authentication_challenge: 'auth_challenge',
45
- authorization_code: 'auth_code',
46
- identity: 'identity',
47
- sso_authorization: 'sso_auth',
48
- refresh_token: 'ref',
49
- device_authorization: 'dev_auth',
50
- api_key: 'api_key',
51
- profile: 'prof',
52
- pipe_connection: 'pipe_conn',
53
- redirect_uri: 'redir',
54
- cors_origin: 'cors',
55
- authorized_application: 'auth_app',
56
- connected_account: 'conn_acct',
57
- role: 'role',
58
- permission: 'perm',
59
- role_permission: 'rp',
60
- authorization_resource: 'auth_res',
61
- role_assignment: 'ra',
62
- audit_log_action: 'audit_action',
63
- audit_log_event: 'audit_event',
64
- audit_log_export: 'audit_export',
65
- feature_flag: 'ff',
66
- flag_target: 'ff_target',
67
- connect_application: 'connect_app',
68
- client_secret: 'client_secret',
69
- data_integration_auth: 'di_auth',
70
- radar_attempt: 'radar_attempt',
71
- webhook_endpoint: 'we',
72
- };
73
- //# sourceMappingURL=id.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"id.js","sourceRoot":"","sources":["../../../src/emulate/core/id.ts"],"names":[],"mappings":"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","sourcesContent":["const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32\nconst ENCODING_LEN = ENCODING.length; // 32\nconst TIME_LEN = 10; // 10 chars encodes 48-bit ms timestamp\nconst RANDOM_LEN = 16; // 16 chars of randomness\n\nlet lastTime = 0;\n\nexport function generateId(prefix: string): string {\n let now = Date.now();\n if (now <= lastTime) {\n now = lastTime + 1;\n }\n lastTime = now;\n\n let timeStr = '';\n let t = now;\n for (let i = TIME_LEN - 1; i >= 0; i--) {\n timeStr = ENCODING[t % ENCODING_LEN] + timeStr;\n t = Math.floor(t / ENCODING_LEN);\n }\n\n let randStr = '';\n for (let i = 0; i < RANDOM_LEN; i++) {\n randStr += ENCODING[Math.floor(Math.random() * ENCODING_LEN)];\n }\n\n return `${prefix}_${timeStr}${randStr}`;\n}\n\nexport function resetIdState(): void {\n lastTime = 0;\n}\n\nexport const ID_PREFIXES = {\n user: 'user',\n organization: 'org',\n organization_membership: 'om',\n organization_domain: 'org_domain',\n connection: 'conn',\n connection_domain: 'conn_domain',\n directory: 'directory',\n directory_user: 'directory_user',\n directory_group: 'directory_grp',\n event: 'evt',\n invitation: 'inv',\n session: 'session',\n email_verification: 'email_verification',\n password_reset: 'password_reset',\n magic_auth: 'magic_auth',\n authentication_factor: 'auth_factor',\n authentication_challenge: 'auth_challenge',\n authorization_code: 'auth_code',\n identity: 'identity',\n sso_authorization: 'sso_auth',\n refresh_token: 'ref',\n device_authorization: 'dev_auth',\n api_key: 'api_key',\n profile: 'prof',\n pipe_connection: 'pipe_conn',\n redirect_uri: 'redir',\n cors_origin: 'cors',\n authorized_application: 'auth_app',\n connected_account: 'conn_acct',\n role: 'role',\n permission: 'perm',\n role_permission: 'rp',\n authorization_resource: 'auth_res',\n role_assignment: 'ra',\n audit_log_action: 'audit_action',\n audit_log_event: 'audit_event',\n audit_log_export: 'audit_export',\n feature_flag: 'ff',\n flag_target: 'ff_target',\n connect_application: 'connect_app',\n client_secret: 'client_secret',\n data_integration_auth: 'di_auth',\n radar_attempt: 'radar_attempt',\n webhook_endpoint: 'we',\n} as const;\n"]}
@@ -1,8 +0,0 @@
1
- export { Store, Collection, type Entity, type InsertInput, type FilterFn, type SortFn, type CollectionHooks, } from './store.js';
2
- export { generateId, resetIdState, ID_PREFIXES } from './id.js';
3
- export { parseListParams, cursorPaginate, type CursorPaginationOptions, type CursorPaginatedResult, } from './pagination.js';
4
- export { JWTManager, type JWTPayload } from './jwt.js';
5
- export { createServer, type ServerOptions } from './server.js';
6
- export { type ServicePlugin, type RouteContext } from './plugin.js';
7
- export { WorkOSApiError, createApiErrorHandler, requestIdMiddleware, notFound, validationError, unauthorized, forbidden, parseJsonBody, } from './middleware/error-handler.js';
8
- export { authMiddleware, type WorkOSAppEnv, type WorkOSAuthContext, type ApiKeyMap } from './middleware/auth.js';
@@ -1,8 +0,0 @@
1
- export { Store, Collection, } from './store.js';
2
- export { generateId, resetIdState, ID_PREFIXES } from './id.js';
3
- export { parseListParams, cursorPaginate, } from './pagination.js';
4
- export { JWTManager } from './jwt.js';
5
- export { createServer } from './server.js';
6
- export { WorkOSApiError, createApiErrorHandler, requestIdMiddleware, notFound, validationError, unauthorized, forbidden, parseJsonBody, } from './middleware/error-handler.js';
7
- export { authMiddleware } from './middleware/auth.js';
8
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/emulate/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,UAAU,GAMX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EACL,eAAe,EACf,cAAc,GAGf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAmB,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAE/D,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,SAAS,EACT,aAAa,GACd,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,cAAc,EAA6D,MAAM,sBAAsB,CAAC","sourcesContent":["export {\n Store,\n Collection,\n type Entity,\n type InsertInput,\n type FilterFn,\n type SortFn,\n type CollectionHooks,\n} from './store.js';\nexport { generateId, resetIdState, ID_PREFIXES } from './id.js';\nexport {\n parseListParams,\n cursorPaginate,\n type CursorPaginationOptions,\n type CursorPaginatedResult,\n} from './pagination.js';\nexport { JWTManager, type JWTPayload } from './jwt.js';\nexport { createServer, type ServerOptions } from './server.js';\nexport { type ServicePlugin, type RouteContext } from './plugin.js';\nexport {\n WorkOSApiError,\n createApiErrorHandler,\n requestIdMiddleware,\n notFound,\n validationError,\n unauthorized,\n forbidden,\n parseJsonBody,\n} from './middleware/error-handler.js';\nexport { authMiddleware, type WorkOSAppEnv, type WorkOSAuthContext, type ApiKeyMap } from './middleware/auth.js';\n"]}
@@ -1,28 +0,0 @@
1
- export interface JWTPayload {
2
- sub: string;
3
- sid?: string;
4
- org_id?: string;
5
- role?: string;
6
- permissions?: string[];
7
- iss: string;
8
- aud: string;
9
- exp: number;
10
- iat: number;
11
- }
12
- interface SignOptions {
13
- expiresIn?: number;
14
- }
15
- export declare class JWTManager {
16
- private privateKey;
17
- private publicKey;
18
- private kid;
19
- issuer: string;
20
- constructor(issuer?: string);
21
- sign(payload: Omit<JWTPayload, 'iss' | 'iat' | 'exp'>, options?: SignOptions): string;
22
- verify(token: string): JWTPayload;
23
- getJWKS(): {
24
- keys: Record<string, unknown>[];
25
- };
26
- getPublicKeyPem(): string;
27
- }
28
- export {};
@@ -1,78 +0,0 @@
1
- import { createSign, createVerify, generateKeyPairSync } from 'node:crypto';
2
- function base64url(input) {
3
- const buf = typeof input === 'string' ? Buffer.from(input) : input;
4
- return buf.toString('base64url');
5
- }
6
- function base64urlDecode(input) {
7
- return Buffer.from(input, 'base64url');
8
- }
9
- export class JWTManager {
10
- privateKey;
11
- publicKey;
12
- kid;
13
- issuer;
14
- constructor(issuer = 'https://api.workos.com') {
15
- this.issuer = issuer;
16
- const { privateKey, publicKey } = generateKeyPairSync('rsa', {
17
- modulusLength: 2048,
18
- });
19
- this.privateKey = privateKey;
20
- this.publicKey = publicKey;
21
- this.kid = `workos_emulate_${Date.now()}`;
22
- }
23
- sign(payload, options) {
24
- const now = Math.floor(Date.now() / 1000);
25
- const expiresIn = options?.expiresIn ?? 3600;
26
- const fullPayload = {
27
- ...payload,
28
- iss: this.issuer,
29
- iat: now,
30
- exp: now + expiresIn,
31
- };
32
- const header = { alg: 'RS256', typ: 'JWT', kid: this.kid };
33
- const headerB64 = base64url(JSON.stringify(header));
34
- const payloadB64 = base64url(JSON.stringify(fullPayload));
35
- const signingInput = `${headerB64}.${payloadB64}`;
36
- const signer = createSign('RSA-SHA256');
37
- signer.update(signingInput);
38
- const signature = signer.sign(this.privateKey, 'base64url');
39
- return `${signingInput}.${signature}`;
40
- }
41
- verify(token) {
42
- const parts = token.split('.');
43
- if (parts.length !== 3) {
44
- throw new Error('Invalid token format');
45
- }
46
- const [headerB64, payloadB64, signature] = parts;
47
- const signingInput = `${headerB64}.${payloadB64}`;
48
- const verifier = createVerify('RSA-SHA256');
49
- verifier.update(signingInput);
50
- const valid = verifier.verify(this.publicKey, signature, 'base64url');
51
- if (!valid) {
52
- throw new Error('Invalid token signature');
53
- }
54
- const payload = JSON.parse(base64urlDecode(payloadB64).toString('utf-8'));
55
- const now = Math.floor(Date.now() / 1000);
56
- if (payload.exp && payload.exp < now) {
57
- throw new Error('Token has expired');
58
- }
59
- return payload;
60
- }
61
- getJWKS() {
62
- const jwk = this.publicKey.export({ format: 'jwk' });
63
- return {
64
- keys: [
65
- {
66
- ...jwk,
67
- kid: this.kid,
68
- alg: 'RS256',
69
- use: 'sig',
70
- },
71
- ],
72
- };
73
- }
74
- getPublicKeyPem() {
75
- return this.publicKey.export({ type: 'spki', format: 'pem' });
76
- }
77
- }
78
- //# sourceMappingURL=jwt.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../src/emulate/core/jwt.ts"],"names":[],"mappings":"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","sourcesContent":["import { createSign, createVerify, generateKeyPairSync, type KeyObject } from 'node:crypto';\n\nexport interface JWTPayload {\n sub: string;\n sid?: string;\n org_id?: string;\n role?: string;\n permissions?: string[];\n iss: string;\n aud: string;\n exp: number;\n iat: number;\n}\n\ninterface SignOptions {\n expiresIn?: number;\n}\n\nfunction base64url(input: Buffer | string): string {\n const buf = typeof input === 'string' ? Buffer.from(input) : input;\n return buf.toString('base64url');\n}\n\nfunction base64urlDecode(input: string): Buffer {\n return Buffer.from(input, 'base64url');\n}\n\nexport class JWTManager {\n private privateKey: KeyObject;\n private publicKey: KeyObject;\n private kid: string;\n issuer: string;\n\n constructor(issuer = 'https://api.workos.com') {\n this.issuer = issuer;\n const { privateKey, publicKey } = generateKeyPairSync('rsa', {\n modulusLength: 2048,\n });\n this.privateKey = privateKey;\n this.publicKey = publicKey;\n this.kid = `workos_emulate_${Date.now()}`;\n }\n\n sign(payload: Omit<JWTPayload, 'iss' | 'iat' | 'exp'>, options?: SignOptions): string {\n const now = Math.floor(Date.now() / 1000);\n const expiresIn = options?.expiresIn ?? 3600;\n\n const fullPayload: JWTPayload = {\n ...payload,\n iss: this.issuer,\n iat: now,\n exp: now + expiresIn,\n };\n\n const header = { alg: 'RS256', typ: 'JWT', kid: this.kid };\n const headerB64 = base64url(JSON.stringify(header));\n const payloadB64 = base64url(JSON.stringify(fullPayload));\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signer = createSign('RSA-SHA256');\n signer.update(signingInput);\n const signature = signer.sign(this.privateKey, 'base64url');\n\n return `${signingInput}.${signature}`;\n }\n\n verify(token: string): JWTPayload {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid token format');\n }\n\n const [headerB64, payloadB64, signature] = parts;\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const verifier = createVerify('RSA-SHA256');\n verifier.update(signingInput);\n const valid = verifier.verify(this.publicKey, signature, 'base64url');\n\n if (!valid) {\n throw new Error('Invalid token signature');\n }\n\n const payload = JSON.parse(base64urlDecode(payloadB64).toString('utf-8')) as JWTPayload;\n\n const now = Math.floor(Date.now() / 1000);\n if (payload.exp && payload.exp < now) {\n throw new Error('Token has expired');\n }\n\n return payload;\n }\n\n getJWKS(): { keys: Record<string, unknown>[] } {\n const jwk = this.publicKey.export({ format: 'jwk' });\n return {\n keys: [\n {\n ...jwk,\n kid: this.kid,\n alg: 'RS256',\n use: 'sig',\n },\n ],\n };\n }\n\n getPublicKeyPem(): string {\n return this.publicKey.export({ type: 'spki', format: 'pem' }) as string;\n }\n}\n"]}
@@ -1,15 +0,0 @@
1
- import type { Context, Next } from 'hono';
2
- export interface WorkOSAuthContext {
3
- environment: string;
4
- apiKey: string;
5
- }
6
- export type WorkOSAppEnv = {
7
- Variables: {
8
- auth?: WorkOSAuthContext;
9
- requestId?: string;
10
- };
11
- };
12
- export type ApiKeyMap = Record<string, {
13
- environment: string;
14
- }>;
15
- export declare function authMiddleware(apiKeys: ApiKeyMap): (c: Context, next: Next) => Promise<void>;
@@ -1,17 +0,0 @@
1
- import { unauthorized } from './error-handler.js';
2
- export function authMiddleware(apiKeys) {
3
- return async (c, next) => {
4
- const authHeader = c.req.header('Authorization');
5
- if (!authHeader)
6
- throw unauthorized();
7
- const token = authHeader.replace(/^Bearer\s+/i, '').trim();
8
- if (!token.startsWith('sk_'))
9
- throw unauthorized();
10
- const keyInfo = apiKeys[token];
11
- if (!keyInfo)
12
- throw unauthorized();
13
- c.set('auth', { environment: keyInfo.environment, apiKey: token });
14
- await next();
15
- };
16
- }
17
- //# sourceMappingURL=auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/emulate/core/middleware/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAgBlD,MAAM,UAAU,cAAc,CAAC,OAAkB;IAC/C,OAAO,KAAK,EAAE,CAAU,EAAE,IAAU,EAAE,EAAE;QACtC,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,CAAC,UAAU;YAAE,MAAM,YAAY,EAAE,CAAC;QAEtC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3D,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,MAAM,YAAY,EAAE,CAAC;QAEnD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO;YAAE,MAAM,YAAY,EAAE,CAAC;QAEnC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAA8B,CAAC,CAAC;QAC/F,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { Context, Next } from 'hono';\nimport { unauthorized } from './error-handler.js';\n\nexport interface WorkOSAuthContext {\n environment: string;\n apiKey: string;\n}\n\nexport type WorkOSAppEnv = {\n Variables: {\n auth?: WorkOSAuthContext;\n requestId?: string;\n };\n};\n\nexport type ApiKeyMap = Record<string, { environment: string }>;\n\nexport function authMiddleware(apiKeys: ApiKeyMap) {\n return async (c: Context, next: Next) => {\n const authHeader = c.req.header('Authorization');\n if (!authHeader) throw unauthorized();\n\n const token = authHeader.replace(/^Bearer\\s+/i, '').trim();\n if (!token.startsWith('sk_')) throw unauthorized();\n\n const keyInfo = apiKeys[token];\n if (!keyInfo) throw unauthorized();\n\n c.set('auth', { environment: keyInfo.environment, apiKey: token } satisfies WorkOSAuthContext);\n await next();\n };\n}\n"]}
@@ -1,22 +0,0 @@
1
- import type { Context, ErrorHandler, MiddlewareHandler } from 'hono';
2
- export declare class WorkOSApiError extends Error {
3
- status: number;
4
- code: string;
5
- errors?: Array<{
6
- field: string;
7
- code: string;
8
- message?: string;
9
- }> | undefined;
10
- constructor(status: number, message: string, code: string, errors?: Array<{
11
- field: string;
12
- code: string;
13
- message?: string;
14
- }> | undefined);
15
- }
16
- export declare function createApiErrorHandler(): ErrorHandler;
17
- export declare function requestIdMiddleware(): MiddlewareHandler;
18
- export declare function notFound(resource?: string): WorkOSApiError;
19
- export declare function validationError(message: string, errors?: WorkOSApiError['errors']): WorkOSApiError;
20
- export declare function unauthorized(): WorkOSApiError;
21
- export declare function forbidden(): WorkOSApiError;
22
- export declare function parseJsonBody(c: Context): Promise<Record<string, unknown>>;
@@ -1,72 +0,0 @@
1
- export class WorkOSApiError extends Error {
2
- status;
3
- code;
4
- errors;
5
- constructor(status, message, code, errors) {
6
- super(message);
7
- this.status = status;
8
- this.code = code;
9
- this.errors = errors;
10
- this.name = 'WorkOSApiError';
11
- }
12
- }
13
- export function createApiErrorHandler() {
14
- return (err, c) => {
15
- if (err instanceof WorkOSApiError) {
16
- const body = {
17
- message: err.message,
18
- code: err.code,
19
- };
20
- if (err.errors) {
21
- body.errors = err.errors;
22
- }
23
- return c.json(body, err.status);
24
- }
25
- const status = errorStatus(err);
26
- return c.json({
27
- message: 'Internal Server Error',
28
- code: 'server_error',
29
- }, status);
30
- };
31
- }
32
- export function requestIdMiddleware() {
33
- return async (c, next) => {
34
- const requestId = c.req.header('X-Request-ID') ?? `req_${crypto.randomUUID()}`;
35
- c.set('requestId', requestId);
36
- c.header('X-Request-ID', requestId);
37
- await next();
38
- };
39
- }
40
- export function notFound(resource) {
41
- return new WorkOSApiError(404, resource ? `${resource} not found` : 'Not Found', 'not_found');
42
- }
43
- export function validationError(message, errors) {
44
- return new WorkOSApiError(422, message, 'unprocessable_entity', errors);
45
- }
46
- export function unauthorized() {
47
- return new WorkOSApiError(401, 'Unauthorized', 'unauthorized');
48
- }
49
- export function forbidden() {
50
- return new WorkOSApiError(403, 'Forbidden', 'forbidden');
51
- }
52
- export async function parseJsonBody(c) {
53
- try {
54
- const body = await c.req.json();
55
- if (body && typeof body === 'object' && !Array.isArray(body)) {
56
- return body;
57
- }
58
- return {};
59
- }
60
- catch {
61
- throw new WorkOSApiError(400, 'Problems parsing JSON', 'invalid_request_body');
62
- }
63
- }
64
- function errorStatus(err) {
65
- if (err && typeof err === 'object' && 'status' in err) {
66
- const s = err.status;
67
- if (typeof s === 'number' && Number.isFinite(s))
68
- return s;
69
- }
70
- return 500;
71
- }
72
- //# sourceMappingURL=error-handler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../../../src/emulate/core/middleware/error-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, ErrorHandler, MiddlewareHandler } from 'hono';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\n\nexport class WorkOSApiError extends Error {\n constructor(\n public status: number,\n message: string,\n public code: string,\n public errors?: Array<{ field: string; code: string; message?: string }>,\n ) {\n super(message);\n this.name = 'WorkOSApiError';\n }\n}\n\nexport function createApiErrorHandler(): ErrorHandler {\n return (err, c) => {\n if (err instanceof WorkOSApiError) {\n const body: Record<string, unknown> = {\n message: err.message,\n code: err.code,\n };\n if (err.errors) {\n body.errors = err.errors;\n }\n return c.json(body, err.status as ContentfulStatusCode);\n }\n\n const status = errorStatus(err);\n return c.json(\n {\n message: 'Internal Server Error',\n code: 'server_error',\n },\n status as ContentfulStatusCode,\n );\n };\n}\n\nexport function requestIdMiddleware(): MiddlewareHandler {\n return async (c, next) => {\n const requestId = c.req.header('X-Request-ID') ?? `req_${crypto.randomUUID()}`;\n c.set('requestId', requestId);\n c.header('X-Request-ID', requestId);\n await next();\n };\n}\n\nexport function notFound(resource?: string): WorkOSApiError {\n return new WorkOSApiError(404, resource ? `${resource} not found` : 'Not Found', 'not_found');\n}\n\nexport function validationError(message: string, errors?: WorkOSApiError['errors']): WorkOSApiError {\n return new WorkOSApiError(422, message, 'unprocessable_entity', errors);\n}\n\nexport function unauthorized(): WorkOSApiError {\n return new WorkOSApiError(401, 'Unauthorized', 'unauthorized');\n}\n\nexport function forbidden(): WorkOSApiError {\n return new WorkOSApiError(403, 'Forbidden', 'forbidden');\n}\n\nexport async function parseJsonBody(c: Context): Promise<Record<string, unknown>> {\n try {\n const body = await c.req.json();\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n return body as Record<string, unknown>;\n }\n return {};\n } catch {\n throw new WorkOSApiError(400, 'Problems parsing JSON', 'invalid_request_body');\n }\n}\n\nfunction errorStatus(err: unknown): number {\n if (err && typeof err === 'object' && 'status' in err) {\n const s = (err as { status: unknown }).status;\n if (typeof s === 'number' && Number.isFinite(s)) return s;\n }\n return 500;\n}\n"]}