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