@rebasepro/server-core 0.5.0 → 0.6.1

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 (279) hide show
  1. package/dist/{server-core/src/api → api}/errors.d.ts +5 -2
  2. package/dist/{server-core/src/api → api}/rest/api-generator.d.ts +8 -2
  3. package/dist/{server-core/src/api → api}/types.d.ts +2 -0
  4. package/dist/auth/admin-user-ops.d.ts +79 -0
  5. package/dist/{server-core/src/auth → auth}/api-keys/index.d.ts +2 -2
  6. package/dist/{server-core/src/auth → auth}/auth-hooks.d.ts +37 -9
  7. package/dist/{server-core/src/auth → auth}/builtin-auth-adapter.d.ts +4 -4
  8. package/dist/{server-core/src/auth → auth}/index.d.ts +5 -2
  9. package/dist/auth/mfa-routes.d.ts +6 -0
  10. package/dist/auth/reset-password-admin.d.ts +29 -0
  11. package/dist/auth/session-routes.d.ts +25 -0
  12. package/dist/backend-CIxN4FVm.js +15 -0
  13. package/dist/backend-CIxN4FVm.js.map +1 -0
  14. package/dist/chunk-Dze3rakg.js +42 -0
  15. package/dist/{server-core/src/cron → cron}/cron-scheduler.d.ts +1 -1
  16. package/dist/dist-CZKP-Xz4.js +832 -0
  17. package/dist/dist-CZKP-Xz4.js.map +1 -0
  18. package/dist/env.d.ts +102 -0
  19. package/dist/from-VbwD7xRf.js +3849 -0
  20. package/dist/from-VbwD7xRf.js.map +1 -0
  21. package/dist/{server-core/src/index.d.ts → index.d.ts} +1 -0
  22. package/dist/index.es.js +60872 -50462
  23. package/dist/index.es.js.map +1 -1
  24. package/dist/index.umd.js +73156 -51298
  25. package/dist/index.umd.js.map +1 -1
  26. package/dist/init/docs.d.ts +4 -0
  27. package/dist/init/health.d.ts +2 -0
  28. package/dist/init/middlewares.d.ts +10 -0
  29. package/dist/init/shutdown.d.ts +11 -0
  30. package/dist/init/storage.d.ts +5 -0
  31. package/dist/{server-core/src/init.d.ts → init.d.ts} +4 -4
  32. package/dist/jwt-DHcQRGC3.js +4168 -0
  33. package/dist/jwt-DHcQRGC3.js.map +1 -0
  34. package/dist/logger-BYU66ENZ.js +94 -0
  35. package/dist/logger-BYU66ENZ.js.map +1 -0
  36. package/dist/ms-BeBSuOXN.js +125 -0
  37. package/dist/ms-BeBSuOXN.js.map +1 -0
  38. package/dist/multipart-parser-CedBDOeC.js +299 -0
  39. package/dist/multipart-parser-CedBDOeC.js.map +1 -0
  40. package/dist/src-COaj0G3P.js +1182 -0
  41. package/dist/src-COaj0G3P.js.map +1 -0
  42. package/dist/{server-core/src/storage → storage}/image-transform.d.ts +3 -1
  43. package/dist/utils/request-id.d.ts +4 -0
  44. package/package.json +29 -29
  45. package/src/api/collections_for_test/callbacks_test_collection.ts +7 -6
  46. package/src/api/errors.ts +75 -19
  47. package/src/api/graphql/graphql-schema-generator.ts +15 -10
  48. package/src/api/logs-routes.ts +5 -2
  49. package/src/api/rest/api-generator-count.test.ts +21 -10
  50. package/src/api/rest/api-generator.ts +102 -26
  51. package/src/api/rest/query-parser.ts +18 -12
  52. package/src/api/server.ts +12 -1
  53. package/src/api/types.ts +2 -0
  54. package/src/auth/adapter-middleware.ts +17 -10
  55. package/src/auth/admin-user-ops.ts +236 -0
  56. package/src/auth/api-keys/api-key-middleware.ts +15 -9
  57. package/src/auth/api-keys/api-key-permission-guard.ts +1 -1
  58. package/src/auth/api-keys/api-key-routes.ts +3 -3
  59. package/src/auth/api-keys/api-key-store.ts +14 -8
  60. package/src/auth/api-keys/index.ts +2 -2
  61. package/src/auth/apple-oauth.ts +4 -3
  62. package/src/auth/auth-hooks.ts +50 -10
  63. package/src/auth/bitbucket-oauth.ts +5 -4
  64. package/src/auth/builtin-auth-adapter.ts +60 -39
  65. package/src/auth/custom-auth-adapter.ts +4 -5
  66. package/src/auth/discord-oauth.ts +5 -4
  67. package/src/auth/facebook-oauth.ts +5 -4
  68. package/src/auth/github-oauth.ts +6 -5
  69. package/src/auth/gitlab-oauth.ts +5 -4
  70. package/src/auth/google-oauth.ts +2 -1
  71. package/src/auth/index.ts +6 -2
  72. package/src/auth/jwt.ts +5 -4
  73. package/src/auth/linkedin-oauth.ts +4 -3
  74. package/src/auth/mfa-routes.ts +299 -0
  75. package/src/auth/mfa.ts +2 -1
  76. package/src/auth/microsoft-oauth.ts +5 -4
  77. package/src/auth/middleware.ts +5 -4
  78. package/src/auth/rate-limiter.ts +1 -1
  79. package/src/auth/reset-password-admin.ts +144 -0
  80. package/src/auth/rls-scope.ts +1 -1
  81. package/src/auth/routes.ts +52 -596
  82. package/src/auth/session-routes.ts +341 -0
  83. package/src/auth/slack-oauth.ts +6 -5
  84. package/src/auth/spotify-oauth.ts +5 -4
  85. package/src/auth/twitter-oauth.ts +4 -3
  86. package/src/collections/loader.ts +5 -4
  87. package/src/cron/cron-scheduler.test.ts +23 -11
  88. package/src/cron/cron-scheduler.ts +14 -8
  89. package/src/cron/cron-store.ts +1 -1
  90. package/src/email/smtp-email-service.ts +3 -2
  91. package/src/env.ts +11 -10
  92. package/src/index.ts +1 -0
  93. package/src/init/docs.ts +47 -0
  94. package/src/init/health.ts +37 -0
  95. package/src/init/middlewares.ts +61 -0
  96. package/src/init/shutdown.ts +56 -0
  97. package/src/init/storage.ts +57 -0
  98. package/src/init.ts +85 -279
  99. package/src/serve-spa.ts +12 -5
  100. package/src/services/driver-registry.ts +4 -3
  101. package/src/storage/S3StorageController.ts +1 -2
  102. package/src/storage/image-transform.ts +25 -10
  103. package/src/storage/routes.ts +34 -11
  104. package/src/storage/storage-registry.ts +4 -3
  105. package/src/storage/tus-handler.ts +12 -12
  106. package/src/utils/request-id.ts +40 -0
  107. package/src/utils/request-logger.ts +6 -0
  108. package/test/api-generator.test.ts +90 -2
  109. package/test/api-key-permission-guard.test.ts +24 -12
  110. package/test/auth-routes.test.ts +5 -3
  111. package/test/backend-hooks-data.test.ts +99 -30
  112. package/test/custom-auth-adapter.test.ts +16 -15
  113. package/test/email-templates.test.ts +10 -5
  114. package/test/env.test.ts +10 -10
  115. package/test/function-loader.test.ts +7 -4
  116. package/test/graphql-schema-generator.test.ts +554 -0
  117. package/test/jwt-security.test.ts +1 -1
  118. package/test/query-parser.test.ts +0 -1
  119. package/test/reset-password-admin.test.ts +113 -0
  120. package/test/singleton.test.ts +5 -5
  121. package/test/smtp-email-service.test.ts +21 -21
  122. package/test/webhook-service.test.ts +22 -11
  123. package/tsconfig.json +2 -0
  124. package/tsconfig.prod.json +3 -0
  125. package/vite.config.ts +10 -5
  126. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  127. package/dist/common/src/collections/default-collections.d.ts +0 -9
  128. package/dist/common/src/collections/index.d.ts +0 -2
  129. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  130. package/dist/common/src/data/query_builder.d.ts +0 -55
  131. package/dist/common/src/index.d.ts +0 -4
  132. package/dist/common/src/util/builders.d.ts +0 -57
  133. package/dist/common/src/util/callbacks.d.ts +0 -6
  134. package/dist/common/src/util/collections.d.ts +0 -11
  135. package/dist/common/src/util/common.d.ts +0 -2
  136. package/dist/common/src/util/conditions.d.ts +0 -26
  137. package/dist/common/src/util/entities.d.ts +0 -58
  138. package/dist/common/src/util/enums.d.ts +0 -3
  139. package/dist/common/src/util/index.d.ts +0 -16
  140. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  141. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  142. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  143. package/dist/common/src/util/paths.d.ts +0 -14
  144. package/dist/common/src/util/permissions.d.ts +0 -14
  145. package/dist/common/src/util/references.d.ts +0 -2
  146. package/dist/common/src/util/relations.d.ts +0 -22
  147. package/dist/common/src/util/resolutions.d.ts +0 -72
  148. package/dist/common/src/util/storage.d.ts +0 -24
  149. package/dist/index-Cr1D21av.js +0 -49
  150. package/dist/index-Cr1D21av.js.map +0 -1
  151. package/dist/server-core/src/auth/admin-routes.d.ts +0 -27
  152. package/dist/server-core/src/env.d.ts +0 -137
  153. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  154. package/dist/types/src/controllers/auth.d.ts +0 -104
  155. package/dist/types/src/controllers/client.d.ts +0 -168
  156. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  157. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  158. package/dist/types/src/controllers/data.d.ts +0 -207
  159. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  160. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  161. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  162. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  163. package/dist/types/src/controllers/email.d.ts +0 -36
  164. package/dist/types/src/controllers/index.d.ts +0 -18
  165. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  166. package/dist/types/src/controllers/navigation.d.ts +0 -225
  167. package/dist/types/src/controllers/registry.d.ts +0 -63
  168. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  169. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  170. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  171. package/dist/types/src/controllers/storage.d.ts +0 -171
  172. package/dist/types/src/index.d.ts +0 -4
  173. package/dist/types/src/rebase_context.d.ts +0 -122
  174. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  175. package/dist/types/src/types/backend.d.ts +0 -571
  176. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  177. package/dist/types/src/types/builders.d.ts +0 -15
  178. package/dist/types/src/types/chips.d.ts +0 -5
  179. package/dist/types/src/types/collections.d.ts +0 -961
  180. package/dist/types/src/types/component_ref.d.ts +0 -47
  181. package/dist/types/src/types/cron.d.ts +0 -102
  182. package/dist/types/src/types/data_source.d.ts +0 -64
  183. package/dist/types/src/types/database_adapter.d.ts +0 -94
  184. package/dist/types/src/types/entities.d.ts +0 -145
  185. package/dist/types/src/types/entity_actions.d.ts +0 -104
  186. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  187. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  188. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  189. package/dist/types/src/types/entity_views.d.ts +0 -87
  190. package/dist/types/src/types/export_import.d.ts +0 -21
  191. package/dist/types/src/types/formex.d.ts +0 -40
  192. package/dist/types/src/types/index.d.ts +0 -28
  193. package/dist/types/src/types/locales.d.ts +0 -4
  194. package/dist/types/src/types/modify_collections.d.ts +0 -5
  195. package/dist/types/src/types/plugins.d.ts +0 -282
  196. package/dist/types/src/types/properties.d.ts +0 -1173
  197. package/dist/types/src/types/property_config.d.ts +0 -74
  198. package/dist/types/src/types/relations.d.ts +0 -336
  199. package/dist/types/src/types/slots.d.ts +0 -262
  200. package/dist/types/src/types/translations.d.ts +0 -900
  201. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  202. package/dist/types/src/types/websockets.d.ts +0 -78
  203. package/dist/types/src/users/index.d.ts +0 -1
  204. package/dist/types/src/users/user.d.ts +0 -50
  205. package/src/auth/admin-routes.ts +0 -534
  206. package/test/admin-routes.test.ts +0 -544
  207. package/test/backend-hooks-admin.test.ts +0 -388
  208. /package/dist/{server-core/src/api → api}/ast-schema-editor.d.ts +0 -0
  209. /package/dist/{server-core/src/api → api}/collections_for_test/callbacks_test_collection.d.ts +0 -0
  210. /package/dist/{server-core/src/api → api}/graphql/graphql-schema-generator.d.ts +0 -0
  211. /package/dist/{server-core/src/api → api}/graphql/index.d.ts +0 -0
  212. /package/dist/{server-core/src/api → api}/index.d.ts +0 -0
  213. /package/dist/{server-core/src/api → api}/logs-routes.d.ts +0 -0
  214. /package/dist/{server-core/src/api → api}/openapi-generator.d.ts +0 -0
  215. /package/dist/{server-core/src/api → api}/rest/index.d.ts +0 -0
  216. /package/dist/{server-core/src/api → api}/rest/query-parser.d.ts +0 -0
  217. /package/dist/{server-core/src/api → api}/schema-editor-routes.d.ts +0 -0
  218. /package/dist/{server-core/src/api → api}/server.d.ts +0 -0
  219. /package/dist/{server-core/src/auth → auth}/adapter-middleware.d.ts +0 -0
  220. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-middleware.d.ts +0 -0
  221. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-permission-guard.d.ts +0 -0
  222. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-routes.d.ts +0 -0
  223. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-store.d.ts +0 -0
  224. /package/dist/{server-core/src/auth → auth}/api-keys/api-key-types.d.ts +0 -0
  225. /package/dist/{server-core/src/auth → auth}/apple-oauth.d.ts +0 -0
  226. /package/dist/{server-core/src/auth → auth}/bitbucket-oauth.d.ts +0 -0
  227. /package/dist/{server-core/src/auth → auth}/crypto-utils.d.ts +0 -0
  228. /package/dist/{server-core/src/auth → auth}/custom-auth-adapter.d.ts +0 -0
  229. /package/dist/{server-core/src/auth → auth}/discord-oauth.d.ts +0 -0
  230. /package/dist/{server-core/src/auth → auth}/facebook-oauth.d.ts +0 -0
  231. /package/dist/{server-core/src/auth → auth}/github-oauth.d.ts +0 -0
  232. /package/dist/{server-core/src/auth → auth}/gitlab-oauth.d.ts +0 -0
  233. /package/dist/{server-core/src/auth → auth}/google-oauth.d.ts +0 -0
  234. /package/dist/{server-core/src/auth → auth}/interfaces.d.ts +0 -0
  235. /package/dist/{server-core/src/auth → auth}/jwt.d.ts +0 -0
  236. /package/dist/{server-core/src/auth → auth}/linkedin-oauth.d.ts +0 -0
  237. /package/dist/{server-core/src/auth → auth}/mfa.d.ts +0 -0
  238. /package/dist/{server-core/src/auth → auth}/microsoft-oauth.d.ts +0 -0
  239. /package/dist/{server-core/src/auth → auth}/middleware.d.ts +0 -0
  240. /package/dist/{server-core/src/auth → auth}/password.d.ts +0 -0
  241. /package/dist/{server-core/src/auth → auth}/rate-limiter.d.ts +0 -0
  242. /package/dist/{server-core/src/auth → auth}/rls-scope.d.ts +0 -0
  243. /package/dist/{server-core/src/auth → auth}/routes.d.ts +0 -0
  244. /package/dist/{server-core/src/auth → auth}/slack-oauth.d.ts +0 -0
  245. /package/dist/{server-core/src/auth → auth}/spotify-oauth.d.ts +0 -0
  246. /package/dist/{server-core/src/auth → auth}/twitter-oauth.d.ts +0 -0
  247. /package/dist/{server-core/src/collections → collections}/BackendCollectionRegistry.d.ts +0 -0
  248. /package/dist/{server-core/src/collections → collections}/loader.d.ts +0 -0
  249. /package/dist/{server-core/src/cron → cron}/cron-loader.d.ts +0 -0
  250. /package/dist/{server-core/src/cron → cron}/cron-routes.d.ts +0 -0
  251. /package/dist/{server-core/src/cron → cron}/cron-store.d.ts +0 -0
  252. /package/dist/{server-core/src/cron → cron}/index.d.ts +0 -0
  253. /package/dist/{server-core/src/db → db}/interfaces.d.ts +0 -0
  254. /package/dist/{server-core/src/email → email}/index.d.ts +0 -0
  255. /package/dist/{server-core/src/email → email}/smtp-email-service.d.ts +0 -0
  256. /package/dist/{server-core/src/email → email}/templates.d.ts +0 -0
  257. /package/dist/{server-core/src/email → email}/types.d.ts +0 -0
  258. /package/dist/{server-core/src/functions → functions}/function-loader.d.ts +0 -0
  259. /package/dist/{server-core/src/functions → functions}/function-routes.d.ts +0 -0
  260. /package/dist/{server-core/src/functions → functions}/index.d.ts +0 -0
  261. /package/dist/{server-core/src/history → history}/history-routes.d.ts +0 -0
  262. /package/dist/{server-core/src/history → history}/index.d.ts +0 -0
  263. /package/dist/{server-core/src/serve-spa.d.ts → serve-spa.d.ts} +0 -0
  264. /package/dist/{server-core/src/services → services}/driver-registry.d.ts +0 -0
  265. /package/dist/{server-core/src/services → services}/webhook-service.d.ts +0 -0
  266. /package/dist/{server-core/src/singleton.d.ts → singleton.d.ts} +0 -0
  267. /package/dist/{server-core/src/storage → storage}/LocalStorageController.d.ts +0 -0
  268. /package/dist/{server-core/src/storage → storage}/S3StorageController.d.ts +0 -0
  269. /package/dist/{server-core/src/storage → storage}/index.d.ts +0 -0
  270. /package/dist/{server-core/src/storage → storage}/routes.d.ts +0 -0
  271. /package/dist/{server-core/src/storage → storage}/storage-registry.d.ts +0 -0
  272. /package/dist/{server-core/src/storage → storage}/tus-handler.d.ts +0 -0
  273. /package/dist/{server-core/src/storage → storage}/types.d.ts +0 -0
  274. /package/dist/{server-core/src/types → types}/index.d.ts +0 -0
  275. /package/dist/{server-core/src/utils → utils}/dev-port.d.ts +0 -0
  276. /package/dist/{server-core/src/utils → utils}/logger.d.ts +0 -0
  277. /package/dist/{server-core/src/utils → utils}/logging.d.ts +0 -0
  278. /package/dist/{server-core/src/utils → utils}/request-logger.d.ts +0 -0
  279. /package/dist/{server-core/src/utils → utils}/sql.d.ts +0 -0
