@rebasepro/server 0.24.0 → 0.24.1-canary.gcb918fc

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 (224) hide show
  1. package/README.md +27 -2
  2. package/dist/GCSStorageController-fv6JAZ87.js +402 -0
  3. package/dist/GCSStorageController-fv6JAZ87.js.map +1 -0
  4. package/dist/S3StorageController-BNg-pXiY.js +480 -0
  5. package/dist/S3StorageController-BNg-pXiY.js.map +1 -0
  6. package/dist/api/mount-path.d.ts +30 -0
  7. package/dist/api/mount.d.ts +6 -13
  8. package/dist/api/rest/api-generator.d.ts +53 -1
  9. package/dist/api/rest/auth-collection-writes.d.ts +28 -21
  10. package/dist/api/rest/field-access-query.d.ts +28 -8
  11. package/dist/api/rest/field-ops.d.ts +19 -7
  12. package/dist/api/rest/query-body.d.ts +10 -0
  13. package/dist/api/rest/write-validation.d.ts +7 -5
  14. package/dist/api/schema-editor-routes.d.ts +10 -1
  15. package/dist/api/sql-export-routes.d.ts +8 -0
  16. package/dist/api/types.d.ts +35 -0
  17. package/dist/{ast-schema-editor-CWqS_sLJ.js → ast-schema-editor-mCeSC03x.js} +3 -3
  18. package/dist/{ast-schema-editor-CWqS_sLJ.js.map → ast-schema-editor-mCeSC03x.js.map} +1 -1
  19. package/dist/auth/access.d.ts +57 -8
  20. package/dist/auth/admin-user-ops.d.ts +24 -2
  21. package/dist/auth/admin-users-route.d.ts +1 -0
  22. package/dist/auth/api-keys/api-key-middleware.d.ts +10 -3
  23. package/dist/auth/api-keys/api-key-routes.d.ts +4 -1
  24. package/dist/auth/builtin-auth-adapter.d.ts +13 -0
  25. package/dist/auth/email-change-routes.d.ts +3 -0
  26. package/dist/auth/expired-token-sweep.d.ts +5 -1
  27. package/dist/auth/interfaces.d.ts +10 -0
  28. package/dist/auth/jwks-routes.d.ts +5 -1
  29. package/dist/auth/jwt-crypto.d.ts +7 -0
  30. package/dist/auth/jwt-keys.d.ts +4 -0
  31. package/dist/auth/jwt.d.ts +282 -166
  32. package/dist/auth/magic-link-routes.d.ts +3 -0
  33. package/dist/auth/mfa-crypto.d.ts +9 -3
  34. package/dist/auth/mfa-gate.d.ts +4 -2
  35. package/dist/auth/mfa-routes.d.ts +6 -0
  36. package/dist/auth/middleware.d.ts +9 -2
  37. package/dist/auth/otp-routes.d.ts +3 -0
  38. package/dist/auth/rate-limiter.d.ts +61 -34
  39. package/dist/auth/registration-policy.d.ts +17 -0
  40. package/dist/auth/require-auth.d.ts +1 -1
  41. package/dist/auth/routes.d.ts +16 -0
  42. package/dist/auth/session-routes.d.ts +6 -0
  43. package/dist/auth/user-picking.d.ts +37 -0
  44. package/dist/auth/verify-credential.d.ts +25 -4
  45. package/dist/{auth-DMLngxn_.js → auth-GnONzteu.js} +2869 -1871
  46. package/dist/auth-GnONzteu.js.map +1 -0
  47. package/dist/auth_adapter-CIItCTc8.js +21 -0
  48. package/dist/auth_adapter-CIItCTc8.js.map +1 -0
  49. package/dist/backend/create-backend.d.ts +281 -0
  50. package/dist/backend/default-backend.d.ts +35 -0
  51. package/dist/backend/options.d.ts +912 -0
  52. package/dist/backend/scope.d.ts +23 -0
  53. package/dist/backend/services.d.ts +54 -0
  54. package/dist/backend-DTAOsLQc.js.map +1 -1
  55. package/dist/backup/backup-common.d.ts +14 -3
  56. package/dist/backup/backup-routes.d.ts +23 -2
  57. package/dist/backup/backup-storage.d.ts +3 -1
  58. package/dist/backup/index.d.ts +3 -1
  59. package/dist/{backup-CN0s50D2.js → backup-BnvkSLHi.js} +176 -35
  60. package/dist/backup-BnvkSLHi.js.map +1 -0
  61. package/dist/boot/boot.d.ts +2 -2
  62. package/dist/boot/driver.d.ts +3 -0
  63. package/dist/boot/env.d.ts +9 -2
  64. package/dist/boot/http-server.d.ts +22 -0
  65. package/dist/boot/options.d.ts +16 -1
  66. package/dist/boot/provision.d.ts +4 -2
  67. package/dist/boot/runtime-cors.d.ts +17 -0
  68. package/dist/boot/sources.d.ts +2 -0
  69. package/dist/boot/static-routing.d.ts +9 -0
  70. package/dist/{contract-routes-fz8i4pxs.js → contract-routes-Ey6ftnKH.js} +4 -20
  71. package/dist/contract-routes-Ey6ftnKH.js.map +1 -0
  72. package/dist/cron/cron-loader.d.ts +77 -18
  73. package/dist/cron/cron-routes.d.ts +1 -1
  74. package/dist/cron/cron-scheduler.d.ts +69 -3
  75. package/dist/cron/cron-store.d.ts +33 -5
  76. package/dist/cron/define-cron.d.ts +6 -2
  77. package/dist/cron/index.d.ts +3 -3
  78. package/dist/cron-loader-B-u0snqq.js +202 -0
  79. package/dist/cron-loader-B-u0snqq.js.map +1 -0
  80. package/dist/{cron-routes-Bc-SB0Se.js → cron-routes-OD6BstUj.js} +27 -4
  81. package/dist/cron-routes-OD6BstUj.js.map +1 -0
  82. package/dist/{cron-scheduler-CYQgco86.js → cron-scheduler-idtLPoN-.js} +299 -33
  83. package/dist/cron-scheduler-idtLPoN-.js.map +1 -0
  84. package/dist/cron-store-CZ30ZBi-.js +577 -0
  85. package/dist/cron-store-CZ30ZBi-.js.map +1 -0
  86. package/dist/email/smtp-email-service.d.ts +23 -0
  87. package/dist/email/types.d.ts +18 -0
  88. package/dist/env.d.ts +0 -37
  89. package/dist/filter-dialect-O1HK6Nvr.js +1628 -0
  90. package/dist/filter-dialect-O1HK6Nvr.js.map +1 -0
  91. package/dist/{function-loader-D7o5Epjj.js → function-loader-iKhOZatS.js} +68 -38
  92. package/dist/function-loader-iKhOZatS.js.map +1 -0
  93. package/dist/{function-routes-CaNG4waN.js → function-routes-j9TwpvQe.js} +5 -4
  94. package/dist/function-routes-j9TwpvQe.js.map +1 -0
  95. package/dist/functions/context.d.ts +9 -0
  96. package/dist/functions/define-function.d.ts +10 -6
  97. package/dist/functions/function-loader.d.ts +75 -18
  98. package/dist/functions/function-routes.d.ts +2 -1
  99. package/dist/functions/index.d.ts +5 -4
  100. package/dist/functions/index.js +42 -13
  101. package/dist/functions/index.js.map +1 -1
  102. package/dist/functions/internal.d.ts +2 -2
  103. package/dist/functions/proxy.d.ts +6 -2
  104. package/dist/functions/request-timeout.d.ts +7 -5
  105. package/dist/history/history-routes.d.ts +9 -0
  106. package/dist/history/retention-sweep.d.ts +81 -0
  107. package/dist/{history-recorder-Nr8zLvoU.js → history-recorder-BfWRwmPy.js} +3 -3
  108. package/dist/{history-recorder-Nr8zLvoU.js.map → history-recorder-BfWRwmPy.js.map} +1 -1
  109. package/dist/{history-store-rcAm_xFR.js → history-store-DPofGVDp.js} +2 -2
  110. package/dist/{history-store-rcAm_xFR.js.map → history-store-DPofGVDp.js.map} +1 -1
  111. package/dist/index.d.ts +23 -10
  112. package/dist/index.es.js +24423 -20038
  113. package/dist/index.es.js.map +1 -1
  114. package/dist/init/docs.d.ts +27 -10
  115. package/dist/init/process-safety.d.ts +7 -2
  116. package/dist/init/shutdown.d.ts +18 -3
  117. package/dist/init.d.ts +49 -865
  118. package/dist/internal-tables-L2t39RdT.js +136 -0
  119. package/dist/internal-tables-L2t39RdT.js.map +1 -0
  120. package/dist/jobs/job-queue.d.ts +8 -0
  121. package/dist/jobs/job-store.d.ts +24 -0
  122. package/dist/jobs/types.d.ts +25 -4
  123. package/dist/{jobs-DqYNfquG.js → jobs-BCbflZj2.js} +234 -24
  124. package/dist/jobs-BCbflZj2.js.map +1 -0
  125. package/dist/{jwt-R6bSPMjk.js → jwt-BlvuKoR6.js} +529 -454
  126. package/dist/jwt-BlvuKoR6.js.map +1 -0
  127. package/dist/{keys-GAVZqbqx.js → keys-COzQ2aE8.js} +2 -2
  128. package/dist/{keys-GAVZqbqx.js.map → keys-COzQ2aE8.js.map} +1 -1
  129. package/dist/load-failure-L5hvfDoG.js +80 -0
  130. package/dist/load-failure-L5hvfDoG.js.map +1 -0
  131. package/dist/mcp/mcp-routes.d.ts +6 -0
  132. package/dist/mcp/mcp-tools.d.ts +8 -0
  133. package/dist/mcp/oauth-routes.d.ts +7 -0
  134. package/dist/metrics/index.d.ts +18 -0
  135. package/dist/metrics/pool-wait.d.ts +50 -0
  136. package/dist/offline-QNQgsPOq-B2yGeXkn.js +3305 -0
  137. package/dist/offline-QNQgsPOq-B2yGeXkn.js.map +1 -0
  138. package/dist/offline-store-Cm5sAX81-CALiO4Gn.js +1915 -0
  139. package/dist/offline-store-Cm5sAX81-CALiO4Gn.js.map +1 -0
  140. package/dist/{openapi-generator-DAq_XVDu.js → openapi-generator-D9zUQmEp.js} +366 -82
  141. package/dist/openapi-generator-D9zUQmEp.js.map +1 -0
  142. package/dist/policy-PdzX-Opd.js +121 -0
  143. package/dist/policy-PdzX-Opd.js.map +1 -0
  144. package/dist/project_manifest-R6y_KHms.js +59 -0
  145. package/dist/project_manifest-R6y_KHms.js.map +1 -0
  146. package/dist/{proxy-qRlqeUmO.js → proxy-CkTlMGmh.js} +4 -3
  147. package/dist/proxy-CkTlMGmh.js.map +1 -0
  148. package/dist/{query-parser-BgiKJKvc.js → query-parser-4WkMoA_K.js} +510 -15
  149. package/dist/query-parser-4WkMoA_K.js.map +1 -0
  150. package/dist/{request-timeout-DgH7j8qO.js → request-timeout-Dnl2_OBc.js} +15 -8
  151. package/dist/request-timeout-Dnl2_OBc.js.map +1 -0
  152. package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
  153. package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
  154. package/dist/schema-edit/apply-schema-change.d.ts +10 -0
  155. package/dist/schema-edit/local-source.d.ts +29 -0
  156. package/dist/schema-edit/remote-source.d.ts +13 -3
  157. package/dist/schema-edit/source-editor.d.ts +28 -0
  158. package/dist/{schema-editor-routes-oIyuWl3L.js → schema-editor-routes-C11Tumyd.js} +54 -10
  159. package/dist/schema-editor-routes-C11Tumyd.js.map +1 -0
  160. package/dist/services/outbound-post.d.ts +50 -0
  161. package/dist/services/outbound-url-guard.d.ts +34 -5
  162. package/dist/services/routed-realtime-service.d.ts +5 -0
  163. package/dist/services/webhook-service.d.ts +66 -14
  164. package/dist/singleton.d.ts +9 -5
  165. package/dist/soft-delete-params-DfV3vVN5.js +668 -0
  166. package/dist/soft-delete-params-DfV3vVN5.js.map +1 -0
  167. package/dist/sql-export-routes-ClRwzIsC.js +240 -0
  168. package/dist/sql-export-routes-ClRwzIsC.js.map +1 -0
  169. package/dist/{src-CatHFUym.js → src-D5Y3N-S4.js} +2 -1
  170. package/dist/{src-CatHFUym.js.map → src-D5Y3N-S4.js.map} +1 -1
  171. package/dist/storage/GCSStorageController.d.ts +46 -2
  172. package/dist/storage/LocalStorageController.d.ts +67 -2
  173. package/dist/storage/S3StorageController.d.ts +37 -3
  174. package/dist/storage/image-transform.d.ts +5 -0
  175. package/dist/storage/maintenance-routes.d.ts +23 -0
  176. package/dist/storage/maintenance.d.ts +126 -0
  177. package/dist/storage/rendition-cache.d.ts +9 -11
  178. package/dist/storage/request-principals.d.ts +30 -0
  179. package/dist/storage/routes.d.ts +20 -0
  180. package/dist/storage/source-variables.d.ts +10 -0
  181. package/dist/storage/streams.d.ts +42 -0
  182. package/dist/storage/tus-handler.d.ts +46 -2
  183. package/dist/storage/types.d.ts +82 -7
  184. package/dist/storage/upload-validation.d.ts +13 -0
  185. package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
  186. package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
  187. package/dist/strings.es-BeZWAodn.js +256 -0
  188. package/dist/strings.es-BeZWAodn.js.map +1 -0
  189. package/dist/upload-validation-Dd-fnsbA.js +172 -0
  190. package/dist/upload-validation-Dd-fnsbA.js.map +1 -0
  191. package/dist/utils/byte-size.d.ts +10 -0
  192. package/dist/utils/compression.d.ts +6 -0
  193. package/dist/utils/load-failure.d.ts +14 -0
  194. package/package.json +5 -5
  195. package/dist/GCSStorageController-BSiP1c-f.js +0 -256
  196. package/dist/GCSStorageController-BSiP1c-f.js.map +0 -1
  197. package/dist/S3StorageController-CAwFRgjV.js +0 -276
  198. package/dist/S3StorageController-CAwFRgjV.js.map +0 -1
  199. package/dist/auth-DMLngxn_.js.map +0 -1
  200. package/dist/backup-CN0s50D2.js.map +0 -1
  201. package/dist/contract-routes-fz8i4pxs.js.map +0 -1
  202. package/dist/cron-loader-CwaANlOG.js +0 -118
  203. package/dist/cron-loader-CwaANlOG.js.map +0 -1
  204. package/dist/cron-routes-Bc-SB0Se.js.map +0 -1
  205. package/dist/cron-scheduler-CYQgco86.js.map +0 -1
  206. package/dist/cron-store-D2Q9-Aco.js +0 -300
  207. package/dist/cron-store-D2Q9-Aco.js.map +0 -1
  208. package/dist/dynamic-import-X40pTZUQ.js +0 -23
  209. package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
  210. package/dist/function-loader-D7o5Epjj.js.map +0 -1
  211. package/dist/function-routes-CaNG4waN.js.map +0 -1
  212. package/dist/jobs-DqYNfquG.js.map +0 -1
  213. package/dist/jwt-R6bSPMjk.js.map +0 -1
  214. package/dist/openapi-generator-DAq_XVDu.js.map +0 -1
  215. package/dist/proxy-qRlqeUmO.js.map +0 -1
  216. package/dist/query-parser-BgiKJKvc.js.map +0 -1
  217. package/dist/request-timeout-DgH7j8qO.js.map +0 -1
  218. package/dist/schema-editor-routes-oIyuWl3L.js.map +0 -1
  219. package/dist/soft-delete-params-BWPilMPF.js +0 -59
  220. package/dist/soft-delete-params-BWPilMPF.js.map +0 -1
  221. package/dist/src-I3aG1PcY.js +0 -6870
  222. package/dist/src-I3aG1PcY.js.map +0 -1
  223. package/dist/types-BfKcm9do.js +0 -39
  224. package/dist/types-BfKcm9do.js.map +0 -1
