workos 0.11.2 → 0.12.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 (186) hide show
  1. package/README.md +165 -6
  2. package/dist/bin.js +22 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/check-coverage.ts +237 -0
  5. package/dist/commands/debug.js +0 -1
  6. package/dist/commands/debug.js.map +1 -1
  7. package/dist/commands/dev.d.ts +23 -0
  8. package/dist/commands/dev.js +139 -0
  9. package/dist/commands/dev.js.map +1 -0
  10. package/dist/commands/emulate.d.ts +6 -0
  11. package/dist/commands/emulate.js +64 -0
  12. package/dist/commands/emulate.js.map +1 -0
  13. package/dist/commands/login.js +0 -4
  14. package/dist/commands/login.js.map +1 -1
  15. package/dist/emulate/core/id.d.ts +48 -0
  16. package/dist/emulate/core/id.js +73 -0
  17. package/dist/emulate/core/id.js.map +1 -0
  18. package/dist/emulate/core/index.d.ts +8 -0
  19. package/dist/emulate/core/index.js +8 -0
  20. package/dist/emulate/core/index.js.map +1 -0
  21. package/dist/emulate/core/jwt.d.ts +28 -0
  22. package/dist/emulate/core/jwt.js +78 -0
  23. package/dist/emulate/core/jwt.js.map +1 -0
  24. package/dist/emulate/core/middleware/auth.d.ts +15 -0
  25. package/dist/emulate/core/middleware/auth.js +17 -0
  26. package/dist/emulate/core/middleware/auth.js.map +1 -0
  27. package/dist/emulate/core/middleware/error-handler.d.ts +22 -0
  28. package/dist/emulate/core/middleware/error-handler.js +72 -0
  29. package/dist/emulate/core/middleware/error-handler.js.map +1 -0
  30. package/dist/emulate/core/pagination.d.ts +27 -0
  31. package/dist/emulate/core/pagination.js +43 -0
  32. package/dist/emulate/core/pagination.js.map +1 -0
  33. package/dist/emulate/core/plugin.d.ts +15 -0
  34. package/dist/emulate/core/plugin.js +2 -0
  35. package/dist/emulate/core/plugin.js.map +1 -0
  36. package/dist/emulate/core/server.d.ts +17 -0
  37. package/dist/emulate/core/server.js +90 -0
  38. package/dist/emulate/core/server.js.map +1 -0
  39. package/dist/emulate/core/store.d.ts +44 -0
  40. package/dist/emulate/core/store.js +169 -0
  41. package/dist/emulate/core/store.js.map +1 -0
  42. package/dist/emulate/index.d.ts +25 -0
  43. package/dist/emulate/index.js +47 -0
  44. package/dist/emulate/index.js.map +1 -0
  45. package/dist/emulate/workos/constants.d.ts +56 -0
  46. package/dist/emulate/workos/constants.js +56 -0
  47. package/dist/emulate/workos/constants.js.map +1 -0
  48. package/dist/emulate/workos/entities.d.ts +360 -0
  49. package/dist/emulate/workos/entities.js +2 -0
  50. package/dist/emulate/workos/entities.js.map +1 -0
  51. package/dist/emulate/workos/event-bus.d.ts +17 -0
  52. package/dist/emulate/workos/event-bus.js +70 -0
  53. package/dist/emulate/workos/event-bus.js.map +1 -0
  54. package/dist/emulate/workos/helpers.d.ts +72 -0
  55. package/dist/emulate/workos/helpers.js +211 -0
  56. package/dist/emulate/workos/helpers.js.map +1 -0
  57. package/dist/emulate/workos/index.d.ts +91 -0
  58. package/dist/emulate/workos/index.js +322 -0
  59. package/dist/emulate/workos/index.js.map +1 -0
  60. package/dist/emulate/workos/role-helpers.d.ts +21 -0
  61. package/dist/emulate/workos/role-helpers.js +130 -0
  62. package/dist/emulate/workos/role-helpers.js.map +1 -0
  63. package/dist/emulate/workos/routes/api-keys.d.ts +2 -0
  64. package/dist/emulate/workos/routes/api-keys.js +32 -0
  65. package/dist/emulate/workos/routes/api-keys.js.map +1 -0
  66. package/dist/emulate/workos/routes/audit-logs.d.ts +2 -0
  67. package/dist/emulate/workos/routes/audit-logs.js +104 -0
  68. package/dist/emulate/workos/routes/audit-logs.js.map +1 -0
  69. package/dist/emulate/workos/routes/auth-challenges.d.ts +2 -0
  70. package/dist/emulate/workos/routes/auth-challenges.js +51 -0
  71. package/dist/emulate/workos/routes/auth-challenges.js.map +1 -0
  72. package/dist/emulate/workos/routes/auth-factors.d.ts +2 -0
  73. package/dist/emulate/workos/routes/auth-factors.js +51 -0
  74. package/dist/emulate/workos/routes/auth-factors.js.map +1 -0
  75. package/dist/emulate/workos/routes/auth.d.ts +2 -0
  76. package/dist/emulate/workos/routes/auth.js +350 -0
  77. package/dist/emulate/workos/routes/auth.js.map +1 -0
  78. package/dist/emulate/workos/routes/authorization-checks.d.ts +10 -0
  79. package/dist/emulate/workos/routes/authorization-checks.js +123 -0
  80. package/dist/emulate/workos/routes/authorization-checks.js.map +1 -0
  81. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +2 -0
  82. package/dist/emulate/workos/routes/authorization-org-roles.js +64 -0
  83. package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -0
  84. package/dist/emulate/workos/routes/authorization-permissions.d.ts +2 -0
  85. package/dist/emulate/workos/routes/authorization-permissions.js +67 -0
  86. package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -0
  87. package/dist/emulate/workos/routes/authorization-resources.d.ts +2 -0
  88. package/dist/emulate/workos/routes/authorization-resources.js +117 -0
  89. package/dist/emulate/workos/routes/authorization-resources.js.map +1 -0
  90. package/dist/emulate/workos/routes/authorization-roles.d.ts +2 -0
  91. package/dist/emulate/workos/routes/authorization-roles.js +13 -0
  92. package/dist/emulate/workos/routes/authorization-roles.js.map +1 -0
  93. package/dist/emulate/workos/routes/config.d.ts +2 -0
  94. package/dist/emulate/workos/routes/config.js +57 -0
  95. package/dist/emulate/workos/routes/config.js.map +1 -0
  96. package/dist/emulate/workos/routes/connect.d.ts +2 -0
  97. package/dist/emulate/workos/routes/connect.js +65 -0
  98. package/dist/emulate/workos/routes/connect.js.map +1 -0
  99. package/dist/emulate/workos/routes/connections.d.ts +2 -0
  100. package/dist/emulate/workos/routes/connections.js +73 -0
  101. package/dist/emulate/workos/routes/connections.js.map +1 -0
  102. package/dist/emulate/workos/routes/data-integrations.d.ts +2 -0
  103. package/dist/emulate/workos/routes/data-integrations.js +55 -0
  104. package/dist/emulate/workos/routes/data-integrations.js.map +1 -0
  105. package/dist/emulate/workos/routes/directories.d.ts +2 -0
  106. package/dist/emulate/workos/routes/directories.js +90 -0
  107. package/dist/emulate/workos/routes/directories.js.map +1 -0
  108. package/dist/emulate/workos/routes/email-verification.d.ts +2 -0
  109. package/dist/emulate/workos/routes/email-verification.js +49 -0
  110. package/dist/emulate/workos/routes/email-verification.js.map +1 -0
  111. package/dist/emulate/workos/routes/events.d.ts +2 -0
  112. package/dist/emulate/workos/routes/events.js +18 -0
  113. package/dist/emulate/workos/routes/events.js.map +1 -0
  114. package/dist/emulate/workos/routes/feature-flags.d.ts +2 -0
  115. package/dist/emulate/workos/routes/feature-flags.js +103 -0
  116. package/dist/emulate/workos/routes/feature-flags.js.map +1 -0
  117. package/dist/emulate/workos/routes/invitations.d.ts +2 -0
  118. package/dist/emulate/workos/routes/invitations.js +122 -0
  119. package/dist/emulate/workos/routes/invitations.js.map +1 -0
  120. package/dist/emulate/workos/routes/legacy-mfa.d.ts +2 -0
  121. package/dist/emulate/workos/routes/legacy-mfa.js +75 -0
  122. package/dist/emulate/workos/routes/legacy-mfa.js.map +1 -0
  123. package/dist/emulate/workos/routes/magic-auth.d.ts +2 -0
  124. package/dist/emulate/workos/routes/magic-auth.js +32 -0
  125. package/dist/emulate/workos/routes/magic-auth.js.map +1 -0
  126. package/dist/emulate/workos/routes/memberships.d.ts +2 -0
  127. package/dist/emulate/workos/routes/memberships.js +114 -0
  128. package/dist/emulate/workos/routes/memberships.js.map +1 -0
  129. package/dist/emulate/workos/routes/organization-domains.d.ts +2 -0
  130. package/dist/emulate/workos/routes/organization-domains.js +58 -0
  131. package/dist/emulate/workos/routes/organization-domains.js.map +1 -0
  132. package/dist/emulate/workos/routes/organizations.d.ts +2 -0
  133. package/dist/emulate/workos/routes/organizations.js +131 -0
  134. package/dist/emulate/workos/routes/organizations.js.map +1 -0
  135. package/dist/emulate/workos/routes/password-reset.d.ts +2 -0
  136. package/dist/emulate/workos/routes/password-reset.js +61 -0
  137. package/dist/emulate/workos/routes/password-reset.js.map +1 -0
  138. package/dist/emulate/workos/routes/pipes.d.ts +2 -0
  139. package/dist/emulate/workos/routes/pipes.js +82 -0
  140. package/dist/emulate/workos/routes/pipes.js.map +1 -0
  141. package/dist/emulate/workos/routes/portal.d.ts +2 -0
  142. package/dist/emulate/workos/routes/portal.js +18 -0
  143. package/dist/emulate/workos/routes/portal.js.map +1 -0
  144. package/dist/emulate/workos/routes/radar.d.ts +2 -0
  145. package/dist/emulate/workos/routes/radar.js +41 -0
  146. package/dist/emulate/workos/routes/radar.js.map +1 -0
  147. package/dist/emulate/workos/routes/sessions.d.ts +2 -0
  148. package/dist/emulate/workos/routes/sessions.js +51 -0
  149. package/dist/emulate/workos/routes/sessions.js.map +1 -0
  150. package/dist/emulate/workos/routes/sso.d.ts +2 -0
  151. package/dist/emulate/workos/routes/sso.js +161 -0
  152. package/dist/emulate/workos/routes/sso.js.map +1 -0
  153. package/dist/emulate/workos/routes/user-features.d.ts +2 -0
  154. package/dist/emulate/workos/routes/user-features.js +50 -0
  155. package/dist/emulate/workos/routes/user-features.js.map +1 -0
  156. package/dist/emulate/workos/routes/users.d.ts +2 -0
  157. package/dist/emulate/workos/routes/users.js +129 -0
  158. package/dist/emulate/workos/routes/users.js.map +1 -0
  159. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +2 -0
  160. package/dist/emulate/workos/routes/webhook-endpoints.js +66 -0
  161. package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -0
  162. package/dist/emulate/workos/routes/widgets.d.ts +2 -0
  163. package/dist/emulate/workos/routes/widgets.js +27 -0
  164. package/dist/emulate/workos/routes/widgets.js.map +1 -0
  165. package/dist/emulate/workos/store.d.ts +48 -0
  166. package/dist/emulate/workos/store.js +102 -0
  167. package/dist/emulate/workos/store.js.map +1 -0
  168. package/dist/emulate/workos/webhook-signer.d.ts +1 -0
  169. package/dist/emulate/workos/webhook-signer.js +8 -0
  170. package/dist/emulate/workos/webhook-signer.js.map +1 -0
  171. package/dist/gen-routes-lib.spec.ts +659 -0
  172. package/dist/gen-routes-lib.ts +647 -0
  173. package/dist/gen-routes.ts +96 -0
  174. package/dist/lib/dev-command.d.ts +26 -0
  175. package/dist/lib/dev-command.js +122 -0
  176. package/dist/lib/dev-command.js.map +1 -0
  177. package/dist/lib/run-with-core.js +0 -3
  178. package/dist/lib/run-with-core.js.map +1 -1
  179. package/dist/lib/settings.js +1 -1
  180. package/dist/lib/settings.js.map +1 -1
  181. package/dist/utils/help-json.js +1 -0
  182. package/dist/utils/help-json.js.map +1 -1
  183. package/dist/utils/register-subcommand.d.ts +5 -2
  184. package/dist/utils/register-subcommand.js +16 -19
  185. package/dist/utils/register-subcommand.js.map +1 -1
  186. package/package.json +21 -8