package/src/init.ts CHANGED
@@ -25,18 +25,18 @@ import { createBuiltinAuthAdapter } from "./auth/builtin-auth-adapter";
25
25
  import { errorHandler } from "./api/errors";
26
26
  import { Hono } from "hono";
27
27
  import { bodyLimit } from "hono/body-limit";
28
- import { csrf } from "hono/csrf";
29
28
  import { HonoEnv } from "./api/types";
30
29
  import { configureLogLevel } from "./utils/logging";
31
30
  import { logger } from "./utils/logger";
32
- import { requestLogger } from "./utils/request-logger";
31
+ import { configureMiddlewares } from "./init/middlewares";
32
+ import { initializeStorage } from "./init/storage";
33
+ import { mountOpenApiDocs } from "./init/docs";
34
+ import { createHealthCheck } from "./init/health";
35
+ import { createShutdown } from "./init/shutdown";
33
36
  import { configureJwt, requireAdmin, requireAuth } from "./auth";
34
37
  import {
35
38
  BackendStorageConfig,
36
- createStorageController,
37
39
  createStorageRoutes,
38
- DEFAULT_STORAGE_ID,
39
- DefaultStorageRegistry,
40
40
  StorageController,
41
41
  StorageRegistry
42
42
  } from "./storage";
@@ -82,7 +82,7 @@ export interface RebaseAuthConfig {
82
82
  * granted admin-level access without JWT verification. This is the
83
83
  * Rebase equivalent of a Firebase Service Account key.
84
84
  *
85
- * Generate with: `node -e "console.log(require('crypto').randomBytes(48).toString('base64'))"`
85
+ * Generate with: `node -e "logger.info(require('crypto').randomBytes(48).toString('base64'))"`
86
86
  *
87
87
  * Set via `REBASE_SERVICE_KEY` in your `.env`.
88
88
  * Must be at least 32 characters.
@@ -223,9 +223,9 @@ export interface RebaseBackendConfig {
223
223
  origin: string | string[] | ((origin: string) => boolean);
224
224
  };
225
225
  /**
226
- * Backend-level hooks for intercepting admin data (users, roles)
227
- * at the API boundary. These run server-side after database reads
228
- * and before API responses are sent.
226
+ * Backend-level hooks for intercepting collection entity data
227
+ * at the REST API boundary. These run server-side after database
228
+ * operations and before API responses are sent.
229
229
  *
230
230
  * Complement the per-collection `EntityCallbacks` system which
231
231
  * handles collection CRUD operations.
@@ -309,36 +309,8 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
309
309
  const basePath = config.basePath || "/api";
310
310
  const isProduction = process.env.NODE_ENV === "production";
311
311
 
312
- // ─── Request Body Size Limit ─────────────────────────────────────────
313
- const maxBodySize = config.maxBodySize ?? 10 * 1024 * 1024; // 10MB default
314
- if (maxBodySize > 0) {
315
- config.app.use(`${basePath}/*`, bodyLimit({
316
- maxSize: maxBodySize,
317
- onError: (c) => {
318
- return c.json({
319
- error: {
320
- message: `Request body too large. Maximum size is ${Math.round(maxBodySize / 1024 / 1024)}MB.`,
321
- code: "PAYLOAD_TOO_LARGE"
322
- }
323
- }, 413);
324
- }
325
- }));
326
- logger.info("Request body limit configured", { maxSizeMB: Math.round(maxBodySize / 1024 / 1024) });
327
- }
328
-
329
- // ─── CSRF Protection (opt-in) ────────────────────────────────────────
330
- // BaaS APIs are consumed by mobile apps, SPAs on different origins, and
331
- // CLI/SDK tools. CSRF is only enabled when the developer explicitly
332
- // configures it with allowed origins.
333
- if (config.csrf?.origin) {
334
- config.app.use(`${basePath}/*`, csrf({
335
- origin: config.csrf.origin
336
- }));
337
- logger.info("CSRF protection enabled");
338
- }
339
-
340
- // ─── Request Logging ─────────────────────────────────────────────────
341
- config.app.use(`${basePath}/*`, requestLogger());
312
+ // Configure Hono middlewares (Request ID, body limit, CSRF, CORS warning, logging)
313
+ configureMiddlewares(config.app, basePath, isProduction, config);
342
314
 
343
315
  const collectionRegistry = new BackendCollectionRegistry();
344
316
  let activeCollections = config.collections || [];
@@ -380,7 +352,7 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
380
352
  initializeHistory: dbAdapter.initializeHistory,
381
353
  initializeWebsockets: dbAdapter.initializeWebsockets,
382
354
  getAdmin: dbAdapter.getAdmin,
383
- mountRoutes: dbAdapter.mountRoutes,
355
+ mountRoutes: dbAdapter.mountRoutes
384
356
  };
385
357
  bootstrappers = [wrappedBootstrapper];
386
358
  }
@@ -449,11 +421,27 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
449
421
  // Populate authConfigResult for backward compatibility
450
422
  // (the return type still exposes `auth?: BootstrappedAuth`)
451
423
  authConfigResult = {
452
- userService: authAdapter.userManagement ?? {},
424
+ userService: authAdapter.userManagement ?? {}
453
425
  };
454
426
  } else {
455
427
  // ── RebaseAuthConfig — wrap in built-in adapter ──
456
428
  const safeAuthConfig = config.auth as RebaseAuthConfig;
429
+
430
+ // Auto-discover the auth collection from activeCollections if not explicitly set
431
+ if (!safeAuthConfig.collection) {
432
+ const foundAuthCollection = activeCollections.find(c => {
433
+ const isAuth = c.auth;
434
+ return isAuth === true || (isAuth && typeof isAuth === "object" && isAuth.enabled === true);
435
+ });
436
+ if (foundAuthCollection) {
437
+ safeAuthConfig.collection = foundAuthCollection;
438
+ logger.info("Auto-discovered auth collection from collection definitions", { slug: foundAuthCollection.slug });
439
+ }
440
+ }
441
+
442
+ // Extract the collection-level auth config (if `auth` is an object, not just `true`)
443
+ const collectionAuth = safeAuthConfig.collection ? safeAuthConfig.collection.auth : undefined;
444
+ const collectionAuthConfig = (typeof collectionAuth === "object" && collectionAuth !== null) ? collectionAuth : undefined;
457
445
  if (safeAuthConfig.jwtSecret) {
458
446
  configureJwt({
459
447
  secret: safeAuthConfig.jwtSecret,
@@ -467,7 +455,7 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
467
455
  if (safeAuthConfig.serviceKey.length < 32) {
468
456
  throw new Error(
469
457
  "REBASE_SERVICE_KEY is too short. Must be at least 32 characters. " +
470
- "Generate one with: node -e \"console.log(require('crypto').randomBytes(48).toString('base64'))\""
458
+ "Generate one with: node -e \"logger.info(require('crypto').randomBytes(48).toString('base64'))\""
471
459
  );
472
460
  }
473
461
  serviceKey = safeAuthConfig.serviceKey;
@@ -478,23 +466,8 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
478
466
  logger.info("Bootstrapping authentication via driver protocol");
479
467
  authConfigResult = await defaultBootstrapper.initializeAuth(config.auth, defaultDriverResult);
480
468
 
481
- // Build the built-in auth adapter from bootstrapper results
482
- if (authConfigResult) {
483
- const oauthProviders: OAuthProvider<unknown>[] = [...(safeAuthConfig.providers || [])];
484
- // OAuth providers are resolved later in route mounting,
485
- // but we need them here for the adapter
486
- authAdapter = createBuiltinAuthAdapter({
487
- authRepository: authConfigResult.authRepository as import("./auth/interfaces").AuthRepository ?? authConfigResult.userService as import("./auth/interfaces").AuthRepository,
488
- emailService: authConfigResult.emailService as import("./email").EmailService,
489
- emailConfig: safeAuthConfig.email,
490
- allowRegistration: safeAuthConfig.allowRegistration ?? false,
491
- defaultRole: safeAuthConfig.defaultRole,
492
- oauthProviders,
493
- serviceKey,
494
- hooks: config.hooks,
495
- authHooks: safeAuthConfig.hooks,
496
- });
497
- }
469
+ // The built-in auth adapter is created after OAuth providers
470
+ // are resolved (below) so it only needs to be constructed once.
498
471
 
499
472
  logger.info("Authentication initialized");
500
473
  } else {
@@ -527,50 +500,12 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
527
500
  }
528
501
 
529
502
  // 3. Initialize Storage
530
- let storageRegistry: StorageRegistry | undefined;
531
- let storageController: StorageController | undefined;
532
-
533
- if (config.storage) {
534
- logger.info("Configuring storage");
535
- const controllers: Record<string, StorageController> = {};
536
-
537
- // Helper: resolve a single storage entry to a controller
538
- const toController = (entry: BackendStorageConfig | StorageController, label: string): StorageController => {
539
- // Duck-type: if it has putObject, it's already a controller instance
540
- if (typeof (entry as StorageController).putObject === "function") {
541
- return entry as StorageController;
542
- }
543
- // Otherwise it's a config object — use the built-in factory
544
- const conf = entry as BackendStorageConfig;
545
- if (isProduction && conf.type === "local") {
546
- logger.warn(`Storage backend "${label}" uses local filesystem in production. ` +
547
- "Files will be lost on container restart. " +
548
- "Configure S3-compatible storage or a custom StorageController.");
549
- }
550
- return createStorageController(conf);
551
- };
552
-
553
- if (typeof config.storage === "object" && ("type" in config.storage || typeof (config.storage as StorageController).putObject === "function")) {
554
- // Single storage config or controller
555
- controllers[DEFAULT_STORAGE_ID] = toController(config.storage as BackendStorageConfig | StorageController, DEFAULT_STORAGE_ID);
556
- } else {
557
- // Multi-backend record
558
- for (const [storageId, entry] of Object.entries(config.storage as Record<string, BackendStorageConfig | StorageController>)) {
559
- controllers[storageId] = toController(entry, storageId);
560
- }
561
- }
562
-
563
- if (Object.keys(controllers).length > 0) {
564
- storageRegistry = DefaultStorageRegistry.create(controllers);
565
- storageController = storageRegistry.getDefault();
566
- logger.info("Initialized storage backends", { count: Object.keys(controllers).length });
567
- }
568
- }
503
+ const { storageRegistry, storageController } = initializeStorage(config.storage, isProduction);
569
504
 
570
505
  // basePath already resolved above
571
506
 
572
507
  // 4. Mount API Routes
573
- if (config.auth && authAdapter) {
508
+ if (config.auth) {
574
509
  // ── Auth Capabilities Endpoint ───────────────────────────────────
575
510
  // Exposes adapter capabilities so the frontend knows what's available
576
511
  // (login form vs external redirect, OAuth providers, etc.)
@@ -584,70 +519,39 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
584
519
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
585
520
  const oauthProviders: OAuthProvider<any>[] = [...(safeAuthConfig.providers || [])];
586
521
 
587
- if (safeAuthConfig.google?.clientId) {
588
- const { createGoogleProvider } = await import("./auth");
589
- oauthProviders.push(createGoogleProvider(safeAuthConfig.google));
590
- }
591
-
592
- if (safeAuthConfig.linkedin?.clientId && safeAuthConfig.linkedin?.clientSecret) {
593
- const { createLinkedinProvider } = await import("./auth");
594
- oauthProviders.push(createLinkedinProvider(safeAuthConfig.linkedin as {
595
- clientId: string;
596
- clientSecret: string
597
- }));
598
- }
599
-
600
- if (safeAuthConfig.github?.clientId && safeAuthConfig.github?.clientSecret) {
601
- const { createGitHubProvider } = await import("./auth");
602
- oauthProviders.push(createGitHubProvider(safeAuthConfig.github));
603
- }
604
-
605
- if (safeAuthConfig.microsoft?.clientId && safeAuthConfig.microsoft?.clientSecret) {
606
- const { createMicrosoftProvider } = await import("./auth");
607
- oauthProviders.push(createMicrosoftProvider(safeAuthConfig.microsoft));
608
- }
609
-
610
- if (safeAuthConfig.apple?.clientId && safeAuthConfig.apple?.teamId && safeAuthConfig.apple?.keyId && safeAuthConfig.apple?.privateKey) {
611
- const { createAppleProvider } = await import("./auth");
612
- oauthProviders.push(createAppleProvider(safeAuthConfig.apple));
613
- }
614
-
615
- if (safeAuthConfig.facebook?.clientId && safeAuthConfig.facebook?.clientSecret) {
616
- const { createFacebookProvider } = await import("./auth");
617
- oauthProviders.push(createFacebookProvider(safeAuthConfig.facebook));
618
- }
619
-
620
- if (safeAuthConfig.twitter?.clientId && safeAuthConfig.twitter?.clientSecret) {
621
- const { createTwitterProvider } = await import("./auth");
622
- oauthProviders.push(createTwitterProvider(safeAuthConfig.twitter));
623
- }
624
-
625
- if (safeAuthConfig.discord?.clientId && safeAuthConfig.discord?.clientSecret) {
626
- const { createDiscordProvider } = await import("./auth");
627
- oauthProviders.push(createDiscordProvider(safeAuthConfig.discord));
628
- }
629
-
630
- if (safeAuthConfig.gitlab?.clientId && safeAuthConfig.gitlab?.clientSecret) {
631
- const { createGitLabProvider } = await import("./auth");
632
- oauthProviders.push(createGitLabProvider(safeAuthConfig.gitlab));
633
- }
634
-
635
- if (safeAuthConfig.bitbucket?.clientId && safeAuthConfig.bitbucket?.clientSecret) {
636
- const { createBitbucketProvider } = await import("./auth");
637
- oauthProviders.push(createBitbucketProvider(safeAuthConfig.bitbucket));
638
- }
639
-
640
- if (safeAuthConfig.slack?.clientId && safeAuthConfig.slack?.clientSecret) {
641
- const { createSlackProvider } = await import("./auth");
642
- oauthProviders.push(createSlackProvider(safeAuthConfig.slack));
643
- }
644
-
645
- if (safeAuthConfig.spotify?.clientId && safeAuthConfig.spotify?.clientSecret) {
646
- const { createSpotifyProvider } = await import("./auth");
647
- oauthProviders.push(createSpotifyProvider(safeAuthConfig.spotify));
522
+ // Resolve configured OAuth providers via data-driven registration.
523
+ // Each entry maps a config key to its factory function name and required fields.
524
+ const OAUTH_PROVIDERS: Array<{
525
+ key: keyof RebaseAuthConfig;
526
+ factory: string;
527
+ requiredFields: string[];
528
+ }> = [
529
+ { key: "google", factory: "createGoogleProvider", requiredFields: ["clientId"] },
530
+ { key: "linkedin", factory: "createLinkedinProvider", requiredFields: ["clientId", "clientSecret"] },
531
+ { key: "github", factory: "createGitHubProvider", requiredFields: ["clientId", "clientSecret"] },
532
+ { key: "microsoft", factory: "createMicrosoftProvider", requiredFields: ["clientId", "clientSecret"] },
533
+ { key: "apple", factory: "createAppleProvider", requiredFields: ["clientId", "teamId", "keyId", "privateKey"] },
534
+ { key: "facebook", factory: "createFacebookProvider", requiredFields: ["clientId", "clientSecret"] },
535
+ { key: "twitter", factory: "createTwitterProvider", requiredFields: ["clientId", "clientSecret"] },
536
+ { key: "discord", factory: "createDiscordProvider", requiredFields: ["clientId", "clientSecret"] },
537
+ { key: "gitlab", factory: "createGitLabProvider", requiredFields: ["clientId", "clientSecret"] },
538
+ { key: "bitbucket", factory: "createBitbucketProvider", requiredFields: ["clientId", "clientSecret"] },
539
+ { key: "slack", factory: "createSlackProvider", requiredFields: ["clientId", "clientSecret"] },
540
+ { key: "spotify", factory: "createSpotifyProvider", requiredFields: ["clientId", "clientSecret"] }
541
+ ];
542
+
543
+ for (const { key, factory, requiredFields } of OAUTH_PROVIDERS) {
544
+ const providerConfig = safeAuthConfig[key] as Record<string, unknown> | undefined;
545
+ if (providerConfig && requiredFields.every(f => Boolean(providerConfig[f]))) {
546
+ const authModule = await import("./auth");
547
+ const createFn = (authModule as unknown as Record<string, (cfg: unknown) => OAuthProvider<unknown>>)[factory];
548
+ oauthProviders.push(createFn(providerConfig));
549
+ }
648
550
  }
649
551
 
650
552
  // Re-create the built-in adapter with all resolved OAuth providers
553
+ const reCollectionAuth = safeAuthConfig.collection ? safeAuthConfig.collection.auth : undefined;
554
+ const collectionAuthConfig = (typeof reCollectionAuth === "object" && reCollectionAuth !== null) ? reCollectionAuth : undefined;
651
555
  authAdapter = createBuiltinAuthAdapter({
652
556
  authRepository: authConfigResult!.authRepository as import("./auth/interfaces").AuthRepository ?? authConfigResult!.userService as import("./auth/interfaces").AuthRepository,
653
557
  emailService: authConfigResult!.emailService as import("./email").EmailService,
@@ -656,13 +560,13 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
656
560
  defaultRole: safeAuthConfig.defaultRole,
657
561
  oauthProviders,
658
562
  serviceKey,
659
- hooks: config.hooks,
660
563
  authHooks: safeAuthConfig.hooks,
564
+ collectionAuthConfig
661
565
  });
662
566
  }
663
567
 
664
568
  // ── Mount auth & admin routes via the adapter ────────────────────
665
- if (authAdapter.createAuthRoutes) {
569
+ if (authAdapter && authAdapter.createAuthRoutes) {
666
570
  const authRoutes = authAdapter.createAuthRoutes();
667
571
  if (authRoutes) {
668
572
  config.app.route(`${basePath}/auth`, authRoutes);
@@ -670,7 +574,7 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
670
574
  }
671
575
  }
672
576
 
673
- if (authAdapter.createAdminRoutes) {
577
+ if (authAdapter && authAdapter.createAdminRoutes) {
674
578
  const adminRoutes = authAdapter.createAdminRoutes();
675
579
  if (adminRoutes) {
676
580
  config.app.route(`${basePath}/admin`, adminRoutes);
@@ -690,7 +594,7 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
690
594
  // Mount API key admin routes
691
595
  const apiKeyRoutes = createApiKeyRoutes({
692
596
  store: apiKeyStore,
693
- serviceKey,
597
+ serviceKey
694
598
  });
695
599
  config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
696
600
  logger.info("API key admin routes mounted", { path: `${basePath}/admin/api-keys` });
@@ -771,14 +675,14 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
771
675
  adapter: authAdapter,
772
676
  driver: defaultDriver,
773
677
  requireAuth: dataRequireAuth,
774
- apiKeyStore,
678
+ apiKeyStore
775
679
  }));
776
680
  } else {
777
681
  dataRouter.use("/*", createAuthMiddleware({
778
682
  driver: defaultDriver,
779
683
  requireAuth: dataRequireAuth,
780
684
  serviceKey,
781
- apiKeyStore,
685
+ apiKeyStore
782
686
  }));
783
687
  }
784
688
 
@@ -798,45 +702,19 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
798
702
  dataRouter.route("/", historyRoutes);
799
703
  }
800
704
 
801
- const restGenerator = new RestApiGenerator(activeCollections, defaultDriver, config.hooks?.data);
705
+ const restGenerator = new RestApiGenerator(
706
+ activeCollections,
707
+ defaultDriver,
708
+ config.hooks?.data,
709
+ authAdapter
710
+ );
802
711
  dataRouter.route("/", restGenerator.generateRoutes());
803
712
 
804
713
  config.app.route(`${basePath}/data`, dataRouter);
805
714
  }
806
715
 
807
716
  // ── OpenAPI / Swagger ─────────────────────────────────────────────────
808
- if (config.enableSwagger !== false && activeCollections.length > 0) {
809
- const { generateOpenApiSpec } = await import("./api/openapi-generator");
810
-
811
- config.app.get(`${basePath}/docs`, (c) => {
812
- const spec = generateOpenApiSpec(activeCollections, {
813
- basePath,
814
- requireAuth: resolveRequireAuth(config.auth)
815
- });
816
- return c.json(spec);
817
- });
818
-
819
- if (process.env.NODE_ENV !== "production") {
820
- config.app.get(`${basePath}/swagger`, (c) => {
821
- return c.html(`<!DOCTYPE html>
822
- <html>
823
- <head>
824
- <title>Rebase API Documentation</title>
825
- <meta charset="utf-8"/>
826
- <meta name="viewport" content="width=device-width, initial-scale=1"/>
827
- <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css"/>
828
- <style>body{margin:0;padding:0;}</style>
829
- </head>
830
- <body>
831
- <div id="swagger-ui"></div>
832
- <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
833
- <script>SwaggerUIBundle({ url: '${basePath}/docs', dom_id: '#swagger-ui' });</script>
834
- </body>
835
- </html>`);
836
- });
837
- logger.info("Swagger UI available", { path: `${basePath}/swagger` });
838
- }
839
- }
717
+ await mountOpenApiDocs(config.app, basePath, config.enableSwagger, activeCollections, resolveRequireAuth(config.auth));
840
718
 
841
719
  // ─── Server-side singleton ────────────────────────────────────────────
842
720
  // Build the admin-level RebaseClient and expose it as the `rebase` singleton.
@@ -925,14 +803,14 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
925
803
  adapter: authAdapter,
926
804
  driver: defaultDriver,
927
805
  requireAuth: functionsRequireAuth,
928
- apiKeyStore,
806
+ apiKeyStore
929
807
  }));
930
808
  } else {
931
809
  functionsRouter.use("/*", createAuthMiddleware({
932
810
  driver: defaultDriver,
933
811
  requireAuth: functionsRequireAuth,
934
812
  serviceKey,
935
- apiKeyStore,
813
+ apiKeyStore
936
814
  }));
937
815
  }
938
816
 
@@ -1005,86 +883,14 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
1005
883
  logger.info("Rebase Backend Initialized");
1006
884
 
1007
885
  // ── Deep Health Check ─────────────────────────────────────────────────
1008
- const healthCheck = async (): Promise<HealthCheckResult> => {
1009
- const start = performance.now();
1010
- try {
1011
- // Use admin.executeSql if available (Postgres), otherwise try fetchCollection as a probe
1012
- const admin = defaultDriver.admin;
1013
- if (isSQLAdmin(admin)) {
1014
- await admin.executeSql("SELECT 1");
1015
- } else {
1016
- // Fallback: try a lightweight fetch to confirm driver is responsive
1017
- await defaultDriver.fetchCollection({
1018
- path: "__health_check_nonexistent__",
1019
- limit: 1
1020
- });
1021
- }
1022
- const latencyMs = Math.round(performance.now() - start);
1023
- return {
1024
- healthy: true,
1025
- latencyMs
1026
- };
1027
- } catch (error: unknown) {
1028
- const latencyMs = Math.round(performance.now() - start);
1029
- logger.error("Health check failed", {
1030
- error: error instanceof Error ? error : new Error(String(error)),
1031
- latencyMs
1032
- });
1033
- return {
1034
- healthy: false,
1035
- latencyMs,
1036
- details: {
1037
- error: error instanceof Error ? error.message : String(error)
1038
- }
1039
- };
1040
- }
1041
- };
886
+ const healthCheck = createHealthCheck(defaultDriver);
1042
887
 
1043
888
  // ── Graceful Shutdown ─────────────────────────────────────────────────
1044
- const shutdown = (timeoutMs = 15_000): Promise<void> => {
1045
- return new Promise<void>((resolve) => {
1046
- (async () => {
1047
- logger.info("Shutting down Rebase Backend...");
1048
-
1049
- // 1. Stop cron scheduler
1050
- if (cronScheduler) {
1051
- cronScheduler.stop();
1052
- logger.info("Cron scheduler stopped");
1053
- }
1054
-
1055
- // 2. Tear down realtime services (LISTEN clients, debounce timers,
1056
- // subscriptions). Must happen BEFORE pool.end() so that pending
1057
- // timer callbacks don't fire against a closed pool.
1058
- for (const [key, rt] of Object.entries(realtimeServices)) {
1059
- try {
1060
- if (typeof rt.destroy === "function") {
1061
- await rt.destroy();
1062
- logger.info(`Realtime service "${key}" destroyed`);
1063
- } else if (typeof rt.stopListening === "function") {
1064
- await rt.stopListening();
1065
- logger.info(`Realtime service "${key}" LISTEN client stopped`);
1066
- }
1067
- } catch (err) {
1068
- logger.warn(`Error destroying realtime service "${key}":`, { error: err });
1069
- }
1070
- }
1071
-
1072
- // 3. Close the HTTP server (stop accepting, drain in-flight)
1073
- config.server.close(() => {
1074
- logger.info("HTTP server closed");
1075
- resolve();
1076
- });
1077
-
1078
- // 4. Force-resolve after timeout (unless disabled with 0)
1079
- if (timeoutMs > 0) {
1080
- setTimeout(() => {
1081
- logger.warn(`Forced shutdown after ${timeoutMs / 1000}s timeout`);
1082
- resolve();
1083
- }, timeoutMs).unref();
1084
- }
1085
- })();
1086
- });
1087
- };
889
+ const shutdown = createShutdown({
890
+ server: config.server,
891
+ cronScheduler,
892
+ realtimeServices
893
+ });
1088
894
 
1089
895
  return {
1090
896
  driverRegistry,
package/src/serve-spa.ts CHANGED
@@ -2,6 +2,7 @@ import { Hono } from "hono";
2
2
  import { serveStatic } from "@hono/node-server/serve-static";
3
3
  import * as path from "path";
4
4
  import * as fs from "fs";
5
+ import fsp from "node:fs/promises";
5
6
  import { logger } from "./utils/logger.js";
6
7
 
7
8
  /**
@@ -59,6 +60,9 @@ export function serveSPA<E extends import("hono").Env>(app: Hono<E>, config: Ser
59
60
  // Build list of paths to exclude from SPA handling
60
61
  const allExcludePaths = [apiBasePath, ...excludePaths];
61
62
 
63
+ // Cache the index.html content to avoid re-reading from disk on every navigation request.
64
+ let cachedHtml: string | null = null;
65
+
62
66
  // SPA fallback - serve index.html for all non-excluded routes
63
67
  app.get("*", async (c, next) => {
64
68
  // Skip excluded paths (API, health checks, etc.)
@@ -68,13 +72,16 @@ export function serveSPA<E extends import("hono").Env>(app: Hono<E>, config: Ser
68
72
 
69
73
  const indexPath = path.join(frontendPath, indexFile);
70
74
 
71
- if (!fs.existsSync(indexPath)) {
72
- logger.warn(`⚠️ Index file not found: ${indexPath}`);
73
- return next();
75
+ if (!cachedHtml) {
76
+ try {
77
+ cachedHtml = await fsp.readFile(indexPath, "utf-8");
78
+ } catch {
79
+ logger.warn(`⚠️ Index file not found: ${indexPath}`);
80
+ return next();
81
+ }
74
82
  }
75
83
 
76
- const html = fs.readFileSync(indexPath, "utf-8");
77
- return c.html(html);
84
+ return c.html(cachedHtml);
78
85
  });
79
86
 
80
87
  logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
@@ -12,6 +12,7 @@
12
12
  */
13
13
 
14
14
  import { DataDriver } from "@rebasepro/types";
15
+ import { logger } from "../utils/logger";
15
16
 
16
17
  /**
17
18
  * The default driver identifier used when:
@@ -95,7 +96,7 @@ export class DefaultDriverRegistry implements DriverRegistry {
95
96
  if (!registry.has(DEFAULT_DRIVER_ID) && registry.size() > 0) {
96
97
  // If no explicit "(default)", use the first one as default
97
98
  const firstId = Object.keys(input)[0];
98
- console.warn(
99
+ logger.warn(
99
100
  `[DriverRegistry] No "${DEFAULT_DRIVER_ID}" driver provided. ` +
100
101
  `Using "${firstId}" as the default.`
101
102
  );
@@ -108,7 +109,7 @@ export class DefaultDriverRegistry implements DriverRegistry {
108
109
 
109
110
  register(id: string, delegate: DataDriver): void {
110
111
  if (this.delegates.has(id)) {
111
- console.warn(`[DriverRegistry] Overwriting driver with id "${id}"`);
112
+ logger.warn(`[DriverRegistry] Overwriting driver with id "${id}"`);
112
113
  }
113
114
  this.delegates.set(id, delegate);
114
115
  }
@@ -144,7 +145,7 @@ export class DefaultDriverRegistry implements DriverRegistry {
144
145
  }
145
146
 
146
147
  // Fallback to default with warning
147
- console.warn(
148
+ logger.warn(
148
149
  `[DriverRegistry] Driver "${id}" not found, falling back to "${DEFAULT_DRIVER_ID}"`
149
150
  );
150
151
  return this.getDefault();
@@ -213,8 +213,7 @@ export class S3StorageController implements StorageController {
213
213
 
214
214
  // Convert stream to buffer
215
215
  const chunks: Uint8Array[] = [];
216
- // @ts-ignore - Body is a ReadableStream in Node.js
217
- for await (const chunk of response.Body) {
216
+ for await (const chunk of response.Body as AsyncIterable<Uint8Array>) {
218
217
  chunks.push(chunk);
219
218
  }
220
219
  const buffer = Buffer.concat(chunks);