@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
package/dist/init.d.ts CHANGED
@@ -1,621 +1,30 @@
1
- import { AuthAdapter, BackendBootstrapper, BootstrappedAuth, DatabaseAdapter, DataDriver, CollectionCallbacks, AnyCollectionConfig, HealthCheckResult, HistoryConfig, InitializedDriver, RealtimeProvider } from "@rebasepro/types";
2
- import { BackendCollectionRegistry } from "./collections/BackendCollectionRegistry.js";
3
- import { DriverRegistry } from "./services/driver-registry.js";
4
- import { Server } from "http";
5
- import { Hono } from "hono";
6
- import { HonoEnv } from "./api/types.js";
7
- import type { SchemaEditPolicy, RemoteRepositoryConfig } from "./schema-edit/schema-edit-permissions.js";
8
1
  /**
9
- * Who may change this project's schema through a running backend.
2
+ * `initializeRebaseBackend` — the one-call boot, written as the public steps it
3
+ * stands for.
10
4
  *
11
- * An alias rather than its own shape: the policy is defined next to the code
12
- * that enforces it, and a second declaration here would be a second place for
13
- * it to drift.
5
+ * Everything it does is a call a reader can make themselves: build the backend
6
+ * (`createRebaseBackend`), make it the process default (`setDefaultBackend`),
7
+ * mount it on the application's Hono app, attach the realtime websocket to its
8
+ * server, start the timers. Copy the body and change
9
+ * one line — mount only the data API, put it under another path, leave the
10
+ * timers to another process — and you have the variant you need, built from
11
+ * the same pieces.
14
12
  *
15
- * @see `packages/server/src/schema-edit/schema-edit-permissions.ts`
13
+ * @module
16
14
  */
