@rebasepro/server-core 0.4.0 → 0.6.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 (282) hide show
  1. package/README.md +62 -25
  2. package/dist/{server-core/src/api → api}/errors.d.ts +20 -2
  3. package/dist/{server-core/src/api → api}/rest/api-generator.d.ts +8 -2
  4. package/dist/{server-core/src/api → api}/types.d.ts +2 -0
  5. package/dist/auth/admin-user-ops.d.ts +79 -0
  6. package/dist/{server-core/src/auth → auth}/api-keys/index.d.ts +2 -2
  7. package/dist/{server-core/src/auth → auth}/auth-hooks.d.ts +37 -9
  8. package/dist/{server-core/src/auth → auth}/builtin-auth-adapter.d.ts +4 -4
  9. package/dist/{server-core/src/auth → auth}/index.d.ts +5 -2
  10. package/dist/{server-core/src/auth → auth}/jwt.d.ts +10 -0
  11. package/dist/auth/mfa-routes.d.ts +6 -0
  12. package/dist/auth/reset-password-admin.d.ts +29 -0
  13. package/dist/auth/session-routes.d.ts +25 -0
  14. package/dist/backend-CIxN4FVm.js +15 -0
  15. package/dist/backend-CIxN4FVm.js.map +1 -0
  16. package/dist/chunk-Dze3rakg.js +42 -0
  17. package/dist/{server-core/src/cron → cron}/cron-scheduler.d.ts +1 -1
  18. package/dist/dist-CZKP-Xz4.js +832 -0
  19. package/dist/dist-CZKP-Xz4.js.map +1 -0
  20. package/dist/env.d.ts +102 -0
  21. package/dist/from-VbwD7xRf.js +3849 -0
  22. package/dist/from-VbwD7xRf.js.map +1 -0
  23. package/dist/{server-core/src/index.d.ts → index.d.ts} +1 -0
  24. package/dist/index.es.js +60871 -50424
  25. package/dist/index.es.js.map +1 -1
  26. package/dist/index.umd.js +73151 -51256
  27. package/dist/index.umd.js.map +1 -1
  28. package/dist/init/docs.d.ts +4 -0
  29. package/dist/init/health.d.ts +2 -0
  30. package/dist/init/middlewares.d.ts +10 -0
  31. package/dist/init/shutdown.d.ts +11 -0
  32. package/dist/init/storage.d.ts +5 -0
  33. package/dist/{server-core/src/init.d.ts → init.d.ts} +4 -4
  34. package/dist/jwt-DHcQRGC3.js +4168 -0
  35. package/dist/jwt-DHcQRGC3.js.map +1 -0
  36. package/dist/logger-BYU66ENZ.js +94 -0
  37. package/dist/logger-BYU66ENZ.js.map +1 -0
  38. package/dist/ms-BeBSuOXN.js +125 -0
  39. package/dist/ms-BeBSuOXN.js.map +1 -0
  40. package/dist/multipart-parser-CedBDOeC.js +299 -0
  41. package/dist/multipart-parser-CedBDOeC.js.map +1 -0
  42. package/dist/src-COaj0G3P.js +1182 -0
  43. package/dist/src-COaj0G3P.js.map +1 -0
  44. package/dist/{server-core/src/storage → storage}/image-transform.d.ts +3 -1
  45. package/dist/utils/request-id.d.ts +4 -0
  46. package/package.json +29 -29
  47. package/src/api/collections_for_test/callbacks_test_collection.ts +7 -6
  48. package/src/api/errors.ts +95 -20
  49. package/src/api/graphql/graphql-schema-generator.ts +15 -10
  50. package/src/api/logs-routes.ts +5 -2
  51. package/src/api/openapi-generator.ts +1 -1
  52. package/src/api/rest/api-generator-count.test.ts +21 -10
  53. package/src/api/rest/api-generator.ts +151 -36
  54. package/src/api/rest/query-parser.ts +18 -12
  55. package/src/api/server.ts +13 -2
  56. package/src/api/types.ts +2 -0
  57. package/src/auth/adapter-middleware.ts +17 -10
  58. package/src/auth/admin-user-ops.ts +236 -0
  59. package/src/auth/api-keys/api-key-middleware.ts +15 -9
  60. package/src/auth/api-keys/api-key-permission-guard.ts +1 -1
  61. package/src/auth/api-keys/api-key-routes.ts +3 -3
  62. package/src/auth/api-keys/api-key-store.ts +14 -8
  63. package/src/auth/api-keys/index.ts +2 -2
  64. package/src/auth/apple-oauth.ts +4 -3
  65. package/src/auth/auth-hooks.ts +50 -10
  66. package/src/auth/bitbucket-oauth.ts +5 -4
  67. package/src/auth/builtin-auth-adapter.ts +64 -54
  68. package/src/auth/custom-auth-adapter.ts +4 -5
  69. package/src/auth/discord-oauth.ts +5 -4
  70. package/src/auth/facebook-oauth.ts +5 -4
  71. package/src/auth/github-oauth.ts +6 -5
  72. package/src/auth/gitlab-oauth.ts +5 -4
  73. package/src/auth/google-oauth.ts +2 -1
  74. package/src/auth/index.ts +6 -2
  75. package/src/auth/jwt.ts +15 -4
  76. package/src/auth/linkedin-oauth.ts +4 -3
  77. package/src/auth/mfa-routes.ts +299 -0
  78. package/src/auth/mfa.ts +2 -1
  79. package/src/auth/microsoft-oauth.ts +5 -4
  80. package/src/auth/middleware.ts +5 -4
  81. package/src/auth/rate-limiter.ts +1 -1
  82. package/src/auth/reset-password-admin.ts +144 -0
  83. package/src/auth/rls-scope.ts +1 -1
  84. package/src/auth/routes.ts +52 -600
  85. package/src/auth/session-routes.ts +341 -0
  86. package/src/auth/slack-oauth.ts +6 -5
  87. package/src/auth/spotify-oauth.ts +5 -4
  88. package/src/auth/twitter-oauth.ts +4 -3
  89. package/src/collections/loader.ts +5 -4
  90. package/src/cron/cron-scheduler.test.ts +23 -11
  91. package/src/cron/cron-scheduler.ts +14 -8
  92. package/src/cron/cron-store.ts +1 -1
  93. package/src/email/smtp-email-service.ts +3 -2
  94. package/src/env.ts +11 -10
  95. package/src/index.ts +1 -0
  96. package/src/init/docs.ts +47 -0
  97. package/src/init/health.ts +37 -0
  98. package/src/init/middlewares.ts +61 -0
  99. package/src/init/shutdown.ts +56 -0
  100. package/src/init/storage.ts +57 -0
  101. package/src/init.ts +93 -295
  102. package/src/serve-spa.ts +12 -5
  103. package/src/services/driver-registry.ts +4 -3
  104. package/src/storage/S3StorageController.ts +1 -2
  105. package/src/storage/image-transform.ts +25 -10
  106. package/src/storage/routes.ts +30 -11
  107. package/src/storage/storage-registry.ts +4 -3
  108. package/src/storage/tus-handler.ts +12 -12
  109. package/src/utils/request-id.ts +40 -0
  110. package/src/utils/request-logger.ts +6 -0
  111. package/test/api-generator.test.ts +90 -2
  112. package/test/api-key-permission-guard.test.ts +24 -12
  113. package/test/auth-routes.test.ts +5 -3
  114. package/test/backend-hooks-data.test.ts +99 -30
  115. package/test/custom-auth-adapter.test.ts +16 -15
  116. package/test/email-templates.test.ts +10 -5
  117. package/test/env.test.ts +10 -10
  118. package/test/function-loader.test.ts +7 -4
  119. package/test/graphql-schema-generator.test.ts +554 -0
  120. package/test/jwt-security.test.ts +1 -1
  121. package/test/query-parser.test.ts +0 -1
  122. package/test/reset-password-admin.test.ts +113 -0
  123. package/test/singleton.test.ts +5 -5
  124. package/test/smtp-email-service.test.ts +21 -21
  125. package/test/webhook-service.test.ts +22 -11
  126. package/tsconfig.json +2 -0
  127. package/tsconfig.prod.json +3 -0
  128. package/vite.config.ts +5 -5
  129. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  130. package/dist/common/src/collections/default-collections.d.ts +0 -9
  131. package/dist/common/src/collections/index.d.ts +0 -2
  132. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  133. package/dist/common/src/data/query_builder.d.ts +0 -55
  134. package/dist/common/src/index.d.ts +0 -4
  135. package/dist/common/src/util/builders.d.ts +0 -57
  136. package/dist/common/src/util/callbacks.d.ts +0 -6
  137. package/dist/common/src/util/collections.d.ts +0 -11
  138. package/dist/common/src/util/common.d.ts +0 -2
  139. package/dist/common/src/util/conditions.d.ts +0 -26
  140. package/dist/common/src/util/entities.d.ts +0 -58
  141. package/dist/common/src/util/enums.d.ts +0 -3
  142. package/dist/common/src/util/index.d.ts +0 -16
  143. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  144. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  145. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  146. package/dist/common/src/util/paths.d.ts +0 -14
  147. package/dist/common/src/util/permissions.d.ts +0 -6
  148. package/dist/common/src/util/references.d.ts +0 -2
  149. package/dist/common/src/util/relations.d.ts +0 -22
  150. package/dist/common/src/util/resolutions.d.ts +0 -72
  151. package/dist/common/src/util/storage.d.ts +0 -24
  152. package/dist/index-Cr1D21av.js +0 -49
  153. package/dist/index-Cr1D21av.js.map +0 -1
  154. package/dist/server-core/src/auth/admin-routes.d.ts +0 -27
  155. package/dist/server-core/src/env.d.ts +0 -137
  156. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  157. package/dist/types/src/controllers/auth.d.ts +0 -104
  158. package/dist/types/src/controllers/client.d.ts +0 -168
  159. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  160. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  161. package/dist/types/src/controllers/data.d.ts +0 -207
  162. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  163. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  164. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  165. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  166. package/dist/types/src/controllers/email.d.ts +0 -36
  167. package/dist/types/src/controllers/index.d.ts +0 -18
  168. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  169. package/dist/types/src/controllers/navigation.d.ts +0 -225
  170. package/dist/types/src/controllers/registry.d.ts +0 -63
  171. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  172. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  173. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  174. package/dist/types/src/controllers/storage.d.ts +0 -171
  175. package/dist/types/src/index.d.ts +0 -4
  176. package/dist/types/src/rebase_context.d.ts +0 -122
  177. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  178. package/dist/types/src/types/backend.d.ts +0 -536
  179. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  180. package/dist/types/src/types/builders.d.ts +0 -15
  181. package/dist/types/src/types/chips.d.ts +0 -5
  182. package/dist/types/src/types/collections.d.ts +0 -941
  183. package/dist/types/src/types/component_ref.d.ts +0 -47
  184. package/dist/types/src/types/cron.d.ts +0 -102
  185. package/dist/types/src/types/data_source.d.ts +0 -64
  186. package/dist/types/src/types/database_adapter.d.ts +0 -94
  187. package/dist/types/src/types/entities.d.ts +0 -145
  188. package/dist/types/src/types/entity_actions.d.ts +0 -104
  189. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  190. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  191. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  192. package/dist/types/src/types/entity_views.d.ts +0 -87
  193. package/dist/types/src/types/export_import.d.ts +0 -21
  194. package/dist/types/src/types/formex.d.ts +0 -40
  195. package/dist/types/src/types/index.d.ts +0 -28
  196. package/dist/types/src/types/locales.d.ts +0 -4
  197. package/dist/types/src/types/modify_collections.d.ts +0 -5
  198. package/dist/types/src/types/plugins.d.ts +0 -282
  199. package/dist/types/src/types/properties.d.ts +0 -1181
  200. package/dist/types/src/types/property_config.d.ts +0 -74
  201. package/dist/types/src/types/relations.d.ts +0 -336
  202. package/dist/types/src/types/slots.d.ts +0 -262
  203. package/dist/types/src/types/translations.d.ts +0 -900
  204. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  205. package/dist/types/src/types/websockets.d.ts +0 -78
  206. package/dist/types/src/users/index.d.ts +0 -1
  207. package/dist/types/src/users/user.d.ts +0 -50
  208. package/src/auth/admin-routes.ts +0 -534
  209. package/test/admin-routes.test.ts +0 -544
  210. package/test/backend-hooks-admin.test.ts +0 -388
  211. package/test.ts +0 -6
  212. /package/dist/{server-core/src/api → api}/ast-schema-editor.d.ts +0 -0
  213. /package/dist/{server-core/src/api → api}/collections_for_test/callbacks_test_collection.d.ts +0 -0
  214. /package/dist/{server-core/src/api → api}/graphql/graphql-schema-generator.d.ts +0 -0
  215. /package/dist/{server-core/src/api → api}/graphql/index.d.ts +0 -0
  216. /package/dist/{server-core/src/api → api}/index.d.ts +0 -0
  217. /package/dist/{server-core/src/api → api}/logs-routes.d.ts +0 -0
  218. /package/dist/{server-core/src/api → api}/openapi-generator.d.ts +0 -0
  219. /package/dist/{server-core/src/api → api}/rest/index.d.ts +0 -0
  220. /package/dist/{server-core/src/api → api}/rest/query-parser.d.ts +0 -0
  221. /package/dist/{server-core/src/api → api}/schema-editor-routes.d.ts +0 -0
  222. /package/dist/{server-core/src/api → api}/server.d.ts +0 -0
  223. /package/dist/{server-core/src/auth → auth}/adapter-middleware.d.ts +0 -0
  224. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-middleware.d.ts +0 -0
  225. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-permission-guard.d.ts +0 -0
  226. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-routes.d.ts +0 -0
  227. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-store.d.ts +0 -0
  228. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-types.d.ts +0 -0
  229. /package/dist/{server-core/src/auth → auth}/apple-oauth.d.ts +0 -0
  230. /package/dist/{server-core/src/auth → auth}/bitbucket-oauth.d.ts +0 -0
  231. /package/dist/{server-core/src/auth → auth}/crypto-utils.d.ts +0 -0
  232. /package/dist/{server-core/src/auth → auth}/custom-auth-adapter.d.ts +0 -0
  233. /package/dist/{server-core/src/auth → auth}/discord-oauth.d.ts +0 -0
  234. /package/dist/{server-core/src/auth → auth}/facebook-oauth.d.ts +0 -0
  235. /package/dist/{server-core/src/auth → auth}/github-oauth.d.ts +0 -0
  236. /package/dist/{server-core/src/auth → auth}/gitlab-oauth.d.ts +0 -0
  237. /package/dist/{server-core/src/auth → auth}/google-oauth.d.ts +0 -0
  238. /package/dist/{server-core/src/auth → auth}/interfaces.d.ts +0 -0
  239. /package/dist/{server-core/src/auth → auth}/linkedin-oauth.d.ts +0 -0
  240. /package/dist/{server-core/src/auth → auth}/mfa.d.ts +0 -0
  241. /package/dist/{server-core/src/auth → auth}/microsoft-oauth.d.ts +0 -0
  242. /package/dist/{server-core/src/auth → auth}/middleware.d.ts +0 -0
  243. /package/dist/{server-core/src/auth → auth}/password.d.ts +0 -0
  244. /package/dist/{server-core/src/auth → auth}/rate-limiter.d.ts +0 -0
  245. /package/dist/{server-core/src/auth → auth}/rls-scope.d.ts +0 -0
  246. /package/dist/{server-core/src/auth → auth}/routes.d.ts +0 -0
  247. /package/dist/{server-core/src/auth → auth}/slack-oauth.d.ts +0 -0
  248. /package/dist/{server-core/src/auth → auth}/spotify-oauth.d.ts +0 -0
  249. /package/dist/{server-core/src/auth → auth}/twitter-oauth.d.ts +0 -0
  250. /package/dist/{server-core/src/collections → collections}/BackendCollectionRegistry.d.ts +0 -0
  251. /package/dist/{server-core/src/collections → collections}/loader.d.ts +0 -0
  252. /package/dist/{server-core/src/cron → cron}/cron-loader.d.ts +0 -0
  253. /package/dist/{server-core/src/cron → cron}/cron-routes.d.ts +0 -0
  254. /package/dist/{server-core/src/cron → cron}/cron-store.d.ts +0 -0
  255. /package/dist/{server-core/src/cron → cron}/index.d.ts +0 -0
  256. /package/dist/{server-core/src/db → db}/interfaces.d.ts +0 -0
  257. /package/dist/{server-core/src/email → email}/index.d.ts +0 -0
  258. /package/dist/{server-core/src/email → email}/smtp-email-service.d.ts +0 -0
  259. /package/dist/{server-core/src/email → email}/templates.d.ts +0 -0
  260. /package/dist/{server-core/src/email → email}/types.d.ts +0 -0
  261. /package/dist/{server-core/src/functions → functions}/function-loader.d.ts +0 -0
  262. /package/dist/{server-core/src/functions → functions}/function-routes.d.ts +0 -0
  263. /package/dist/{server-core/src/functions → functions}/index.d.ts +0 -0
  264. /package/dist/{server-core/src/history → history}/history-routes.d.ts +0 -0
  265. /package/dist/{server-core/src/history → history}/index.d.ts +0 -0
  266. /package/dist/{server-core/src/serve-spa.d.ts → serve-spa.d.ts} +0 -0
  267. /package/dist/{server-core/src/services → services}/driver-registry.d.ts +0 -0
  268. /package/dist/{server-core/src/services → services}/webhook-service.d.ts +0 -0
  269. /package/dist/{server-core/src/singleton.d.ts → singleton.d.ts} +0 -0
  270. /package/dist/{server-core/src/storage → storage}/LocalStorageController.d.ts +0 -0
  271. /package/dist/{server-core/src/storage → storage}/S3StorageController.d.ts +0 -0
  272. /package/dist/{server-core/src/storage → storage}/index.d.ts +0 -0
  273. /package/dist/{server-core/src/storage → storage}/routes.d.ts +0 -0
  274. /package/dist/{server-core/src/storage → storage}/storage-registry.d.ts +0 -0
  275. /package/dist/{server-core/src/storage → storage}/tus-handler.d.ts +0 -0
  276. /package/dist/{server-core/src/storage → storage}/types.d.ts +0 -0
  277. /package/dist/{server-core/src/types → types}/index.d.ts +0 -0
  278. /package/dist/{server-core/src/utils → utils}/dev-port.d.ts +0 -0
  279. /package/dist/{server-core/src/utils → utils}/logger.d.ts +0 -0
  280. /package/dist/{server-core/src/utils → utils}/logging.d.ts +0 -0
  281. /package/dist/{server-core/src/utils → utils}/request-logger.d.ts +0 -0
  282. /package/dist/{server-core/src/utils → utils}/sql.d.ts +0 -0
