@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.
- package/README.md +27 -2
- package/dist/GCSStorageController-fv6JAZ87.js +402 -0
- package/dist/GCSStorageController-fv6JAZ87.js.map +1 -0
- package/dist/S3StorageController-BNg-pXiY.js +480 -0
- package/dist/S3StorageController-BNg-pXiY.js.map +1 -0
- package/dist/api/mount-path.d.ts +30 -0
- package/dist/api/mount.d.ts +6 -13
- package/dist/api/rest/api-generator.d.ts +53 -1
- package/dist/api/rest/auth-collection-writes.d.ts +28 -21
- package/dist/api/rest/field-access-query.d.ts +28 -8
- package/dist/api/rest/field-ops.d.ts +19 -7
- package/dist/api/rest/query-body.d.ts +10 -0
- package/dist/api/rest/write-validation.d.ts +7 -5
- package/dist/api/schema-editor-routes.d.ts +10 -1
- package/dist/api/sql-export-routes.d.ts +8 -0
- package/dist/api/types.d.ts +35 -0
- package/dist/{ast-schema-editor-CWqS_sLJ.js → ast-schema-editor-mCeSC03x.js} +3 -3
- package/dist/{ast-schema-editor-CWqS_sLJ.js.map → ast-schema-editor-mCeSC03x.js.map} +1 -1
- package/dist/auth/access.d.ts +57 -8
- package/dist/auth/admin-user-ops.d.ts +24 -2
- package/dist/auth/admin-users-route.d.ts +1 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +10 -3
- package/dist/auth/api-keys/api-key-routes.d.ts +4 -1
- package/dist/auth/builtin-auth-adapter.d.ts +13 -0
- package/dist/auth/email-change-routes.d.ts +3 -0
- package/dist/auth/expired-token-sweep.d.ts +5 -1
- package/dist/auth/interfaces.d.ts +10 -0
- package/dist/auth/jwks-routes.d.ts +5 -1
- package/dist/auth/jwt-crypto.d.ts +7 -0
- package/dist/auth/jwt-keys.d.ts +4 -0
- package/dist/auth/jwt.d.ts +282 -166
- package/dist/auth/magic-link-routes.d.ts +3 -0
- package/dist/auth/mfa-crypto.d.ts +9 -3
- package/dist/auth/mfa-gate.d.ts +4 -2
- package/dist/auth/mfa-routes.d.ts +6 -0
- package/dist/auth/middleware.d.ts +9 -2
- package/dist/auth/otp-routes.d.ts +3 -0
- package/dist/auth/rate-limiter.d.ts +61 -34
- package/dist/auth/registration-policy.d.ts +17 -0
- package/dist/auth/require-auth.d.ts +1 -1
- package/dist/auth/routes.d.ts +16 -0
- package/dist/auth/session-routes.d.ts +6 -0
- package/dist/auth/user-picking.d.ts +37 -0
- package/dist/auth/verify-credential.d.ts +25 -4
- package/dist/{auth-DMLngxn_.js → auth-GnONzteu.js} +2869 -1871
- package/dist/auth-GnONzteu.js.map +1 -0
- package/dist/auth_adapter-CIItCTc8.js +21 -0
- package/dist/auth_adapter-CIItCTc8.js.map +1 -0
- package/dist/backend/create-backend.d.ts +281 -0
- package/dist/backend/default-backend.d.ts +35 -0
- package/dist/backend/options.d.ts +912 -0
- package/dist/backend/scope.d.ts +23 -0
- package/dist/backend/services.d.ts +54 -0
- package/dist/backend-DTAOsLQc.js.map +1 -1
- package/dist/backup/backup-common.d.ts +14 -3
- package/dist/backup/backup-routes.d.ts +23 -2
- package/dist/backup/backup-storage.d.ts +3 -1
- package/dist/backup/index.d.ts +3 -1
- package/dist/{backup-CN0s50D2.js → backup-BnvkSLHi.js} +176 -35
- package/dist/backup-BnvkSLHi.js.map +1 -0
- package/dist/boot/boot.d.ts +2 -2
- package/dist/boot/driver.d.ts +3 -0
- package/dist/boot/env.d.ts +9 -2
- package/dist/boot/http-server.d.ts +22 -0
- package/dist/boot/options.d.ts +16 -1
- package/dist/boot/provision.d.ts +4 -2
- package/dist/boot/runtime-cors.d.ts +17 -0
- package/dist/boot/sources.d.ts +2 -0
- package/dist/boot/static-routing.d.ts +9 -0
- package/dist/{contract-routes-fz8i4pxs.js → contract-routes-Ey6ftnKH.js} +4 -20
- package/dist/contract-routes-Ey6ftnKH.js.map +1 -0
- package/dist/cron/cron-loader.d.ts +77 -18
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +69 -3
- package/dist/cron/cron-store.d.ts +33 -5
- package/dist/cron/define-cron.d.ts +6 -2
- package/dist/cron/index.d.ts +3 -3
- package/dist/cron-loader-B-u0snqq.js +202 -0
- package/dist/cron-loader-B-u0snqq.js.map +1 -0
- package/dist/{cron-routes-Bc-SB0Se.js → cron-routes-OD6BstUj.js} +27 -4
- package/dist/cron-routes-OD6BstUj.js.map +1 -0
- package/dist/{cron-scheduler-CYQgco86.js → cron-scheduler-idtLPoN-.js} +299 -33
- package/dist/cron-scheduler-idtLPoN-.js.map +1 -0
- package/dist/cron-store-CZ30ZBi-.js +577 -0
- package/dist/cron-store-CZ30ZBi-.js.map +1 -0
- package/dist/email/smtp-email-service.d.ts +23 -0
- package/dist/email/types.d.ts +18 -0
- package/dist/env.d.ts +0 -37
- package/dist/filter-dialect-O1HK6Nvr.js +1628 -0
- package/dist/filter-dialect-O1HK6Nvr.js.map +1 -0
- package/dist/{function-loader-D7o5Epjj.js → function-loader-iKhOZatS.js} +68 -38
- package/dist/function-loader-iKhOZatS.js.map +1 -0
- package/dist/{function-routes-CaNG4waN.js → function-routes-j9TwpvQe.js} +5 -4
- package/dist/function-routes-j9TwpvQe.js.map +1 -0
- package/dist/functions/context.d.ts +9 -0
- package/dist/functions/define-function.d.ts +10 -6
- package/dist/functions/function-loader.d.ts +75 -18
- package/dist/functions/function-routes.d.ts +2 -1
- package/dist/functions/index.d.ts +5 -4
- package/dist/functions/index.js +42 -13
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/internal.d.ts +2 -2
- package/dist/functions/proxy.d.ts +6 -2
- package/dist/functions/request-timeout.d.ts +7 -5
- package/dist/history/history-routes.d.ts +9 -0
- package/dist/history/retention-sweep.d.ts +81 -0
- package/dist/{history-recorder-Nr8zLvoU.js → history-recorder-BfWRwmPy.js} +3 -3
- package/dist/{history-recorder-Nr8zLvoU.js.map → history-recorder-BfWRwmPy.js.map} +1 -1
- package/dist/{history-store-rcAm_xFR.js → history-store-DPofGVDp.js} +2 -2
- package/dist/{history-store-rcAm_xFR.js.map → history-store-DPofGVDp.js.map} +1 -1
- package/dist/index.d.ts +23 -10
- package/dist/index.es.js +24423 -20038
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +27 -10
- package/dist/init/process-safety.d.ts +7 -2
- package/dist/init/shutdown.d.ts +18 -3
- package/dist/init.d.ts +49 -865
- package/dist/internal-tables-L2t39RdT.js +136 -0
- package/dist/internal-tables-L2t39RdT.js.map +1 -0
- package/dist/jobs/job-queue.d.ts +8 -0
- package/dist/jobs/job-store.d.ts +24 -0
- package/dist/jobs/types.d.ts +25 -4
- package/dist/{jobs-DqYNfquG.js → jobs-BCbflZj2.js} +234 -24
- package/dist/jobs-BCbflZj2.js.map +1 -0
- package/dist/{jwt-R6bSPMjk.js → jwt-BlvuKoR6.js} +529 -454
- package/dist/jwt-BlvuKoR6.js.map +1 -0
- package/dist/{keys-GAVZqbqx.js → keys-COzQ2aE8.js} +2 -2
- package/dist/{keys-GAVZqbqx.js.map → keys-COzQ2aE8.js.map} +1 -1
- package/dist/load-failure-L5hvfDoG.js +80 -0
- package/dist/load-failure-L5hvfDoG.js.map +1 -0
- package/dist/mcp/mcp-routes.d.ts +6 -0
- package/dist/mcp/mcp-tools.d.ts +8 -0
- package/dist/mcp/oauth-routes.d.ts +7 -0
- package/dist/metrics/index.d.ts +18 -0
- package/dist/metrics/pool-wait.d.ts +50 -0
- package/dist/offline-QNQgsPOq-B2yGeXkn.js +3305 -0
- package/dist/offline-QNQgsPOq-B2yGeXkn.js.map +1 -0
- package/dist/offline-store-Cm5sAX81-CALiO4Gn.js +1915 -0
- package/dist/offline-store-Cm5sAX81-CALiO4Gn.js.map +1 -0
- package/dist/{openapi-generator-DAq_XVDu.js → openapi-generator-D9zUQmEp.js} +366 -82
- package/dist/openapi-generator-D9zUQmEp.js.map +1 -0
- package/dist/policy-PdzX-Opd.js +121 -0
- package/dist/policy-PdzX-Opd.js.map +1 -0
- package/dist/project_manifest-R6y_KHms.js +59 -0
- package/dist/project_manifest-R6y_KHms.js.map +1 -0
- package/dist/{proxy-qRlqeUmO.js → proxy-CkTlMGmh.js} +4 -3
- package/dist/proxy-CkTlMGmh.js.map +1 -0
- package/dist/{query-parser-BgiKJKvc.js → query-parser-4WkMoA_K.js} +510 -15
- package/dist/query-parser-4WkMoA_K.js.map +1 -0
- package/dist/{request-timeout-DgH7j8qO.js → request-timeout-Dnl2_OBc.js} +15 -8
- package/dist/request-timeout-Dnl2_OBc.js.map +1 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
- package/dist/schema-edit/apply-schema-change.d.ts +10 -0
- package/dist/schema-edit/local-source.d.ts +29 -0
- package/dist/schema-edit/remote-source.d.ts +13 -3
- package/dist/schema-edit/source-editor.d.ts +28 -0
- package/dist/{schema-editor-routes-oIyuWl3L.js → schema-editor-routes-C11Tumyd.js} +54 -10
- package/dist/schema-editor-routes-C11Tumyd.js.map +1 -0
- package/dist/services/outbound-post.d.ts +50 -0
- package/dist/services/outbound-url-guard.d.ts +34 -5
- package/dist/services/routed-realtime-service.d.ts +5 -0
- package/dist/services/webhook-service.d.ts +66 -14
- package/dist/singleton.d.ts +9 -5
- package/dist/soft-delete-params-DfV3vVN5.js +668 -0
- package/dist/soft-delete-params-DfV3vVN5.js.map +1 -0
- package/dist/sql-export-routes-ClRwzIsC.js +240 -0
- package/dist/sql-export-routes-ClRwzIsC.js.map +1 -0
- package/dist/{src-CatHFUym.js → src-D5Y3N-S4.js} +2 -1
- package/dist/{src-CatHFUym.js.map → src-D5Y3N-S4.js.map} +1 -1
- package/dist/storage/GCSStorageController.d.ts +46 -2
- package/dist/storage/LocalStorageController.d.ts +67 -2
- package/dist/storage/S3StorageController.d.ts +37 -3
- package/dist/storage/image-transform.d.ts +5 -0
- package/dist/storage/maintenance-routes.d.ts +23 -0
- package/dist/storage/maintenance.d.ts +126 -0
- package/dist/storage/rendition-cache.d.ts +9 -11
- package/dist/storage/request-principals.d.ts +30 -0
- package/dist/storage/routes.d.ts +20 -0
- package/dist/storage/source-variables.d.ts +10 -0
- package/dist/storage/streams.d.ts +42 -0
- package/dist/storage/tus-handler.d.ts +46 -2
- package/dist/storage/types.d.ts +82 -7
- package/dist/storage/upload-validation.d.ts +13 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
- package/dist/strings.es-BeZWAodn.js +256 -0
- package/dist/strings.es-BeZWAodn.js.map +1 -0
- package/dist/upload-validation-Dd-fnsbA.js +172 -0
- package/dist/upload-validation-Dd-fnsbA.js.map +1 -0
- package/dist/utils/byte-size.d.ts +10 -0
- package/dist/utils/compression.d.ts +6 -0
- package/dist/utils/load-failure.d.ts +14 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-BSiP1c-f.js +0 -256
- package/dist/GCSStorageController-BSiP1c-f.js.map +0 -1
- package/dist/S3StorageController-CAwFRgjV.js +0 -276
- package/dist/S3StorageController-CAwFRgjV.js.map +0 -1
- package/dist/auth-DMLngxn_.js.map +0 -1
- package/dist/backup-CN0s50D2.js.map +0 -1
- package/dist/contract-routes-fz8i4pxs.js.map +0 -1
- package/dist/cron-loader-CwaANlOG.js +0 -118
- package/dist/cron-loader-CwaANlOG.js.map +0 -1
- package/dist/cron-routes-Bc-SB0Se.js.map +0 -1
- package/dist/cron-scheduler-CYQgco86.js.map +0 -1
- package/dist/cron-store-D2Q9-Aco.js +0 -300
- package/dist/cron-store-D2Q9-Aco.js.map +0 -1
- package/dist/dynamic-import-X40pTZUQ.js +0 -23
- package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
- package/dist/function-loader-D7o5Epjj.js.map +0 -1
- package/dist/function-routes-CaNG4waN.js.map +0 -1
- package/dist/jobs-DqYNfquG.js.map +0 -1
- package/dist/jwt-R6bSPMjk.js.map +0 -1
- package/dist/openapi-generator-DAq_XVDu.js.map +0 -1
- package/dist/proxy-qRlqeUmO.js.map +0 -1
- package/dist/query-parser-BgiKJKvc.js.map +0 -1
- package/dist/request-timeout-DgH7j8qO.js.map +0 -1
- package/dist/schema-editor-routes-oIyuWl3L.js.map +0 -1
- package/dist/soft-delete-params-BWPilMPF.js +0 -59
- package/dist/soft-delete-params-BWPilMPF.js.map +0 -1
- package/dist/src-I3aG1PcY.js +0 -6870
- package/dist/src-I3aG1PcY.js.map +0 -1
- package/dist/types-BfKcm9do.js +0 -39
- 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
|
+
}
|