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