@rebasepro/server-core 0.0.1-canary.4d4fb3e

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 (254) hide show
  1. package/LICENSE +6 -0
  2. package/README.md +40 -0
  3. package/build-errors.txt +52 -0
  4. package/coverage/clover.xml +3739 -0
  5. package/coverage/coverage-final.json +31 -0
  6. package/coverage/lcov-report/base.css +224 -0
  7. package/coverage/lcov-report/block-navigation.js +87 -0
  8. package/coverage/lcov-report/favicon.png +0 -0
  9. package/coverage/lcov-report/index.html +266 -0
  10. package/coverage/lcov-report/prettify.css +1 -0
  11. package/coverage/lcov-report/prettify.js +2 -0
  12. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  13. package/coverage/lcov-report/sorter.js +210 -0
  14. package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +952 -0
  15. package/coverage/lcov-report/src/api/errors.ts.html +472 -0
  16. package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +1069 -0
  17. package/coverage/lcov-report/src/api/graphql/index.html +116 -0
  18. package/coverage/lcov-report/src/api/index.html +176 -0
  19. package/coverage/lcov-report/src/api/openapi-generator.ts.html +565 -0
  20. package/coverage/lcov-report/src/api/rest/api-generator.ts.html +994 -0
  21. package/coverage/lcov-report/src/api/rest/index.html +131 -0
  22. package/coverage/lcov-report/src/api/rest/query-parser.ts.html +550 -0
  23. package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +202 -0
  24. package/coverage/lcov-report/src/api/server.ts.html +823 -0
  25. package/coverage/lcov-report/src/auth/admin-routes.ts.html +973 -0
  26. package/coverage/lcov-report/src/auth/index.html +176 -0
  27. package/coverage/lcov-report/src/auth/jwt.ts.html +574 -0
  28. package/coverage/lcov-report/src/auth/middleware.ts.html +745 -0
  29. package/coverage/lcov-report/src/auth/password.ts.html +310 -0
  30. package/coverage/lcov-report/src/auth/services.ts.html +2074 -0
  31. package/coverage/lcov-report/src/collections/index.html +116 -0
  32. package/coverage/lcov-report/src/collections/loader.ts.html +232 -0
  33. package/coverage/lcov-report/src/db/auth-schema.ts.html +523 -0
  34. package/coverage/lcov-report/src/db/data-transformer.ts.html +1753 -0
  35. package/coverage/lcov-report/src/db/entityService.ts.html +700 -0
  36. package/coverage/lcov-report/src/db/index.html +146 -0
  37. package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +4048 -0
  38. package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +883 -0
  39. package/coverage/lcov-report/src/db/services/RelationService.ts.html +3121 -0
  40. package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +442 -0
  41. package/coverage/lcov-report/src/db/services/index.html +176 -0
  42. package/coverage/lcov-report/src/db/services/index.ts.html +124 -0
  43. package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +1960 -0
  44. package/coverage/lcov-report/src/index.html +116 -0
  45. package/coverage/lcov-report/src/services/driver-registry.ts.html +631 -0
  46. package/coverage/lcov-report/src/services/index.html +131 -0
  47. package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +3025 -0
  48. package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +1189 -0
  49. package/coverage/lcov-report/src/storage/S3StorageController.ts.html +970 -0
  50. package/coverage/lcov-report/src/storage/index.html +161 -0
  51. package/coverage/lcov-report/src/storage/storage-registry.ts.html +646 -0
  52. package/coverage/lcov-report/src/storage/types.ts.html +451 -0
  53. package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +3082 -0
  54. package/coverage/lcov-report/src/utils/index.html +116 -0
  55. package/coverage/lcov.info +7179 -0
  56. package/dist/common/src/collections/CollectionRegistry.d.ts +48 -0
  57. package/dist/common/src/collections/index.d.ts +1 -0
  58. package/dist/common/src/data/buildRebaseData.d.ts +14 -0
  59. package/dist/common/src/index.d.ts +3 -0
  60. package/dist/common/src/util/builders.d.ts +57 -0
  61. package/dist/common/src/util/callbacks.d.ts +6 -0
  62. package/dist/common/src/util/collections.d.ts +11 -0
  63. package/dist/common/src/util/common.d.ts +2 -0
  64. package/dist/common/src/util/conditions.d.ts +26 -0
  65. package/dist/common/src/util/entities.d.ts +36 -0
  66. package/dist/common/src/util/enums.d.ts +3 -0
  67. package/dist/common/src/util/index.d.ts +16 -0
  68. package/dist/common/src/util/navigation_from_path.d.ts +34 -0
  69. package/dist/common/src/util/navigation_utils.d.ts +20 -0
  70. package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
  71. package/dist/common/src/util/paths.d.ts +14 -0
  72. package/dist/common/src/util/permissions.d.ts +5 -0
  73. package/dist/common/src/util/references.d.ts +2 -0
  74. package/dist/common/src/util/relations.d.ts +12 -0
  75. package/dist/common/src/util/resolutions.d.ts +72 -0
  76. package/dist/common/src/util/storage.d.ts +24 -0
  77. package/dist/index-BeMqpmfQ.js +239 -0
  78. package/dist/index-BeMqpmfQ.js.map +1 -0
  79. package/dist/index-bl4J3lNb.js +55823 -0
  80. package/dist/index-bl4J3lNb.js.map +1 -0
  81. package/dist/index.es.js +58 -0
  82. package/dist/index.es.js.map +1 -0
  83. package/dist/index.umd.js +56062 -0
  84. package/dist/index.umd.js.map +1 -0
  85. package/dist/server-core/src/api/ast-schema-editor.d.ts +21 -0
  86. package/dist/server-core/src/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
  87. package/dist/server-core/src/api/errors.d.ts +35 -0
  88. package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts +35 -0
  89. package/dist/server-core/src/api/graphql/index.d.ts +1 -0
  90. package/dist/server-core/src/api/index.d.ts +9 -0
  91. package/dist/server-core/src/api/openapi-generator.d.ts +2 -0
  92. package/dist/server-core/src/api/rest/api-generator.d.ts +64 -0
  93. package/dist/server-core/src/api/rest/index.d.ts +1 -0
  94. package/dist/server-core/src/api/rest/query-parser.d.ts +9 -0
  95. package/dist/server-core/src/api/schema-editor-routes.d.ts +3 -0
  96. package/dist/server-core/src/api/server.d.ts +40 -0
  97. package/dist/server-core/src/api/types.d.ts +90 -0
  98. package/dist/server-core/src/auth/admin-routes.d.ts +7 -0
  99. package/dist/server-core/src/auth/google-oauth.d.ts +20 -0
  100. package/dist/server-core/src/auth/index.d.ts +12 -0
  101. package/dist/server-core/src/auth/interfaces.d.ts +270 -0
  102. package/dist/server-core/src/auth/jwt.d.ts +42 -0
  103. package/dist/server-core/src/auth/middleware.d.ts +56 -0
  104. package/dist/server-core/src/auth/password.d.ts +22 -0
  105. package/dist/server-core/src/auth/rate-limiter.d.ts +31 -0
  106. package/dist/server-core/src/auth/routes.d.ts +17 -0
  107. package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
  108. package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts +13 -0
  109. package/dist/server-core/src/collections/loader.d.ts +5 -0
  110. package/dist/server-core/src/db/interfaces.d.ts +18 -0
  111. package/dist/server-core/src/email/index.d.ts +6 -0
  112. package/dist/server-core/src/email/smtp-email-service.d.ts +25 -0
  113. package/dist/server-core/src/email/templates.d.ts +33 -0
  114. package/dist/server-core/src/email/types.d.ts +110 -0
  115. package/dist/server-core/src/functions/function-loader.d.ts +17 -0
  116. package/dist/server-core/src/functions/function-routes.d.ts +10 -0
  117. package/dist/server-core/src/functions/index.d.ts +3 -0
  118. package/dist/server-core/src/history/history-routes.d.ts +23 -0
  119. package/dist/server-core/src/history/index.d.ts +1 -0
  120. package/dist/server-core/src/index.d.ts +24 -0
  121. package/dist/server-core/src/init.d.ts +49 -0
  122. package/dist/server-core/src/serve-spa.d.ts +30 -0
  123. package/dist/server-core/src/services/driver-registry.d.ts +78 -0
  124. package/dist/server-core/src/storage/LocalStorageController.d.ts +46 -0
  125. package/dist/server-core/src/storage/S3StorageController.d.ts +36 -0
  126. package/dist/server-core/src/storage/index.d.ts +18 -0
  127. package/dist/server-core/src/storage/routes.d.ts +38 -0
  128. package/dist/server-core/src/storage/storage-registry.d.ts +78 -0
  129. package/dist/server-core/src/storage/types.d.ts +91 -0
  130. package/dist/server-core/src/types/index.d.ts +11 -0
  131. package/dist/server-core/src/utils/logging.d.ts +9 -0
  132. package/dist/server-core/src/utils/sql.d.ts +27 -0
  133. package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
  134. package/dist/types/src/controllers/auth.d.ts +117 -0
  135. package/dist/types/src/controllers/client.d.ts +58 -0
  136. package/dist/types/src/controllers/collection_registry.d.ts +44 -0
  137. package/dist/types/src/controllers/customization_controller.d.ts +54 -0
  138. package/dist/types/src/controllers/data.d.ts +141 -0
  139. package/dist/types/src/controllers/data_driver.d.ts +168 -0
  140. package/dist/types/src/controllers/database_admin.d.ts +11 -0
  141. package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
  142. package/dist/types/src/controllers/effective_role.d.ts +4 -0
  143. package/dist/types/src/controllers/index.d.ts +17 -0
  144. package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
  145. package/dist/types/src/controllers/navigation.d.ts +213 -0
  146. package/dist/types/src/controllers/registry.d.ts +51 -0
  147. package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
  148. package/dist/types/src/controllers/side_entity_controller.d.ts +89 -0
  149. package/dist/types/src/controllers/snackbar.d.ts +24 -0
  150. package/dist/types/src/controllers/storage.d.ts +173 -0
  151. package/dist/types/src/index.d.ts +4 -0
  152. package/dist/types/src/rebase_context.d.ts +101 -0
  153. package/dist/types/src/types/backend.d.ts +533 -0
  154. package/dist/types/src/types/builders.d.ts +14 -0
  155. package/dist/types/src/types/chips.d.ts +5 -0
  156. package/dist/types/src/types/collections.d.ts +812 -0
  157. package/dist/types/src/types/data_source.d.ts +64 -0
  158. package/dist/types/src/types/entities.d.ts +145 -0
  159. package/dist/types/src/types/entity_actions.d.ts +98 -0
  160. package/dist/types/src/types/entity_callbacks.d.ts +173 -0
  161. package/dist/types/src/types/entity_link_builder.d.ts +7 -0
  162. package/dist/types/src/types/entity_overrides.d.ts +9 -0
  163. package/dist/types/src/types/entity_views.d.ts +61 -0
  164. package/dist/types/src/types/export_import.d.ts +21 -0
  165. package/dist/types/src/types/index.d.ts +22 -0
  166. package/dist/types/src/types/locales.d.ts +4 -0
  167. package/dist/types/src/types/modify_collections.d.ts +5 -0
  168. package/dist/types/src/types/plugins.d.ts +225 -0
  169. package/dist/types/src/types/properties.d.ts +1091 -0
  170. package/dist/types/src/types/property_config.d.ts +70 -0
  171. package/dist/types/src/types/relations.d.ts +336 -0
  172. package/dist/types/src/types/slots.d.ts +228 -0
  173. package/dist/types/src/types/translations.d.ts +826 -0
  174. package/dist/types/src/types/user_management_delegate.d.ts +120 -0
  175. package/dist/types/src/types/websockets.d.ts +78 -0
  176. package/dist/types/src/users/index.d.ts +2 -0
  177. package/dist/types/src/users/roles.d.ts +22 -0
  178. package/dist/types/src/users/user.d.ts +46 -0
  179. package/history_diff.log +385 -0
  180. package/jest.config.cjs +16 -0
  181. package/package.json +86 -0
  182. package/scratch.ts +8 -0
  183. package/src/api/ast-schema-editor.ts +289 -0
  184. package/src/api/collections_for_test/callbacks_test_collection.ts +57 -0
  185. package/src/api/errors.ts +155 -0
  186. package/src/api/graphql/graphql-schema-generator.ts +334 -0
  187. package/src/api/graphql/index.ts +2 -0
  188. package/src/api/index.ts +11 -0
  189. package/src/api/openapi-generator.ts +160 -0
  190. package/src/api/rest/api-generator.ts +466 -0
  191. package/src/api/rest/index.ts +2 -0
  192. package/src/api/rest/query-parser.ts +155 -0
  193. package/src/api/schema-editor-routes.ts +39 -0
  194. package/src/api/server.ts +245 -0
  195. package/src/api/types.ts +90 -0
  196. package/src/auth/admin-routes.ts +488 -0
  197. package/src/auth/google-oauth.ts +60 -0
  198. package/src/auth/index.ts +21 -0
  199. package/src/auth/interfaces.ts +316 -0
  200. package/src/auth/jwt.ts +164 -0
  201. package/src/auth/middleware.ts +235 -0
  202. package/src/auth/password.ts +75 -0
  203. package/src/auth/rate-limiter.ts +129 -0
  204. package/src/auth/routes.ts +730 -0
  205. package/src/bootstrappers/index.ts +1 -0
  206. package/src/collections/BackendCollectionRegistry.ts +20 -0
  207. package/src/collections/loader.ts +49 -0
  208. package/src/db/interfaces.ts +60 -0
  209. package/src/email/index.ts +17 -0
  210. package/src/email/smtp-email-service.ts +88 -0
  211. package/src/email/templates.ts +301 -0
  212. package/src/email/types.ts +112 -0
  213. package/src/functions/function-loader.ts +91 -0
  214. package/src/functions/function-routes.ts +31 -0
  215. package/src/functions/index.ts +3 -0
  216. package/src/history/history-routes.ts +128 -0
  217. package/src/history/index.ts +2 -0
  218. package/src/index.ts +56 -0
  219. package/src/init.ts +309 -0
  220. package/src/serve-spa.ts +81 -0
  221. package/src/services/driver-registry.ts +182 -0
  222. package/src/storage/LocalStorageController.ts +368 -0
  223. package/src/storage/S3StorageController.ts +295 -0
  224. package/src/storage/index.ts +32 -0
  225. package/src/storage/routes.ts +247 -0
  226. package/src/storage/storage-registry.ts +187 -0
  227. package/src/storage/types.ts +122 -0
  228. package/src/types/index.ts +27 -0
  229. package/src/utils/logging.ts +35 -0
  230. package/src/utils/sql.ts +38 -0
  231. package/test/admin-routes.test.ts +591 -0
  232. package/test/api-generator.test.ts +458 -0
  233. package/test/ast-schema-editor.test.ts +61 -0
  234. package/test/auth-middleware-hono.test.ts +321 -0
  235. package/test/auth-routes.test.ts +868 -0
  236. package/test/driver-registry.test.ts +280 -0
  237. package/test/errors-hono.test.ts +133 -0
  238. package/test/errors.test.ts +150 -0
  239. package/test/jwt-security.test.ts +173 -0
  240. package/test/jwt.test.ts +311 -0
  241. package/test/middleware.test.ts +295 -0
  242. package/test/password.test.ts +165 -0
  243. package/test/query-parser.test.ts +258 -0
  244. package/test/rate-limiter.test.ts +102 -0
  245. package/test/storage-local.test.ts +278 -0
  246. package/test/storage-registry.test.ts +280 -0
  247. package/test/storage-routes.test.ts +218 -0
  248. package/test/storage-s3.test.ts +301 -0
  249. package/test-ast.ts +28 -0
  250. package/test_output.txt +1133 -0
  251. package/tsconfig.json +49 -0
  252. package/tsconfig.prod.json +20 -0
  253. package/vite.config.ts +78 -0
  254. package/vite.config.ts.timestamp-1775065397568-8a853255edf6e.mjs +46 -0
