@rebasepro/server-core 0.6.1 → 0.8.0
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/dist/api/graphql/graphql-schema-generator.d.ts +9 -0
- package/dist/api/rest/api-generator.d.ts +2 -14
- package/dist/api/rest/query-parser.d.ts +2 -5
- package/dist/api/types.d.ts +8 -2
- package/dist/auth/adapter-middleware.d.ts +7 -1
- package/dist/auth/admin-roles-route.d.ts +18 -0
- package/dist/auth/admin-users-route.d.ts +28 -0
- package/dist/auth/api-keys/api-key-types.d.ts +8 -0
- package/dist/auth/auth-hooks.d.ts +17 -0
- package/dist/auth/builtin-auth-adapter.d.ts +3 -1
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/interfaces.d.ts +32 -2
- package/dist/auth/magic-link-routes.d.ts +30 -0
- package/dist/auth/mfa-crypto.d.ts +23 -0
- package/dist/auth/mfa-routes.d.ts +2 -1
- package/dist/auth/middleware.d.ts +9 -2
- package/dist/auth/routes.d.ts +3 -1
- package/dist/auth/session-routes.d.ts +2 -0
- package/dist/backend-CIxN4FVm.js.map +1 -1
- package/dist/base64-js-C_frYBkI.js +1687 -0
- package/dist/base64-js-C_frYBkI.js.map +1 -0
- package/dist/{dist-CZKP-Xz4.js → dist-DMO-zF6D.js} +2 -2
- package/dist/{dist-CZKP-Xz4.js.map → dist-DMO-zF6D.js.map} +1 -1
- package/dist/email/index.d.ts +2 -2
- package/dist/email/templates.d.ts +8 -0
- package/dist/email/types.d.ts +18 -0
- package/dist/functions/define-function.d.ts +50 -0
- package/dist/functions/index.d.ts +2 -0
- package/dist/index.d.ts +16 -18
- package/dist/index.es.js +1947 -2356
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +40615 -10203
- package/dist/index.umd.js.map +1 -1
- package/dist/init.d.ts +68 -12
- package/dist/jws-BqRRaK11.js +616 -0
- package/dist/jws-BqRRaK11.js.map +1 -0
- package/dist/{jwt-DHcQRGC3.js → jwt-BETC8a1J.js} +3 -611
- package/dist/jwt-BETC8a1J.js.map +1 -0
- package/dist/services/routed-realtime-service.d.ts +43 -0
- package/dist/{src-COaj0G3P.js → src-CB2PIpBe.js} +2 -2
- package/dist/{src-COaj0G3P.js.map → src-CB2PIpBe.js.map} +1 -1
- package/dist/src-DjzOT1kG.js +29746 -0
- package/dist/src-DjzOT1kG.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +43 -0
- package/dist/storage/index.d.ts +5 -2
- package/dist/storage/routes.d.ts +33 -1
- package/dist/storage/tus-handler.d.ts +3 -1
- package/dist/storage/types.d.ts +28 -3
- package/dist/utils/dev-port.d.ts +2 -0
- package/package.json +13 -5
- package/src/api/errors.ts +16 -2
- package/src/api/graphql/graphql-schema-generator.ts +50 -15
- package/src/api/openapi-generator.ts +2 -2
- package/src/api/rest/api-generator.ts +44 -123
- package/src/api/rest/query-parser.ts +6 -23
- package/src/api/server.ts +10 -2
- package/src/api/types.ts +8 -2
- package/src/auth/adapter-middleware.ts +10 -2
- package/src/auth/admin-roles-route.ts +36 -0
- package/src/auth/admin-users-route.ts +302 -0
- package/src/auth/api-keys/api-key-middleware.ts +4 -3
- package/src/auth/api-keys/api-key-routes.ts +12 -2
- package/src/auth/api-keys/api-key-store.ts +83 -66
- package/src/auth/api-keys/api-key-types.ts +8 -0
- package/src/auth/apple-oauth.ts +2 -1
- package/src/auth/auth-hooks.ts +21 -0
- package/src/auth/bitbucket-oauth.ts +2 -1
- package/src/auth/builtin-auth-adapter.ts +29 -6
- package/src/auth/custom-auth-adapter.ts +2 -0
- package/src/auth/discord-oauth.ts +2 -1
- package/src/auth/facebook-oauth.ts +2 -1
- package/src/auth/github-oauth.ts +2 -1
- package/src/auth/gitlab-oauth.ts +2 -1
- package/src/auth/google-oauth.ts +8 -4
- package/src/auth/index.ts +2 -0
- package/src/auth/interfaces.ts +38 -2
- package/src/auth/linkedin-oauth.ts +2 -1
- package/src/auth/magic-link-routes.ts +167 -0
- package/src/auth/mfa-crypto.ts +91 -0
- package/src/auth/mfa-routes.ts +34 -10
- package/src/auth/microsoft-oauth.ts +2 -1
- package/src/auth/middleware.ts +14 -5
- package/src/auth/reset-password-admin.ts +17 -1
- package/src/auth/routes.ts +78 -10
- package/src/auth/session-routes.ts +15 -3
- package/src/auth/slack-oauth.ts +2 -1
- package/src/auth/spotify-oauth.ts +2 -1
- package/src/auth/twitter-oauth.ts +8 -1
- package/src/cron/cron-store.ts +25 -23
- package/src/email/index.ts +3 -2
- package/src/email/templates.ts +82 -0
- package/src/email/types.ts +16 -0
- package/src/functions/define-function.ts +59 -0
- package/src/functions/function-loader.ts +16 -0
- package/src/functions/index.ts +2 -0
- package/src/index.ts +70 -37
- package/src/init.ts +214 -25
- package/src/services/routed-realtime-service.ts +113 -0
- package/src/storage/GCSStorageController.ts +334 -0
- package/src/storage/index.ts +9 -3
- package/src/storage/routes.ts +191 -23
- package/src/storage/tus-handler.ts +16 -4
- package/src/storage/types.ts +25 -3
- package/src/utils/dev-port.ts +13 -7
- package/test/api-generator.test.ts +1 -1
- package/test/auth-config-types.test.ts +40 -0
- package/test/auth-routes.test.ts +54 -4
- package/test/custom-auth-adapter.test.ts +20 -1
- package/test/define-function.test.ts +45 -0
- package/test/env.test.ts +9 -19
- package/test/multi-datasource-routing.test.ts +113 -0
- package/test/routed-realtime-service.test.ts +86 -0
- package/test/storage-routes.test.ts +160 -0
- package/test/transform-auth-response.test.ts +305 -0
- package/dist/jwt-DHcQRGC3.js.map +0 -1
- package/test/backend-hooks-data.test.ts +0 -477
|
@@ -10,6 +10,15 @@ export declare class GraphQLSchemaGenerator {
|
|
|
10
10
|
private typeRegistry;
|
|
11
11
|
private inputTypeRegistry;
|
|
12
12
|
constructor(collections: EntityCollection[], driver: DataDriver);
|
|
13
|
+
/**
|
|
14
|
+
* Enforce API key permission scoping for a GraphQL resolver.
|
|
15
|
+
*
|
|
16
|
+
* Extracts the `apiKey` from the Hono context passed by `@hono/graphql-server`.
|
|
17
|
+
* If the request was made with an API key and the key does not have the
|
|
18
|
+
* required permission for the target collection/operation, an error is thrown.
|
|
19
|
+
* Non-API-key requests (e.g. session auth) are allowed through.
|
|
20
|
+
*/
|
|
21
|
+
private enforceApiKeyPermission;
|
|
13
22
|
/**
|
|
14
23
|
* Generate complete GraphQL schema using existing DataDriver
|
|
15
24
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import { AuthAdapter, DataDriver, EntityCollection
|
|
2
|
+
import { AuthAdapter, DataDriver, EntityCollection } from "@rebasepro/types";
|
|
3
3
|
import { HonoEnv } from "../types";
|
|
4
4
|
/**
|
|
5
5
|
* Lightweight REST API generator that leverages existing Rebase DataDriver.
|
|
@@ -9,11 +9,8 @@ export declare class RestApiGenerator {
|
|
|
9
9
|
private collections;
|
|
10
10
|
private router;
|
|
11
11
|
private driver;
|
|
12
|
-
private dataHooks?;
|
|
13
12
|
private authAdapter?;
|
|
14
|
-
constructor(collections: EntityCollection[], driver: DataDriver,
|
|
15
|
-
/** Build a BackendHookContext from a Hono context */
|
|
16
|
-
private buildHookContext;
|
|
13
|
+
constructor(collections: EntityCollection[], driver: DataDriver, authAdapter?: AuthAdapter);
|
|
17
14
|
/**
|
|
18
15
|
* Generate REST routes using existing DataDriver
|
|
19
16
|
*/
|
|
@@ -76,13 +73,4 @@ export declare class RestApiGenerator {
|
|
|
76
73
|
* Fetch single entity raw data without Entity wrapper (fallback)
|
|
77
74
|
*/
|
|
78
75
|
private fetchRawEntity;
|
|
79
|
-
/**
|
|
80
|
-
* Apply data.afterRead hook to a single entity.
|
|
81
|
-
* Returns the transformed entity, or null to filter it out.
|
|
82
|
-
*/
|
|
83
|
-
private applyAfterRead;
|
|
84
|
-
/**
|
|
85
|
-
* Apply data.afterRead hook to an array of entities, filtering out nulls.
|
|
86
|
-
*/
|
|
87
|
-
private applyAfterReadBatch;
|
|
88
76
|
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import type { LogicalCondition, FilterCondition } from "@rebasepro/types";
|
|
1
|
+
import type { LogicalCondition, FilterCondition, WhereFilterOp } from "@rebasepro/types";
|
|
2
|
+
export declare const mapOperator: (op: string) => WhereFilterOp | null;
|
|
2
3
|
import { QueryOptions } from "../types";
|
|
3
|
-
/**
|
|
4
|
-
* Map PostgREST-style operators to Rebase WhereFilterOp
|
|
5
|
-
*/
|
|
6
|
-
export declare function mapOperator(op: string): string | null;
|
|
7
4
|
export declare function parseLogicalString(str: string): FilterCondition | LogicalCondition;
|
|
8
5
|
/**
|
|
9
6
|
* Parse query parameters into QueryOptions
|
package/dist/api/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityCollection, VectorSearchParams, LogicalCondition } from "@rebasepro/types";
|
|
1
|
+
import { EntityCollection, VectorSearchParams, LogicalCondition, FilterValues } from "@rebasepro/types";
|
|
2
2
|
import { AuthResult } from "../auth/middleware";
|
|
3
3
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
4
4
|
import { DataDriver } from "@rebasepro/types";
|
|
@@ -29,6 +29,12 @@ export type HonoEnv = {
|
|
|
29
29
|
export interface ApiConfig {
|
|
30
30
|
collections?: EntityCollection[];
|
|
31
31
|
collectionsDir?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Declared data sources. When provided, collections on a `direct`/`custom`
|
|
34
|
+
* transport are treated as client-only and no server routes are generated
|
|
35
|
+
* for them.
|
|
36
|
+
*/
|
|
37
|
+
dataSources?: import("@rebasepro/types").DataSourceDefinition[];
|
|
32
38
|
basePath?: string;
|
|
33
39
|
enableGraphQL?: boolean;
|
|
34
40
|
enableREST?: boolean;
|
|
@@ -76,7 +82,7 @@ export interface ApiResponse<T = unknown> {
|
|
|
76
82
|
export interface QueryOptions {
|
|
77
83
|
limit?: number;
|
|
78
84
|
offset?: number;
|
|
79
|
-
where?:
|
|
85
|
+
where?: FilterValues<string>;
|
|
80
86
|
logical?: LogicalCondition;
|
|
81
87
|
orderBy?: Array<{
|
|
82
88
|
field: string;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* The behavior is identical to `createAuthMiddleware()` — only the
|
|
15
15
|
* token verification strategy is pluggable.
|
|
16
16
|
*/
|
|
17
|
-
import type { MiddlewareHandler } from "hono";
|
|
17
|
+
import type { MiddlewareHandler, Context } from "hono";
|
|
18
18
|
import type { DataDriver, AuthAdapter } from "@rebasepro/types";
|
|
19
19
|
import type { HonoEnv } from "../api/types";
|
|
20
20
|
import type { ApiKeyStore } from "./api-keys/api-key-store";
|
|
@@ -23,6 +23,12 @@ export interface AdapterAuthMiddlewareOptions {
|
|
|
23
23
|
adapter: AuthAdapter;
|
|
24
24
|
/** The DataDriver to scope via withAuth() for RLS. */
|
|
25
25
|
driver: DataDriver;
|
|
26
|
+
/**
|
|
27
|
+
* Optional per-request driver resolver for multi-data-source backends.
|
|
28
|
+
* Returns the unscoped delegate to use for this request (e.g. Postgres vs
|
|
29
|
+
* Mongo). When omitted, `driver` is used for every request.
|
|
30
|
+
*/
|
|
31
|
+
resolveDriver?: (c: Context<HonoEnv>) => DataDriver;
|
|
26
32
|
/**
|
|
27
33
|
* If true, return 401 when no valid user is resolved.
|
|
28
34
|
* Defaults to `true` (secure by default).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin endpoint for listing all roles.
|
|
3
|
+
*
|
|
4
|
+
* Mounts: GET /roles
|
|
5
|
+
*/
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
import type { AuthRepository } from "./interfaces";
|
|
8
|
+
import type { HonoEnv } from "../api/types";
|
|
9
|
+
export interface AdminRolesRouteConfig {
|
|
10
|
+
authRepo: AuthRepository;
|
|
11
|
+
serviceKey?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a standalone admin route for listing roles.
|
|
15
|
+
*
|
|
16
|
+
* Mounts: GET /roles
|
|
17
|
+
*/
|
|
18
|
+
export declare function createAdminRolesRoute(config: AdminRolesRouteConfig): Hono<HonoEnv>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone admin endpoint for user management.
|
|
3
|
+
*
|
|
4
|
+
* Mounts:
|
|
5
|
+
* GET /users
|
|
6
|
+
* GET /users/:userId
|
|
7
|
+
* POST /users
|
|
8
|
+
* PUT /users/:userId
|
|
9
|
+
* DELETE /users/:userId
|
|
10
|
+
* POST /bootstrap
|
|
11
|
+
*/
|
|
12
|
+
import { Hono } from "hono";
|
|
13
|
+
import type { AuthRepository } from "./interfaces";
|
|
14
|
+
import type { AuthHooks } from "./auth-hooks";
|
|
15
|
+
import type { EmailService, EmailConfig } from "../email";
|
|
16
|
+
import type { HonoEnv } from "../api/types";
|
|
17
|
+
import type { AuthCollectionConfig } from "@rebasepro/types";
|
|
18
|
+
export interface AdminUsersRouteConfig {
|
|
19
|
+
authRepo: AuthRepository;
|
|
20
|
+
emailService?: EmailService;
|
|
21
|
+
emailConfig?: EmailConfig;
|
|
22
|
+
serviceKey?: string;
|
|
23
|
+
authHooks?: AuthHooks;
|
|
24
|
+
collectionAuthConfig?: AuthCollectionConfig;
|
|
25
|
+
isBootstrapCompleted?: () => Promise<boolean>;
|
|
26
|
+
setBootstrapCompleted?: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare function createAdminUsersRoute(config: AdminUsersRouteConfig): Hono<HonoEnv>;
|
|
@@ -30,6 +30,8 @@ export interface ApiKey {
|
|
|
30
30
|
/** SHA-256 hash of the full plaintext key. */
|
|
31
31
|
key_hash: string;
|
|
32
32
|
permissions: ApiKeyPermission[];
|
|
33
|
+
/** When true, the key is granted the `admin` role (access to admin routes). */
|
|
34
|
+
admin: boolean;
|
|
33
35
|
/** Requests per 15-minute window. `null` means unlimited. */
|
|
34
36
|
rate_limit: number | null;
|
|
35
37
|
created_by: string;
|
|
@@ -48,6 +50,8 @@ export interface ApiKeyMasked {
|
|
|
48
50
|
name: string;
|
|
49
51
|
key_prefix: string;
|
|
50
52
|
permissions: ApiKeyPermission[];
|
|
53
|
+
/** When true, the key is granted the `admin` role (access to admin routes). */
|
|
54
|
+
admin: boolean;
|
|
51
55
|
rate_limit: number | null;
|
|
52
56
|
created_by: string;
|
|
53
57
|
created_at: string;
|
|
@@ -62,6 +66,8 @@ export interface ApiKeyMasked {
|
|
|
62
66
|
export interface CreateApiKeyRequest {
|
|
63
67
|
name: string;
|
|
64
68
|
permissions: ApiKeyPermission[];
|
|
69
|
+
/** When true, grants the `admin` role — allows access to admin routes. */
|
|
70
|
+
admin?: boolean;
|
|
65
71
|
/** Requests per 15-minute window. Omit or `null` for unlimited. */
|
|
66
72
|
rate_limit?: number | null;
|
|
67
73
|
/** ISO-8601 expiration timestamp. Omit for no expiration. */
|
|
@@ -74,6 +80,8 @@ export interface CreateApiKeyRequest {
|
|
|
74
80
|
export interface UpdateApiKeyRequest {
|
|
75
81
|
name?: string;
|
|
76
82
|
permissions?: ApiKeyPermission[];
|
|
83
|
+
/** When true, grants the `admin` role — allows access to admin routes. */
|
|
84
|
+
admin?: boolean;
|
|
77
85
|
rate_limit?: number | null;
|
|
78
86
|
expires_at?: string | null;
|
|
79
87
|
}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
import type { PasswordValidationResult } from "./password";
|
|
40
40
|
import type { AuthRepository, UserData, CreateUserData } from "./interfaces";
|
|
41
41
|
import type { EmailService, EmailConfig } from "../email";
|
|
42
|
+
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
42
43
|
/**
|
|
43
44
|
* Authentication method identifier for lifecycle hooks.
|
|
44
45
|
*/
|
|
@@ -154,6 +155,22 @@ export interface AuthHooks {
|
|
|
154
155
|
* @returns Modified claims to include in the JWT.
|
|
155
156
|
*/
|
|
156
157
|
customizeAccessToken?(claims: Record<string, unknown>, user: UserData): Promise<Record<string, unknown>>;
|
|
158
|
+
/**
|
|
159
|
+
* Transform the auth response before sending it to the client.
|
|
160
|
+
*
|
|
161
|
+
* Called after successful login, register, refresh, OAuth, anonymous,
|
|
162
|
+
* magic-link, and MFA flows. The hook receives the fully-formed
|
|
163
|
+
* response and returns a (potentially enriched) response.
|
|
164
|
+
*
|
|
165
|
+
* Use cases:
|
|
166
|
+
* - Inject tokens from external auth systems (custom provider tokens, etc.)
|
|
167
|
+
* - Add project-specific metadata to the response
|
|
168
|
+
* - Enrich the user object with data from external sources
|
|
169
|
+
*
|
|
170
|
+
* The hook runs in the request path — keep it fast.
|
|
171
|
+
* Heavy work should be offloaded to `onAuthenticated` (fire-and-forget).
|
|
172
|
+
*/
|
|
173
|
+
transformAuthResponse?(response: AuthResponsePayload, context: TransformAuthResponseContext): Promise<AuthResponsePayload>;
|
|
157
174
|
/**
|
|
158
175
|
* Called after a successful password reset.
|
|
159
176
|
*
|
|
@@ -31,13 +31,15 @@ export interface BuiltinAuthAdapterConfig {
|
|
|
31
31
|
/** Default role to assign to new users. */
|
|
32
32
|
defaultRole?: string;
|
|
33
33
|
/** OAuth providers to register. */
|
|
34
|
-
oauthProviders?: OAuthProvider<
|
|
34
|
+
oauthProviders?: OAuthProvider<unknown>[];
|
|
35
35
|
/** Static service key for server-to-server auth. */
|
|
36
36
|
serviceKey?: string;
|
|
37
37
|
/** Auth hooks for customizing password, credentials, lifecycle, etc. */
|
|
38
38
|
authHooks?: AuthHooks;
|
|
39
39
|
/** The parsed auth config from the collection (if `auth` is an object, not just `true`). */
|
|
40
40
|
collectionAuthConfig?: import("@rebasepro/types").AuthCollectionConfig;
|
|
41
|
+
/** Enable magic link (passwordless email) login. Requires email service. */
|
|
42
|
+
enableMagicLink?: boolean;
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
45
|
* Create the built-in Rebase auth adapter.
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export { requireAuth, requireAdmin, optionalAuth, extractUserFromToken, createAu
|
|
|
24
24
|
export type { AuthMiddlewareOptions, AuthResult } from "./middleware";
|
|
25
25
|
export { createAuthRoutes } from "./routes";
|
|
26
26
|
export type { AuthModuleConfig } from "./routes";
|
|
27
|
+
export { mountMagicLinkRoutes } from "./magic-link-routes";
|
|
27
28
|
export { createResetPasswordRoute } from "./reset-password-admin";
|
|
28
29
|
export type { ResetPasswordRouteConfig } from "./reset-password-admin";
|
|
29
30
|
export { createRateLimiter, defaultAuthLimiter, strictAuthLimiter, createApiKeyRateLimiter, apiKeyKeyGenerator } from "./rate-limiter";
|
|
@@ -55,6 +55,8 @@ export interface OAuthProviderProfile {
|
|
|
55
55
|
email: string;
|
|
56
56
|
displayName?: string | null;
|
|
57
57
|
photoUrl?: string | null;
|
|
58
|
+
/** Whether the OAuth provider has verified the user's email address */
|
|
59
|
+
emailVerified?: boolean;
|
|
58
60
|
}
|
|
59
61
|
/**
|
|
60
62
|
* Pluggable OAuth Provider integration strategy
|
|
@@ -64,8 +66,17 @@ export interface OAuthProvider<T = unknown> {
|
|
|
64
66
|
id: string;
|
|
65
67
|
/** Zod schema validating the expected request payload (e.g. { code: string }) */
|
|
66
68
|
schema: z.ZodSchema<T>;
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Verify external tokens/codes and return a standardized user profile.
|
|
71
|
+
*
|
|
72
|
+
* NOTE: Declared as method syntax (not arrow property) intentionally.
|
|
73
|
+
* This makes `OAuthProvider` bivariant in `T`, which is correct because
|
|
74
|
+
* each provider is self-contained — `schema` validates the request body
|
|
75
|
+
* and `verify` consumes the same `T`. Bivariance lets heterogeneous
|
|
76
|
+
* providers (`OAuthProvider<SpecificPayload>`) coexist in a single
|
|
77
|
+
* `OAuthProvider<unknown>[]` array without resorting to `any`.
|
|
78
|
+
*/
|
|
79
|
+
verify(payload: T): Promise<OAuthProviderProfile | null>;
|
|
69
80
|
}
|
|
70
81
|
/**
|
|
71
82
|
* Role data structure
|
|
@@ -116,6 +127,13 @@ export interface PasswordResetTokenInfo {
|
|
|
116
127
|
userId: string;
|
|
117
128
|
expiresAt: Date;
|
|
118
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Magic link token info
|
|
132
|
+
*/
|
|
133
|
+
export interface MagicLinkTokenInfo {
|
|
134
|
+
userId: string;
|
|
135
|
+
expiresAt: Date;
|
|
136
|
+
}
|
|
119
137
|
/**
|
|
120
138
|
* Options for paginated user listing
|
|
121
139
|
*/
|
|
@@ -303,6 +321,18 @@ export interface TokenRepository {
|
|
|
303
321
|
* Clean up expired tokens
|
|
304
322
|
*/
|
|
305
323
|
deleteExpiredTokens(): Promise<void>;
|
|
324
|
+
/**
|
|
325
|
+
* Create a magic link token
|
|
326
|
+
*/
|
|
327
|
+
createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
328
|
+
/**
|
|
329
|
+
* Find a valid (not expired, not used) magic link token by hash
|
|
330
|
+
*/
|
|
331
|
+
findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
332
|
+
/**
|
|
333
|
+
* Mark a magic link token as used
|
|
334
|
+
*/
|
|
335
|
+
markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
|
|
306
336
|
}
|
|
307
337
|
/**
|
|
308
338
|
* MFA factor data structure
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { AuthModuleConfig } from "./routes";
|
|
3
|
+
import type { ResolvedAuthHooks } from "./auth-hooks";
|
|
4
|
+
import type { HonoEnv } from "../api/types";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
7
|
+
/**
|
|
8
|
+
* Mount magic link routes onto the auth router.
|
|
9
|
+
*
|
|
10
|
+
* Follows the same delegation pattern as `mountMfaRoutes()` and `mountSessionRoutes()`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mountMagicLinkRoutes(deps: {
|
|
13
|
+
router: Hono<HonoEnv>;
|
|
14
|
+
config: AuthModuleConfig;
|
|
15
|
+
ops: ResolvedAuthHooks;
|
|
16
|
+
parseBody: <T>(schema: z.ZodSchema<T>, body: unknown) => T;
|
|
17
|
+
buildAuthResponse: (user: {
|
|
18
|
+
id: string;
|
|
19
|
+
email: string;
|
|
20
|
+
displayName?: string | null;
|
|
21
|
+
photoUrl?: string | null;
|
|
22
|
+
metadata?: Record<string, unknown> | null;
|
|
23
|
+
}, roleIds: string[], accessToken: string, refreshToken: string) => unknown;
|
|
24
|
+
createSessionAndTokens: (userId: string, userAgent: string, ipAddress: string) => Promise<{
|
|
25
|
+
roleIds: string[];
|
|
26
|
+
accessToken: string;
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
}>;
|
|
29
|
+
applyTransformHook: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, userId: string) => Promise<AuthResponsePayload>;
|
|
30
|
+
}): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Envelope encryption for TOTP secrets using AES-256-GCM.
|
|
3
|
+
*
|
|
4
|
+
* - Derives a 32-byte key via SHA-256 from `MFA_ENCRYPTION_KEY` or `JWT_SECRET`.
|
|
5
|
+
* - Generates a random 12-byte IV per encryption call.
|
|
6
|
+
* - Returns ciphertexts in the format `iv_hex:authTag_hex:ciphertext_hex`.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Encrypt a plaintext TOTP secret.
|
|
12
|
+
*
|
|
13
|
+
* @param plaintext - The Base32 TOTP secret to encrypt.
|
|
14
|
+
* @returns A string in the format `iv_hex:authTag_hex:ciphertext_hex`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function encryptTotpSecret(plaintext: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Decrypt a previously encrypted TOTP secret.
|
|
19
|
+
*
|
|
20
|
+
* @param ciphertext - A string in the format `iv_hex:authTag_hex:ciphertext_hex`.
|
|
21
|
+
* @returns The original Base32 TOTP secret.
|
|
22
|
+
*/
|
|
23
|
+
export declare function decryptTotpSecret(ciphertext: string): string;
|
|
@@ -3,4 +3,5 @@ import { z } from "zod";
|
|
|
3
3
|
import { HonoEnv } from "../api/types";
|
|
4
4
|
import type { AuthModuleConfig } from "./routes";
|
|
5
5
|
import { resolveAuthHooks } from "./auth-hooks";
|
|
6
|
-
|
|
6
|
+
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
7
|
+
export declare function mountMfaRoutes(router: Hono<HonoEnv>, config: AuthModuleConfig, ops: ReturnType<typeof resolveAuthHooks>, parseBody: <T>(schema: z.ZodSchema<T>, body: unknown) => T, applyTransformHook?: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, userId: string) => Promise<AuthResponsePayload>): void;
|
|
@@ -21,6 +21,13 @@ export type AuthResult = boolean | null | undefined | {
|
|
|
21
21
|
export interface AuthMiddlewareOptions {
|
|
22
22
|
/** DataDriver to scope via withAuth() for RLS */
|
|
23
23
|
driver: DataDriver;
|
|
24
|
+
/**
|
|
25
|
+
* Optional per-request driver resolver for multi-data-source backends.
|
|
26
|
+
* Given the request context, returns the unscoped delegate to use (e.g.
|
|
27
|
+
* Postgres vs Mongo, picked by the request's collection data source).
|
|
28
|
+
* When omitted, `driver` is used for every request.
|
|
29
|
+
*/
|
|
30
|
+
resolveDriver?: (c: Context<HonoEnv>) => DataDriver;
|
|
24
31
|
/**
|
|
25
32
|
* If true, return 401 when no valid token is present.
|
|
26
33
|
*
|
|
@@ -29,7 +36,7 @@ export interface AuthMiddlewareOptions {
|
|
|
29
36
|
* to Postgres Row-Level Security policies.
|
|
30
37
|
*/
|
|
31
38
|
requireAuth?: boolean;
|
|
32
|
-
/** Optional custom validator (for non-JWT auth, e.g.
|
|
39
|
+
/** Optional custom validator (for non-JWT auth, e.g. external auth providers) */
|
|
33
40
|
validator?: (c: Context<HonoEnv>) => Promise<AuthResult>;
|
|
34
41
|
/**
|
|
35
42
|
* A static secret key for server-to-server / script authentication.
|
|
@@ -39,7 +46,7 @@ export interface AuthMiddlewareOptions {
|
|
|
39
46
|
* roles: `["admin"]`) **without** JWT verification. The driver is scoped
|
|
40
47
|
* via `withAuth()` with the service identity.
|
|
41
48
|
*
|
|
42
|
-
* This is the Rebase equivalent of a
|
|
49
|
+
* This is the Rebase equivalent of a Service Account key.
|
|
43
50
|
* Set via `REBASE_SERVICE_KEY` in `.env` and pass through the backend config.
|
|
44
51
|
*
|
|
45
52
|
* **Security:** The comparison uses constant-time equality to prevent
|
package/dist/auth/routes.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface AuthModuleConfig {
|
|
|
15
15
|
/** Default role ID to assign to new users (default: none). Must NOT be "admin". */
|
|
16
16
|
defaultRole?: string;
|
|
17
17
|
/** Optional array of OAuth providers */
|
|
18
|
-
oauthProviders?: OAuthProvider<
|
|
18
|
+
oauthProviders?: OAuthProvider<unknown>[];
|
|
19
19
|
/** When true, blocks all self-registration regardless of `allowRegistration`. */
|
|
20
20
|
disableSelfRegistration?: boolean;
|
|
21
21
|
/**
|
|
@@ -29,5 +29,7 @@ export interface AuthModuleConfig {
|
|
|
29
29
|
* When not provided, falls back to checking if any users exist.
|
|
30
30
|
*/
|
|
31
31
|
isBootstrapCompleted?: () => Promise<boolean>;
|
|
32
|
+
/** Enable magic link (passwordless email) login. Requires email service. */
|
|
33
|
+
enableMagicLink?: boolean;
|
|
32
34
|
}
|
|
33
35
|
export declare function createAuthRoutes(config: AuthModuleConfig): Hono<HonoEnv>;
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { HonoEnv } from "../api/types";
|
|
4
4
|
import type { AuthModuleConfig } from "./routes";
|
|
5
5
|
import { resolveAuthHooks } from "./auth-hooks";
|
|
6
|
+
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
6
7
|
interface SessionRoutesConfig {
|
|
7
8
|
router: Hono<HonoEnv>;
|
|
8
9
|
config: AuthModuleConfig;
|
|
@@ -20,6 +21,7 @@ interface SessionRoutesConfig {
|
|
|
20
21
|
accessToken: string;
|
|
21
22
|
refreshToken: string;
|
|
22
23
|
}>;
|
|
24
|
+
applyTransformHook: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, userId: string) => Promise<AuthResponsePayload>;
|
|
23
25
|
}
|
|
24
26
|
export declare function mountSessionRoutes(opts: SessionRoutesConfig): void;
|
|
25
27
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend-CIxN4FVm.js","names":[],"sources":["../../types/src/types/backend.ts"],"sourcesContent":["import type { Entity } from \"./entities\";\nimport type { EntityCollection, FilterValues, WhereFilterOp } from \"./collections\";\nimport type { AuthAdapter } from \"./auth_adapter\";\n\n// =============================================================================\n// DATABASE CONNECTION INTERFACES\n// =============================================================================\n\n/**\n * Abstract database connection interface.\n * Represents a connection to any database system.\n */\nexport interface DatabaseConnection {\n /**\n * Type identifier for this database (e.g., 'postgres', 'mongodb', 'mysql')\n */\n readonly type: string;\n\n /**\n * Whether the connection is currently active\n */\n readonly isConnected?: boolean;\n\n /**\n * Close the database connection and release resources.\n */\n close?(): Promise<void>;\n}\n\n// =============================================================================\n// QUERY BUILDING INTERFACES\n// =============================================================================\n\n/**\n * A single filter condition for database queries\n */\nexport interface QueryFilter {\n field: string;\n operator: WhereFilterOp;\n value: unknown;\n}\n\n/**\n * Options for fetching a collection of entities\n */\nexport interface FetchCollectionOptions<M extends Record<string, unknown> = Record<string, unknown>> {\n filter?: FilterValues<Extract<keyof M, string>>;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n offset?: number;\n startAfter?: unknown;\n searchString?: string;\n databaseId?: string;\n collection?: EntityCollection;\n}\n\n/**\n * Options for searching entities\n */\nexport interface SearchOptions<M extends Record<string, unknown> = Record<string, unknown>> {\n filter?: FilterValues<Extract<keyof M, string>>;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n databaseId?: string;\n collection?: EntityCollection;\n}\n\n/**\n * Options for counting entities\n */\nexport interface CountOptions<M extends Record<string, unknown> = Record<string, unknown>> {\n filter?: FilterValues<Extract<keyof M, string>>;\n searchString?: string;\n databaseId?: string;\n}\n\n/**\n * Abstract condition builder interface.\n * Implementations translate Rebase filter conditions to database-specific queries.\n *\n * Note: This interface can be implemented as instance methods or as a class with static methods.\n * For static implementations (like DrizzleConditionBuilder), use the ConditionBuilderStatic type.\n *\n * @template T The type of condition returned by the builder (e.g., SQL for PostgreSQL, Filter<Document> for MongoDB)\n */\nexport interface ConditionBuilder<T = unknown> {\n /**\n * Build filter conditions from Rebase FilterValues\n */\n buildFilterConditions<M extends Record<string, unknown>>(\n filter: FilterValues<Extract<keyof M, string>>,\n collectionPath: string,\n ...args: unknown[]\n ): T[];\n\n /**\n * Build search conditions for text search\n */\n buildSearchConditions(\n searchString: string,\n properties: Record<string, unknown>,\n ...args: unknown[]\n ): T[];\n\n /**\n * Combine multiple conditions with AND operator\n */\n combineConditionsWithAnd(conditions: T[]): T | undefined;\n\n /**\n * Combine multiple conditions with OR operator\n */\n combineConditionsWithOr(conditions: T[]): T | undefined;\n}\n\n/**\n * Static condition builder type for implementations using static methods.\n * Use this type when the class provides static methods rather than instance methods.\n *\n * @example\n * // DrizzleConditionBuilder satisfies this type\n * const builder: ConditionBuilderStatic<SQL> = DrizzleConditionBuilder;\n */\nexport type ConditionBuilderStatic<T = unknown> = {\n buildFilterConditions<M extends Record<string, unknown>>(\n filter: FilterValues<Extract<keyof M, string>>,\n ...args: unknown[]\n ): T[];\n buildSearchConditions(\n searchString: string,\n properties: Record<string, unknown>,\n ...args: unknown[]\n ): T[];\n combineConditionsWithAnd(conditions: T[]): T | undefined;\n combineConditionsWithOr(conditions: T[]): T | undefined;\n};\n\n// =============================================================================\n// ENTITY REPOSITORY INTERFACES\n// =============================================================================\n\n/**\n * Abstract entity repository interface.\n * Handles all CRUD operations for entities in the database.\n *\n * Implementations should handle:\n * - Entity serialization/deserialization\n * - Relation resolution\n * - ID generation and conversion\n */\nexport interface EntityRepository {\n /**\n * Fetch a single entity by ID\n */\n fetchEntity<M extends Record<string, unknown>>(\n collectionPath: string,\n entityId: string | number,\n databaseId?: string\n ): Promise<Entity<M> | undefined>;\n\n /**\n * Fetch a collection of entities with optional filtering, ordering, and pagination\n */\n fetchCollection<M extends Record<string, unknown>>(\n collectionPath: string,\n options?: FetchCollectionOptions<M>\n ): Promise<Entity<M>[]>;\n\n /**\n * Search entities by text\n */\n searchEntities<M extends Record<string, unknown>>(\n collectionPath: string,\n searchString: string,\n options?: SearchOptions<M>\n ): Promise<Entity<M>[]>;\n\n /**\n * Count entities in a collection\n */\n countEntities<M extends Record<string, unknown>>(\n collectionPath: string,\n options?: CountOptions<M>\n ): Promise<number>;\n\n /**\n * Save an entity (create or update)\n */\n saveEntity<M extends Record<string, unknown>>(\n collectionPath: string,\n values: Partial<M>,\n entityId?: string | number,\n databaseId?: string\n ): Promise<Entity<M>>;\n\n /**\n * Delete an entity by ID\n */\n deleteEntity(\n collectionPath: string,\n entityId: string | number,\n databaseId?: string\n ): Promise<void>;\n\n /**\n * Check if a field value is unique in a collection\n */\n checkUniqueField(\n collectionPath: string,\n fieldName: string,\n value: unknown,\n excludeEntityId?: string,\n databaseId?: string\n ): Promise<boolean>;\n\n}\n\n// =============================================================================\n// REALTIME INTERFACES\n// =============================================================================\n\n/**\n * Configuration for subscribing to a collection\n */\nexport interface CollectionSubscriptionConfig {\n clientId: string;\n path: string;\n filter?: unknown;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n startAfter?: unknown;\n databaseId?: string;\n searchString?: string;\n}\n\n/**\n * Configuration for subscribing to a single entity\n */\nexport interface EntitySubscriptionConfig {\n clientId: string;\n path: string;\n entityId: string | number;\n}\n\n/**\n * Abstract realtime provider interface.\n * Handles real-time subscriptions and notifications for entity changes.\n */\nexport interface RealtimeProvider {\n /**\n * Subscribe to collection changes\n */\n subscribeToCollection(\n subscriptionId: string,\n config: CollectionSubscriptionConfig,\n callback?: (entities: Entity[]) => void\n ): void;\n\n /**\n * Subscribe to single entity changes\n */\n subscribeToEntity(\n subscriptionId: string,\n config: EntitySubscriptionConfig,\n callback?: (entity: Entity | null) => void\n ): void;\n\n /**\n * Unsubscribe from a subscription\n */\n unsubscribe(subscriptionId: string): void;\n\n /**\n * Notify all relevant subscribers of an entity update\n */\n notifyEntityUpdate(\n path: string,\n entityId: string,\n entity: Entity | null,\n databaseId?: string\n ): Promise<void>;\n\n /**\n * Called when the HTTP server is ready and listening.\n * Useful for providers that need the server address for callbacks.\n */\n onServerReady?(serverInfo: { port: number; hostname?: string }): void;\n\n /**\n * Gracefully shut down the realtime provider.\n * Called during server shutdown to clean up resources.\n */\n destroy?(): Promise<void>;\n\n /**\n * Stop the internal LISTEN client (e.g., PostgreSQL LISTEN/NOTIFY).\n * Called during graceful shutdown before closing database connections.\n */\n stopListening?(): Promise<void>;\n}\n\n// =============================================================================\n// COLLECTION REGISTRY INTERFACES\n// =============================================================================\n\n/**\n * Abstract collection registry interface.\n * Manages registration and lookup of entity collections.\n */\nexport interface CollectionRegistryInterface {\n /**\n * Register a collection\n */\n register(collection: EntityCollection): void;\n\n /**\n * Get a collection by its path\n */\n getCollectionByPath(path: string): EntityCollection | undefined;\n\n /**\n * Get all registered collections\n */\n getCollections(): EntityCollection[];\n}\n\n// =============================================================================\n// DATA TRANSFORMER INTERFACES\n// =============================================================================\n\n/**\n * Abstract data transformer interface.\n * Handles serialization/deserialization between frontend and database formats.\n */\nexport interface DataTransformer {\n /**\n * Transform entity data for storage in the database\n */\n serializeToDatabase<M extends Record<string, unknown>>(\n entity: M,\n collection: EntityCollection\n ): Record<string, unknown>;\n\n /**\n * Transform database data back to entity format\n */\n deserializeFromDatabase<M extends Record<string, unknown>>(\n data: Record<string, unknown>,\n collection: EntityCollection\n ): Promise<M>;\n}\n\n// =============================================================================\n// DATABASE ADMIN — CAPABILITY-SPECIFIC INTERFACES (1.3)\n// =============================================================================\n\n/**\n * Administrative operations for SQL-based databases (PostgreSQL, MySQL, etc.).\n * Used by the SQL Editor, RLS Editor, and schema browser.\n *\n * @group Admin\n */\nexport interface SQLAdmin {\n /**\n * Execute raw SQL against the database.\n */\n executeSql(sql: string, options?: { database?: string; role?: string }): Promise<Record<string, unknown>[]>;\n\n /**\n * Fetch the available databases on the server.\n */\n fetchAvailableDatabases?(): Promise<string[]>;\n\n /**\n * Fetch the available database roles.\n */\n fetchAvailableRoles?(): Promise<string[]>;\n\n /**\n * Fetch the current database name.\n */\n fetchCurrentDatabase?(): Promise<string | undefined>;\n}\n\n/**\n * Administrative operations for document-based databases (MongoDB, Firestore, etc.).\n * Used by future document administration tools.\n *\n * @group Admin\n */\nexport interface DocumentAdmin {\n /**\n * Execute an aggregation pipeline or equivalent query.\n */\n executeAggregate?(pipeline: Record<string, unknown>[]): Promise<Record<string, unknown>[]>;\n\n /**\n * Fetch statistics for a collection (document count, size, etc.).\n */\n fetchCollectionStats?(collectionName: string): Promise<{ count: number; sizeBytes?: number }>;\n}\n\n/**\n * Administrative operations for schema management.\n * Shared across SQL and document databases.\n *\n * @group Admin\n */\nexport interface SchemaAdmin {\n /**\n * Fetch database tables/collections not yet mapped to a Rebase collection.\n */\n fetchUnmappedTables?(mappedPaths?: string[]): Promise<string[]>;\n\n /**\n * Fetch column/field metadata for a single table/collection.\n * The return type is generic — SQL backends return TableMetadata,\n * document backends may return a different shape.\n */\n fetchTableMetadata?(tableName: string): Promise<unknown>;\n}\n\n/**\n * Metadata for a database branch.\n * @group Admin\n */\nexport interface BranchInfo {\n /** Branch name (without prefix). */\n name: string;\n /** The database this branch was created from. */\n parentDatabase: string;\n /** When the branch was created. */\n createdAt: Date;\n /** Size in bytes, if available from the server. */\n sizeBytes?: number;\n}\n\n/**\n * Administrative operations for database branching.\n * Allows creating isolated database copies for development/preview workflows.\n *\n * @group Admin\n */\nexport interface BranchAdmin {\n /** Create a new branch (database copy) from the current or specified source database. */\n createBranch(name: string, options?: { source?: string }): Promise<BranchInfo>;\n\n /** Delete a branch database. Cannot delete the main/default database. */\n deleteBranch(name: string): Promise<void>;\n\n /** List all branches (databases that were created via branching). */\n listBranches(): Promise<BranchInfo[]>;\n\n /** Get info about a specific branch. */\n getBranchInfo(name: string): Promise<BranchInfo | undefined>;\n}\n\n/**\n * Union type for all admin capabilities.\n * A backend may implement any combination of these interfaces.\n *\n * Use type guards (`isSQLAdmin`, `isDocumentAdmin`, `isSchemaAdmin`, `isBranchAdmin`)\n * to safely narrow the type before calling methods.\n *\n * @group Admin\n */\nexport type DatabaseAdmin = Partial<SQLAdmin> & Partial<DocumentAdmin> & Partial<SchemaAdmin> & Partial<BranchAdmin>;\n\n/**\n * Type guard: does this admin support SQL operations?\n * @group Admin\n */\nexport function isSQLAdmin(admin: DatabaseAdmin | undefined): admin is SQLAdmin {\n return !!admin && typeof (admin as SQLAdmin).executeSql === \"function\";\n}\n\n/**\n * Type guard: does this admin support document operations?\n * @group Admin\n */\nexport function isDocumentAdmin(admin: DatabaseAdmin | undefined): admin is DocumentAdmin {\n return !!admin && (\n typeof (admin as DocumentAdmin).executeAggregate === \"function\" ||\n typeof (admin as DocumentAdmin).fetchCollectionStats === \"function\"\n );\n}\n\n/**\n * Type guard: does this admin support schema management?\n * @group Admin\n */\nexport function isSchemaAdmin(admin: DatabaseAdmin | undefined): admin is SchemaAdmin {\n return !!admin && (\n typeof (admin as SchemaAdmin).fetchUnmappedTables === \"function\" ||\n typeof (admin as SchemaAdmin).fetchTableMetadata === \"function\"\n );\n}\n\n/**\n * Type guard: does this admin support database branching?\n * @group Admin\n */\nexport function isBranchAdmin(admin: DatabaseAdmin | undefined): admin is BranchAdmin {\n return !!admin && typeof (admin as BranchAdmin).createBranch === \"function\";\n}\n\n// =============================================================================\n// LIFECYCLE INTERFACES (1.4)\n// =============================================================================\n\n/**\n * Health check result returned by `healthCheck()`.\n * @group Lifecycle\n */\nexport interface HealthCheckResult {\n /** Whether the backend is healthy and able to serve requests. */\n healthy: boolean;\n /** Round-trip latency to the database in milliseconds. */\n latencyMs: number;\n /** Optional details (e.g., pool stats, replication lag). */\n details?: Record<string, unknown>;\n}\n\n/**\n * Lifecycle contract for backend components that hold resources\n * (database connections, WebSocket pools, timers, etc.).\n *\n * All methods are optional — simple backends (e.g., in-memory) can skip them.\n * @group Lifecycle\n */\nexport interface BackendLifecycle {\n /**\n * Initialize the backend: open connections, run migrations, seed data.\n * Called once during startup. Idempotent.\n */\n initialize?(): Promise<void>;\n\n /**\n * Check whether the backend is healthy and reachable.\n * Should be fast (< 1 s) and safe to call frequently.\n */\n healthCheck?(): Promise<HealthCheckResult>;\n\n /**\n * Gracefully shut down: close connections, flush buffers, cancel timers.\n * After calling `destroy()`, no other methods should be called.\n */\n destroy?(): Promise<void>;\n}\n\n// =============================================================================\n// BACKEND FACTORY INTERFACES\n// =============================================================================\n\n/**\n * Configuration for creating a database backend\n */\nexport interface BackendConfig {\n /**\n * Type of database backend\n */\n type: string;\n\n /**\n * Database connection (implementation-specific)\n */\n connection: unknown;\n\n /**\n * Schema definition (implementation-specific, e.g., Drizzle schema for PostgreSQL)\n */\n schema?: unknown;\n}\n\n/**\n * A complete backend instance with all required services.\n *\n * Now includes optional lifecycle management and admin capabilities.\n */\nexport interface BackendInstance extends BackendLifecycle {\n /**\n * Entity repository for CRUD operations\n */\n entityRepository: EntityRepository;\n\n /**\n * Realtime provider for subscriptions\n */\n realtimeProvider: RealtimeProvider;\n\n /**\n * Collection registry\n */\n collectionRegistry: CollectionRegistryInterface;\n\n /**\n * The underlying database connection\n */\n connection: DatabaseConnection;\n\n /**\n * Administrative operations (SQL, schema, documents).\n * What's available depends on the backend type — use type guards\n * (`isSQLAdmin`, `isSchemaAdmin`, etc.) to narrow.\n */\n admin?: DatabaseAdmin;\n}\n\n/**\n * Factory function type for creating backend instances\n */\nexport type BackendFactory<TConfig extends BackendConfig = BackendConfig> =\n (config: TConfig) => BackendInstance;\n\n// =============================================================================\n// BACKEND BOOTSTRAPPER (1.2)\n// =============================================================================\n\n/**\n * A `BackendBootstrapper` encapsulates all driver-specific initialization logic.\n *\n * Instead of hard-coding Postgres setup into `initializeRebaseBackend()`,\n * each database backend provides its own bootstrapper that knows how to:\n * - Create the DataDriver from a config object\n * - Optionally initialize auth tables\n * - Optionally create a realtime service\n * - Mount driver-specific API routes\n *\n * The main `initializeRebaseBackend()` becomes a **coordinator** that iterates\n * registered bootstrappers, calls their hooks, and wires the results together.\n *\n * @group Backend\n *\n * @example\n * ```typescript\n * // Third-party MySQL bootstrapper\n * const mysqlBootstrapper: BackendBootstrapper = {\n * type: \"mysql\",\n * initializeDriver: async (config) => new MySQLDataDriver(config.connection),\n * initializeRealtime: async (config) => new MySQLChangeStreamRealtime(config.connection),\n * };\n *\n * initializeRebaseBackend({\n * ...config,\n * bootstrappers: [postgresBootstrapper, mysqlBootstrapper]\n * });\n * ```\n */\nexport interface BackendBootstrapper {\n /**\n * Which driver type this bootstrapper handles.\n * Must match the `type` field on the driver config object\n * (e.g., `\"postgres\"`, `\"mongodb\"`, `\"mysql\"`).\n */\n type: string;\n\n /**\n * Unique identifier for this bootstrapper instance.\n * Used to register the driver in the driver registry.\n * Defaults to `type` if not set.\n */\n id?: string;\n\n /**\n * Whether this bootstrapper provides the default driver.\n * When true, the coordinator uses this driver as the primary one.\n */\n isDefault?: boolean;\n\n /**\n * Run database migrations for this driver.\n * Called by the coordinator after all drivers are initialized.\n */\n runMigrations?(config: unknown, driverResult: InitializedDriver): Promise<void>;\n\n /**\n * Create a DataDriver from the given config.\n * This is the only **required** method.\n */\n initializeDriver(config: unknown): Promise<InitializedDriver>;\n\n /**\n * Initialize auth tables / services if this driver supports them.\n * Return undefined if auth is not supported by this backend.\n */\n initializeAuth?(config: unknown, driverResult: InitializedDriver): Promise<BootstrappedAuth | undefined>;\n\n /**\n * Initialize history tables / services if this driver supports them.\n * Return undefined if history is not supported by this backend.\n */\n initializeHistory?(config: unknown, driverResult: InitializedDriver): Promise<{ historyService: unknown } | undefined>;\n\n /**\n * Create a realtime provider for this driver.\n * Return undefined if the driver does not support realtime.\n */\n initializeRealtime?(config: unknown, driverResult: InitializedDriver): Promise<RealtimeProvider | undefined>;\n\n /**\n * Mount any driver-specific HTTP routes (e.g., custom admin endpoints).\n * Called after all drivers are initialized.\n */\n mountRoutes?(app: unknown, basePath: string, driverResult: InitializedDriver): void;\n\n /**\n * Return admin capabilities for this driver.\n */\n getAdmin?(driverResult: InitializedDriver): DatabaseAdmin | undefined;\n\n /**\n * Initialize WebSocket server for realtime operations.\n */\n initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import(\"../controllers/data_driver\").DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> | void;\n}\n\n/**\n * Result of `BackendBootstrapper.initializeDriver()`.\n * @group Backend\n */\nexport interface InitializedDriver {\n /** The DataDriver instance, ready for use. */\n driver: import(\"../controllers/data_driver\").DataDriver;\n\n /** The realtime service, if the driver created one during init. */\n realtimeProvider?: RealtimeProvider;\n\n /** A collection registry to register schema / tables into. */\n collectionRegistry?: CollectionRegistryInterface;\n\n /** The underlying database connection (for lifecycle management). */\n connection?: DatabaseConnection;\n\n /**\n * Opaque handle that the bootstrapper can use in subsequent hooks\n * (e.g., `initializeAuth`, `mountRoutes`) to access driver internals.\n * Not used by the coordinator.\n */\n internals?: unknown;\n}\n\n/**\n * Result of `BackendBootstrapper.initializeAuth()`.\n * @group Backend\n */\nexport interface BootstrappedAuth {\n /** User management service. */\n userService: unknown;\n /** Role management service (optional, roles are now simple strings). */\n roleService?: unknown;\n /** Email service (optional). */\n emailService?: unknown;\n /** Combined Auth Repository for unified token and user management. */\n authRepository?: unknown;\n}\n"],"mappings":";;;;;;;;AA2dA,SAAgB,WAAW,OAAqD;CAC5E,OAAO,CAAC,CAAC,SAAS,OAAQ,MAAmB,eAAe;AAChE"}
|
|
1
|
+
{"version":3,"file":"backend-CIxN4FVm.js","names":[],"sources":["../../types/src/types/backend.ts"],"sourcesContent":["import type { Entity } from \"./entities\";\nimport type { EntityCollection, FilterValues, WhereFilterOp } from \"./collections\";\nimport type { AuthAdapter } from \"./auth_adapter\";\n\n// =============================================================================\n// DATABASE CONNECTION INTERFACES\n// =============================================================================\n\n/**\n * Abstract database connection interface.\n * Represents a connection to any database system.\n */\nexport interface DatabaseConnection {\n /**\n * Type identifier for this database (e.g., 'postgres', 'mongodb', 'mysql')\n */\n readonly type: string;\n\n /**\n * Whether the connection is currently active\n */\n readonly isConnected?: boolean;\n\n /**\n * Close the database connection and release resources.\n */\n close?(): Promise<void>;\n}\n\n// =============================================================================\n// QUERY BUILDING INTERFACES\n// =============================================================================\n\n/**\n * A single filter condition for database queries\n */\nexport interface QueryFilter {\n field: string;\n operator: WhereFilterOp;\n value: unknown;\n}\n\n/**\n * Options for fetching a collection of entities\n */\nexport interface FetchCollectionOptions<M extends Record<string, unknown> = Record<string, unknown>> {\n filter?: FilterValues<Extract<keyof M, string>>;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n offset?: number;\n startAfter?: unknown;\n searchString?: string;\n databaseId?: string;\n collection?: EntityCollection;\n}\n\n/**\n * Options for searching entities\n */\nexport interface SearchOptions<M extends Record<string, unknown> = Record<string, unknown>> {\n filter?: FilterValues<Extract<keyof M, string>>;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n databaseId?: string;\n collection?: EntityCollection;\n}\n\n/**\n * Options for counting entities\n */\nexport interface CountOptions<M extends Record<string, unknown> = Record<string, unknown>> {\n filter?: FilterValues<Extract<keyof M, string>>;\n searchString?: string;\n databaseId?: string;\n}\n\n/**\n * Abstract condition builder interface.\n * Implementations translate Rebase filter conditions to database-specific queries.\n *\n * Note: This interface can be implemented as instance methods or as a class with static methods.\n * For static implementations (like DrizzleConditionBuilder), use the ConditionBuilderStatic type.\n *\n * @template T The type of condition returned by the builder (e.g., SQL for PostgreSQL, Filter<Document> for MongoDB)\n */\nexport interface ConditionBuilder<T = unknown> {\n /**\n * Build filter conditions from Rebase FilterValues\n */\n buildFilterConditions<M extends Record<string, unknown>>(\n filter: FilterValues<Extract<keyof M, string>>,\n collectionPath: string,\n ...args: unknown[]\n ): T[];\n\n /**\n * Build search conditions for text search\n */\n buildSearchConditions(\n searchString: string,\n properties: Record<string, unknown>,\n ...args: unknown[]\n ): T[];\n\n /**\n * Combine multiple conditions with AND operator\n */\n combineConditionsWithAnd(conditions: T[]): T | undefined;\n\n /**\n * Combine multiple conditions with OR operator\n */\n combineConditionsWithOr(conditions: T[]): T | undefined;\n}\n\n/**\n * Static condition builder type for implementations using static methods.\n * Use this type when the class provides static methods rather than instance methods.\n *\n * @example\n * // DrizzleConditionBuilder satisfies this type\n * const builder: ConditionBuilderStatic<SQL> = DrizzleConditionBuilder;\n */\nexport type ConditionBuilderStatic<T = unknown> = {\n buildFilterConditions<M extends Record<string, unknown>>(\n filter: FilterValues<Extract<keyof M, string>>,\n ...args: unknown[]\n ): T[];\n buildSearchConditions(\n searchString: string,\n properties: Record<string, unknown>,\n ...args: unknown[]\n ): T[];\n combineConditionsWithAnd(conditions: T[]): T | undefined;\n combineConditionsWithOr(conditions: T[]): T | undefined;\n};\n\n// =============================================================================\n// ENTITY REPOSITORY INTERFACES\n// =============================================================================\n\n/**\n * Abstract entity repository interface.\n * Handles all CRUD operations for entities in the database.\n *\n * Implementations should handle:\n * - Entity serialization/deserialization\n * - Relation resolution\n * - ID generation and conversion\n */\nexport interface EntityRepository {\n /**\n * Fetch a single entity by ID\n */\n fetchEntity<M extends Record<string, unknown>>(\n collectionPath: string,\n entityId: string | number,\n databaseId?: string\n ): Promise<Entity<M> | undefined>;\n\n /**\n * Fetch a collection of entities with optional filtering, ordering, and pagination\n */\n fetchCollection<M extends Record<string, unknown>>(\n collectionPath: string,\n options?: FetchCollectionOptions<M>\n ): Promise<Entity<M>[]>;\n\n /**\n * Search entities by text\n */\n searchEntities<M extends Record<string, unknown>>(\n collectionPath: string,\n searchString: string,\n options?: SearchOptions<M>\n ): Promise<Entity<M>[]>;\n\n /**\n * Count entities in a collection\n */\n countEntities<M extends Record<string, unknown>>(\n collectionPath: string,\n options?: CountOptions<M>\n ): Promise<number>;\n\n /**\n * Save an entity (create or update)\n */\n saveEntity<M extends Record<string, unknown>>(\n collectionPath: string,\n values: Partial<M>,\n entityId?: string | number,\n databaseId?: string\n ): Promise<Entity<M>>;\n\n /**\n * Delete an entity by ID\n */\n deleteEntity(\n collectionPath: string,\n entityId: string | number,\n databaseId?: string\n ): Promise<void>;\n\n /**\n * Check if a field value is unique in a collection\n */\n checkUniqueField(\n collectionPath: string,\n fieldName: string,\n value: unknown,\n excludeEntityId?: string,\n databaseId?: string\n ): Promise<boolean>;\n\n}\n\n// =============================================================================\n// REALTIME INTERFACES\n// =============================================================================\n\n/**\n * Configuration for subscribing to a collection\n */\nexport interface CollectionSubscriptionConfig {\n clientId: string;\n path: string;\n filter?: unknown;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n startAfter?: unknown;\n databaseId?: string;\n searchString?: string;\n}\n\n/**\n * Configuration for subscribing to a single entity\n */\nexport interface EntitySubscriptionConfig {\n clientId: string;\n path: string;\n entityId: string | number;\n}\n\n/**\n * Abstract realtime provider interface.\n * Handles real-time subscriptions and notifications for entity changes.\n */\nexport interface RealtimeProvider {\n /**\n * Subscribe to collection changes\n */\n subscribeToCollection(\n subscriptionId: string,\n config: CollectionSubscriptionConfig,\n callback?: (entities: Entity[]) => void\n ): void;\n\n /**\n * Subscribe to single entity changes\n */\n subscribeToEntity(\n subscriptionId: string,\n config: EntitySubscriptionConfig,\n callback?: (entity: Entity | null) => void\n ): void;\n\n /**\n * Unsubscribe from a subscription\n */\n unsubscribe(subscriptionId: string): void;\n\n /**\n * Notify all relevant subscribers of an entity update\n */\n notifyEntityUpdate(\n path: string,\n entityId: string,\n entity: Entity | null,\n databaseId?: string\n ): Promise<void>;\n\n /**\n * Called when the HTTP server is ready and listening.\n * Useful for providers that need the server address for callbacks.\n */\n onServerReady?(serverInfo: { port: number; hostname?: string }): void;\n\n /**\n * Gracefully shut down the realtime provider.\n * Called during server shutdown to clean up resources.\n */\n destroy?(): Promise<void>;\n\n /**\n * Stop the internal LISTEN client (e.g., PostgreSQL LISTEN/NOTIFY).\n * Called during graceful shutdown before closing database connections.\n */\n stopListening?(): Promise<void>;\n}\n\n// =============================================================================\n// COLLECTION REGISTRY INTERFACES\n// =============================================================================\n\n/**\n * Abstract collection registry interface.\n * Manages registration and lookup of entity collections.\n */\nexport interface CollectionRegistryInterface {\n /**\n * Register a collection\n */\n register(collection: EntityCollection): void;\n\n /**\n * Get a collection by its path\n */\n getCollectionByPath(path: string): EntityCollection | undefined;\n\n /**\n * Get all registered collections\n */\n getCollections(): EntityCollection[];\n\n /**\n * Get the currently registered global callbacks, if any.\n */\n getGlobalCallbacks(): any | undefined;\n}\n\n// =============================================================================\n// DATA TRANSFORMER INTERFACES\n// =============================================================================\n\n/**\n * Abstract data transformer interface.\n * Handles serialization/deserialization between frontend and database formats.\n */\nexport interface DataTransformer {\n /**\n * Transform entity data for storage in the database\n */\n serializeToDatabase<M extends Record<string, unknown>>(\n entity: M,\n collection: EntityCollection\n ): Record<string, unknown>;\n\n /**\n * Transform database data back to entity format\n */\n deserializeFromDatabase<M extends Record<string, unknown>>(\n data: Record<string, unknown>,\n collection: EntityCollection\n ): Promise<M>;\n}\n\n// =============================================================================\n// DATABASE ADMIN — CAPABILITY-SPECIFIC INTERFACES (1.3)\n// =============================================================================\n\n/**\n * Administrative operations for SQL-based databases (PostgreSQL, MySQL, etc.).\n * Used by the SQL Editor, RLS Editor, and schema browser.\n *\n * @group Admin\n */\nexport interface SQLAdmin {\n /**\n * Execute raw SQL against the database.\n */\n executeSql(sql: string, options?: { database?: string; role?: string; params?: unknown[] }): Promise<Record<string, unknown>[]>;\n\n /**\n * Fetch the available databases on the server.\n */\n fetchAvailableDatabases?(): Promise<string[]>;\n\n /**\n * Fetch the available database roles.\n */\n fetchAvailableRoles?(): Promise<string[]>;\n\n /**\n * Fetch the current database name.\n */\n fetchCurrentDatabase?(): Promise<string | undefined>;\n}\n\n/**\n * Administrative operations for document-based databases (MongoDB, Firestore, etc.).\n * Used by future document administration tools.\n *\n * @group Admin\n */\nexport interface DocumentAdmin {\n /**\n * Execute an aggregation pipeline or equivalent query.\n */\n executeAggregate?(pipeline: Record<string, unknown>[]): Promise<Record<string, unknown>[]>;\n\n /**\n * Fetch statistics for a collection (document count, size, etc.).\n */\n fetchCollectionStats?(collectionName: string): Promise<{ count: number; sizeBytes?: number }>;\n}\n\n/**\n * Administrative operations for schema management.\n * Shared across SQL and document databases.\n *\n * @group Admin\n */\nexport interface SchemaAdmin {\n /**\n * Fetch database tables/collections not yet mapped to a Rebase collection.\n */\n fetchUnmappedTables?(mappedPaths?: string[]): Promise<string[]>;\n\n /**\n * Fetch column/field metadata for a single table/collection.\n * The return type is generic — SQL backends return TableMetadata,\n * document backends may return a different shape.\n */\n fetchTableMetadata?(tableName: string): Promise<unknown>;\n}\n\n/**\n * Metadata for a database branch.\n * @group Admin\n */\nexport interface BranchInfo {\n /** Branch name (without prefix). */\n name: string;\n /** The database this branch was created from. */\n parentDatabase: string;\n /** When the branch was created. */\n createdAt: Date;\n /** Size in bytes, if available from the server. */\n sizeBytes?: number;\n}\n\n/**\n * Administrative operations for database branching.\n * Allows creating isolated database copies for development/preview workflows.\n *\n * @group Admin\n */\nexport interface BranchAdmin {\n /** Create a new branch (database copy) from the current or specified source database. */\n createBranch(name: string, options?: { source?: string }): Promise<BranchInfo>;\n\n /** Delete a branch database. Cannot delete the main/default database. */\n deleteBranch(name: string): Promise<void>;\n\n /** List all branches (databases that were created via branching). */\n listBranches(): Promise<BranchInfo[]>;\n\n /** Get info about a specific branch. */\n getBranchInfo(name: string): Promise<BranchInfo | undefined>;\n}\n\n/**\n * Union type for all admin capabilities.\n * A backend may implement any combination of these interfaces.\n *\n * Use type guards (`isSQLAdmin`, `isDocumentAdmin`, `isSchemaAdmin`, `isBranchAdmin`)\n * to safely narrow the type before calling methods.\n *\n * @group Admin\n */\nexport type DatabaseAdmin = Partial<SQLAdmin> & Partial<DocumentAdmin> & Partial<SchemaAdmin> & Partial<BranchAdmin>;\n\n/**\n * Type guard: does this admin support SQL operations?\n * @group Admin\n */\nexport function isSQLAdmin(admin: DatabaseAdmin | undefined): admin is SQLAdmin {\n return !!admin && typeof (admin as SQLAdmin).executeSql === \"function\";\n}\n\n/**\n * Type guard: does this admin support document operations?\n * @group Admin\n */\nexport function isDocumentAdmin(admin: DatabaseAdmin | undefined): admin is DocumentAdmin {\n return !!admin && (\n typeof (admin as DocumentAdmin).executeAggregate === \"function\" ||\n typeof (admin as DocumentAdmin).fetchCollectionStats === \"function\"\n );\n}\n\n/**\n * Type guard: does this admin support schema management?\n * @group Admin\n */\nexport function isSchemaAdmin(admin: DatabaseAdmin | undefined): admin is SchemaAdmin {\n return !!admin && (\n typeof (admin as SchemaAdmin).fetchUnmappedTables === \"function\" ||\n typeof (admin as SchemaAdmin).fetchTableMetadata === \"function\"\n );\n}\n\n/**\n * Type guard: does this admin support database branching?\n * @group Admin\n */\nexport function isBranchAdmin(admin: DatabaseAdmin | undefined): admin is BranchAdmin {\n return !!admin && typeof (admin as BranchAdmin).createBranch === \"function\";\n}\n\n// =============================================================================\n// LIFECYCLE INTERFACES (1.4)\n// =============================================================================\n\n/**\n * Health check result returned by `healthCheck()`.\n * @group Lifecycle\n */\nexport interface HealthCheckResult {\n /** Whether the backend is healthy and able to serve requests. */\n healthy: boolean;\n /** Round-trip latency to the database in milliseconds. */\n latencyMs: number;\n /** Optional details (e.g., pool stats, replication lag). */\n details?: Record<string, unknown>;\n}\n\n/**\n * Lifecycle contract for backend components that hold resources\n * (database connections, WebSocket pools, timers, etc.).\n *\n * All methods are optional — simple backends (e.g., in-memory) can skip them.\n * @group Lifecycle\n */\nexport interface BackendLifecycle {\n /**\n * Initialize the backend: open connections, run migrations, seed data.\n * Called once during startup. Idempotent.\n */\n initialize?(): Promise<void>;\n\n /**\n * Check whether the backend is healthy and reachable.\n * Should be fast (< 1 s) and safe to call frequently.\n */\n healthCheck?(): Promise<HealthCheckResult>;\n\n /**\n * Gracefully shut down: close connections, flush buffers, cancel timers.\n * After calling `destroy()`, no other methods should be called.\n */\n destroy?(): Promise<void>;\n}\n\n// =============================================================================\n// BACKEND FACTORY INTERFACES\n// =============================================================================\n\n/**\n * Configuration for creating a database backend\n */\nexport interface BackendConfig {\n /**\n * Type of database backend\n */\n type: string;\n\n /**\n * Database connection (implementation-specific)\n */\n connection: unknown;\n\n /**\n * Schema definition (implementation-specific, e.g., Drizzle schema for PostgreSQL)\n */\n schema?: unknown;\n}\n\n/**\n * A complete backend instance with all required services.\n *\n * Now includes optional lifecycle management and admin capabilities.\n */\nexport interface BackendInstance extends BackendLifecycle {\n /**\n * Entity repository for CRUD operations\n */\n entityRepository: EntityRepository;\n\n /**\n * Realtime provider for subscriptions\n */\n realtimeProvider: RealtimeProvider;\n\n /**\n * Collection registry\n */\n collectionRegistry: CollectionRegistryInterface;\n\n /**\n * The underlying database connection\n */\n connection: DatabaseConnection;\n\n /**\n * Administrative operations (SQL, schema, documents).\n * What's available depends on the backend type — use type guards\n * (`isSQLAdmin`, `isSchemaAdmin`, etc.) to narrow.\n */\n admin?: DatabaseAdmin;\n}\n\n/**\n * Factory function type for creating backend instances\n */\nexport type BackendFactory<TConfig extends BackendConfig = BackendConfig> =\n (config: TConfig) => BackendInstance;\n\n// =============================================================================\n// BACKEND BOOTSTRAPPER (1.2)\n// =============================================================================\n\n/**\n * A `BackendBootstrapper` encapsulates all driver-specific initialization logic.\n *\n * Instead of hard-coding Postgres setup into `initializeRebaseBackend()`,\n * each database backend provides its own bootstrapper that knows how to:\n * - Create the DataDriver from a config object\n * - Optionally initialize auth tables\n * - Optionally create a realtime service\n * - Mount driver-specific API routes\n *\n * The main `initializeRebaseBackend()` becomes a **coordinator** that iterates\n * registered bootstrappers, calls their hooks, and wires the results together.\n *\n * @group Backend\n *\n * @example\n * ```typescript\n * // Third-party MySQL bootstrapper\n * const mysqlBootstrapper: BackendBootstrapper = {\n * type: \"mysql\",\n * initializeDriver: async (config) => new MySQLDataDriver(config.connection),\n * initializeRealtime: async (config) => new MySQLChangeStreamRealtime(config.connection),\n * };\n *\n * initializeRebaseBackend({\n * ...config,\n * bootstrappers: [postgresBootstrapper, mysqlBootstrapper]\n * });\n * ```\n */\nexport interface BackendBootstrapper {\n /**\n * Which driver type this bootstrapper handles.\n * Must match the `type` field on the driver config object\n * (e.g., `\"postgres\"`, `\"mongodb\"`, `\"mysql\"`).\n */\n type: string;\n\n /**\n * Unique identifier for this bootstrapper instance.\n * Used to register the driver in the driver registry.\n * Defaults to `type` if not set.\n */\n id?: string;\n\n /**\n * Whether this bootstrapper provides the default driver.\n * When true, the coordinator uses this driver as the primary one.\n */\n isDefault?: boolean;\n\n /**\n * Run database migrations for this driver.\n * Called by the coordinator after all drivers are initialized.\n */\n runMigrations?(config: unknown, driverResult: InitializedDriver): Promise<void>;\n\n /**\n * Create a DataDriver from the given config.\n * This is the only **required** method.\n */\n initializeDriver(config: unknown): Promise<InitializedDriver>;\n\n /**\n * Initialize auth tables / services if this driver supports them.\n * Return undefined if auth is not supported by this backend.\n */\n initializeAuth?(config: unknown, driverResult: InitializedDriver): Promise<BootstrappedAuth | undefined>;\n\n /**\n * Initialize history tables / services if this driver supports them.\n * Return undefined if history is not supported by this backend.\n */\n initializeHistory?(config: unknown, driverResult: InitializedDriver): Promise<{ historyService: unknown } | undefined>;\n\n /**\n * Create a realtime provider for this driver.\n * Return undefined if the driver does not support realtime.\n */\n initializeRealtime?(config: unknown, driverResult: InitializedDriver): Promise<RealtimeProvider | undefined>;\n\n /**\n * Mount any driver-specific HTTP routes (e.g., custom admin endpoints).\n * Called after all drivers are initialized.\n */\n mountRoutes?(app: unknown, basePath: string, driverResult: InitializedDriver): void;\n\n /**\n * Return admin capabilities for this driver.\n */\n getAdmin?(driverResult: InitializedDriver): DatabaseAdmin | undefined;\n\n /**\n * Initialize WebSocket server for realtime operations.\n */\n initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import(\"../controllers/data_driver\").DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> | void;\n}\n\n/**\n * Result of `BackendBootstrapper.initializeDriver()`.\n * @group Backend\n */\nexport interface InitializedDriver {\n /** The DataDriver instance, ready for use. */\n driver: import(\"../controllers/data_driver\").DataDriver;\n\n /** The realtime service, if the driver created one during init. */\n realtimeProvider?: RealtimeProvider;\n\n /** A collection registry to register schema / tables into. */\n collectionRegistry?: CollectionRegistryInterface;\n\n /** The underlying database connection (for lifecycle management). */\n connection?: DatabaseConnection;\n\n /**\n * Opaque handle that the bootstrapper can use in subsequent hooks\n * (e.g., `initializeAuth`, `mountRoutes`) to access driver internals.\n * Not used by the coordinator.\n */\n internals?: unknown;\n}\n\n/**\n * Result of `BackendBootstrapper.initializeAuth()`.\n * @group Backend\n */\nexport interface BootstrappedAuth {\n /** User management service. */\n userService: unknown;\n /** Role management service (optional, roles are now simple strings). */\n roleService?: unknown;\n /** Email service (optional). */\n emailService?: unknown;\n /** Combined Auth Repository for unified token and user management. */\n authRepository?: unknown;\n}\n"],"mappings":";;;;;;;;AAgeA,SAAgB,WAAW,OAAqD;CAC5E,OAAO,CAAC,CAAC,SAAS,OAAQ,MAAmB,eAAe;AAChE"}
|