17
- export interface LiveSchemaConfig extends SchemaEditPolicy {
18
- /**
19
- * A repository to commit to when the source is not on this machine.
20
- *
21
- * A bundle deployment ships compiled output, so there is nothing for the
22
- * collection editor to rewrite locally. Configure this and the change is
23
- * read from the repository, rewritten, and committed back through the Git
24
- * Data API — no clone, no working tree.
25
- *
26
- * Absent, live schema editing needs the project mounted and says so.
27
- */
28
- repository?: RemoteRepositoryConfig;
29
- }
30
- import { type RlsAuditConfig } from "./rls-audit/index.js";
31
- import type { CaptchaConfig } from "./auth/captcha.js";
32
- import { type RuntimeOwnershipOptions, type RuntimeSurfaceOptions } from "./init/surfaces.js";
33
- import type { JwtSigningKeyConfig } from "./auth/jwt-keys.js";
34
- import type { JobQueueOptions } from "./jobs/types.js";
35
- import { BackendStorageConfig, StorageController, StorageRegistry } from "./storage/index.js";
36
- import { type DataRateLimitConfig } from "./auth/rate-limiter.js";
37
- import { EmailConfig } from "./email/index.js";
38
- import type { OAuthProvider } from "./auth/interfaces.js";
39
- import type { AuthHooks } from "./auth/auth-hooks.js";
40
- export interface RebaseAuthConfig {
41
- /**
42
- * The collection that represents auth users.
43
- *
44
- * When provided, this collection's underlying database table is used
45
- * for all auth operations (login, registration, password reset, etc.).
46
- *
47
- * Import the built-in default:
48
- * ```ts
49
- * import { defaultUsersCollection } from "@rebasepro/common";
50
- * auth: { collection: defaultUsersCollection, jwtSecret: "..." }
51
- * ```
52
- *
53
- * Or pass your own collection with the required auth fields
54
- * (email, passwordHash, displayName, etc.).
55
- */
56
- /**
57
- * Accepts a collection of any row type: `defineCollection` infers `M` from
58
- * the properties, and `CollectionConfig` is invariant in `M`, so a bare
59
- * `CollectionConfig` here rejects everything the builder returns.
60
- */
61
- collection?: AnyCollectionConfig;
62
- jwtSecret?: string;
63
- /**
64
- * Asymmetric keys for signing access tokens, newest first. Additive —
65
- * without them tokens stay HS256, exactly as before. With them, anyone can
66
- * verify a session from `/.well-known/jwks.json` without holding the key
67
- * that mints one, and a key can be rotated without signing users out.
68
- *
69
- * ```ts
70
- * auth: {
71
- * jwtSecret: process.env.JWT_SECRET,
72
- * signingKeys: [{ kid: "2026-08", privateKey: process.env.JWT_PRIVATE_KEY! }]
73
- * }
74
- * ```
75
- */
76
- signingKeys?: JwtSigningKeyConfig[];
77
- /** Which of {@link signingKeys} mints new tokens. Defaults to the first. */
78
- activeKid?: string;
79
- accessExpiresIn?: string;
80
- refreshExpiresIn?: string;
81
- requireAuth?: boolean;
82
- allowRegistration?: boolean;
83
- /**
84
- * Block self-registration outright — the hard kill switch.
85
- *
86
- * `allowRegistration: false` still admits the very first user on an empty
87
- * database, because otherwise a fresh deployment has no way to create its
88
- * own admin: `POST /admin/bootstrap` needs an authenticated caller. This
89
- * closes that window too, for operators who provision every account out of
90
- * band and never want a public first-come-first-admin race.
91
- *
92
- * With this set, an empty backend has no self-service path in at all —
93
- * create the first user with the CLI or a seed script.
94
- */
95
- disableSelfRegistration?: boolean;
96
- /**
97
- * Opt-in: allow `POST /auth/anonymous` to mint a user with no credentials.
98
- *
99
- * Off by default. Anonymous sign-in inserts a `users` row and assigns
100
- * `defaultRole` exactly as registration does, so leaving it always-on meant
101
- * a backend with `allowRegistration: false` and `disableSelfRegistration:
102
- * true` still handed out permanent accounts: `/auth/anonymous` for the row
103
- * and the session, then `/auth/anonymous/link` to put an email and password
104
- * on it. `disableSelfRegistration` overrides this key.
105
- */
106
- allowAnonymous?: boolean;
107
- /**
108
- * Opt-in: expose `POST /auth/find-user` so an authenticated user can resolve
109
- * an email address to a minimal public profile (`uid`, `displayName`,
110
- * `photoURL` only). This powers invite-by-email flows without a custom
111
- * admin server function. Off by default because it enables user enumeration
112
- * by any signed-in user. Available on the client as `auth.findUserByEmail`.
113
- */
114
- allowUserLookup?: boolean;
115
- /**
116
- * Bot protection on the auth routes that cost something to hit.
117
- *
118
- * Off unless `enabled`. Rate limiting bounds one caller; a botnet of a
119
- * thousand addresses sending one request each never touches a per-IP window,
120
- * and `/register` and `/forgot-password` both send mail — so the bill for an
121
- * unprotected form is paid in reputation on a sending domain.
122
- *
123
- * ```ts
124
- * auth: { captcha: { enabled: true, provider: "turnstile", secret: process.env.TURNSTILE_SECRET } }
125
- * ```
126
- *
127
- * Verification fails closed: if the provider cannot be reached, the request
128
- * is refused. See `auth/captcha.ts` for why that is the safe direction.
129
- */
130
- captcha?: CaptchaConfig;
131
- /**
132
- * A static secret key for server-to-server / script authentication.
133
- *
134
- * When a request includes `Authorization: Bearer <serviceKey>`, it is
135
- * granted admin-level access without JWT verification. This is the
136
- * Rebase equivalent of a Service Account key.
137
- *
138
- * Generate with: `node -e "logger.info(require('crypto').randomBytes(48).toString('base64'))"`
139
- *
140
- * Set via `REBASE_SERVICE_KEY` in your `.env`.
141
- * Must be at least 32 characters.
142
- */
143
- serviceKey?: string;
144
- email?: EmailConfig;
145
- google?: {
146
- clientId: string;
147
- clientSecret?: string;
148
- };
149
- linkedin?: {
150
- clientId: string;
151
- clientSecret: string;
152
- };
153
- github?: {
154
- clientId: string;
155
- clientSecret: string;
156
- };
157
- microsoft?: {
158
- clientId: string;
159
- clientSecret: string;
160
- tenantId?: string;
161
- };
162
- apple?: {
163
- clientId: string;
164
- teamId: string;
165
- keyId: string;
166
- privateKey: string;
167
- };
168
- facebook?: {
169
- clientId: string;
170
- clientSecret: string;
171
- };
172
- twitter?: {
173
- clientId: string;
174
- clientSecret: string;
175
- };
176
- discord?: {
177
- clientId: string;
178
- clientSecret: string;
179
- };
180
- gitlab?: {
181
- clientId: string;
182
- clientSecret: string;
183
- baseUrl?: string;
184
- };
185
- bitbucket?: {
186
- clientId: string;
187
- clientSecret: string;
188
- };
189
- slack?: {
190
- clientId: string;
191
- clientSecret: string;
192
- };
193
- spotify?: {
194
- clientId: string;
195
- clientSecret: string;
196
- };
197
- defaultRole?: string;
198
- /**
199
- * Canonical array of OAuth providers.
200
- *
201
- * This is the primary extension point for **all** OAuth integrations.
202
- * Each entry is an `OAuthProvider<unknown>` constructed via one of
203
- * the `create*Provider` factories exported from `@rebasepro/server`
204
- * (e.g. `createGoogleProvider`, `createGitHubProvider`).
205
- *
206
- * The named convenience fields above (`google`, `github`, etc.) are
207
- * automatically resolved into this array at startup. You can mix both
208
- * approaches; named fields and explicit entries are merged (named
209
- * fields are appended after explicit entries).
210
- *
211
- * @example
212
- * ```ts
213
- * import { createGoogleProvider } from "@rebasepro/server";
214
- *
215
- * auth: {
216
- * providers: [
217
- * createGoogleProvider({ clientId: "…", clientSecret: "…" }),
218
- * ],
219
- * }
220
- * ```
221
- */
222
- providers?: OAuthProvider<unknown>[];
223
- /**
224
- * Redirect URIs the OAuth sign-in routes will accept, for every provider.
225
- *
226
- * Left unset, the only check is the provider's own registered-URI match —
227
- * which authorises *every* URI registered on that OAuth client, including
228
- * the `localhost` entry someone added for development and the staging host
229
- * nobody removed. List the origins this backend actually serves to narrow
230
- * it. Compared on origin plus path; query, fragment and a trailing slash
231
- * are ignored.
232
- *
233
- * @example
234
- * ```ts
235
- * auth: { allowedRedirectUris: ["https://admin.example.com/"] }
236
- * ```
237
- */
238
- allowedRedirectUris?: string[];
239
- /**
240
- * Override specific parts of the built-in auth implementation.
241
- *
242
- * Each override replaces one piece of the default behavior while
243
- * keeping everything else intact. Unset overrides fall through
244
- * to the built-in defaults (scrypt passwords, standard validation, etc.).
245
- *
246
- * @example bcrypt passwords with a custom hash
247
- * ```ts
248
- * import bcrypt from "bcrypt";
249
- *
250
- * hooks: {
251
- * hashPassword: (pw) => bcrypt.hash(pw, 12),
252
- * verifyPassword: (pw, hash) => bcrypt.compare(pw, hash),
253
- * }
254
- * ```
255
- */
256
- hooks?: AuthHooks;
257
- /**
258
- * Enable magic link (passwordless email) authentication.
259
- * Requires email to be configured.
260
- */
261
- magicLink?: boolean;
262
- /**
263
- * Enable email one-time codes — six digits, typed in, instead of a link.
264
- *
265
- * A magic link opens the session on whichever device holds the mailbox,
266
- * which is the wrong device whenever those are two different machines: a
267
- * television, a terminal, a second browser. A code crosses that gap because
268
- * a person carries it.
269
- *
270
- * `POST /auth/otp` sends one; `POST /auth/otp/verify` trades it for a
271
- * session. Requires email to be configured, as magic link does.
272
- */
273
- emailOtp?: boolean;
274
- /**
275
- * Opt-in httpOnly cookie mode for refresh tokens.
276
- *
277
- * When set, the refresh token is delivered as an `httpOnly`, `Secure`,
278
- * `SameSite` cookie instead of in the JSON response body. This
279
- * prevents XSS from stealing the long-lived refresh token.
280
- *
281
- * The access token remains in the JSON body so the client can use it
282
- * in `Authorization: Bearer` headers for API calls.
283
- *
284
- * **Requires** `credentials: "include"` on client-side fetch calls to
285
- * auth endpoints, and CORS must allow credentials (no `origin: "*"`).
286
- */
287
- cookieAuth?: import("./auth/index.js").CookieAuthConfig;
288
- /**
289
- * Refuse password sign-in until the account's email address is verified,
290
- * and make registration confirm-first. Off by default.
291
- *
292
- * Off, `POST /auth/register` signs the new account in and mails it a
293
- * verification link; an address that already has an account is answered
294
- * `409 EMAIL_EXISTS`. On, register signs nobody in and answers the same
295
- * "check your inbox" whether or not the address has an account (an
296
- * unconfirmed one is mailed its link again), and `POST /auth/login`
297
- * answers `403 EMAIL_NOT_CONFIRMED` for an unverified account —
298
- * only once the password is right. Following the link with the password completes
299
- * the sign-up (`POST /auth/verify-email`).
300
- *
301
- * Needs email: the boot refuses it without, since nobody could ever
302
- * confirm. Set by `AUTH_REQUIRE_EMAIL_VERIFICATION=true`.
303
- */
304
- requireEmailVerification?: boolean;
305
- /**
306
- * How long a refresh token that was rotated away still mints a sibling of
307
- * the same session, in seconds. Default 10 (GoTrue's
308
- * `refresh_token_reuse_interval`): a client that lost a refresh answer —
309
- * a deploy, a suspended laptop, two tabs at once — is not signed out.
310
- */
311
- refreshTokenReuseIntervalSeconds?: number;
312
- /**
313
- * What a refresh token presented *after* that window does. Default
314
- * `"reject"`: the request is refused and logged, and the session stands,
315
- * because its live token is still good — which also means a thief who
316
- * refreshed first keeps it. `"revoke-session"` ends the whole sign-in on
317
- * such a replay, thief and owner alike, as GoTrue does: the owner signs
318
- * in again. Set by `AUTH_REFRESH_TOKEN_REUSE`; anything else fails the boot.
319
- */
320
- refreshTokenReuse?: "reject" | "revoke-session";
321
- /**
322
- * Let a magic-link or email-code request for an address with no account
323
- * create one — passwordless sign-up — while registration is open
324
- * (`allowRegistration`, not `disableSelfRegistration`). The account has no
325
- * password and is unverified until the link or code is used, which proves
326
- * the address and signs it in. Off by default, so those requests answer an
327
- * unknown address the same as a known one and create nothing. Set by
328
- * `AUTH_MAGIC_LINK_CREATES_USERS`.
329
- */
330
- magicLinkCreatesUsers?: boolean;
331
- }
332
- /** @see RebaseBackendConfig.baas */
333
- export interface BaasOptions {
334
- /**
335
- * What to do with introspected tables that have row-level security
336
- * disabled.
337
- *
338
- * Such a table carries no authorization model. Every authenticated request
339
- * runs as `rebase_user`, which is granted DML on the schema, so serving one
340
- * hands every row to every logged-in user — the API would be an open door
341
- * onto whatever the database happens to contain.
342
- *
343
- * - `"exclude"` (default) — do not serve it. Each excluded table is logged
344
- * with the SQL to protect it. Secure by default, consistent with the rest
345
- * of the driver, which fails a boot rather than serve unenforced requests.
346
- * - `"serve"` — serve it anyway. Only sensible when every caller is already
347
- * trusted, e.g. an internal service behind its own authorization.
348
- */
349
- unprotectedTables?: "exclude" | "serve";
350
- }
351
- export interface RebaseBackendConfig {
352
- /** Invariance again — see the note on `RebaseAuthConfig.collection`. */
353
- collections?: AnyCollectionConfig[];
354
- collectionsDir?: string;
15
+ import type { Server } from "node:http";
16
+ import type { Hono } from "hono";
17
+ import type { HonoEnv } from "./api/types.js";
18
+ import { type RebaseBackend } from "./backend/create-backend.js";
19
+ import type { RebaseBackendOptions } from "./backend/options.js";
20
+ /** What {@link initializeRebaseBackend} takes: a backend's options, and where to serve it. */
21
+ export interface RebaseBackendConfig extends Omit<RebaseBackendOptions, "provision"> {
22
+ /** The HTTP server the realtime websocket attaches to, and that `shutdown()` drains and closes. */
355
23
  server: Server;
24
+ /** The application's Hono app, which the backend is mounted onto. */
356
25
  app: Hono<HonoEnv>;
26
+ /** The prefix every route group is mounted under. Default `/api`. */
357
27
  basePath?: string;
358
- /**
359
- * Rate limiting for the data API, per caller: an API key by its id, a
360
- * signed-in user by their uid, anyone else by IP.
361
- *
362
- * On by default with loose limits — a floor against a runaway client, not a
363
- * quota. Counts live in this process's memory unless you pass a `store`, so
364
- * N replicas enforce N times the limit between them; set real quotas at a
365
- * proxy or supply a shared store if that matters. `{ enabled: false }` for
366
- * a deployment whose edge already does this.
367
- */
368
- rateLimit?: DataRateLimitConfig;
369
- /**
370
- * Force the schema-editor routes on or off.
371
- *
372
- * Defaults to enabled when `collectionsDir` is set, outside production, in
373
- * `cms` mode. The editor rewrites collection files, so it needs a
374
- * `collectionsDir` to write to.
375
- */
376
- schemaEditor?: boolean;
377
- /**
378
- * Live schema editing — changing the database and committing the change.
379
- *
380
- * Distinct from `schemaEditor`, which only rewrites collection source.
381
- * There is nothing to switch on here: the surface mounts whenever there is
382
- * a `collectionsDir`, and refuses in its own words when it cannot act. What
383
- * this configures is *who* may act.
384
- */
385
- liveSchema?: LiveSchemaConfig;
386
- /** Options that only apply when collections are derived from the database. */
387
- baas?: BaasOptions;
388
- /**
389
- * The pre-init handle to hand the boot-time schema hooks, when the caller
390
- * has one.
391
- *
392
- * Those hooks run before any driver is initialized, so there is no driver
393
- * result to pass — the bundle boot path can still supply a stand-in built
394
- * from the connection its coordinator opened, and does, because a driver
395
- * written before this was optional dereferences `driverResult.internals`
396
- * unconditionally. An application that constructed its own adapter has no
397
- * such handle to give (it gave the connection to the adapter instead), so it
398
- * leaves this unset and the adapter falls back to its own connection.
399
- *
400
- * @internal Set by `bootFromBundle`; not part of the ordinary configuration
401
- * surface.
402
- */
403
- provisioningDriverResult?: InitializedDriver;
404
- /**
405
- * Database bootstrappers, one per data source.
406
- *
407
- * The `id` of each is the key `collection.dataSource` routes against, so a
408
- * project with several databases passes several — `initializeDataSources`
409
- * builds them from the declared graph. The list of *declared* sources is no
410
- * longer an option here: it is read from the resource graph, which is the
411
- * one place a project declares them.
412
- *
413
- * @internal Built by `initializeDataSources` from the declared resource graph. Pass `database` instead.
414
- */
415
- bootstrappers?: BackendBootstrapper[];
416
- /**
417
- * Database adapter.
418
- *
419
- * When set, this takes precedence over `bootstrappers`.
420
- *
421
- * @example
422
- * ```ts
423
- * import { createPostgresAdapter } from "@rebasepro/server-postgres";
424
- * database: createPostgresAdapter({ connection: db, schema }),
425
- * ```
426
- */
427
- database?: DatabaseAdapter;
428
- logging?: {
429
- level?: "error" | "warn" | "info" | "debug";
430
- };
431
- /**
432
- * Authentication configuration.
433
- *
434
- * Accepts **either**:
435
- * - `RebaseAuthConfig` — built-in configuration
436
- * - `AuthAdapter` — pluggable adapter for external auth (Clerk, Auth0, etc.)
437
- *
438
- * When a plain config object is provided, the built-in adapter is created
439
- * automatically from the bootstrapper's `initializeAuth()` result.
440
- */
441
- auth?: RebaseAuthConfig | AuthAdapter;
442
- /**
443
- * Storage configuration. Accepts:
444
- *
445
- * - A `BackendStorageConfig` object (`{ type: 'local' | 's3' | 'gcs', ... }`)
446
- * - A `StorageController` instance (for custom providers like Azure, etc.)
447
- * - A `Record<string, ...>` of either, for multi-backend setups
448
- */
449
- storage?: BackendStorageConfig | StorageController | Record<string, BackendStorageConfig | StorageController>;
450
- /**
451
- * Declared storage sources. Drives the client-side StorageSourceRegistry
452
- * and the transport distinction (server vs direct).
453
- *
454
- * Server-backed sources are auto-derived from the `storage` map — you
455
- * only need explicit entries for "direct" transport sources (e.g.
456
- * external storage) that the backend does not proxy.
457
- */
458
- /**
459
- * Per-object access control for storage — the analogue of a collection's
460
- * security rules, and the thing `requireAuth` / `publicRead` cannot
461
- * express because they are global switches.
462
- *
463
- * Called after authentication on every storage route with the key, bucket,
464
- * operation (`read` / `write` / `delete` / `list`) and resolved user.
465
- * Return false to deny with a 403; throwing denies too.
466
- *
467
- * ```ts
468
- * storageAuthorize: async ({ key, user, operation }) => {
469
- * if (!user) return false;
470
- * const [ownerId] = key.split("/");
471
- * return ownerId === user.uid || operation === "read";
472
- * }
473
- * ```
474
- *
475
- * Without it, any authenticated caller may read any key they can name, so
476
- * multi-tenant apps should treat this as required rather than optional.
477
- *
478
- * In production, storage refuses to boot unless one of `storageAuthorize`,
479
- * {@link storagePublicRead}, or {@link storageInsecureAllowAnyAuthenticated}
480
- * is set — see `assertStorageAccessControlConfigured`.
481
- */
482
- storageAuthorize?: import("./storage/types.js").StorageAuthorize;
483
- /**
484
- * Per-object access control, declared rather than coded.
485
- *
486
- * Storage is not under row-level security, so without this or
487
- * {@link storageAuthorize} any authenticated caller may read, overwrite,
488
- * delete or list any key they can name — the keys share one flat namespace.
489
- *
490
- * ```ts
491
- * storagePolicies: [
492
- * { path: "public/**", operations: ["read"], allow: "public" },
493
- * { path: "users/:uid/**", allow: ({ params, user }) => user?.uid === params.uid }
494
- * ]
495
- * ```
496
- *
497
- * **A key matched by no policy is refused.** Every widening is an explicit
498
- * line, and a mistake here denies rather than grants.
499
- *
500
- * `storageAuthorize` still runs when no policy matched, so ownership that
501
- * lives in a row — which no path pattern can express — stays expressible.
502
- * Neither can narrow the other: each only says yes.
503
- *
504
- * Satisfies the production boot guard on its own.
505
- */
506
- storagePolicies?: import("./storage/policies.js").StoragePolicy[];
507
- /**
508
- * Allow unauthenticated read access to stored files (default: false).
509
- *
510
- * Set this only when the bucket is genuinely a public, read-only CDN.
511
- * Because it is a deliberate statement that reads are public, it also
512
- * satisfies the production storage boot guard (see {@link storageAuthorize}).
513
- *
514
- * On its own — no {@link storagePolicies}, no {@link storageAuthorize} — it
515
- * means public READ and nothing else: writes, deletes and listings are
516
- * admin-only. The sentence "writes still require authentication" used to be
517
- * the whole of it, and it was not enough, because the deployment this flag
518
- * is for is the public site, which commonly runs with `requireAuth` off —
519
- * so "requires authentication" required nothing. A deployment that wants
520
- * anonymous or per-user writes says where, with a policy or a hook.
521
- */
522
- storagePublicRead?: boolean;
523
- /**
524
- * Keep derived image renditions — the results of `?width=`, `?format=` and
525
- * friends — in the storage source rather than only in this process's
526
- * memory.
527
- *
528
- * Without it a transform is recomputed by every instance, and again by all
529
- * of them after every deploy: the cache is per-process and does not survive
530
- * a restart. With it, the first instance to compute a variant writes it
531
- * under a reserved prefix in the same bucket as the source, and every
532
- * instance reads it from there.
533
- *
534
- * ```ts
535
- * storageRenditionCache: { enabled: true }
536
- * ```
537
- *
538
- * Off by default because turning it on makes a `GET` write to your bucket:
539
- * it costs per object, and on read-only credentials it cannot work. When a
540
- * write does fail the request still succeeds — the transform is served from
541
- * memory and the failure is logged once.
542
- *
543
- * Renditions are keyed by the source object's version, so replacing an
544
- * image serves the new one immediately; the superseded rendition is left
545
- * behind. Set a lifecycle rule on `_rebase/renditions/` to collect them.
546
- */
547
- storageRenditionCache?: import("./storage/rendition-cache.js").RenditionCacheConfig;
548
- /**
549
- * How long a private file's download URL works, in seconds — the lifetime
550
- * of the token `GET /storage/metadata/*` mints. Default 300; at most a week.
551
- * `STORAGE_DOWNLOAD_TOKEN_TTL` on a bundle deployment.
552
- *
553
- * Five minutes renders a page; a private `<video>` that plays longer asks
554
- * for its next range with an expired token. The token travels in the URL,
555
- * which is the reason for the ceiling: an object that must be linked
556
- * indefinitely belongs under the public prefix instead.
557
- */
558
- storageDownloadTokenTtl?: number;
559
- /**
560
- * Run something when an object lands, or when one goes.
561
- *
562
- * Every other write in the product can be reacted to — a row has
563
- * `beforeSave` and `afterSave`, a schedule has a cron job — and an upload
564
- * had nothing, so the work an upload implies had to be done by the client
565
- * after the upload returned, which means it is not done at all when the
566
- * client goes away between the two calls.
567
- *
568
- * ```ts
569
- * storageTriggers: [
570
- * {
571
- * path: "uploads/:uid/**",
572
- * events: ["finalize"],
573
- * handler: async ({ key, params }) => { … }
574
- * }
575
- * ]
576
- * ```
577
- *
578
- * Same pattern language as {@link storagePolicies}. Fires for the
579
- * multipart and resumable paths alike, after the object is durably
580
- * written, and never for internal writes such as the image-rendition cache.
581
- * Handlers are awaited and a throwing one is logged rather than failing the
582
- * request — the object is already stored, so telling the client otherwise
583
- * would invite it to repeat a write that succeeded. Enqueue a job for
584
- * anything that must happen or is slow.
585
- */
586
- storageTriggers?: import("./storage/triggers.js").StorageTrigger[];
587
- /**
588
- * Opt out of the storage access-control boot guard, keeping the legacy
589
- * behaviour where **any** authenticated user can read, overwrite, delete or
590
- * list **any** key (storage keys share one flat namespace and are not under
591
- * RLS).
592
- *
593
- * Only safe for single-tenant apps where every signed-in user is trusted
594
- * with every file. Multi-tenant apps must use `storageAuthorize` instead.
595
- * Without one of these, storage refuses to boot in production.
596
- */
597
- storageInsecureAllowAnyAuthenticated?: boolean;
598
- /**
599
- * Entity history / audit-log configuration.
600
- *
601
- * - `true` — enable history with default settings
602
- * - `{ retention?: number }` — enable with optional retention period (days)
603
- */
604
- history?: HistoryConfig;
605
- enableSwagger?: boolean;
606
- /**
607
- * Which HTTP surfaces this process mounts. Every one, unless named.
608
- *
609
- * The point of naming a subset is to run the same bundle as several
610
- * cooperating processes — one answering `/api/data`, another `/api/functions`
611
- * — so a custom function that pins the event loop does not do it in the
612
- * process serving the data API. `bootFromBundle` derives this from
613
- * `REBASE_ROLE`; passing it directly is for tests and for embedders.
614
- *
615
- * Omitting it, or naming only some surfaces, leaves the rest mounted. That
616
- * default is deliberate: a surface added in a later version is served by
617
- * every existing deployment without anyone editing a list.
618
- */
619
28
  /**
620
29
  * Whether this process provisions the collection schema and its RLS
621
30
  * policies at boot. Default `true` — what every deployment does today.
@@ -625,268 +34,43 @@ export interface RebaseBackendConfig {
625
34
  * provision the same schema is a state nobody designed. `bootFromBundle`
626
35
  * derives it from `REBASE_ROLE`.
627
36
  *
37
+ * Becomes `createRebaseBackend`'s `provision`: everything when `true`; the
38
+ * framework's own tables and role but neither the collection schema nor
39
+ * change capture when `false`.
40
+ *
628
41
  * @internal Derived from `REBASE_ROLE` by `bootFromBundle`.
629
42
  */
630
43
  provisionSchema?: boolean;
631
- surfaces?: RuntimeSurfaceOptions;
632
- /**
633
- * Which background singletons this process runs. All of them, unless named.
634
- *
635
- * Separate from {@link surfaces} because "which URLs answer" and "which
636
- * timers fire" are independent: a process can serve the cron *admin* surface
637
- * without being the one that fires the jobs, and vice versa.
638
- *
639
- * Neither is a correctness control — the cron scheduler claims each
640
- * `(job, slot)` pair and the job store claims rows `FOR UPDATE SKIP LOCKED`,
641
- * so running several of each is already safe. It is about not handing
642
- * scheduled work to a process whose replica count is a scaling decision.
643
- */
644
- ownership?: RuntimeOwnershipOptions;
645
- functionsDir?: string;
646
- /**
647
- * Per-request ceiling for `/api/functions/*`, in milliseconds.
648
- *
649
- * Custom functions run code the framework did not write, and nothing else
650
- * in the stack bounds it. Defaults to 30000 (or
651
- * `REBASE_FUNCTIONS_TIMEOUT_MS`). `0` disables the ceiling — for a function
652
- * that legitimately runs long, or a deployment whose proxy already imposes
653
- * one. Timed-out requests answer 504; the handler itself cannot be
654
- * cancelled, so give outbound calls an `AbortSignal`.
655
- */
656
- functionsTimeoutMs?: number;
657
- /**
658
- * Serve only some of the bundle's functions.
659
- *
660
- * How one expensive function gets its own replica count and its own blast
661
- * radius without its code moving anywhere. `bootFromBundle` fills this from
662
- * `REBASE_FUNCTIONS_ONLY` / `REBASE_FUNCTIONS_EXCLUDE`.
663
- *
664
- * A name that is not in the bundle fails the boot — see `selectFunctions`.
665
- *
666
- * @internal Filled from `REBASE_FUNCTIONS_ONLY` / `REBASE_FUNCTIONS_EXCLUDE`.
667
- */
668
- functionsSelection?: import("./functions/selection.js").FunctionSelection;
669
- /**
670
- * Forward `/api/functions/*` to another process instead of serving it here.
671
- *
672
- * Only consulted when the `functions` surface is off — a process that serves
673
- * them has nothing to forward. `bootFromBundle` fills this from
674
- * `REBASE_FUNCTIONS_UPSTREAM` on the `api` role.
675
- *
676
- * @internal Filled from `REBASE_FUNCTIONS_UPSTREAM` on the `api` role.
677
- */
678
- functionsUpstream?: string;
679
- cronsDir?: string;
680
- /**
681
- * Enable/disable database persistence for cron job execution logs.
682
- * When set to false, cron jobs will run but logs will not be persisted to the database.
683
- * Default: true.
684
- */
685
- cronPersistence?: boolean;
686
- /**
687
- * The durable job queue: background work that survives a restart.
688
- *
689
- * Off unless `enabled` is set, because a worker polls the database forever
690
- * and that is not a default anyone chose. Requires a driver that can run
691
- * SQL; on one that cannot, the queue is unavailable and callers are told
692
- * so at boot rather than at the first enqueue.
693
- *
694
- * ```ts
695
- * jobs: {
696
- * enabled: true,
697
- * tasks: {
698
- * "send-welcome": async ({ payload }) => { await sendEmail(payload.email); }
699
- * }
700
- * }
701
- * ```
702
- */
703
- jobs?: JobQueueOptions;
704
- /**
705
- * Maximum request body size in bytes for API routes (default: 10MB).
706
- * Set to 0 to disable the global limit entirely.
707
- *
708
- * Note: `POST /storage/upload` is not under this limit. It is capped by the
709
- * storage config's `maxFileSize` instead (default: 50MB), whether that is
710
- * larger or smaller than this. Every other route stays under this limit,
711
- * the rest of `/storage` included. A TUS upload is many requests, so this
712
- * caps each chunk (`PATCH /storage/tus/:id`), and `maxFileSize` caps the
713
- * whole file.
714
- */
715
- maxBodySize?: number;
716
- /**
717
- * Response compression for API routes. **Enabled by default.**
718
- *
719
- * Compresses responses with gzip/deflate, negotiated from the request's
720
- * `Accept-Encoding`. Bodies that are already compressed (images, video),
721
- * streamed (`text/event-stream`), or explicitly marked
722
- * `Cache-Control: no-transform` are left untouched, so this is safe to
723
- * leave on — a large JSON list response typically drops by ~20x.
724
- *
725
- * Set to `false` when something in front of the app already compresses
726
- * (nginx, Cloudflare, or another reverse proxy / load balancer), to avoid
727
- * paying for it twice.
728
- */
729
- compression?: boolean;
730
- /**
731
- * CSRF protection configuration. **Opt-in** — disabled by default.
732
- *
733
- * BaaS APIs are consumed by mobile apps, SPAs on different domains,
734
- * and CLI tools, so CSRF is intentionally not enabled unless you
735
- * explicitly configure it with allowed origins.
736
- *
737
- * @example
738
- * ```ts
739
- * csrf: { origin: ["https://myapp.com", "https://admin.myapp.com"] }
740
- * ```
741
- */
742
- csrf?: {
743
- /** Allowed origins for CSRF validation. */
744
- origin: string | string[] | ((origin: string) => boolean);
745
- };
746
- /**
747
- * Global lifecycle callbacks applied to every collection.
748
- *
749
- * Same type as per-collection `callbacks` — fires on **every** data path
750
- * (REST API, WebSocket / realtime, server-side `rebase.dataAsAdmin`).
751
- *
752
- * Execution order: global callbacks → collection callbacks → property callbacks.
753
- *
754
- * @example
755
- * ```ts
756
- * callbacks: {
757
- * afterRead({ row, collection }) {
758
- * console.log(`Read ${collection.slug}/${row.id}`);
759
- * return row;
760
- * }
761
- * }
762
- * ```
763
- */
764
- callbacks?: CollectionCallbacks;
765
- /**
766
- * Declare that this application installs its own CORS middleware.
767
- *
768
- * Suppresses the "no CORS configuration detected" warning, which exists for
769
- * hand-wired backends that genuinely have no origin policy.
770
- *
771
- * @internal Set by the boot path that installs CORS itself.
772
- */
773
- corsHandled?: boolean;
774
- /**
775
- * The schema version this deployment serves, as recorded when it was built.
776
- *
777
- * Published by the contract endpoint so a client generated elsewhere can
778
- * tell whether it is current. Leave unset and the runtime computes one from
779
- * the live collections — correct, but it means the value moves whenever the
780
- * collections do, which is exactly right for `baas` mode and slightly less
781
- * useful for a built bundle that already knows its own answer.
782
- */
783
- schemaVersion?: string;
784
- /**
785
- * Runtime version reported by the contract endpoint. Informational.
786
- *
787
- * @internal Read from the bundle manifest by `bootFromBundle`.
788
- */
789
- runtimeVersion?: string;
790
- /**
791
- * Periodic row-level-security audit of the database this server uses.
792
- *
793
- * Off unless `enabled` is set. `@rebasepro/rls-check` is thorough and
794
- * read-only, and the reason nobody runs it is that it is a command — so this
795
- * runs it on a timer and serves the result at `GET /api/admin/rls-audit`.
796
- *
797
- * Needs the optional peer `@rebasepro/rls-check`, which is not a hard
798
- * dependency because it carries a Postgres driver and this package is
799
- * engine-agnostic.
800
- */
801
- rlsAudit?: RlsAuditConfig;
802
44
  }
803
- export { isAuthAdapter } from "./auth/require-auth.js";
804
45
  /**
805
- * Type guard to detect whether `database` is a `DatabaseAdapter`.
46
+ * The backend {@link initializeRebaseBackend} returns: a {@link RebaseBackend}
47
+ * that is already mounted, attached and started, whose `shutdown()` also
48
+ * drains and closes the server it was given.
806
49
  */
807
- export declare function isDatabaseAdapter(db: unknown): db is DatabaseAdapter;
808
- export interface RebaseBackendInstance {
809
- driverRegistry: DriverRegistry;
810
- driver: DataDriver;
811
- realtimeServices: Record<string, RealtimeProvider>;
812
- realtimeService: RealtimeProvider;
813
- auth?: BootstrappedAuth;
814
- history?: {
815
- historyService: import("./history/history-routes.js").HistoryService;
816
- };
817
- /**
818
- * Stored metrics for this deployment, when it can keep any.
819
- *
820
- * Absent on a driver with no SQL admin. The `/metrics/history` route turns
821
- * that absence into a named 501 rather than an empty series, because an
822
- * empty chart and "this deployment keeps no history" look identical.
823
- */
824
- metricsHistory?: import("./metrics/history-recorder.js").MetricsHistory;
825
- storageRegistry?: StorageRegistry;
826
- storageController?: StorageController;
827
- collectionRegistry: BackendCollectionRegistry;
828
- cronScheduler?: import("./cron/index.js").CronScheduler;
829
- /**
830
- * The durable job queue, when `jobs.enabled` is set and the driver can run
831
- * SQL. Present on the instance because the *producers* live in application
832
- * code — a `WebhookDispatcher` is constructed by the app, so this is what
833
- * it gets handed:
834
- *
835
- * ```ts
836
- * const { jobQueue } = await initializeRebaseBackend({ jobs: { enabled: true } });
837
- * const dispatcher = new WebhookDispatcher({ jobQueue });
838
- * jobQueue?.register(WEBHOOK_DELIVERY_TASK, ctx => dispatcher.deliverQueuedJob(ctx.payload));
839
- * ```
840
- */
841
- jobQueue?: import("./jobs/index.js").JobQueue;
842
- /**
843
- * Attach collection callbacks AFTER initialization.
844
- *
845
- * Use this instead of mutating `collectionRegistry.get(slug).callbacks`.
846
- * Every registry normalizes its collections through `{ ...c }`, so the
847
- * backend registry and each driver's registry hold **separate copies** of
848
- * the same collection — assigning callbacks to one is invisible to the
849
- * driver that actually invokes them, and the hooks silently never fire.
850
- * This writes to all of them.
851
- *
852
- * (Assignment, not `Object.defineProperty`: the driver resolves callbacks
853
- * with a spread, which copies only enumerable properties, and
854
- * defineProperty defaults `enumerable` to false.)
855
- */
856
- setCollectionCallbacks(slug: string, callbacks: import("@rebasepro/types").CollectionCallbacks): void;
857
- /**
858
- * Deep health check that verifies database connectivity.
859
- * Returns latency and component status.
860
- */
861
- healthCheck(): Promise<HealthCheckResult>;
862
- /**
863
- * Graceful shutdown helper for the BaaS instance.
864
- * Stops the cron scheduler and closes the HTTP server, allowing
865
- * in-flight requests to drain within the given timeout.
866
- *
867
- * @param timeoutMs - Maximum time (ms) to wait for drain before force-exit (default: 15000).
868
- * Pass 0 to skip the force-exit timer (useful in tests).
869
- */
870
- shutdown(timeoutMs?: number): Promise<void>;
871
- }
50
+ export type RebaseBackendInstance = RebaseBackend;
872
51
  /**
873
- * Present a `DatabaseAdapter` as a `BackendBootstrapper`.
52
+ * Build a backend and serve it: the process default, mounted on `config.app`
53
+ * under `config.basePath`, its websocket on `config.server`, its timers
54
+ * started.
55
+ *
56
+ * Exactly these steps, and nothing else:
57
+ *
58
+ * ```ts
59
+ * installUnhandledRejectionHandler();
60
+ * const backend = await createRebaseBackend({ ...options, provision: true });
61
+ * setDefaultBackend(backend);
62
+ * backend.mount(app, { basePath });
63
+ * await backend.attachRealtime(server);
64
+ * backend.start();
65
+ * ```
874
66
  *
875
- * The `config.database` convenience path — one adapter, no explicit source keys
876
- * — funnels through here. `adapterToBootstrapper` in `boot/driver.ts` does the
877
- * same job for the multi-source path, and the two stay separate because only
878
- * that one carries a registry id and a default flag; this path has exactly one
879
- * driver and needs neither.
67
+ * The default is what the module-level API answers for — `rebase` outside a
68
+ * request, `verifyAccessToken`, `getAccessModel`, `verifyCredential` — so code
69
+ * written against those keeps answering for the one backend this process
70
+ * serves.
880
71
  *
881
- * Extracted from the middle of `initializeRebaseBackend` so it can be tested.
882
- * Both wrappers rebuild the bootstrapper field by field, which means any
883
- * capability nobody remembers to list is dropped in silence — no type error,
884
- * because every one of them is optional, and no runtime error either, because
885
- * the caller's own fallback for "driver does not implement this" is to skip.
886
- * That is not hypothetical: `ensureCollectionSchema` was missing from both
887
- * wrappers for months, so every managed tenant booted with no collection tables
888
- * and 500'd on every data route. `bootstrapper-forwarding.test.ts` now asserts
889
- * both wrappers pass through the whole optional surface.
72
+ * No try/catch: an init error propagates to the caller. The app entry point
73
+ * (e.g. `startServer()`) should catch it and `process.exit(1)` — returning a
74
+ * fake instance hides critical failures and leads to silent data loss.
890
75
  */
891
- export declare function wrapDatabaseAdapter(dbAdapter: DatabaseAdapter): BackendBootstrapper;
892
76
  export declare function initializeRebaseBackend(config: RebaseBackendConfig): Promise<RebaseBackendInstance>;