@@ -0,0 +1,90 @@
1
+ import { notFound, parseListParams } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatDirectory, formatDirectoryUser, formatDirectoryGroup, formatListResponse } from '../helpers.js';
4
+ export function directoryRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ // List directories
8
+ app.get('/directories', (c) => {
9
+ const url = new URL(c.req.url);
10
+ const params = parseListParams(url);
11
+ const orgFilter = url.searchParams.get('organization_id') ?? undefined;
12
+ const search = url.searchParams.get('search') ?? undefined;
13
+ const result = ws.directories.list({
14
+ ...params,
15
+ filter: (d) => {
16
+ if (orgFilter && d.organization_id !== orgFilter)
17
+ return false;
18
+ if (search && !d.name.toLowerCase().includes(search.toLowerCase()))
19
+ return false;
20
+ return true;
21
+ },
22
+ });
23
+ return c.json(formatListResponse(result, formatDirectory));
24
+ });
25
+ // Get directory
26
+ app.get('/directories/:id', (c) => {
27
+ const dir = ws.directories.get(c.req.param('id'));
28
+ if (!dir)
29
+ throw notFound('Directory');
30
+ return c.json(formatDirectory(dir));
31
+ });
32
+ // Delete directory (cascade users + groups)
33
+ app.delete('/directories/:id', (c) => {
34
+ const dir = ws.directories.get(c.req.param('id'));
35
+ if (!dir)
36
+ throw notFound('Directory');
37
+ ws.directoryUsers.deleteBy('directory_id', dir.id);
38
+ ws.directoryGroups.deleteBy('directory_id', dir.id);
39
+ ws.directories.delete(dir.id);
40
+ return c.body(null, 204);
41
+ });
42
+ // List directory users
43
+ app.get('/directory_users', (c) => {
44
+ const url = new URL(c.req.url);
45
+ const params = parseListParams(url);
46
+ const directoryId = url.searchParams.get('directory_id') ?? undefined;
47
+ const groupId = url.searchParams.get('group_id') ?? undefined;
48
+ const result = ws.directoryUsers.list({
49
+ ...params,
50
+ filter: (u) => {
51
+ if (directoryId && u.directory_id !== directoryId)
52
+ return false;
53
+ if (groupId && !u.groups.some((g) => g.id === groupId))
54
+ return false;
55
+ return true;
56
+ },
57
+ });
58
+ return c.json(formatListResponse(result, formatDirectoryUser));
59
+ });
60
+ // Get directory user
61
+ app.get('/directory_users/:id', (c) => {
62
+ const user = ws.directoryUsers.get(c.req.param('id'));
63
+ if (!user)
64
+ throw notFound('DirectoryUser');
65
+ return c.json(formatDirectoryUser(user));
66
+ });
67
+ // List directory groups
68
+ app.get('/directory_groups', (c) => {
69
+ const url = new URL(c.req.url);
70
+ const params = parseListParams(url);
71
+ const directoryId = url.searchParams.get('directory_id') ?? undefined;
72
+ const result = ws.directoryGroups.list({
73
+ ...params,
74
+ filter: (g) => {
75
+ if (directoryId && g.directory_id !== directoryId)
76
+ return false;
77
+ return true;
78
+ },
79
+ });
80
+ return c.json(formatListResponse(result, formatDirectoryGroup));
81
+ });
82
+ // Get directory group
83
+ app.get('/directory_groups/:id', (c) => {
84
+ const group = ws.directoryGroups.get(c.req.param('id'));
85
+ if (!group)
86
+ throw notFound('DirectoryGroup');
87
+ return c.json(formatDirectoryGroup(group));
88
+ });
89
+ }
90
+ //# sourceMappingURL=directories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directories.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/directories.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatDirectory, formatDirectoryUser, formatDirectoryGroup, formatListResponse } from '../helpers.js';\n\nexport function directoryRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // List directories\n app.get('/directories', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const orgFilter = url.searchParams.get('organization_id') ?? undefined;\n const search = url.searchParams.get('search') ?? undefined;\n\n const result = ws.directories.list({\n ...params,\n filter: (d) => {\n if (orgFilter && d.organization_id !== orgFilter) return false;\n if (search && !d.name.toLowerCase().includes(search.toLowerCase())) return false;\n return true;\n },\n });\n\n return c.json(formatListResponse(result, formatDirectory));\n });\n\n // Get directory\n app.get('/directories/:id', (c) => {\n const dir = ws.directories.get(c.req.param('id'));\n if (!dir) throw notFound('Directory');\n return c.json(formatDirectory(dir));\n });\n\n // Delete directory (cascade users + groups)\n app.delete('/directories/:id', (c) => {\n const dir = ws.directories.get(c.req.param('id'));\n if (!dir) throw notFound('Directory');\n\n ws.directoryUsers.deleteBy('directory_id', dir.id);\n ws.directoryGroups.deleteBy('directory_id', dir.id);\n\n ws.directories.delete(dir.id);\n return c.body(null, 204);\n });\n\n // List directory users\n app.get('/directory_users', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const directoryId = url.searchParams.get('directory_id') ?? undefined;\n const groupId = url.searchParams.get('group_id') ?? undefined;\n\n const result = ws.directoryUsers.list({\n ...params,\n filter: (u) => {\n if (directoryId && u.directory_id !== directoryId) return false;\n if (groupId && !u.groups.some((g) => g.id === groupId)) return false;\n return true;\n },\n });\n\n return c.json(formatListResponse(result, formatDirectoryUser));\n });\n\n // Get directory user\n app.get('/directory_users/:id', (c) => {\n const user = ws.directoryUsers.get(c.req.param('id'));\n if (!user) throw notFound('DirectoryUser');\n return c.json(formatDirectoryUser(user));\n });\n\n // List directory groups\n app.get('/directory_groups', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const directoryId = url.searchParams.get('directory_id') ?? undefined;\n\n const result = ws.directoryGroups.list({\n ...params,\n filter: (g) => {\n if (directoryId && g.directory_id !== directoryId) return false;\n return true;\n },\n });\n\n return c.json(formatListResponse(result, formatDirectoryGroup));\n });\n\n // Get directory group\n app.get('/directory_groups/:id', (c) => {\n const group = ws.directoryGroups.get(c.req.param('id'));\n if (!group) throw notFound('DirectoryGroup');\n return c.json(formatDirectoryGroup(group));\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function emailVerificationRoutes(ctx: RouteContext): void;
@@ -0,0 +1,49 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatEmailVerification, formatUser, generateCode, expiresIn, isExpired } from '../helpers.js';
4
+ export function emailVerificationRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/user_management/email_verification/:id', (c) => {
8
+ const ev = ws.emailVerifications.get(c.req.param('id'));
9
+ if (!ev)
10
+ throw notFound('Email Verification');
11
+ return c.json(formatEmailVerification(ev));
12
+ });
13
+ app.post('/user_management/users/:id/email_verification/send', (c) => {
14
+ const user = ws.users.get(c.req.param('id'));
15
+ if (!user)
16
+ throw notFound('User');
17
+ const ev = ws.emailVerifications.insert({
18
+ object: 'email_verification',
19
+ user_id: user.id,
20
+ email: user.email,
21
+ code: generateCode(),
22
+ expires_at: expiresIn(10),
23
+ });
24
+ return c.json(formatEmailVerification(ev), 201);
25
+ });
26
+ app.post('/user_management/users/:id/email_verification/confirm', async (c) => {
27
+ const user = ws.users.get(c.req.param('id'));
28
+ if (!user)
29
+ throw notFound('User');
30
+ const body = await parseJsonBody(c);
31
+ const code = body.code;
32
+ if (!code) {
33
+ throw new WorkOSApiError(400, 'code is required', 'invalid_request');
34
+ }
35
+ const verifications = ws.emailVerifications.findBy('user_id', user.id);
36
+ const ev = verifications.find((v) => v.code === code);
37
+ if (!ev) {
38
+ throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');
39
+ }
40
+ if (isExpired(ev.expires_at)) {
41
+ throw new WorkOSApiError(400, 'Code has expired', 'expired_code');
42
+ }
43
+ ws.users.update(user.id, { email_verified: true });
44
+ ws.emailVerifications.delete(ev.id);
45
+ const updated = ws.users.get(user.id);
46
+ return c.json(formatUser(updated));
47
+ });
48
+ }
49
+ //# sourceMappingURL=email-verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-verification.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/email-verification.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatEmailVerification, formatUser, generateCode, expiresIn, isExpired } from '../helpers.js';\n\nexport function emailVerificationRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/email_verification/:id', (c) => {\n const ev = ws.emailVerifications.get(c.req.param('id'));\n if (!ev) throw notFound('Email Verification');\n return c.json(formatEmailVerification(ev));\n });\n\n app.post('/user_management/users/:id/email_verification/send', (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n const ev = ws.emailVerifications.insert({\n object: 'email_verification',\n user_id: user.id,\n email: user.email,\n code: generateCode(),\n expires_at: expiresIn(10),\n });\n\n return c.json(formatEmailVerification(ev), 201);\n });\n\n app.post('/user_management/users/:id/email_verification/confirm', async (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n const body = await parseJsonBody(c);\n const code = body.code as string | undefined;\n if (!code) {\n throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n }\n\n const verifications = ws.emailVerifications.findBy('user_id', user.id);\n const ev = verifications.find((v) => v.code === code);\n\n if (!ev) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(ev.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n ws.users.update(user.id, { email_verified: true });\n ws.emailVerifications.delete(ev.id);\n\n const updated = ws.users.get(user.id)!;\n return c.json(formatUser(updated));\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function eventRoutes(ctx: RouteContext): void;
@@ -0,0 +1,18 @@
1
+ import { parseListParams } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatEvent, formatListResponse } from '../helpers.js';
4
+ export function eventRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/events', (c) => {
8
+ const url = new URL(c.req.url);
9
+ const params = parseListParams(url);
10
+ const eventTypes = url.searchParams.getAll('events[]');
11
+ const result = ws.events.list({
12
+ ...params,
13
+ filter: eventTypes.length > 0 ? (e) => eventTypes.includes(e.event) : undefined,
14
+ });
15
+ return c.json(formatListResponse(result, formatEvent));
16
+ });
17
+ }
18
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEhE,MAAM,UAAU,WAAW,CAAC,GAAiB;IAC3C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC3B,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAEvD,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;YAC5B,GAAG,MAAM;YACT,MAAM,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;SAChF,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatEvent, formatListResponse } from '../helpers.js';\n\nexport function eventRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/events', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const eventTypes = url.searchParams.getAll('events[]');\n\n const result = ws.events.list({\n ...params,\n filter: eventTypes.length > 0 ? (e) => eventTypes.includes(e.event) : undefined,\n });\n\n return c.json(formatListResponse(result, formatEvent));\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function featureFlagRoutes(ctx: RouteContext): void;
@@ -0,0 +1,103 @@
1
+ import { notFound, parseJsonBody, parseListParams } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatFeatureFlag, formatFlagTarget, formatListResponse } from '../helpers.js';
4
+ function evaluateFlags(ws, resourceId) {
5
+ const flags = ws.featureFlags.all();
6
+ return flags.map((flag) => {
7
+ const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);
8
+ return {
9
+ slug: flag.slug,
10
+ type: flag.type,
11
+ value: target ? target.value : flag.enabled ? flag.default_value : null,
12
+ enabled: flag.enabled,
13
+ };
14
+ });
15
+ }
16
+ export function featureFlagRoutes(ctx) {
17
+ const { app, store } = ctx;
18
+ const ws = getWorkOSStore(store);
19
+ // List all flags
20
+ app.get('/feature-flags', (c) => {
21
+ const url = new URL(c.req.url);
22
+ const params = parseListParams(url);
23
+ const result = ws.featureFlags.list({ ...params });
24
+ return c.json(formatListResponse(result, formatFeatureFlag));
25
+ });
26
+ // Get flag by slug
27
+ app.get('/feature-flags/:slug', (c) => {
28
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
29
+ if (!flag)
30
+ throw notFound('FeatureFlag');
31
+ return c.json(formatFeatureFlag(flag));
32
+ });
33
+ // Enable flag
34
+ app.post('/feature-flags/:slug/enable', (c) => {
35
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
36
+ if (!flag)
37
+ throw notFound('FeatureFlag');
38
+ const updated = ws.featureFlags.update(flag.id, { enabled: true });
39
+ return c.json(formatFeatureFlag(updated));
40
+ });
41
+ // Disable flag
42
+ app.post('/feature-flags/:slug/disable', (c) => {
43
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
44
+ if (!flag)
45
+ throw notFound('FeatureFlag');
46
+ const updated = ws.featureFlags.update(flag.id, { enabled: false });
47
+ return c.json(formatFeatureFlag(updated));
48
+ });
49
+ // Add/update target
50
+ app.put('/feature-flags/:slug/targets/:resourceId', async (c) => {
51
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
52
+ if (!flag)
53
+ throw notFound('FeatureFlag');
54
+ const resourceId = c.req.param('resourceId');
55
+ const body = await parseJsonBody(c);
56
+ // Upsert: find existing target or create
57
+ const existing = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);
58
+ if (existing) {
59
+ const updated = ws.flagTargets.update(existing.id, {
60
+ value: body.value,
61
+ resource_type: body.resource_type ?? existing.resource_type,
62
+ });
63
+ return c.json(formatFlagTarget(updated));
64
+ }
65
+ const target = ws.flagTargets.insert({
66
+ object: 'flag_target',
67
+ flag_slug: flag.slug,
68
+ resource_id: resourceId,
69
+ resource_type: body.resource_type ?? 'user',
70
+ value: body.value,
71
+ });
72
+ return c.json(formatFlagTarget(target), 201);
73
+ });
74
+ // Remove target
75
+ app.delete('/feature-flags/:slug/targets/:resourceId', (c) => {
76
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
77
+ if (!flag)
78
+ throw notFound('FeatureFlag');
79
+ const resourceId = c.req.param('resourceId');
80
+ const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);
81
+ if (!target)
82
+ throw notFound('FlagTarget');
83
+ ws.flagTargets.delete(target.id);
84
+ return c.body(null, 204);
85
+ });
86
+ // Evaluate flags for organization
87
+ app.get('/organizations/:orgId/feature-flags', (c) => {
88
+ return c.json({
89
+ object: 'list',
90
+ data: evaluateFlags(ws, c.req.param('orgId')),
91
+ list_metadata: { before: null, after: null },
92
+ });
93
+ });
94
+ // Evaluate flags for user
95
+ app.get('/user_management/users/:userId/feature-flags', (c) => {
96
+ return c.json({
97
+ object: 'list',
98
+ data: evaluateFlags(ws, c.req.param('userId')),
99
+ list_metadata: { before: null, after: null },
100
+ });
101
+ });
102
+ }
103
+ //# sourceMappingURL=feature-flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-flags.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/feature-flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExF,SAAS,aAAa,CAAC,EAAe,EAAE,UAAkB;IACxD,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC;QACvG,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;YACvE,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAiB;IACjD,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC3B,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,iBAAiB;IACjB,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,mBAAmB;IACnB,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,CAAC,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;QACzC,OAAO,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,cAAc;IACd,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,CAAC,EAAE,EAAE;QAC5C,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAQ,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE,CAAC,CAAC,EAAE,EAAE;QAC7C,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAQ,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,oBAAoB;IACpB,GAAG,CAAC,GAAG,CAAC,0CAA0C,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9D,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;QAEzC,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QAEpC,yCAAyC;QACzC,MAAM,QAAQ,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC;QAEzG,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;gBACjD,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,aAAa,EAAG,IAAI,CAAC,aAAwB,IAAI,QAAQ,CAAC,aAAa;aACxE,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAQ,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;YACnC,MAAM,EAAE,aAAa;YACrB,SAAS,EAAE,IAAI,CAAC,IAAI;YACpB,WAAW,EAAE,UAAU;YACvB,aAAa,EAAG,IAAI,CAAC,aAAwB,IAAI,MAAM;YACvD,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,gBAAgB;IAChB,GAAG,CAAC,MAAM,CAAC,0CAA0C,EAAE,CAAC,CAAC,EAAE,EAAE;QAC3D,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;QAEzC,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC;QACvG,IAAI,CAAC,MAAM;YAAE,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QAE1C,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,kCAAkC;IAClC,GAAG,CAAC,GAAG,CAAC,qCAAqC,EAAE,CAAC,CAAC,EAAE,EAAE;QACnD,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7C,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,GAAG,CAAC,GAAG,CAAC,8CAA8C,EAAE,CAAC,CAAC,EAAE,EAAE;QAC5D,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore, type WorkOSStore } from '../store.js';\nimport { formatFeatureFlag, formatFlagTarget, formatListResponse } from '../helpers.js';\n\nfunction evaluateFlags(ws: WorkOSStore, resourceId: string) {\n const flags = ws.featureFlags.all();\n return flags.map((flag) => {\n const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);\n return {\n slug: flag.slug,\n type: flag.type,\n value: target ? target.value : flag.enabled ? flag.default_value : null,\n enabled: flag.enabled,\n };\n });\n}\n\nexport function featureFlagRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // List all flags\n app.get('/feature-flags', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const result = ws.featureFlags.list({ ...params });\n return c.json(formatListResponse(result, formatFeatureFlag));\n });\n\n // Get flag by slug\n app.get('/feature-flags/:slug', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n return c.json(formatFeatureFlag(flag));\n });\n\n // Enable flag\n app.post('/feature-flags/:slug/enable', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n const updated = ws.featureFlags.update(flag.id, { enabled: true });\n return c.json(formatFeatureFlag(updated!));\n });\n\n // Disable flag\n app.post('/feature-flags/:slug/disable', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n const updated = ws.featureFlags.update(flag.id, { enabled: false });\n return c.json(formatFeatureFlag(updated!));\n });\n\n // Add/update target\n app.put('/feature-flags/:slug/targets/:resourceId', async (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n\n const resourceId = c.req.param('resourceId');\n const body = await parseJsonBody(c);\n\n // Upsert: find existing target or create\n const existing = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);\n\n if (existing) {\n const updated = ws.flagTargets.update(existing.id, {\n value: body.value,\n resource_type: (body.resource_type as string) ?? existing.resource_type,\n });\n return c.json(formatFlagTarget(updated!));\n }\n\n const target = ws.flagTargets.insert({\n object: 'flag_target',\n flag_slug: flag.slug,\n resource_id: resourceId,\n resource_type: (body.resource_type as string) ?? 'user',\n value: body.value,\n });\n\n return c.json(formatFlagTarget(target), 201);\n });\n\n // Remove target\n app.delete('/feature-flags/:slug/targets/:resourceId', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n\n const resourceId = c.req.param('resourceId');\n const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);\n if (!target) throw notFound('FlagTarget');\n\n ws.flagTargets.delete(target.id);\n return c.body(null, 204);\n });\n\n // Evaluate flags for organization\n app.get('/organizations/:orgId/feature-flags', (c) => {\n return c.json({\n object: 'list',\n data: evaluateFlags(ws, c.req.param('orgId')),\n list_metadata: { before: null, after: null },\n });\n });\n\n // Evaluate flags for user\n app.get('/user_management/users/:userId/feature-flags', (c) => {\n return c.json({\n object: 'list',\n data: evaluateFlags(ws, c.req.param('userId')),\n list_metadata: { before: null, after: null },\n });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function invitationRoutes(ctx: RouteContext): void;
@@ -0,0 +1,122 @@
1
+ import { notFound, validationError, parseJsonBody, WorkOSApiError, parseListParams, } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatInvitation, generateVerificationToken, expiresIn, formatListResponse } from '../helpers.js';
4
+ import { STORE_KEYS, EVENTS } from '../constants.js';
5
+ export function invitationRoutes(ctx) {
6
+ const { app, store, baseUrl } = ctx;
7
+ const ws = getWorkOSStore(store);
8
+ app.post('/user_management/invitations', async (c) => {
9
+ const body = await parseJsonBody(c);
10
+ const email = body.email;
11
+ if (!email) {
12
+ throw validationError('email is required', [{ field: 'email', code: 'required' }]);
13
+ }
14
+ const token = generateVerificationToken();
15
+ const inv = ws.invitations.insert({
16
+ object: 'invitation',
17
+ email,
18
+ state: 'pending',
19
+ token,
20
+ accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${token}`,
21
+ organization_id: body.organization_id ?? null,
22
+ inviter_user_id: body.inviter_user_id ?? null,
23
+ role_slug: body.role_slug ?? null,
24
+ expires_at: expiresIn(72 * 60), // 72 hours
25
+ });
26
+ return c.json(formatInvitation(inv), 201);
27
+ });
28
+ app.get('/user_management/invitations', (c) => {
29
+ const url = new URL(c.req.url);
30
+ const params = parseListParams(url);
31
+ const emailFilter = url.searchParams.get('email') ?? undefined;
32
+ const orgFilter = url.searchParams.get('organization_id') ?? undefined;
33
+ const result = ws.invitations.list({
34
+ ...params,
35
+ filter: (inv) => {
36
+ if (emailFilter && inv.email !== emailFilter)
37
+ return false;
38
+ if (orgFilter && inv.organization_id !== orgFilter)
39
+ return false;
40
+ return true;
41
+ },
42
+ });
43
+ return c.json(formatListResponse(result, formatInvitation));
44
+ });
45
+ app.get('/user_management/invitations/by_token/:token', (c) => {
46
+ const inv = ws.invitations.findOneBy('token', c.req.param('token'));
47
+ if (!inv)
48
+ throw notFound('Invitation');
49
+ return c.json(formatInvitation(inv));
50
+ });
51
+ app.get('/user_management/invitations/:id', (c) => {
52
+ const inv = ws.invitations.get(c.req.param('id'));
53
+ if (!inv)
54
+ throw notFound('Invitation');
55
+ return c.json(formatInvitation(inv));
56
+ });
57
+ app.post('/user_management/invitations/:id/accept', (c) => {
58
+ const inv = ws.invitations.get(c.req.param('id'));
59
+ if (!inv)
60
+ throw notFound('Invitation');
61
+ if (inv.state !== 'pending') {
62
+ throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');
63
+ }
64
+ ws.invitations.update(inv.id, { state: 'accepted' });
65
+ const eventBus = store.getData(STORE_KEYS.eventBus);
66
+ eventBus?.emit({ event: EVENTS.invitationAccepted, data: formatInvitation(ws.invitations.get(inv.id)) });
67
+ // Create org membership if invitation has an organization
68
+ if (inv.organization_id) {
69
+ const user = ws.users.findOneBy('email', inv.email);
70
+ if (user) {
71
+ ws.organizationMemberships.insert({
72
+ object: 'organization_membership',
73
+ organization_id: inv.organization_id,
74
+ user_id: user.id,
75
+ role: { slug: inv.role_slug ?? 'member' },
76
+ status: 'active',
77
+ external_id: null,
78
+ metadata: {},
79
+ });
80
+ }
81
+ }
82
+ const updated = ws.invitations.get(inv.id);
83
+ return c.json(formatInvitation(updated));
84
+ });
85
+ app.post('/user_management/invitations/:id/revoke', (c) => {
86
+ const inv = ws.invitations.get(c.req.param('id'));
87
+ if (!inv)
88
+ throw notFound('Invitation');
89
+ if (inv.state !== 'pending') {
90
+ throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');
91
+ }
92
+ ws.invitations.update(inv.id, { state: 'revoked' });
93
+ const eventBus = store.getData(STORE_KEYS.eventBus);
94
+ eventBus?.emit({ event: EVENTS.invitationRevoked, data: formatInvitation(ws.invitations.get(inv.id)) });
95
+ const updated = ws.invitations.get(inv.id);
96
+ return c.json(formatInvitation(updated));
97
+ });
98
+ app.post('/user_management/invitations/:id/resend', (c) => {
99
+ const inv = ws.invitations.get(c.req.param('id'));
100
+ if (!inv)
101
+ throw notFound('Invitation');
102
+ const newToken = generateVerificationToken();
103
+ ws.invitations.update(inv.id, {
104
+ token: newToken,
105
+ accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${newToken}`,
106
+ expires_at: expiresIn(72 * 60),
107
+ state: 'pending',
108
+ });
109
+ const eventBus = store.getData(STORE_KEYS.eventBus);
110
+ eventBus?.emit({ event: EVENTS.invitationResent, data: formatInvitation(ws.invitations.get(inv.id)) });
111
+ const updated = ws.invitations.get(inv.id);
112
+ return c.json(formatInvitation(updated));
113
+ });
114
+ app.delete('/user_management/invitations/:id', (c) => {
115
+ const inv = ws.invitations.get(c.req.param('id'));
116
+ if (!inv)
117
+ throw notFound('Invitation');
118
+ ws.invitations.delete(inv.id);
119
+ return c.body(null, 204);
120
+ });
121
+ }
122
+ //# sourceMappingURL=invitations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invitations.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/invitations.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type RouteContext,\n notFound,\n validationError,\n parseJsonBody,\n WorkOSApiError,\n parseListParams,\n} from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatInvitation, generateVerificationToken, expiresIn, formatListResponse } from '../helpers.js';\nimport type { EventBus } from '../event-bus.js';\nimport { STORE_KEYS, EVENTS } from '../constants.js';\n\nexport function invitationRoutes(ctx: RouteContext): void {\n const { app, store, baseUrl } = ctx;\n const ws = getWorkOSStore(store);\n\n app.post('/user_management/invitations', async (c) => {\n const body = await parseJsonBody(c);\n const email = body.email as string | undefined;\n if (!email) {\n throw validationError('email is required', [{ field: 'email', code: 'required' }]);\n }\n\n const token = generateVerificationToken();\n const inv = ws.invitations.insert({\n object: 'invitation',\n email,\n state: 'pending',\n token,\n accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${token}`,\n organization_id: (body.organization_id as string) ?? null,\n inviter_user_id: (body.inviter_user_id as string) ?? null,\n role_slug: (body.role_slug as string) ?? null,\n expires_at: expiresIn(72 * 60), // 72 hours\n });\n\n return c.json(formatInvitation(inv), 201);\n });\n\n app.get('/user_management/invitations', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const emailFilter = url.searchParams.get('email') ?? undefined;\n const orgFilter = url.searchParams.get('organization_id') ?? undefined;\n\n const result = ws.invitations.list({\n ...params,\n filter: (inv) => {\n if (emailFilter && inv.email !== emailFilter) return false;\n if (orgFilter && inv.organization_id !== orgFilter) return false;\n return true;\n },\n });\n\n return c.json(formatListResponse(result, formatInvitation));\n });\n\n app.get('/user_management/invitations/by_token/:token', (c) => {\n const inv = ws.invitations.findOneBy('token', c.req.param('token'));\n if (!inv) throw notFound('Invitation');\n return c.json(formatInvitation(inv));\n });\n\n app.get('/user_management/invitations/:id', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n return c.json(formatInvitation(inv));\n });\n\n app.post('/user_management/invitations/:id/accept', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n\n if (inv.state !== 'pending') {\n throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');\n }\n\n ws.invitations.update(inv.id, { state: 'accepted' });\n const eventBus = store.getData<EventBus>(STORE_KEYS.eventBus);\n eventBus?.emit({ event: EVENTS.invitationAccepted, data: formatInvitation(ws.invitations.get(inv.id)!) });\n\n // Create org membership if invitation has an organization\n if (inv.organization_id) {\n const user = ws.users.findOneBy('email', inv.email);\n if (user) {\n ws.organizationMemberships.insert({\n object: 'organization_membership',\n organization_id: inv.organization_id,\n user_id: user.id,\n role: { slug: inv.role_slug ?? 'member' },\n status: 'active',\n external_id: null,\n metadata: {},\n });\n }\n }\n\n const updated = ws.invitations.get(inv.id)!;\n return c.json(formatInvitation(updated));\n });\n\n app.post('/user_management/invitations/:id/revoke', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n\n if (inv.state !== 'pending') {\n throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');\n }\n\n ws.invitations.update(inv.id, { state: 'revoked' });\n const eventBus = store.getData<EventBus>(STORE_KEYS.eventBus);\n eventBus?.emit({ event: EVENTS.invitationRevoked, data: formatInvitation(ws.invitations.get(inv.id)!) });\n const updated = ws.invitations.get(inv.id)!;\n return c.json(formatInvitation(updated));\n });\n\n app.post('/user_management/invitations/:id/resend', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n\n const newToken = generateVerificationToken();\n ws.invitations.update(inv.id, {\n token: newToken,\n accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${newToken}`,\n expires_at: expiresIn(72 * 60),\n state: 'pending',\n });\n\n const eventBus = store.getData<EventBus>(STORE_KEYS.eventBus);\n eventBus?.emit({ event: EVENTS.invitationResent, data: formatInvitation(ws.invitations.get(inv.id)!) });\n const updated = ws.invitations.get(inv.id)!;\n return c.json(formatInvitation(updated));\n });\n\n app.delete('/user_management/invitations/:id', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n ws.invitations.delete(inv.id);\n return c.body(null, 204);\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function legacyMfaRoutes(ctx: RouteContext): void;
@@ -0,0 +1,75 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatAuthFactor, formatAuthChallenge, expiresIn, isExpired, generateCode } from '../helpers.js';
4
+ import { randomBytes } from 'node:crypto';
5
+ export function legacyMfaRoutes(ctx) {
6
+ const { app, store } = ctx;
7
+ const ws = getWorkOSStore(store);
8
+ // Enroll factor (legacy path — not tied to user management users)
9
+ app.post('/auth/factors/enroll', async (c) => {
10
+ const body = await parseJsonBody(c);
11
+ const type = body.type ?? 'totp';
12
+ const issuer = body.totp_issuer ?? 'WorkOS Emulator';
13
+ const totpUser = body.totp_user ?? 'legacy@emulator';
14
+ const secret = randomBytes(20).toString('hex').slice(0, 32).toUpperCase();
15
+ const uri = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(totpUser)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}`;
16
+ const factor = ws.authFactors.insert({
17
+ object: 'authentication_factor',
18
+ user_id: 'legacy',
19
+ type: type,
20
+ totp: { issuer, user: totpUser, uri },
21
+ });
22
+ return c.json(formatAuthFactor(factor), 201);
23
+ });
24
+ // Get factor
25
+ app.get('/auth/factors/:id', (c) => {
26
+ const factor = ws.authFactors.get(c.req.param('id'));
27
+ if (!factor)
28
+ throw notFound('AuthenticationFactor');
29
+ return c.json(formatAuthFactor(factor));
30
+ });
31
+ // Delete factor
32
+ app.delete('/auth/factors/:id', (c) => {
33
+ const factor = ws.authFactors.get(c.req.param('id'));
34
+ if (!factor)
35
+ throw notFound('AuthenticationFactor');
36
+ ws.authFactors.delete(factor.id);
37
+ return c.body(null, 204);
38
+ });
39
+ // Create challenge
40
+ app.post('/auth/factors/:id/challenge', async (c) => {
41
+ const factor = ws.authFactors.get(c.req.param('id'));
42
+ if (!factor)
43
+ throw notFound('AuthenticationFactor');
44
+ const code = generateCode();
45
+ const challenge = ws.authChallenges.insert({
46
+ object: 'authentication_challenge',
47
+ user_id: factor.user_id,
48
+ factor_id: factor.id,
49
+ expires_at: expiresIn(10),
50
+ code,
51
+ });
52
+ return c.json(formatAuthChallenge(challenge), 201);
53
+ });
54
+ // Verify challenge
55
+ app.post('/auth/challenges/:id/verify', async (c) => {
56
+ const challenge = ws.authChallenges.get(c.req.param('id'));
57
+ if (!challenge)
58
+ throw notFound('AuthenticationChallenge');
59
+ if (isExpired(challenge.expires_at)) {
60
+ ws.authChallenges.delete(challenge.id);
61
+ throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');
62
+ }
63
+ const body = await parseJsonBody(c);
64
+ const code = body.code;
65
+ if (!code) {
66
+ throw new WorkOSApiError(400, 'code is required', 'invalid_request');
67
+ }
68
+ if (challenge.code && code !== challenge.code) {
69
+ throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');
70
+ }
71
+ ws.authChallenges.delete(challenge.id);
72
+ return c.json({ challenge: formatAuthChallenge(challenge), valid: true });
73
+ });
74
+ }
75
+ //# sourceMappingURL=legacy-mfa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-mfa.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/legacy-mfa.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatAuthFactor, formatAuthChallenge, expiresIn, isExpired, generateCode } from '../helpers.js';\nimport { randomBytes } from 'node:crypto';\n\nexport function legacyMfaRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // Enroll factor (legacy path — not tied to user management users)\n app.post('/auth/factors/enroll', async (c) => {\n const body = await parseJsonBody(c);\n const type = (body.type as string) ?? 'totp';\n const issuer = (body.totp_issuer as string) ?? 'WorkOS Emulator';\n const totpUser = (body.totp_user as string) ?? 'legacy@emulator';\n const secret = randomBytes(20).toString('hex').slice(0, 32).toUpperCase();\n const uri = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(totpUser)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}`;\n\n const factor = ws.authFactors.insert({\n object: 'authentication_factor',\n user_id: 'legacy',\n type: type as 'totp',\n totp: { issuer, user: totpUser, uri },\n });\n\n return c.json(formatAuthFactor(factor), 201);\n });\n\n // Get factor\n app.get('/auth/factors/:id', (c) => {\n const factor = ws.authFactors.get(c.req.param('id'));\n if (!factor) throw notFound('AuthenticationFactor');\n return c.json(formatAuthFactor(factor));\n });\n\n // Delete factor\n app.delete('/auth/factors/:id', (c) => {\n const factor = ws.authFactors.get(c.req.param('id'));\n if (!factor) throw notFound('AuthenticationFactor');\n ws.authFactors.delete(factor.id);\n return c.body(null, 204);\n });\n\n // Create challenge\n app.post('/auth/factors/:id/challenge', async (c) => {\n const factor = ws.authFactors.get(c.req.param('id'));\n if (!factor) throw notFound('AuthenticationFactor');\n\n const code = generateCode();\n const challenge = ws.authChallenges.insert({\n object: 'authentication_challenge',\n user_id: factor.user_id,\n factor_id: factor.id,\n expires_at: expiresIn(10),\n code,\n });\n\n return c.json(formatAuthChallenge(challenge), 201);\n });\n\n // Verify challenge\n app.post('/auth/challenges/:id/verify', async (c) => {\n const challenge = ws.authChallenges.get(c.req.param('id'));\n if (!challenge) throw notFound('AuthenticationChallenge');\n\n if (isExpired(challenge.expires_at)) {\n ws.authChallenges.delete(challenge.id);\n throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');\n }\n\n const body = await parseJsonBody(c);\n const code = body.code as string;\n if (!code) {\n throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n }\n if (challenge.code && code !== challenge.code) {\n throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');\n }\n\n ws.authChallenges.delete(challenge.id);\n return c.json({ challenge: formatAuthChallenge(challenge), valid: true });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function magicAuthRoutes(ctx: RouteContext): void;
@@ -0,0 +1,32 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatMagicAuth, generateCode, expiresIn } from '../helpers.js';
4
+ export function magicAuthRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/user_management/magic_auth/:id', (c) => {
8
+ const ma = ws.magicAuths.get(c.req.param('id'));
9
+ if (!ma)
10
+ throw notFound('Magic Auth');
11
+ return c.json(formatMagicAuth(ma));
12
+ });
13
+ app.post('/user_management/magic_auth', async (c) => {
14
+ const body = await parseJsonBody(c);
15
+ const email = body.email;
16
+ if (!email) {
17
+ throw new WorkOSApiError(400, 'email is required', 'invalid_request');
18
+ }
19
+ const user = ws.users.findOneBy('email', email);
20
+ if (!user)
21
+ throw notFound('User');
22
+ const ma = ws.magicAuths.insert({
23
+ object: 'magic_auth',
24
+ user_id: user.id,
25
+ email: user.email,
26
+ code: generateCode(),
27
+ expires_at: expiresIn(10),
28
+ });
29
+ return c.json(formatMagicAuth(ma), 201);
30
+ });
31
+ }
32
+ //# sourceMappingURL=magic-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"magic-auth.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/magic-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzE,MAAM,UAAU,eAAe,CAAC,GAAiB;IAC/C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC3B,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,GAAG,CAAC,GAAG,CAAC,iCAAiC,EAAE,CAAC,CAAC,EAAE,EAAE;QAC/C,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,EAAE;YAAE,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAClD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAA2B,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,iBAAiB,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAC9B,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,YAAY,EAAE;YACpB,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;SAC1B,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatMagicAuth, generateCode, expiresIn } from '../helpers.js';\n\nexport function magicAuthRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/magic_auth/:id', (c) => {\n const ma = ws.magicAuths.get(c.req.param('id'));\n if (!ma) throw notFound('Magic Auth');\n return c.json(formatMagicAuth(ma));\n });\n\n app.post('/user_management/magic_auth', async (c) => {\n const body = await parseJsonBody(c);\n const email = body.email as string | undefined;\n if (!email) {\n throw new WorkOSApiError(400, 'email is required', 'invalid_request');\n }\n\n const user = ws.users.findOneBy('email', email);\n if (!user) throw notFound('User');\n\n const ma = ws.magicAuths.insert({\n object: 'magic_auth',\n user_id: user.id,\n email: user.email,\n code: generateCode(),\n expires_at: expiresIn(10),\n });\n\n return c.json(formatMagicAuth(ma), 201);\n });\n}\n"]}