@@ -0,0 +1,321 @@
1
+ import { Hono, Context } from "hono";
2
+ import { configureJwt, generateAccessToken } from "../src/auth/jwt.js";
3
+ import { requireAuth, optionalAuth, requireAdmin, createAuthMiddleware } from "../src/auth/middleware.js";
4
+ import type { HonoEnv } from "../src/api/types.js";
5
+ import type { DataDriver } from "../../types/src/controllers/data_driver.js";
6
+
7
+ const TEST_SECRET = "test-secret-key-for-hono-middleware-testing-1234567890";
8
+
9
+ describe("Auth Middleware (Hono)", () => {
10
+
11
+ beforeAll(() => {
12
+ configureJwt({ secret: TEST_SECRET, accessExpiresIn: "1h" });
13
+ });
14
+
15
+ // ── requireAuth ─────────────────────────────────────────
16
+ describe("requireAuth", () => {
17
+ function createApp() {
18
+ const app = new Hono<HonoEnv>();
19
+ app.use("/protected/*", requireAuth);
20
+ app.get("/protected/resource", (c: Context<HonoEnv>) => {
21
+ const user = c.get("user");
22
+ return c.json({ user });
23
+ });
24
+ return app;
25
+ }
26
+
27
+ it("passes with valid Bearer token", async () => {
28
+ const app = createApp();
29
+ const token = generateAccessToken("user-1", ["admin"]);
30
+ const res = await app.request("/protected/resource", {
31
+ headers: { Authorization: `Bearer ${token}` },
32
+ });
33
+ expect(res.status).toBe(200);
34
+ const body = await res.json() as any;
35
+ expect(body.user.userId).toBe("user-1");
36
+ expect(body.user.roles).toEqual(["admin"]);
37
+ });
38
+
39
+ it("returns 401 for missing Authorization header", async () => {
40
+ const app = createApp();
41
+ const res = await app.request("/protected/resource");
42
+ expect(res.status).toBe(401);
43
+ const body = await res.json() as any;
44
+ expect(body.error.code).toBe("UNAUTHORIZED");
45
+ });
46
+
47
+ it("returns 401 for non-Bearer prefix", async () => {
48
+ const app = createApp();
49
+ const res = await app.request("/protected/resource", {
50
+ headers: { Authorization: "Basic abc123" },
51
+ });
52
+ expect(res.status).toBe(401);
53
+ });
54
+
55
+ it("returns 401 for invalid/expired token", async () => {
56
+ const app = createApp();
57
+ const res = await app.request("/protected/resource", {
58
+ headers: { Authorization: "Bearer invalid.token.here" },
59
+ });
60
+ expect(res.status).toBe(401);
61
+ const body = await res.json() as any;
62
+ expect(body.error.code).toBe("UNAUTHORIZED");
63
+ });
64
+
65
+ it("accepts token via query parameter", async () => {
66
+ const app = createApp();
67
+ const token = generateAccessToken("user-2", ["editor"]);
68
+ const res = await app.request(`/protected/resource?token=${token}`);
69
+ expect(res.status).toBe(200);
70
+ const body = await res.json() as any;
71
+ expect(body.user.userId).toBe("user-2");
72
+ });
73
+
74
+ it("prefers Bearer token over query parameter", async () => {
75
+ const app = createApp();
76
+ const bearerToken = generateAccessToken("bearer-user", ["admin"]);
77
+ const queryToken = generateAccessToken("query-user", ["viewer"]);
78
+ const res = await app.request(`/protected/resource?token=${queryToken}`, {
79
+ headers: { Authorization: `Bearer ${bearerToken}` },
80
+ });
81
+ expect(res.status).toBe(200);
82
+ const body = await res.json() as any;
83
+ expect(body.user.userId).toBe("bearer-user");
84
+ });
85
+ });
86
+
87
+ // ── requireAdmin ────────────────────────────────────────
88
+ describe("requireAdmin", () => {
89
+ function createApp() {
90
+ const app = new Hono<HonoEnv>();
91
+ app.use("/admin/*", requireAuth, requireAdmin);
92
+ app.get("/admin/dashboard", (c: Context<HonoEnv>) => c.json({ ok: true }));
93
+ return app;
94
+ }
95
+
96
+ it("allows admin users", async () => {
97
+ const app = createApp();
98
+ const token = generateAccessToken("admin-1", ["admin"]);
99
+ const res = await app.request("/admin/dashboard", {
100
+ headers: { Authorization: `Bearer ${token}` },
101
+ });
102
+ expect(res.status).toBe(200);
103
+ });
104
+
105
+ it("allows schema-admin users", async () => {
106
+ const app = createApp();
107
+ const token = generateAccessToken("schema-admin-1", ["schema-admin"]);
108
+ const res = await app.request("/admin/dashboard", {
109
+ headers: { Authorization: `Bearer ${token}` },
110
+ });
111
+ expect(res.status).toBe(200);
112
+ });
113
+
114
+ it("returns 403 for non-admin users", async () => {
115
+ const app = createApp();
116
+ const token = generateAccessToken("user-1", ["editor"]);
117
+ const res = await app.request("/admin/dashboard", {
118
+ headers: { Authorization: `Bearer ${token}` },
119
+ });
120
+ expect(res.status).toBe(403);
121
+ const body = await res.json() as any;
122
+ expect(body.error.code).toBe("FORBIDDEN");
123
+ });
124
+
125
+ it("returns 401 when requireAdmin is used without requireAuth", async () => {
126
+ const app = new Hono<HonoEnv>();
127
+ app.use("/admin/*", requireAdmin);
128
+ app.get("/admin/dashboard", (c: Context<HonoEnv>) => c.json({ ok: true }));
129
+
130
+ const res = await app.request("/admin/dashboard");
131
+ expect(res.status).toBe(401);
132
+ });
133
+ });
134
+
135
+ // ── optionalAuth ────────────────────────────────────────
136
+ describe("optionalAuth", () => {
137
+ function createApp() {
138
+ const app = new Hono<HonoEnv>();
139
+ app.use("/public/*", optionalAuth);
140
+ app.get("/public/feed", (c: Context<HonoEnv>) => {
141
+ const user = c.get("user");
142
+ return c.json({ authenticated: !!user, user: user ?? null });
143
+ });
144
+ return app;
145
+ }
146
+
147
+ it("sets user when valid token is present", async () => {
148
+ const app = createApp();
149
+ const token = generateAccessToken("opt-user", ["viewer"]);
150
+ const res = await app.request("/public/feed", {
151
+ headers: { Authorization: `Bearer ${token}` },
152
+ });
153
+ const body = await res.json() as any;
154
+ expect(res.status).toBe(200);
155
+ expect(body.authenticated).toBe(true);
156
+ expect(body.user.userId).toBe("opt-user");
157
+ });
158
+
159
+ it("proceeds without user when no token", async () => {
160
+ const app = createApp();
161
+ const res = await app.request("/public/feed");
162
+ const body = await res.json() as any;
163
+ expect(res.status).toBe(200);
164
+ expect(body.authenticated).toBe(false);
165
+ expect(body.user).toBeNull();
166
+ });
167
+
168
+ it("proceeds without user when token is invalid", async () => {
169
+ const app = createApp();
170
+ const res = await app.request("/public/feed", {
171
+ headers: { Authorization: "Bearer garbage.token.value" },
172
+ });
173
+ const body = await res.json() as any;
174
+ expect(res.status).toBe(200);
175
+ expect(body.authenticated).toBe(false);
176
+ });
177
+ });
178
+
179
+ // ── createAuthMiddleware ────────────────────────────────
180
+ describe("createAuthMiddleware", () => {
181
+ const mockDriver: DataDriver = {
182
+ fetchCollection: jest.fn() as any,
183
+ fetchEntity: jest.fn() as any,
184
+ saveEntity: jest.fn() as any,
185
+ deleteEntity: jest.fn() as any,
186
+ };
187
+
188
+ it("sets driver in context", async () => {
189
+ const app = new Hono<HonoEnv>();
190
+ app.use("/*", createAuthMiddleware({ driver: mockDriver }));
191
+ app.get("/test", (c) => {
192
+ const driver = c.get("driver");
193
+ return c.json({ hasDriver: !!driver });
194
+ });
195
+
196
+ const res = await app.request("/test");
197
+ expect(res.status).toBe(200);
198
+ const body = await res.json() as any;
199
+ expect(body.hasDriver).toBe(true);
200
+ });
201
+
202
+ it("enforces auth when requireAuth is true", async () => {
203
+ const app = new Hono<HonoEnv>();
204
+ app.use("/*", createAuthMiddleware({ driver: mockDriver, requireAuth: true }));
205
+ app.get("/test", (c) => c.json({ ok: true }));
206
+
207
+ const res = await app.request("/test");
208
+ expect(res.status).toBe(401);
209
+ });
210
+
211
+ it("allows anonymous when requireAuth is false", async () => {
212
+ const app = new Hono<HonoEnv>();
213
+ app.use("/*", createAuthMiddleware({ driver: mockDriver, requireAuth: false }));
214
+ app.get("/test", (c) => c.json({ ok: true }));
215
+
216
+ const res = await app.request("/test");
217
+ expect(res.status).toBe(200);
218
+ });
219
+
220
+ it("extracts JWT and sets user in context", async () => {
221
+ const app = new Hono<HonoEnv>();
222
+ app.use("/*", createAuthMiddleware({ driver: mockDriver }));
223
+ app.get("/test", (c) => {
224
+ const user = c.get("user");
225
+ return c.json({ user });
226
+ });
227
+
228
+ const token = generateAccessToken("jwt-user", ["admin"]);
229
+ const res = await app.request("/test", {
230
+ headers: { Authorization: `Bearer ${token}` },
231
+ });
232
+ const body = await res.json() as any;
233
+ expect(body.user.userId).toBe("jwt-user");
234
+ expect(body.user.roles).toEqual(["admin"]);
235
+ });
236
+
237
+ it("uses custom validator when provided", async () => {
238
+ const app = new Hono<HonoEnv>();
239
+ app.use("/*", createAuthMiddleware({
240
+ driver: mockDriver,
241
+ validator: async (c: Context<HonoEnv>) => {
242
+ const apiKey = c.req.header("x-api-key");
243
+ if (apiKey === "valid-key") {
244
+ return { userId: "api-user", roles: ["api"] };
245
+ }
246
+ return false;
247
+ },
248
+ }));
249
+ app.get("/test", (c) => {
250
+ const user = c.get("user");
251
+ return c.json({ user: user ?? null });
252
+ });
253
+
254
+ // Valid API key
255
+ const res = await app.request("/test", {
256
+ headers: { "x-api-key": "valid-key" },
257
+ });
258
+ const body = await res.json() as any;
259
+ expect(body.user.userId).toBe("api-user");
260
+
261
+ // Invalid API key
262
+ const res2 = await app.request("/test", {
263
+ headers: { "x-api-key": "bad-key" },
264
+ });
265
+ expect(res2.status).toBe(200); // Not enforcing auth
266
+ });
267
+
268
+ it("returns 401 when custom validator throws", async () => {
269
+ const app = new Hono<HonoEnv>();
270
+ app.use("/*", createAuthMiddleware({
271
+ driver: mockDriver,
272
+ validator: async () => { throw new Error("auth failed"); },
273
+ }));
274
+ app.get("/test", (c) => c.json({ ok: true }));
275
+
276
+ const res = await app.request("/test");
277
+ expect(res.status).toBe(401);
278
+ });
279
+
280
+ it("calls withAuth on driver when available", async () => {
281
+ const scopedDriver = { ...mockDriver, isScopedDriver: true };
282
+ const driverWithAuth = {
283
+ ...mockDriver,
284
+ withAuth: jest.fn().mockResolvedValue(scopedDriver),
285
+ };
286
+
287
+ const app = new Hono<HonoEnv>();
288
+ app.use("/*", createAuthMiddleware({ driver: driverWithAuth as any }));
289
+ app.get("/test", (c) => {
290
+ const driver = c.get("driver") as any;
291
+ return c.json({ scoped: !!driver?.isScopedDriver });
292
+ });
293
+
294
+ const token = generateAccessToken("rls-user", ["editor"]);
295
+ const res = await app.request("/test", {
296
+ headers: { Authorization: `Bearer ${token}` },
297
+ });
298
+ const body = await res.json() as any;
299
+ expect(body.scoped).toBe(true);
300
+ expect(driverWithAuth.withAuth).toHaveBeenCalledWith(
301
+ expect.objectContaining({ uid: "rls-user", roles: ["editor"] })
302
+ );
303
+ });
304
+
305
+ it("handles validator returning true (default user)", async () => {
306
+ const app = new Hono<HonoEnv>();
307
+ app.use("/*", createAuthMiddleware({
308
+ driver: mockDriver,
309
+ validator: async () => true,
310
+ }));
311
+ app.get("/test", (c) => {
312
+ const user = c.get("user");
313
+ return c.json({ user });
314
+ });
315
+
316
+ const res = await app.request("/test");
317
+ const body = await res.json() as any;
318
+ expect(body.user.userId).toBe("default");
319
+ });
320
+ });
321
+ });