@@ -0,0 +1,21 @@
1
+ import { createRequire as __rebaseCreateRequire } from "module";
2
+ import __rebaseProcess from "process";
3
+ globalThis.process ??= __rebaseProcess;
4
+ __rebaseCreateRequire(import.meta.url);
5
+ //#region ../types/src/types/auth_adapter.ts
6
+ /**
7
+ * The request header that asks for a data-plane request to run as another
8
+ * user, named by uid.
9
+ *
10
+ * The server honours it only on a signed-in administrator's own session —
11
+ * never on an API key or the service key — and only through an adapter that
12
+ * implements {@link AuthAdapter.resolveUser}. Everywhere else a request that
13
+ * carries it is refused, never run as its caller.
14
+ *
15
+ * @group Auth
16
+ */
17
+ var IMPERSONATE_HEADER = "x-rebase-impersonate";
18
+ //#endregion
19
+ export { IMPERSONATE_HEADER as t };
20
+
21
+ //# sourceMappingURL=auth_adapter-CIItCTc8.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth_adapter-CIItCTc8.js","names":[],"sources":["../../types/src/types/auth_adapter.ts"],"sourcesContent":["/**\n * @module AuthAdapter\n *\n * Pluggable authentication abstraction for Rebase.\n *\n * An `AuthAdapter` decouples authentication from the database layer,\n * allowing users to bring their own auth system (Clerk, Auth0, or other\n * external providers) while keeping the Rebase admin frontend fully functional.\n *\n * @example Built-in auth (default — zero config change)\n * ```ts\n * initializeRebaseBackend({\n * auth: { jwtSecret: \"...\", google: { clientId: \"...\" } },\n * database: createPostgresAdapter({ ... }),\n * });\n * ```\n *\n * @example Custom auth\n * ```ts\n * import { createCustomAuthAdapter } from \"@rebasepro/server\";\n *\n * initializeRebaseBackend({\n * auth: createCustomAuthAdapter({\n * verifyRequest: async (req) => { ... },\n * }),\n * database: createPostgresAdapter({ ... }),\n * });\n * ```\n *\n * @group Auth\n */\n\n/**\n * A sub-app an adapter hands back for the framework to mount.\n *\n * Structural on purpose. This was `Hono<any, any, any>`, and that one\n * `import type { Hono } from \"hono\"` was the whole reason `@rebasepro/types`\n * peer-depended on hono — which npm and pnpm auto-install, so **every browser\n * app that installed `@rebasepro/client` got a 2.8 MB server framework in its\n * lockfile and its security scanners**, for a type used by two optional methods\n * that a browser app can never call.\n *\n * The shape is the same duck-type the function loader already validates at\n * runtime (`isHonoLike`: a `fetch` method and a `routes` array), and a real\n * `Hono` satisfies it — asserted in `auth_adapter.test.ts`, which does import\n * hono, because a structural type is only worth having if something proves the\n * real thing still fits.\n *\n * @group Auth\n */\nexport interface MountableRouter {\n /** Handle a request. Hono, Elysia and a hand-rolled router all have this. */\n fetch: (request: Request) => Response | Promise<Response>;\n /** The routes the sub-app declares. Only its presence is contractual. */\n routes: readonly unknown[];\n}\n\n// ─── Impersonation ───────────────────────────────────────────────────────────\n\n/**\n * The request header that asks for a data-plane request to run as another\n * user, named by uid.\n *\n * The server honours it only on a signed-in administrator's own session —\n * never on an API key or the service key — and only through an adapter that\n * implements {@link AuthAdapter.resolveUser}. Everywhere else a request that\n * carries it is refused, never run as its caller.\n *\n * @group Auth\n */\nexport const IMPERSONATE_HEADER = \"x-rebase-impersonate\";\n\n// ─── Authenticated User ──────────────────────────────────────────────────────\n\n/**\n * The normalized user object returned by `AuthAdapter.verifyRequest()`.\n *\n * Regardless of the auth provider, every request is resolved to this shape\n * so that downstream middleware (RLS scoping, route guards) can work uniformly.\n *\n * @group Auth\n */\nexport interface AuthenticatedUser {\n /** Unique user identifier (provider-specific). */\n uid: string;\n /** Primary email address. */\n email: string;\n /** Human-readable display name. */\n displayName?: string | null;\n /** Avatar URL. */\n photoUrl?: string | null;\n /** Role identifiers the user holds. */\n roles: string[];\n /**\n * Whether `roles` includes `admin`. Informational — every authorization\n * decision reads `roles` and the scopes they hold, so an adapter that\n * wants a user treated as an administrator gives them the `admin` role.\n */\n isAdmin: boolean;\n /**\n * Whether this session is a GUEST — anonymous sign-in rather than an\n * account.\n *\n * Optional, and absent means \"not a guest\", so an external adapter that\n * has no such concept keeps behaving exactly as it did. When present it\n * reaches the database as `rebase.is_anonymous()`, which is what lets a\n * policy say \"a signed-in person, not somebody who pressed Continue as\n * guest\" — two things that were the same principal inside every rule,\n * because anonymous sign-in mints a real user row with a real uid.\n */\n isAnonymous?: boolean;\n /** Raw bearer token from the request (for forwarding). */\n rawToken?: string;\n /** Extra claims/metadata from the auth provider. */\n claims?: Record<string, unknown>;\n /**\n * What this credential may do, when it is narrower than its person — set\n * for an API key. Absent means everything the user's roles hold\n * (`scopesForRoles`).\n */\n scopes?: string[];\n}\n\n// ─── Capabilities ────────────────────────────────────────────────────────────\n\n/**\n * Feature flags advertised by an auth adapter.\n *\n * The frontend reads these from `GET /api/auth/config` to dynamically\n * show/hide UI elements (login form, registration, password reset, etc.).\n *\n * @group Auth\n */\nexport interface AuthAdapterCapabilities {\n /**\n * Whether this adapter mounts its own `/auth/*` routes.\n *\n * - `true` for the built-in Rebase auth (login, register, refresh, etc.)\n * - `false` for external providers like Clerk or Auth0 that handle\n * auth flows outside of the Rebase backend.\n */\n hasBuiltInAuthRoutes: boolean;\n\n /** Supports email/password login. */\n emailPasswordLogin: boolean;\n /**\n * Whether self-registration is open **right now**.\n *\n * A runtime answer, not a static feature list: the built-in adapter also\n * reports `true` during the first-user bootstrap window (an empty user\n * table) and `false` the moment `disableSelfRegistration` is set. Whatever\n * this says, `POST /auth/register` does — both read the same predicate, so\n * the UI can never be sent to a form that can only 403.\n */\n registrationEnabled: boolean;\n /**\n * Supports the end-user password reset flow (emailing a reset link).\n *\n * This is about *self-service* reset, so it is typically tied to whether an\n * email service is configured. It says nothing about whether an admin can\n * reset someone else's password — see `adminPasswordReset`.\n */\n passwordReset: boolean;\n /**\n * Whether the adapter exposes `POST /admin/users/:uid/reset-password`,\n * letting an admin reset another user's password.\n *\n * Independent of `passwordReset`: the built-in adapter supports this even\n * with no email service configured (it returns a one-time temporary\n * password instead of sending a link). Adapters that mount their own admin\n * routes must set this to `true` only once that route actually exists —\n * the admin UI hides the \"Reset Password\" action when it is `false`.\n */\n adminPasswordReset: boolean;\n /** Supports session listing/revocation. */\n sessionManagement: boolean;\n /** Supports profile updates (display name, photo). */\n profileUpdate: boolean;\n /** Supports email verification. */\n emailVerification: boolean;\n /** Supports passwordless magic link login. */\n magicLink: boolean;\n /**\n * Supports passwordless sign-in with a six-digit code sent by email.\n *\n * Optional so that an external adapter written before this existed still\n * satisfies the interface; absent reads as \"no\", which is what an adapter\n * that has never heard of the flow means.\n */\n emailOtp?: boolean;\n /** Whether `POST /auth/anonymous` will mint a credential-less session. */\n anonymousLogin: boolean;\n /** List of enabled OAuth provider IDs (e.g. `[\"google\", \"github\"]`). */\n enabledProviders: string[];\n\n /**\n * For external auth (Clerk, Auth0, etc.): the URL where the user should\n * be redirected for login. The Rebase frontend will navigate here instead\n * of showing its own login form.\n */\n externalLoginUrl?: string;\n\n /**\n * True when no users exist yet — first-user bootstrap mode.\n * Only applicable for built-in auth.\n */\n needsSetup?: boolean;\n}\n\n/**\n * What `GET /api/auth/config` answers: the auth adapter's capabilities, plus\n * the optional features of the backend around it.\n *\n * The admin panel reads this document before anything else, so it is where\n * the panel learns that a feature is off, without first asking for that\n * feature and getting a refusal.\n *\n * @group Auth\n */\nexport interface AuthConfigDocument extends AuthAdapterCapabilities {\n /**\n * Whether this backend serves file storage. When it is `false`, no storage\n * backend is configured: `GET /storage/sources` lists nothing and every\n * other `/storage` route answers 501 `STORAGE_NOT_CONFIGURED`. Absent from\n * a backend that predates the field, so a client must ask\n * `GET /storage/sources` there.\n */\n storage?: boolean;\n}\n\n// ─── User Management ────────────────────────────────────────────────────────\n\n/**\n * Options for paginated user listing.\n * @group Auth\n */\nexport interface AuthUserListOptions {\n limit?: number;\n offset?: number;\n search?: string;\n orderBy?: string;\n orderDir?: \"asc\" | \"desc\";\n roleId?: string;\n}\n\n/**\n * Paginated user listing result.\n * @group Auth\n */\nexport interface AuthUserListResult {\n users: AuthUserData[];\n total: number;\n limit: number;\n offset: number;\n}\n\n/**\n * User data exposed by the auth adapter.\n * @group Auth\n */\nexport interface AuthUserData {\n id: string;\n email: string;\n displayName?: string | null;\n photoUrl?: string | null;\n emailVerified?: boolean;\n metadata?: Record<string, unknown>;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n/**\n * Data for creating a user.\n * @group Auth\n */\nexport interface AuthCreateUserData {\n email: string;\n password?: string;\n displayName?: string;\n photoUrl?: string;\n metadata?: Record<string, unknown>;\n /**\n * Store the address as verified: whoever creates the account named it\n * (an operator, an administrator), so nobody needs to prove it.\n */\n emailVerified?: boolean;\n}\n\n/**\n * User management operations for the admin panel.\n *\n * Optional — if not provided by the adapter, the user management UI is hidden.\n *\n * @group Auth\n */\nexport interface UserManagementAdapter {\n listUsers(options?: AuthUserListOptions): Promise<AuthUserListResult>;\n getUserById(id: string): Promise<AuthUserData | null>;\n createUser(data: AuthCreateUserData): Promise<AuthUserData>;\n updateUser(id: string, data: Partial<AuthCreateUserData>): Promise<AuthUserData | null>;\n deleteUser(id: string): Promise<void>;\n getUserRoles(uid: string): Promise<string[]>;\n setUserRoles(uid: string, roleIds: string[]): Promise<void>;\n}\n\n// ─── User Creation Lifecycle ─────────────────────────────────────────────────\n\n/**\n * Result of `AuthAdapter.prepareUserCreation()`.\n *\n * Contains the processed values ready for persistence and metadata\n * needed by the finalization step.\n *\n * @group Auth\n */\nexport interface UserCreationPrepareResult {\n /** Processed values to persist (passwordHash instead of raw password, etc.). */\n values: Record<string, unknown>;\n /** Cleartext password for post-save processing (email or admin display). */\n clearPassword?: string;\n /**\n * Whether the hook already handled the invitation (email, etc.). When\n * `true`, `finalizeUserCreation` is skipped and `clearPassword` is returned\n * to the admin as the temporary password.\n */\n hookHandledEmail: boolean;\n /** Whether an invitation was sent (only relevant when hookHandledEmail is true). */\n invitationSent: boolean;\n}\n\n/**\n * What the door creating a user knows about its caller, for\n * `AuthAdapter.prepareUserCreation()`.\n *\n * @group Auth\n */\nexport interface UserCreationOptions {\n /**\n * Whether the caller may waive the password policy with\n * `skipPasswordPolicy: true` in the body. The doors set it for an\n * administrator (the service key signs in as one) and for nobody else, so\n * an adapter that reads the flag is taking an administrator at their word —\n * an import of accounts whose passwords were set under another system's\n * rules. Absent, the flag waives nothing.\n */\n mayWaivePasswordPolicy?: boolean;\n}\n\n/**\n * What a create body for an auth collection may name beyond the collection's\n * own declared fields — see `AuthAdapter.describeUserCreationContract()`.\n *\n * @group Auth\n */\nexport interface UserCreationWriteContract {\n /**\n * Whether to check the body for fields neither the collection nor\n * {@link extraFields} declares. `false` skips the check entirely.\n */\n validate: boolean;\n /**\n * Credential and provider keys the adapter consumes itself, which the\n * collection therefore does not declare as columns. `password` is the\n * canonical one: `prepareUserCreation` hashes it into `passwordHash` and\n * deletes it before the row is ever built.\n */\n extraFields: string[];\n /**\n * Each body's `prepareUserCreation` touches nothing but its own result —\n * no hook, no email, no write — so a bulk create may prepare several at\n * once. The built-in adapter's path with no `onCreateUser`, no\n * `onAdminCreateUser` and the default `hashPassword` is: it hashes a\n * password and normalizes an email, and a 1,000-row bulk spent half a\n * minute hashing one password at a time. Leave it unset when a hook runs:\n * preparations then run one at a time, in the order the rows were given.\n */\n independent?: boolean;\n}\n\n/**\n * Result of `AuthAdapter.finalizeUserCreation()`.\n *\n * Returned to the REST API for inclusion in the response.\n *\n * @group Auth\n */\nexport interface UserCreationFinalizeResult {\n /** If set, returned to the admin in the API response. */\n temporaryPassword?: string;\n /** Whether an invitation email was sent. */\n invitationSent: boolean;\n /**\n * Whether an email service was configured but delivery failed, causing the\n * fallback to `temporaryPassword`. Absent when no email service is configured.\n */\n emailDeliveryFailed?: boolean;\n}\n\n// ─── Auth Response Transform ─────────────────────────────────────────────────\n\n/**\n * The auth response payload shape that flows through `transformAuthResponse`.\n *\n * For login, register, OAuth, anonymous, and magic-link flows the payload\n * contains both `user` and `tokens`. For refresh and MFA flows the payload\n * contains only `tokens` (no `user`).\n *\n * @group Auth\n */\nexport interface AuthResponsePayload {\n user?: {\n uid: string;\n email: string;\n displayName: string | null;\n photoURL: string | null;\n providerId?: string;\n isAnonymous?: boolean;\n emailVerified?: boolean;\n roles: string[];\n metadata: Record<string, unknown>;\n };\n tokens: {\n accessToken: string;\n refreshToken: string;\n accessTokenExpiresAt: number;\n /** Additional tokens injected by `transformAuthResponse`. */\n [key: string]: unknown;\n };\n}\n\n/**\n * Context passed to the `transformAuthResponse` hook.\n *\n * @group Auth\n */\nexport interface TransformAuthResponseContext {\n /** The authenticated user's ID. */\n uid: string;\n /** The auth method that triggered this response. */\n method: \"login\" | \"register\" | \"oauth\" | \"refresh\" | \"anonymous\" | \"magic-link\" | \"otp\" | \"mfa\";\n /** The raw HTTP request (for reading headers, IP, etc.). */\n request: Request;\n}\n\n// ─── Auth Adapter ────────────────────────────────────────────────────────────\n\n/**\n * Pluggable authentication adapter for Rebase.\n *\n * This is the **key interface** that decouples authentication from the\n * database layer. Each auth adapter knows how to:\n *\n * 1. Verify incoming HTTP requests (`verifyRequest`)\n * 2. Optionally manage users (for the admin panel)\n * 3. Optionally mount auth-specific routes (login, register, etc.)\n * 4. Advertise its capabilities so the frontend can adapt\n *\n * The built-in Rebase auth implements this interface internally.\n * External providers (Clerk, Auth0, or others) provide their own adapters.\n * Users with custom auth can use `createCustomAuthAdapter()` for a minimal setup.\n *\n * @group Auth\n */\nexport interface AuthAdapter {\n /**\n * Unique identifier for this auth adapter.\n *\n * @example \"rebase-builtin\", \"clerk\", \"auth0\", \"external-provider\", \"custom\"\n */\n readonly id: string;\n\n // ── Request Authentication ──────────────────────────────────────────\n\n /**\n * Verify an incoming request and extract the authenticated user.\n *\n * This replaces the hardcoded JWT verification in server's middleware.\n * Each adapter implements its own token verification strategy:\n * - Built-in: verify Rebase JWT\n * - Clerk: call Clerk's `verifyToken()`\n * - Auth0: validate Auth0 JWT with JWKS\n * - Custom: whatever logic the user provides\n *\n * @param request - The raw `Request` object (portable across Hono, Express, Fastify)\n * @returns The authenticated user, or `null` for unauthenticated requests.\n * Throw an error to reject the request with 401.\n */\n verifyRequest(request: Request): Promise<AuthenticatedUser | null>;\n\n /**\n * Verify a raw bearer token and extract the authenticated user.\n *\n * Used for **WebSocket authentication**, where there is no HTTP `Request`\n * object — only a token string sent over the socket.\n *\n * If not implemented, the default behavior synthesizes a minimal `Request`\n * with an `Authorization: Bearer <token>` header and delegates to\n * `verifyRequest()`. Adapters should override this if their token\n * verification logic doesn't depend on request headers/cookies.\n *\n * @param token - The raw bearer token string.\n * @returns The authenticated user, or `null` if the token is invalid.\n */\n verifyToken?(token: string): Promise<AuthenticatedUser | null>;\n\n /**\n * The identity a verified request from this account would carry right\n * now, with no credential involved: its uid, its roles as they are now,\n * its guest flag, and the custom claims a token minted for it now would\n * hold. `null` when no active account has this uid — none at all, or one\n * that is disabled.\n *\n * This is what lets an administrator run a request as another user (the\n * {@link IMPERSONATE_HEADER} header). The server calls it only after it\n * has decided the caller may do that, and it calls it for the caller too,\n * so the decision rests on the caller's roles as they are now rather than\n * on the ones their token claims.\n *\n * Optional. An adapter without it cannot be impersonated through, and a\n * request carrying the header is refused rather than run as its caller.\n */\n resolveUser?(uid: string): Promise<AuthenticatedUser | null>;\n\n // ── User Management (for admin panel) ────────────────────────────\n\n /**\n * User CRUD for the admin panel's user management UI.\n * Optional — if not provided, user management UI is hidden.\n */\n userManagement?: UserManagementAdapter;\n\n // ── Auth Routes ─────────────────────────────────────────────────────\n\n /**\n * Mount adapter-specific auth routes (login, register, refresh, etc.).\n *\n * - Built-in adapter: mounts `/auth/login`, `/auth/register`, etc.\n * - External adapter: typically returns `undefined` (auth is handled externally).\n * - Custom adapter: user mounts their own routes.\n *\n * The return type is the structural {@link MountableRouter} rather than\n * `Hono`, so this package needs no hono dependency. A real `Hono<HonoEnv>`\n * satisfies it, and implementations are free to use their own env.\n *\n * @returns A Hono sub-app with auth routes, or `undefined` to skip route mounting.\n */\n createAuthRoutes?(): MountableRouter | undefined;\n\n /**\n * Mount admin routes (e.g. password reset for users).\n *\n * @returns A Hono sub-app with admin routes, or `undefined` to skip.\n */\n createAdminRoutes?(): MountableRouter | undefined;\n\n // ── Feature Detection ───────────────────────────────────────────────\n\n /**\n * Advertise what this auth adapter supports.\n *\n * The frontend reads this from `GET /api/auth/config` to dynamically\n * show/hide UI elements. This is the bridge between backend capabilities\n * and the frontend's `AuthCapabilities` type.\n */\n getCapabilities(): AuthAdapterCapabilities | Promise<AuthAdapterCapabilities>;\n\n // ── Lifecycle ───────────────────────────────────────────────────────\n\n /**\n * Called during backend initialization.\n * Use for running migrations, creating tables, seeding initial data, etc.\n */\n initialize?(): Promise<void>;\n\n /**\n * Called during graceful shutdown.\n * Use for closing connections, flushing caches, etc.\n */\n destroy?(): Promise<void>;\n\n // ── Collection User Creation (for auth collections) ───────────────\n\n /**\n * Prepare values for creating a user via the auth collection's REST API.\n *\n * Called on POST to a collection with `auth: true`. Handles password\n * hashing, email normalization, and any collection/backend-level hooks.\n *\n * If not implemented, the collection saves values as-is (no password hashing).\n *\n * A chosen `password` is held to the password policy unless the body says\n * `skipPasswordPolicy: true` and `options.mayWaivePasswordPolicy` is set.\n *\n * @param values - Raw request body from the client.\n * @param collectionAuth - The parsed `AuthCollectionConfig` from the collection (if `auth` is an object).\n * @param options - What the door knows about its caller.\n * @returns Processed values ready for `driver.save()`, plus metadata for the post-save step.\n */\n prepareUserCreation?(\n values: Record<string, unknown>,\n collectionAuth?: unknown,\n options?: UserCreationOptions\n ): Promise<UserCreationPrepareResult>;\n\n /**\n * Describe what a create body for this auth collection is allowed to name,\n * so unknown-field validation can run on it.\n *\n * A signup body is not the collection's shape: it carries credential fields\n * like `password` that the users table never declares as columns, and\n * `prepareUserCreation` maps them onto real ones. Validating the raw body\n * against the collection alone would reject every legitimate signup — which\n * is why the check used to be skipped outright for auth collections. That\n * skip was total, so an undeclared field was silently dropped and the write\n * still returned 201, while the same typo on a normal collection was a 400.\n *\n * This narrows the exemption to the fields the adapter actually consumes.\n *\n * `validate: false` disables the check for this collection, and is the right\n * answer when a custom `onCreateUser` hook is configured: the body is then\n * the hook's contract, not the collection's, and this layer cannot know what\n * the hook accepts.\n *\n * If not implemented, validation is skipped — the pre-existing behaviour.\n *\n * @param collectionAuth - The parsed `AuthCollectionConfig` from the collection (if `auth` is an object).\n */\n describeUserCreationContract?(\n collectionAuth?: unknown\n ): UserCreationWriteContract;\n\n /**\n * Finalize a user creation after the entity has been persisted.\n *\n * Handles post-save work: sending invitation emails, generating\n * password-reset tokens, or falling back to returning a temporary password.\n *\n * Not called when `prepareUserCreation` reported `hookHandledEmail`. A\n * create hook then owns delivery, and the response carries the\n * `invitationSent` and `clearPassword` the prepare step returned. This\n * holds for both ways of creating a user: a `POST` to the auth collection\n * and `POST /admin/users`.\n *\n * @param entity - The persisted entity (id + values).\n * @param clearPassword - The cleartext password from the prepare step (if any).\n * @returns Metadata for the API response (temporary password, invitation status).\n */\n finalizeUserCreation?(\n entity: { id: string; values: Record<string, unknown> },\n clearPassword?: string\n ): Promise<UserCreationFinalizeResult>;\n\n // ── User Administration Through the Auth Collection ─────────────────\n\n /**\n * Check, and put in stored form, updates to rows of the auth collection\n * that arrive through the data API — `PATCH /<users>/:id`, `PATCH /bulk`,\n * `_batch`, the realtime socket.\n *\n * Those rows are the users, so an update to one is user administration,\n * and has to hold to what the admin user routes hold to: no update that\n * leaves the project without an administrator, and an email stored the\n * way sign-in looks it up. Throws to refuse; the updates are judged\n * together, so a bulk demotion of every admin is refused even though each\n * row alone would pass.\n *\n * If not implemented, the values are written as they arrived.\n *\n * @returns Each update's values, as they should be written, in order.\n */\n prepareUserUpdates?(\n updates: ReadonlyArray<{ uid: string; values: Record<string, unknown> }>\n ): Promise<Record<string, unknown>[]>;\n\n /**\n * Check a deletion of these users through the data API before any row goes,\n * and run the hooks that may veto it (`beforeUserDelete`). Throws to\n * refuse — for one that would leave the project without an administrator,\n * among others.\n *\n * If not implemented, the rows are deleted as any other.\n */\n prepareUserDeletions?(uids: readonly string[]): Promise<void>;\n\n /**\n * After those users' rows are deleted through the data API: end their\n * sessions and run the after-delete hooks (`afterUserDelete`).\n */\n finalizeUserDeletions?(uids: readonly string[]): Promise<void>;\n\n // ── Service Key (optional) ──────────────────────────────────────────\n\n /**\n * A static secret key for server-to-server / script authentication.\n *\n * When set, requests with `Authorization: Bearer <serviceKey>` bypass\n * normal token verification and are granted admin-level access.\n */\n serviceKey?: string;\n\n // ── Response Transform ───────────────────────────────────────────────\n\n /**\n * Transform the auth response before sending it to the client.\n *\n * Called after successful login, register, refresh, OAuth, anonymous,\n * magic-link, and MFA flows. The hook receives the fully-formed\n * response and returns a (potentially enriched) response.\n *\n * Use cases:\n * - Inject tokens from external auth systems (custom provider tokens, etc.)\n * - Add project-specific metadata to the response\n * - Enrich the user object with data from external sources\n *\n * The hook runs in the request path — keep it fast.\n * Heavy work should be offloaded to `onAuthenticated` (fire-and-forget).\n */\n transformAuthResponse?(\n response: AuthResponsePayload,\n context: TransformAuthResponseContext\n ): Promise<AuthResponsePayload>;\n}\n\n// ─── Custom Auth Adapter Options ─────────────────────────────────────────────\n\n/**\n * Options for creating a minimal custom auth adapter via `createCustomAuthAdapter()`.\n *\n * This is the simplest way to plug an existing auth system into Rebase.\n * Only `verifyRequest` is required — everything else is optional.\n *\n * @group Auth\n */\nexport interface CustomAuthAdapterOptions {\n /**\n * Verify an incoming request and return the authenticated user.\n * This is the only required method.\n */\n verifyRequest: (request: Request) => Promise<AuthenticatedUser | null>;\n\n /**\n * Verify a raw bearer token for WebSocket authentication.\n * Optional — if omitted, a synthetic `Request` is constructed and passed\n * to `verifyRequest`.\n */\n verifyToken?: (token: string) => Promise<AuthenticatedUser | null>;\n\n /** Optional user management for the admin panel. */\n userManagement?: UserManagementAdapter;\n\n /** Static service key for server-to-server auth. */\n serviceKey?: string;\n\n /** Override default capabilities. */\n capabilities?: Partial<AuthAdapterCapabilities>;\n\n /**\n * Transform the auth response before sending it to the client.\n * Same semantics as `AuthAdapter.transformAuthResponse`.\n */\n transformAuthResponse?: (\n response: AuthResponsePayload,\n context: TransformAuthResponseContext\n ) => Promise<AuthResponsePayload>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAsEA,IAAa,qBAAqB"}
@@ -0,0 +1,281 @@
1
+ /**
2
+ * A Rebase backend as a value the application holds.
3
+ *
4
+ * `createRebaseBackend(options)` builds everything a backend is — the drivers,
5
+ * auth, storage, the in-process client, every route group — and stops there:
6
+ * it mounts nothing, starts no timer and adds no process listener. What happens
7
+ * next is the caller's, one public method at a time:
8
+ *
9
+ * ```ts
10
+ * const backend = await createRebaseBackend({ database, auth, collections, provision: true });
11
+ * app.route("/api/data", backend.routes.data()); // one group, at a path of your choosing
12
+ * backend.mount(app, { basePath: "/api" }); // or every group, exactly as initializeRebaseBackend lays them out
13
+ * await backend.attachRealtime(server); // the websocket
14
+ * backend.start(); // cron, job workers, sweeps, the metrics sampler, the RLS audit
15
+ * await backend.shutdown();
16
+ * ```
17
+ *
18
+ * `initializeRebaseBackend` (`../init.ts`) is those calls in that order, and
19
+ * nothing else.
20
+ *
21
+ * @module
22
+ */
23
+ import { BackendBootstrapper, BootstrappedAuth, DatabaseAdapter, DataDriver, HealthCheckResult, RealtimeProvider, type CollectionCallbacks, type RebaseServerClient } from "@rebasepro/types";
24
+ import type { Server } from "node:http";
25
+ import { Hono } from "hono";
26
+ import { BackendCollectionRegistry } from "../collections/BackendCollectionRegistry.js";
27
+ import { DriverRegistry } from "../services/driver-registry.js";
28
+ import type { HonoEnv } from "../api/types.js";
29
+ import { StorageController, StorageRegistry } from "../storage/index.js";
30
+ import { type VerifiedCredential } from "../auth/verify-credential.js";
31
+ import type { HistoryService } from "../history/history-routes.js";
32
+ import type { CronScheduler } from "../cron/index.js";
33
+ import type { JobQueue } from "../jobs/index.js";
34
+ import type { MetricsHistory } from "../metrics/history-recorder.js";
35
+ import type { RebaseBackendOptions } from "./options.js";
36
+ import type { BackendServices } from "./services.js";
37
+ /**
38
+ * A backend's HTTP surface, one route group at a time.
39
+ *
40
+ * Each group is a self-contained Hono sub-app: it carries the middleware it
41
+ * needs — the auth context that puts `user`, `driver` and `scopes` on the
42
+ * request, its rate limits, its admin gate — so it answers the same wherever it
43
+ * is mounted:
44
+ *
45
+ * ```ts
46
+ * app.route("/v2/data", backend.routes.data());
47
+ * app.route("/hooks", backend.routes.functions()); // getDriver(c) and getUser(c) work inside
48
+ * ```
49
+ *
50
+ * What a group does not carry is the process-wide HTTP hygiene `mount()`
51
+ * installs ahead of every group — request ids, the global body limit,
52
+ * compression, CSRF, the request log and the JSON envelope for errors and
53
+ * unmatched paths. An application mounting groups one by one adds its own
54
+ * (`hono/body-limit`, `app.onError(errorHandler)`).
55
+ *
56
+ * Every call returns a fresh router over the same handlers, so a group can be
57
+ * mounted in several places. Asking for a group whose surface is off
58
+ * (`surfaces: { data: false }`) throws: that backend never built it.
59
+ */
60
+ export interface RebaseRoutes {
61
+ /**
62
+ * The sign-in surface, normally at `/api/auth`: registration, sessions,
63
+ * OAuth, password reset, `GET /config` (what the login screen offers),
64
+ * the caller's own API keys (`/keys`) and the scope catalogue (`/scopes`).
65
+ */
66
+ auth(): Hono<HonoEnv>;
67
+ /**
68
+ * The data API, normally at `/api/data`: the collections' REST routes and
69
+ * their history, behind authentication, RLS scoping and rate limits.
70
+ */
71
+ data(): Hono<HonoEnv>;
72
+ /**
73
+ * File storage, normally at `/api/storage` — or, with no storage
74
+ * configured, the router that says so (`STORAGE_NOT_CONFIGURED`).
75
+ */
76
+ storage(): Hono<HonoEnv>;
77
+ /**
78
+ * Custom functions, normally at `/api/functions` — or the forwarder to the
79
+ * process that serves them, when this one has a `functionsUpstream`.
80
+ */
81
+ functions(): Hono<HonoEnv>;
82
+ /**
83
+ * Every admin-gated surface, normally at `/api/admin`: users and roles,
84
+ * service API keys, live schema editing and the schema editor, storage
85
+ * maintenance, backups, the RLS audit, SQL export, the development
86
+ * mailbox and the log reader. Cron's admin surface is {@link cron}.
87
+ */
88
+ admin(): Hono<HonoEnv>;
89
+ /** The cron admin surface — list, trigger, read runs — normally at `/api/admin/cron`. */
90
+ cron(): Hono<HonoEnv>;
91
+ /** The project contract a generated SDK reads, normally at `/api/meta`. */
92
+ meta(): Hono<HonoEnv>;
93
+ }
94
+ /** Where {@link RebaseBackend.mount} puts the backend. */
95
+ export interface BackendMountOptions {
96
+ /** The prefix every group is mounted under. Default `/api`. */
97
+ basePath?: string;
98
+ }
99
+ /** How {@link RebaseBackend.attachRealtime} attaches the websocket. */
100
+ export interface AttachRealtimeOptions {
101
+ /**
102
+ * The URL path the socket accepts upgrades on. The SDK dials its `baseUrl`
103
+ * with no extra segment while HTTP goes to `${baseUrl}/api`, so the default
104
+ * is the base path given to `mount()` with a trailing `/api` removed —
105
+ * `/api` → `/`, `/x/api` → `/x` — and `/` when nothing was mounted. An
106
+ * upgrade to any other path is left for the server's other listeners.
107
+ */
108
+ path?: string;
109
+ }
110
+ /**
111
+ * A backend built by {@link createRebaseBackend}: everything constructed,
112
+ * nothing mounted, nothing started.
113
+ */
114
+ export interface RebaseBackend {
115
+ /**
116
+ * The in-process client — what `rebase` from `@rebasepro/server` is inside
117
+ * every request, cron run and job this backend serves (and everywhere,
118
+ * once it is the process default: {@link setDefaultBackend}).
119
+ * `dataAsAdmin`, `sql`, `storage`, `bucket` and `email` call the drivers
120
+ * and controllers directly; `auth`, `admin`, `functions` and `cron` reach
121
+ * this backend's routes in-process, without an HTTP server and wherever
122
+ * (or whether) the application mounted them.
123
+ */
124
+ rebase: RebaseServerClient;
125
+ /**
126
+ * What this backend holds and its requests ask it for: its JWT keys
127
+ * (`services.jwt` signs and verifies its tokens), its access model, its
128
+ * API-key store, its rate limiters, its collections. Its own — another
129
+ * backend in the same process has others.
130
+ *
131
+ * ```ts
132
+ * const token = await backend.services.jwt.generateAccessToken("u1", ["editor"]);
133
+ * ```
134
+ */
135
+ services: BackendServices;
136
+ /**
137
+ * The identity and scopes behind a bearer credential — a session token or
138
+ * an `rk_` API key — as this backend verifies it, or null. For an app's
139
+ * own socket or tunnel.
140
+ */
141
+ verifyCredential(token: string): Promise<VerifiedCredential | null>;
142
+ driverRegistry: DriverRegistry;
143
+ driver: DataDriver;
144
+ realtimeServices: Record<string, RealtimeProvider>;
145
+ realtimeService: RealtimeProvider;
146
+ auth?: BootstrappedAuth;
147
+ history?: {
148
+ historyService: HistoryService;
149
+ };
150
+ /**
151
+ * Stored metrics for this deployment, when it can keep any.
152
+ *
153
+ * Absent on a driver with no SQL admin. The `/metrics/history` route turns
154
+ * that absence into a named 501 rather than an empty series, because an
155
+ * empty chart and "this deployment keeps no history" look identical.
156
+ */
157
+ metricsHistory?: MetricsHistory;
158
+ storageRegistry?: StorageRegistry;
159
+ storageController?: StorageController;
160
+ collectionRegistry: BackendCollectionRegistry;
161
+ /** The cron scheduler. Its jobs are registered; `start()` starts its timers. */
162
+ cronScheduler?: CronScheduler;
163
+ /**
164
+ * The durable job queue, when `jobs.enabled` is set and the driver can run
165
+ * SQL. Enqueueing works at once; `start()` starts the workers. Present on
166
+ * the instance because the *producers* live in application code:
167
+ *
168
+ * ```ts
169
+ * const dispatcher = new WebhookDispatcher({ jobQueue: backend.jobQueue });
170
+ * backend.jobQueue?.register(WEBHOOK_DELIVERY_TASK, ctx => dispatcher.deliverQueuedJob(ctx.payload, ctx));
171
+ * ```
172
+ */
173
+ jobQueue?: JobQueue;
174
+ /** The route groups, each mountable on its own. */
175
+ routes: RebaseRoutes;
176
+ /**
177
+ * The whole backend as one fetch handler, laid out as `mount()` lays it
178
+ * out at `/api`. For in-process calls, tests and scripts — no server, no
179
+ * port: `await backend.fetch(new Request("http://local/api/data/posts"))`.
180
+ */
181
+ fetch(request: Request): Promise<Response>;
182
+ /**
183
+ * Mount every group this backend serves onto `app` under `basePath`, with
184
+ * the process-wide middleware in front of them — exactly the layout
185
+ * `initializeRebaseBackend` produces, legacy aliases included. Call it
186
+ * before adding routes of your own under the same prefix.
187
+ */
188
+ mount(app: Hono<HonoEnv>, options?: BackendMountOptions): void;
189
+ /**
190
+ * Attach the realtime websocket to an HTTP server. Does nothing when the
191
+ * `realtime` surface is off or the driver offers no realtime. Once only.
192
+ */
193
+ attachRealtime(server: Server, options?: AttachRealtimeOptions): Promise<void>;
194
+ /**
195
+ * Start what runs on a timer: the cron scheduler, the job workers, the
196
+ * expired-token and history-retention sweeps, the metrics sampler and the
197
+ * scheduled RLS audit — each only where `ownership` gives it to this
198
+ * process. Idempotent.
199
+ */
200
+ start(): void;
201
+ /**
202
+ * Attach collection callbacks after the backend is built.
203
+ *
204
+ * Use this instead of mutating `collectionRegistry.get(slug).callbacks`.
205
+ * Every registry normalizes its collections through `{ ...c }`, so the
206
+ * backend registry and each driver's registry hold **separate copies** of
207
+ * the same collection — assigning callbacks to one is invisible to the
208
+ * driver that actually invokes them, and the hooks silently never fire.
209
+ * This writes to all of them.
210
+ *
211
+ * It REPLACES the callbacks the collection declared. To keep them, attach
212
+ * `composeCallbacks(declared, mine)`.
213
+ */
214
+ setCollectionCallbacks(slug: string, callbacks: CollectionCallbacks): void;
215
+ /**
216
+ * Deep health check that verifies database connectivity.
217
+ * Returns latency and component status.
218
+ */
219
+ healthCheck(): Promise<HealthCheckResult>;
220
+ /**
221
+ * Stop what `start()` started and `attachRealtime()` attached, waiting for
222
+ * work in flight (cron runs, jobs, `waitUntil()` tasks) within the budget.
223
+ *
224
+ * The HTTP server is the application's: it is closed — drained, then
225
+ * force-closed at the deadline — only when passed as `options.server`.
226
+ *
227
+ * @param timeoutMs - Maximum time (ms) to wait for drain before giving up
228
+ * (default: 15000). Pass 0 to wait without a deadline.
229
+ */
230
+ shutdown(timeoutMs?: number, options?: {
231
+ server?: Server;
232
+ }): Promise<void>;
233
+ }
234
+ /**
235
+ * Type guard to detect whether `database` is a `DatabaseAdapter`.
236
+ */
237
+ export declare function isDatabaseAdapter(db: unknown): db is DatabaseAdapter;
238
+ /**
239
+ * Present a `DatabaseAdapter` as a `BackendBootstrapper`.
240
+ *
241
+ * The `database` convenience path — one adapter, no explicit source keys —
242
+ * funnels through here. `adapterToBootstrapper` in `boot/driver.ts` does the
243
+ * same job for the multi-source path, and the two stay separate because only
244
+ * that one carries a registry id and a default flag; this path has exactly one
245
+ * driver and needs neither.
246
+ *
247
+ * Both wrappers rebuild the bootstrapper field by field, which means any
248
+ * capability nobody remembers to list is dropped in silence — no type error,
249
+ * because every one of them is optional, and no runtime error either, because
250
+ * the caller's own fallback for "driver does not implement this" is to skip.
251
+ * That is not hypothetical: `ensureCollectionSchema` was missing from both
252
+ * wrappers for months, so every managed tenant booted with no collection tables
253
+ * and 500'd on every data route. `bootstrapper-forwarding.test.ts` now asserts
254
+ * both wrappers pass through the whole optional surface.
255
+ */
256
+ export declare function wrapDatabaseAdapter(dbAdapter: DatabaseAdapter): BackendBootstrapper;
257
+ /**
258
+ * Build a backend: open the drivers, provision what `options.provision` allows,
259
+ * initialise auth, storage and the in-process client, and build every route
260
+ * group. Mounts nothing, starts no timer, adds no process listener — see
261
+ * {@link RebaseBackend} for the steps that do.
262
+ *
263
+ * Writes no process global either. The backend holds its own JWT keys, access
264
+ * model, API-key store, rate-limit counts and client (`backend.services`,
265
+ * `backend.rebase`), and code it runs — a function, a cron job, a hook — sees
266
+ * it as `rebase`. The module-level API (`rebase` outside any request,
267
+ * `verifyAccessToken`, `getAccessModel`, …) answers for the process default,
268
+ * which {@link setDefaultBackend} sets and `initializeRebaseBackend` sets for
269
+ * its backend. The one process-wide setting it touches is the log level, and
270
+ * only when `logging.level` is given: the logger is the process's.
271
+ */
272
+ export declare function createRebaseBackend(options: RebaseBackendOptions): Promise<RebaseBackend>;
273
+ /**
274
+ * Where the SDK's socket dials for an API mounted at `basePath`.
275
+ *
276
+ * The client sends HTTP to `${baseUrl}/api` and opens the socket at `baseUrl`
277
+ * itself, so the socket's path is the base path without its trailing `/api`:
278
+ * `/api` → `/`, `/x/api` → `/x`. Any other base path gives `/`, the default an
279
+ * SDK configured with that path as its `apiPath` dials.
280
+ */
281
+ export declare function realtimeSocketPath(basePath: string | undefined): string;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The process default: the backend the module-level API answers for.
3
+ *
4
+ * `rebase` outside any request, `verifyAccessToken` and the other JWT helpers,
5
+ * `getAccessModel`, `verifyCredential`, `extractUserFromToken`, the auth
6
+ * limiters exported as constants, and topics and queues published from code
7
+ * no backend is running. Each is one shallow step above the backend's own
8
+ * services, for code that has no backend in hand — a function file, a script,
9
+ * a test.
10
+ *
11
+ * `initializeRebaseBackend` installs its backend here. `createRebaseBackend`
12
+ * does not: a backend built by hand answers for itself
13
+ * (`backend.services`, `backend.rebase`) and sets nothing anyone else reads,
14
+ * so two of them in one process stay two. Make one of them the default when
15
+ * the process uses the module-level API.
16
+ *
17
+ * @module
18
+ */
19
+ import type { RebaseServerClient } from "@rebasepro/types";
20
+ import type { BackendServices } from "./services.js";
21
+ /**
22
+ * Make `backend` the process default: what `rebase`, the JWT helpers,
23
+ * `getAccessModel`, `verifyCredential` and topics and queues answer for
24
+ * outside any request a backend is serving.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const backend = await createRebaseBackend({ database, auth, collections, provision: true });
29
+ * setDefaultBackend(backend); // `import { rebase } from "@rebasepro/server"` is now this backend, everywhere
30
+ * ```
31
+ */
32
+ export declare function setDefaultBackend(backend: {
33
+ rebase: RebaseServerClient;
34
+ services: BackendServices;
35
+ }): void;