@@ -1,388 +0,0 @@
1
- /**
2
- * BackendHooks — Admin Routes Integration Tests
3
- *
4
- * Verifies that BackendHooks (users + roles) are correctly applied
5
- * within the admin route handlers.
6
- */
7
-
8
- import { Hono } from "hono";
9
- import type { HonoEnv } from "../src/api/types";
10
- import { errorHandler } from "../src/api/errors";
11
- import { createAdminRoutes } from "../src/auth/admin-routes";
12
- import { configureJwt, generateAccessToken } from "../src/auth/jwt";
13
- import type { AuthModuleConfig } from "../src/auth/routes";
14
- import type { BackendHooks } from "@rebasepro/types";
15
-
16
- // ── Mocks ───────────────────────────────────────────────────────────────────
17
-
18
- jest.mock("../src/auth/password");
19
-
20
- import { hashPassword, validatePasswordStrength } from "../src/auth/password";
21
-
22
- // ── Helpers ─────────────────────────────────────────────────────────────────
23
-
24
- const TEST_SECRET = "backend-hooks-test-secret-that-is-32-chars-long!!!!!";
25
-
26
- function mockUser(overrides: Partial<{
27
- id: string;
28
- email: string;
29
- displayName: string | null;
30
- photoUrl: string | null;
31
- }> = {}) {
32
- return {
33
- id: overrides.id ?? "user-1",
34
- email: overrides.email ?? "test@example.com",
35
- passwordHash: "salt:hash",
36
- displayName: overrides.displayName ?? "Test User",
37
- photoUrl: overrides.photoUrl ?? null,
38
- emailVerified: false,
39
- emailVerificationToken: null,
40
- emailVerificationSentAt: null,
41
- createdAt: new Date("2024-01-01"),
42
- updatedAt: new Date("2024-01-01")
43
- };
44
- }
45
-
46
- function mockRole(id: string, isAdmin = false) {
47
- return {
48
- id,
49
- name: id.charAt(0).toUpperCase() + id.slice(1),
50
- isAdmin,
51
- defaultPermissions: null,
52
- collectionPermissions: null
53
- };
54
- }
55
-
56
- let mockAuthRepo: jest.Mocked<any>;
57
-
58
- function createApp(hooks?: BackendHooks) {
59
- mockAuthRepo = {
60
- getUserByEmail: jest.fn().mockResolvedValue(null),
61
- getUserByIdentity: jest.fn().mockResolvedValue(null),
62
- linkUserIdentity: jest.fn().mockResolvedValue(undefined),
63
- getUserIdentities: jest.fn().mockResolvedValue([]),
64
- getUserById: jest.fn().mockResolvedValue(null),
65
- createUser: jest.fn().mockImplementation((data) =>
66
- Promise.resolve(mockUser({ email: data.email, displayName: data.displayName }))
67
- ),
68
- listUsers: jest.fn().mockResolvedValue([]),
69
- listUsersPaginated: jest.fn().mockResolvedValue({ users: [], total: 0, limit: 25, offset: 0 }),
70
- getUserRoles: jest.fn().mockResolvedValue([mockRole("editor")]),
71
- getUserRoleIds: jest.fn().mockResolvedValue(["editor"]),
72
- assignDefaultRole: jest.fn().mockResolvedValue(undefined),
73
- setUserRoles: jest.fn().mockResolvedValue(undefined),
74
- updateUser: jest.fn().mockImplementation((id, data) =>
75
- Promise.resolve(mockUser({ id, ...data }))
76
- ),
77
- deleteUser: jest.fn().mockResolvedValue(undefined),
78
- updatePassword: jest.fn().mockResolvedValue(undefined),
79
- setEmailVerified: jest.fn().mockResolvedValue(undefined),
80
- setVerificationToken: jest.fn().mockResolvedValue(undefined),
81
- getUserByVerificationToken: jest.fn().mockResolvedValue(null),
82
- getUserWithRoles: jest.fn().mockImplementation(async (userId) => {
83
- const user = mockUser({ id: userId });
84
- return { user, roles: [mockRole("editor")] };
85
- }),
86
- createRefreshToken: jest.fn().mockResolvedValue(undefined),
87
- findRefreshTokenByHash: jest.fn().mockResolvedValue(null),
88
- deleteRefreshToken: jest.fn().mockResolvedValue(undefined),
89
- deleteAllRefreshTokensForUser: jest.fn().mockResolvedValue(undefined),
90
- listRefreshTokensForUser: jest.fn().mockResolvedValue([]),
91
- deleteRefreshTokenById: jest.fn().mockResolvedValue(undefined),
92
- createPasswordResetToken: jest.fn().mockResolvedValue(undefined),
93
- findValidPasswordResetToken: jest.fn().mockResolvedValue(null),
94
- markPasswordResetTokenUsed: jest.fn().mockResolvedValue(undefined),
95
- deleteExpiredPasswordResetTokens: jest.fn().mockResolvedValue(undefined),
96
- listRoles: jest.fn().mockResolvedValue([]),
97
- getRoleById: jest.fn().mockResolvedValue(null),
98
- createRole: jest.fn().mockImplementation(r => Promise.resolve({
99
- id: r.id, name: r.name, isAdmin: r.isAdmin || false,
100
- defaultPermissions: null, collectionPermissions: null
101
- })),
102
- updateRole: jest.fn().mockImplementation((id, r) => Promise.resolve({
103
- id, name: r.name, isAdmin: r.isAdmin || false,
104
- defaultPermissions: null, collectionPermissions: null
105
- })),
106
- deleteRole: jest.fn().mockResolvedValue(undefined)
107
- };
108
-
109
- (validatePasswordStrength as jest.Mock).mockReturnValue({ valid: true, errors: [] });
110
- (hashPassword as jest.Mock).mockResolvedValue("hashed-pw");
111
-
112
- const config: AuthModuleConfig & { hooks?: BackendHooks } = {
113
- authRepo: mockAuthRepo,
114
- hooks
115
- };
116
-
117
- const app = new Hono<HonoEnv>();
118
- app.onError(errorHandler);
119
- app.route("/admin", createAdminRoutes(config));
120
- return app;
121
- }
122
-
123
- function adminAuth(userId = "admin-1") {
124
- return { Authorization: `Bearer ${generateAccessToken(userId, ["admin"])}` };
125
- }
126
-
127
- function json(body: Record<string, unknown>) {
128
- return {
129
- method: "POST" as const,
130
- headers: { "Content-Type": "application/json" },
131
- body: JSON.stringify(body)
132
- };
133
- }
134
-
135
- // ═════════════════════════════════════════════════════════════════════════════
136
- // TESTS
137
- // ═════════════════════════════════════════════════════════════════════════════
138
-
139
- describe("BackendHooks — Admin Routes", () => {
140
- beforeAll(() => {
141
- configureJwt({ secret: TEST_SECRET, accessExpiresIn: "1h" });
142
- });
143
-
144
- beforeEach(() => {
145
- jest.clearAllMocks();
146
- });
147
-
148
- // ── users.afterRead ─────────────────────────────────────────────────
149
- describe("users.afterRead", () => {
150
- it("filters out users from GET /admin/users list", async () => {
151
- const hooks: BackendHooks = {
152
- users: {
153
- afterRead(user) {
154
- // Hide system users
155
- if (user.email.endsWith("@system.internal")) return null;
156
- return user;
157
- }
158
- }
159
- };
160
- const app = createApp(hooks);
161
- mockAuthRepo.listUsersPaginated.mockResolvedValueOnce({
162
- users: [
163
- mockUser({ id: "u1", email: "alice@test.com" }),
164
- mockUser({ id: "u2", email: "bot@system.internal" }),
165
- mockUser({ id: "u3", email: "bob@test.com" })
166
- ],
167
- total: 3,
168
- limit: 25,
169
- offset: 0
170
- });
171
- mockAuthRepo.getUserRoleIds
172
- .mockResolvedValueOnce(["editor"])
173
- .mockResolvedValueOnce(["editor"])
174
- .mockResolvedValueOnce(["editor"]);
175
-
176
- const res = await app.request("/admin/users", { headers: { ...adminAuth() } });
177
- expect(res.status).toBe(200);
178
- const body = await res.json() as any;
179
- expect(body.users).toHaveLength(2);
180
- expect(body.users.map((u: any) => u.email)).toEqual(["alice@test.com", "bob@test.com"]);
181
- });
182
-
183
- it("transforms user data in GET /admin/users list", async () => {
184
- const hooks: BackendHooks = {
185
- users: {
186
- afterRead(user) {
187
- // Mask emails
188
- return { ...user, email: "***@masked.com" };
189
- }
190
- }
191
- };
192
- const app = createApp(hooks);
193
- mockAuthRepo.listUsersPaginated.mockResolvedValueOnce({
194
- users: [
195
- mockUser({ id: "u1", email: "alice@secret.com" })
196
- ],
197
- total: 1,
198
- limit: 25,
199
- offset: 0
200
- });
201
- mockAuthRepo.getUserRoleIds.mockResolvedValueOnce(["editor"]);
202
-
203
- const res = await app.request("/admin/users", { headers: { ...adminAuth() } });
204
- const body = await res.json() as any;
205
- expect(body.users[0].email).toBe("***@masked.com");
206
- });
207
-
208
- it("returns 404 when afterRead filters single user GET /admin/users/:id", async () => {
209
- const hooks: BackendHooks = {
210
- users: {
211
- afterRead(user) {
212
- if (user.uid === "hidden-user") return null;
213
- return user;
214
- }
215
- }
216
- };
217
- const app = createApp(hooks);
218
- mockAuthRepo.getUserWithRoles.mockResolvedValueOnce({
219
- user: mockUser({ id: "hidden-user" }),
220
- roles: [mockRole("editor")]
221
- });
222
-
223
- const res = await app.request("/admin/users/hidden-user", { headers: { ...adminAuth() } });
224
- expect(res.status).toBe(404);
225
- });
226
-
227
- it("passes context with request user info", async () => {
228
- const afterReadSpy = jest.fn((user, ctx) => user);
229
- const hooks: BackendHooks = { users: { afterRead: afterReadSpy } };
230
- const app = createApp(hooks);
231
- mockAuthRepo.listUsersPaginated.mockResolvedValueOnce({
232
- users: [mockUser({ id: "u1" })],
233
- total: 1,
234
- limit: 25,
235
- offset: 0
236
- });
237
- mockAuthRepo.getUserRoleIds.mockResolvedValueOnce(["editor"]);
238
-
239
- await app.request("/admin/users", { headers: { ...adminAuth("admin-42") } });
240
-
241
- expect(afterReadSpy).toHaveBeenCalledTimes(1);
242
- const ctx = afterReadSpy.mock.calls[0][1];
243
- expect(ctx.method).toBe("GET");
244
- expect(ctx.requestUser).toBeDefined();
245
- expect(ctx.requestUser.userId).toBe("admin-42");
246
- expect(ctx.requestUser.roles).toContain("admin");
247
- });
248
- });
249
-
250
- // ── users.beforeSave ────────────────────────────────────────────────
251
- describe("users.beforeSave", () => {
252
- it("transforms data before creating a user (POST)", async () => {
253
- const hooks: BackendHooks = {
254
- users: {
255
- beforeSave(data) {
256
- // Force lowercase display name
257
- return { ...data, displayName: data.displayName?.toLowerCase() };
258
- }
259
- }
260
- };
261
- const app = createApp(hooks);
262
-
263
- await app.request("/admin/users", {
264
- ...json({ email: "new@test.com", displayName: "ALICE", password: "StrongPass1" }),
265
- headers: { ...json({}).headers, ...adminAuth() }
266
- });
267
-
268
- expect(mockAuthRepo.createUser).toHaveBeenCalledWith(
269
- expect.objectContaining({ displayName: "alice" })
270
- );
271
- });
272
-
273
- it("transforms data before updating a user (PUT)", async () => {
274
- const hooks: BackendHooks = {
275
- users: {
276
- beforeSave(data) {
277
- return { ...data, displayName: "hook-modified" };
278
- }
279
- }
280
- };
281
- const app = createApp(hooks);
282
- mockAuthRepo.getUserById.mockResolvedValueOnce(mockUser({ id: "u1" }));
283
- mockAuthRepo.getUserWithRoles.mockResolvedValueOnce({
284
- user: mockUser({ id: "u1", displayName: "hook-modified" }),
285
- roles: [mockRole("editor")]
286
- });
287
-
288
- const res = await app.request("/admin/users/u1", {
289
- method: "PUT",
290
- headers: { "Content-Type": "application/json", ...adminAuth() },
291
- body: JSON.stringify({ displayName: "Original" })
292
- });
293
-
294
- expect(res.status).toBe(200);
295
- expect(mockAuthRepo.updateUser).toHaveBeenCalledWith("u1",
296
- expect.objectContaining({ displayName: "hook-modified" })
297
- );
298
- });
299
- });
300
-
301
- // ── users.afterSave ─────────────────────────────────────────────────
302
- describe("users.afterSave", () => {
303
- it("fires afterSave after user creation", async () => {
304
- const afterSaveSpy = jest.fn();
305
- const hooks: BackendHooks = { users: { afterSave: afterSaveSpy } };
306
- const app = createApp(hooks);
307
-
308
- const res = await app.request("/admin/users", {
309
- ...json({ email: "new@test.com", password: "StrongPass1" }),
310
- headers: { ...json({}).headers, ...adminAuth() }
311
- });
312
-
313
- expect(res.status).toBe(201);
314
- // afterSave is fire-and-forget, give it a tick
315
- await new Promise(r => setTimeout(r, 50));
316
- expect(afterSaveSpy).toHaveBeenCalledTimes(1);
317
- expect(afterSaveSpy.mock.calls[0][0]).toMatchObject({ email: "new@test.com" });
318
- });
319
- });
320
-
321
- // ── users.beforeDelete ──────────────────────────────────────────────
322
- describe("users.beforeDelete", () => {
323
- it("aborts deletion when beforeDelete throws", async () => {
324
- const hooks: BackendHooks = {
325
- users: {
326
- beforeDelete(userId) {
327
- if (userId === "protected-user") {
328
- throw new Error("Cannot delete protected user");
329
- }
330
- }
331
- }
332
- };
333
- const app = createApp(hooks);
334
- mockAuthRepo.getUserById.mockResolvedValueOnce(mockUser({ id: "protected-user" }));
335
-
336
- const res = await app.request("/admin/users/protected-user", {
337
- method: "DELETE",
338
- headers: { ...adminAuth("admin-1") }
339
- });
340
-
341
- expect(res.status).toBe(500);
342
- expect(mockAuthRepo.deleteUser).not.toHaveBeenCalled();
343
- });
344
- });
345
-
346
- // ── users.afterDelete ───────────────────────────────────────────────
347
- describe("users.afterDelete", () => {
348
- it("fires afterDelete after user is deleted", async () => {
349
- const afterDeleteSpy = jest.fn();
350
- const hooks: BackendHooks = { users: { afterDelete: afterDeleteSpy } };
351
- const app = createApp(hooks);
352
- mockAuthRepo.getUserById.mockResolvedValueOnce(mockUser({ id: "u1" }));
353
-
354
- const res = await app.request("/admin/users/u1", {
355
- method: "DELETE",
356
- headers: { ...adminAuth("admin-1") }
357
- });
358
-
359
- expect(res.status).toBe(200);
360
- await new Promise(r => setTimeout(r, 50));
361
- expect(afterDeleteSpy).toHaveBeenCalledWith("u1", expect.objectContaining({ method: "DELETE" }));
362
- });
363
- });
364
-
365
-
366
-
367
- // ── no hooks (passthrough) ──────────────────────────────────────────
368
- describe("no hooks configured", () => {
369
- it("returns data unchanged when no hooks are provided", async () => {
370
- const app = createApp(); // no hooks
371
- mockAuthRepo.listUsersPaginated.mockResolvedValueOnce({
372
- users: [
373
- mockUser({ id: "u1", email: "alice@test.com" })
374
- ],
375
- total: 1,
376
- limit: 25,
377
- offset: 0
378
- });
379
- mockAuthRepo.getUserRoleIds.mockResolvedValueOnce(["editor"]);
380
-
381
- const res = await app.request("/admin/users", { headers: { ...adminAuth() } });
382
- expect(res.status).toBe(200);
383
- const body = await res.json() as any;
384
- expect(body.users).toHaveLength(1);
385
- expect(body.users[0].email).toBe("alice@test.com");
386
- });
387
- });
388
- });
package/test.ts DELETED
@@ -1,6 +0,0 @@
1
- interface I {
2
- verify: (payload: any) => void;
3
- }
4
- const obj: I = {
5
- verify: (payload: { code: string }) => {}
6
- };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes