@rebasepro/server-core 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +22 -6
  2. package/dist/common/src/util/entities.d.ts +2 -2
  3. package/dist/common/src/util/relations.d.ts +1 -1
  4. package/dist/{index-DXVBFp5V.js → index-BZoAtuqi.js} +6 -2
  5. package/dist/index-BZoAtuqi.js.map +1 -0
  6. package/dist/index.es.js +15851 -15065
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/index.umd.js +15825 -15035
  9. package/dist/index.umd.js.map +1 -1
  10. package/dist/server-core/src/auth/adapter-middleware.d.ts +33 -0
  11. package/dist/server-core/src/auth/admin-routes.d.ts +6 -0
  12. package/dist/server-core/src/auth/auth-overrides.d.ts +139 -0
  13. package/dist/server-core/src/auth/builtin-auth-adapter.d.ts +49 -0
  14. package/dist/server-core/src/auth/crypto-utils.d.ts +16 -0
  15. package/dist/server-core/src/auth/custom-auth-adapter.d.ts +39 -0
  16. package/dist/server-core/src/auth/index.d.ts +7 -0
  17. package/dist/server-core/src/auth/interfaces.d.ts +2 -0
  18. package/dist/server-core/src/auth/middleware.d.ts +18 -0
  19. package/dist/server-core/src/auth/rls-scope.d.ts +31 -0
  20. package/dist/server-core/src/auth/routes.d.ts +7 -1
  21. package/dist/server-core/src/env.d.ts +131 -0
  22. package/dist/server-core/src/index.d.ts +2 -0
  23. package/dist/server-core/src/init.d.ts +62 -3
  24. package/dist/types/src/controllers/auth.d.ts +9 -8
  25. package/dist/types/src/controllers/client.d.ts +3 -0
  26. package/dist/types/src/types/auth_adapter.d.ts +356 -0
  27. package/dist/types/src/types/collections.d.ts +67 -2
  28. package/dist/types/src/types/database_adapter.d.ts +94 -0
  29. package/dist/types/src/types/entity_actions.d.ts +7 -1
  30. package/dist/types/src/types/entity_callbacks.d.ts +1 -1
  31. package/dist/types/src/types/entity_views.d.ts +36 -1
  32. package/dist/types/src/types/index.d.ts +2 -0
  33. package/dist/types/src/types/plugins.d.ts +1 -1
  34. package/dist/types/src/types/properties.d.ts +24 -5
  35. package/dist/types/src/types/property_config.d.ts +6 -2
  36. package/dist/types/src/types/relations.d.ts +1 -1
  37. package/dist/types/src/types/translations.d.ts +8 -0
  38. package/dist/types/src/users/user.d.ts +5 -0
  39. package/package.json +26 -26
  40. package/src/api/errors.ts +1 -1
  41. package/src/api/graphql/graphql-schema-generator.ts +7 -0
  42. package/src/api/openapi-generator.ts +13 -1
  43. package/src/api/rest/api-generator-count.test.ts +14 -12
  44. package/src/api/rest/query-parser.ts +2 -20
  45. package/src/auth/adapter-middleware.ts +83 -0
  46. package/src/auth/admin-routes.ts +36 -43
  47. package/src/auth/auth-overrides.ts +172 -0
  48. package/src/auth/builtin-auth-adapter.ts +384 -0
  49. package/src/auth/crypto-utils.ts +31 -0
  50. package/src/auth/custom-auth-adapter.ts +85 -0
  51. package/src/auth/index.ts +10 -0
  52. package/src/auth/interfaces.ts +2 -0
  53. package/src/auth/jwt.ts +3 -1
  54. package/src/auth/middleware.ts +2 -46
  55. package/src/auth/rls-scope.ts +58 -0
  56. package/src/auth/routes.ts +74 -32
  57. package/src/cron/cron-scheduler.test.ts +9 -9
  58. package/src/cron/cron-scheduler.ts +1 -1
  59. package/src/env.ts +224 -0
  60. package/src/index.ts +4 -0
  61. package/src/init.ts +355 -135
  62. package/src/storage/routes.ts +1 -19
  63. package/src/utils/logging.ts +3 -3
  64. package/test/admin-routes.test.ts +10 -4
  65. package/test/auth-routes.test.ts +2 -2
  66. package/test/backend-hooks-admin.test.ts +32 -12
  67. package/test/custom-auth-adapter.test.ts +177 -0
  68. package/test/env.test.ts +138 -0
  69. package/test/query-parser.test.ts +0 -29
  70. package/tsconfig.json +3 -0
  71. package/dist/index-DXVBFp5V.js.map +0 -1
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Adapter Auth Middleware
3
+ *
4
+ * Creates a Hono middleware that delegates authentication to an `AuthAdapter`
5
+ * instead of hardcoded JWT verification. This is used when the user passes
6
+ * an `AuthAdapter` to `initializeRebaseBackend()`.
7
+ *
8
+ * The middleware:
9
+ * 1. Calls `adapter.verifyRequest(request)` to resolve the user
10
+ * 2. Scopes the DataDriver via `withAuth()` for RLS
11
+ * 3. Enforces auth (401) when `requireAuth` is true and no user is found
12
+ *
13
+ * The behavior is identical to `createAuthMiddleware()` — only the
14
+ * token verification strategy is pluggable.
15
+ */
16
+ import type { MiddlewareHandler } from "hono";
17
+ import type { DataDriver, AuthAdapter } from "@rebasepro/types";
18
+ import type { HonoEnv } from "../api/types";
19
+ export interface AdapterAuthMiddlewareOptions {
20
+ /** The auth adapter to delegate verification to. */
21
+ adapter: AuthAdapter;
22
+ /** The DataDriver to scope via withAuth() for RLS. */
23
+ driver: DataDriver;
24
+ /**
25
+ * If true, return 401 when no valid user is resolved.
26
+ * Defaults to `true` (secure by default).
27
+ */
28
+ requireAuth?: boolean;
29
+ }
30
+ /**
31
+ * Create a Hono middleware that uses an `AuthAdapter` for request verification.
32
+ */
33
+ export declare function createAdapterAuthMiddleware(options: AdapterAuthMiddlewareOptions): MiddlewareHandler<HonoEnv>;
@@ -1,4 +1,5 @@
1
1
  import { Hono } from "hono";
2
+ import type { AuthOverrides } from "./auth-overrides";
2
3
  import { AuthModuleConfig } from "./routes";
3
4
  import type { BackendHooks } from "@rebasepro/types";
4
5
  interface AdminRouteOptions extends AuthModuleConfig {
@@ -12,6 +13,11 @@ interface AdminRouteOptions extends AuthModuleConfig {
12
13
  * Backend-level hooks for intercepting admin data.
13
14
  */
14
15
  hooks?: BackendHooks;
16
+ /**
17
+ * Auth overrides for customizing password hashing, credential
18
+ * verification, lifecycle hooks, etc.
19
+ */
20
+ overrides?: AuthOverrides;
15
21
  }
16
22
  import { HonoEnv } from "../api/types";
17
23
  /**
@@ -0,0 +1,139 @@
1
+ /**
2
+ * AuthOverrides
3
+ *
4
+ * Override specific behaviors of the built-in Rebase auth system.
5
+ *
6
+ * Each method replaces one piece of the default implementation.
7
+ * Unset methods fall through to the built-in defaults (scrypt passwords,
8
+ * standard validation rules, etc.).
9
+ *
10
+ * This interface is intentionally open for extension — new overrides
11
+ * can be added as optional methods without breaking existing configurations.
12
+ *
13
+ * @example bcrypt password support
14
+ * ```ts
15
+ * import bcrypt from "bcrypt";
16
+ *
17
+ * const overrides: AuthOverrides = {
18
+ * hashPassword: (pw) => bcrypt.hash(pw, 12),
19
+ * verifyPassword: (pw, hash) => bcrypt.compare(pw, hash),
20
+ * validatePasswordStrength: (pw) => ({
21
+ * valid: pw.length >= 6,
22
+ * errors: pw.length < 6 ? ["Password must be at least 6 characters"] : []
23
+ * })
24
+ * };
25
+ * ```
26
+ *
27
+ * @example Override the entire login credential check
28
+ * ```ts
29
+ * const overrides: AuthOverrides = {
30
+ * verifyCredentials: async (email, password, repo) => {
31
+ * const user = await repo.getUserByEmail(email);
32
+ * if (!user || !user.passwordHash) return null;
33
+ * const valid = await myCustomVerify(password, user.passwordHash);
34
+ * return valid ? user : null;
35
+ * }
36
+ * };
37
+ * ```
38
+ */
39
+ import type { PasswordValidationResult } from "./password";
40
+ import type { AuthRepository, UserData, CreateUserData } from "./interfaces";
41
+ /**
42
+ * Authentication method identifier for lifecycle hooks.
43
+ */
44
+ export type AuthMethod = "login" | "register" | "oauth" | "refresh" | "password-reset";
45
+ /**
46
+ * Override specific parts of the built-in Rebase auth implementation.
47
+ *
48
+ * Every method is optional. The built-in defaults apply for any method
49
+ * that is not provided.
50
+ */
51
+ export interface AuthOverrides {
52
+ /**
53
+ * Hash a cleartext password for storage.
54
+ *
55
+ * Default: scrypt (Node.js crypto, 64-byte key, random 32-byte salt).
56
+ *
57
+ * @param password - The cleartext password.
58
+ * @returns The hashed password string (format is implementation-defined).
59
+ */
60
+ hashPassword?(password: string): Promise<string>;
61
+ /**
62
+ * Verify a cleartext password against a stored hash.
63
+ *
64
+ * Default: scrypt verification with timing-safe comparison.
65
+ *
66
+ * @param password - The cleartext password to check.
67
+ * @param storedHash - The hash string retrieved from the database.
68
+ * @returns `true` if the password matches the hash.
69
+ */
70
+ verifyPassword?(password: string, storedHash: string): Promise<boolean>;
71
+ /**
72
+ * Validate password strength before hashing.
73
+ *
74
+ * Default: minimum 8 characters, at least one uppercase, one lowercase, one digit.
75
+ *
76
+ * @param password - The cleartext password to validate.
77
+ * @returns Validation result with `valid` flag and error messages.
78
+ */
79
+ validatePasswordStrength?(password: string): PasswordValidationResult;
80
+ /**
81
+ * Override the complete credential verification during email/password login.
82
+ *
83
+ * When set, this replaces the default flow:
84
+ * 1. Look up user by email
85
+ * 2. Verify password hash
86
+ *
87
+ * The auth repository is provided for database access. Return the user
88
+ * data if credentials are valid, or `null` to reject the login.
89
+ *
90
+ * Default: `getUserByEmail(email)` + `verifyPassword(password, user.passwordHash)`.
91
+ */
92
+ verifyCredentials?(email: string, password: string, repo: AuthRepository): Promise<UserData | null>;
93
+ /**
94
+ * Called after any successful authentication event (login, register,
95
+ * OAuth, token refresh, password reset).
96
+ *
97
+ * Use for audit logging, syncing external state, updating
98
+ * last-login timestamps, etc.
99
+ *
100
+ * This is fire-and-forget — errors are logged but do not fail the request.
101
+ */
102
+ onAuthenticated?(user: UserData, method: AuthMethod): Promise<void>;
103
+ /**
104
+ * Called before a new user is created (registration or admin creation).
105
+ *
106
+ * Return modified data to alter what gets stored, or throw an error
107
+ * to reject the creation entirely.
108
+ *
109
+ * Default: passthrough (returns data unchanged).
110
+ */
111
+ beforeUserCreate?(data: CreateUserData): Promise<CreateUserData>;
112
+ /**
113
+ * Called after a new user is created.
114
+ *
115
+ * Use for provisioning external resources, sending notifications
116
+ * to third-party systems, etc.
117
+ *
118
+ * This is fire-and-forget — errors are logged but do not fail the request.
119
+ */
120
+ afterUserCreate?(user: UserData): Promise<void>;
121
+ }
122
+ /**
123
+ * Resolved auth operations — every method is guaranteed to exist.
124
+ * Created by `resolveAuthOverrides()` which merges user overrides
125
+ * with built-in defaults.
126
+ */
127
+ export interface ResolvedAuthOperations {
128
+ hashPassword(password: string): Promise<string>;
129
+ verifyPassword(password: string, storedHash: string): Promise<boolean>;
130
+ validatePasswordStrength(password: string): PasswordValidationResult;
131
+ }
132
+ /**
133
+ * Merge user-provided overrides with the built-in defaults to produce
134
+ * a complete set of resolved operations.
135
+ *
136
+ * This is the single point where defaults are applied — all consumers
137
+ * call this once and use the resolved operations throughout.
138
+ */
139
+ export declare function resolveAuthOverrides(overrides?: AuthOverrides): ResolvedAuthOperations;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * RebaseBuiltinAuthAdapter
3
+ *
4
+ * Wraps Rebase's existing built-in JWT auth system (routes, middleware, user/role
5
+ * management) into the `AuthAdapter` interface. This is the default adapter used
6
+ * when the user passes a plain `RebaseAuthConfig` object.
7
+ *
8
+ * This is NOT a rewrite — it delegates to the existing `createAuthRoutes()`,
9
+ * `createAdminRoutes()`, and `verifyAccessToken()` functions. The goal is to
10
+ * present the same functionality through the pluggable `AuthAdapter` contract.
11
+ */
12
+ import type { AuthAdapter, BackendHooks } from "@rebasepro/types";
13
+ import type { AuthRepository, OAuthProvider } from "./interfaces";
14
+ import type { AuthOverrides } from "./auth-overrides";
15
+ import type { EmailService, EmailConfig } from "../email";
16
+ /**
17
+ * Configuration for the built-in Rebase auth adapter.
18
+ *
19
+ * This mirrors the existing `RebaseAuthConfig` — users pass this and
20
+ * server-core auto-wraps it in a `RebaseBuiltinAuthAdapter`.
21
+ */
22
+ export interface BuiltinAuthAdapterConfig {
23
+ /** The bootstrapper-provided auth repository (users, roles, tokens). */
24
+ authRepository: AuthRepository;
25
+ /** Email service for password resets, verification, etc. */
26
+ emailService?: EmailService;
27
+ /** Email configuration. */
28
+ emailConfig?: EmailConfig;
29
+ /** Whether to allow new user registration. */
30
+ allowRegistration?: boolean;
31
+ /** Default role to assign to new users. */
32
+ defaultRole?: string;
33
+ /** OAuth providers to register. */
34
+ oauthProviders?: OAuthProvider<any>[];
35
+ /** Static service key for server-to-server auth. */
36
+ serviceKey?: string;
37
+ /** Backend hooks for intercepting admin data. */
38
+ hooks?: BackendHooks;
39
+ /** Auth overrides for customizing password, credentials, lifecycle, etc. */
40
+ overrides?: AuthOverrides;
41
+ }
42
+ /**
43
+ * Create the built-in Rebase auth adapter.
44
+ *
45
+ * This wraps the existing auth infrastructure (JWT, OAuth, user/role management)
46
+ * into the `AuthAdapter` interface. It's used internally by `initializeRebaseBackend()`
47
+ * when the user passes a plain `RebaseAuthConfig` object.
48
+ */
49
+ export declare function createBuiltinAuthAdapter(config: BuiltinAuthAdapterConfig): AuthAdapter;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Cryptographic utility functions for auth.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Constant-time string comparison to prevent timing attacks.
8
+ *
9
+ * Used for comparing service keys, tokens, and other secrets where
10
+ * timing side-channels could leak information about the expected value.
11
+ *
12
+ * @param a - First string to compare.
13
+ * @param b - Second string to compare.
14
+ * @returns `true` if the strings are identical, `false` otherwise.
15
+ */
16
+ export declare function safeCompare(a: string, b: string): boolean;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Custom Auth Adapter Factory
3
+ *
4
+ * Provides a minimal-config way for users with existing auth systems
5
+ * to plug into Rebase. Only `verifyRequest` is required — everything
6
+ * else is optional.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { createCustomAuthAdapter } from "@rebasepro/server-core";
11
+ *
12
+ * const auth = createCustomAuthAdapter({
13
+ * verifyRequest: async (request) => {
14
+ * const token = request.headers.get("Authorization")?.replace("Bearer ", "");
15
+ * if (!token) return null;
16
+ * const decoded = jwt.verify(token, MY_SECRET);
17
+ * return {
18
+ * uid: decoded.sub,
19
+ * email: decoded.email,
20
+ * displayName: decoded.name,
21
+ * roles: decoded.roles || [],
22
+ * isAdmin: decoded.roles?.includes("admin") ?? false,
23
+ * };
24
+ * },
25
+ * });
26
+ * ```
27
+ */
28
+ import type { AuthAdapter, CustomAuthAdapterOptions } from "@rebasepro/types";
29
+ /**
30
+ * Create a custom auth adapter from minimal options.
31
+ *
32
+ * This is the recommended entry point for users who already have their own
33
+ * auth system and just need to tell Rebase how to extract the user from
34
+ * incoming requests.
35
+ *
36
+ * @param options - Configuration options. Only `verifyRequest` is required.
37
+ * @returns A fully-formed `AuthAdapter` ready for `initializeRebaseBackend()`.
38
+ */
39
+ export declare function createCustomAuthAdapter(options: CustomAuthAdapterOptions): AuthAdapter;
@@ -3,6 +3,8 @@ export { configureJwt, generateAccessToken, verifyAccessToken, generateRefreshTo
3
3
  export type { JwtConfig, AccessTokenPayload } from "./jwt";
4
4
  export { hashPassword, verifyPassword, validatePasswordStrength } from "./password";
5
5
  export type { PasswordValidationResult } from "./password";
6
+ export type { AuthOverrides, AuthMethod, ResolvedAuthOperations } from "./auth-overrides";
7
+ export { resolveAuthOverrides } from "./auth-overrides";
6
8
  export { createGoogleProvider } from "./google-oauth";
7
9
  export type { GoogleProviderConfig } from "./google-oauth";
8
10
  export { createLinkedinProvider } from "./linkedin-oauth";
@@ -22,3 +24,8 @@ export { createAuthRoutes } from "./routes";
22
24
  export type { AuthModuleConfig } from "./routes";
23
25
  export { createAdminRoutes } from "./admin-routes";
24
26
  export { createRateLimiter, defaultAuthLimiter, strictAuthLimiter } from "./rate-limiter";
27
+ export { createBuiltinAuthAdapter } from "./builtin-auth-adapter";
28
+ export type { BuiltinAuthAdapterConfig } from "./builtin-auth-adapter";
29
+ export { createCustomAuthAdapter } from "./custom-auth-adapter";
30
+ export { createAdapterAuthMiddleware } from "./adapter-middleware";
31
+ export type { AdapterAuthMiddlewareOptions } from "./adapter-middleware";
@@ -18,6 +18,7 @@ export interface UserData {
18
18
  emailVerified: boolean;
19
19
  emailVerificationToken?: string | null;
20
20
  emailVerificationSentAt?: Date | null;
21
+ metadata?: Record<string, unknown>;
21
22
  createdAt: Date;
22
23
  updatedAt: Date;
23
24
  }
@@ -30,6 +31,7 @@ export interface CreateUserData {
30
31
  displayName?: string;
31
32
  photoUrl?: string;
32
33
  emailVerified?: boolean;
34
+ metadata?: Record<string, unknown>;
33
35
  }
34
36
  /**
35
37
  * User Identity Data (OAuth accounts linked to user)
@@ -78,4 +78,22 @@ export declare const optionalAuth: MiddlewareHandler<HonoEnv>;
78
78
  * Extract user from token - for WebSocket authentication
79
79
  */
80
80
  export declare function extractUserFromToken(token: string): AccessTokenPayload | null;
81
+ /**
82
+ * Create a configurable auth middleware that handles:
83
+ * 1. Token extraction (via custom validator or JWT Bearer token)
84
+ * 2. RLS-scoped DataDriver via withAuth()
85
+ * 3. Enforcement (401 when requireAuth is true and no user)
86
+ *
87
+ * **Secure by default:** `requireAuth` defaults to `true`. Anonymous
88
+ * access is only allowed when the developer explicitly opts out by
89
+ * setting `requireAuth: false`, indicating that Postgres RLS policies
90
+ * fully control access.
91
+ *
92
+ * **Fail-closed:** The raw unscoped driver is never placed in the
93
+ * request context. Every code path either scopes via `withAuth()` or
94
+ * rejects the request. This prevents silent RLS bypass.
95
+ *
96
+ * This is the single source of truth for HTTP auth in Rebase.
97
+ * Use this instead of manually parsing tokens in route handlers.
98
+ */
81
99
  export declare function createAuthMiddleware(options: AuthMiddlewareOptions): MiddlewareHandler<HonoEnv>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Shared RLS (Row-Level Security) scoping helper.
3
+ *
4
+ * DataDrivers may implement a `withAuth()` method that returns a scoped
5
+ * clone of the driver with RLS policies applied for the given user.
6
+ * This is database-specific (e.g. Postgres SET LOCAL ROLE) and is not
7
+ * part of the core DataDriver interface.
8
+ *
9
+ * This module provides the shared duck-typing logic used by the
10
+ * adapter-aware middleware.
11
+ *
12
+ * @module
13
+ */
14
+ import type { DataDriver } from "@rebasepro/types";
15
+ /**
16
+ * Scope a DataDriver via `withAuth()` for RLS.
17
+ *
18
+ * SECURITY: If `withAuth()` is available but fails, the error is re-thrown
19
+ * so the request is **denied** rather than proceeding with unscoped access
20
+ * (fail-closed behavior).
21
+ *
22
+ * If the driver does not support RLS, the original driver is returned.
23
+ *
24
+ * @param driver - The DataDriver to scope.
25
+ * @param user - The authenticated user identity for RLS.
26
+ * @returns The RLS-scoped DataDriver (or the original if RLS is unsupported).
27
+ */
28
+ export declare function scopeDataDriver(driver: DataDriver, user: {
29
+ uid: string;
30
+ roles?: string[];
31
+ }): Promise<DataDriver>;
@@ -1,5 +1,6 @@
1
1
  import { Hono } from "hono";
2
2
  import type { AuthRepository, OAuthProvider } from "./interfaces";
3
+ import type { AuthOverrides } from "./auth-overrides";
3
4
  import { EmailService, EmailConfig } from "../email";
4
5
  import { HonoEnv } from "../api/types";
5
6
  /**
@@ -14,9 +15,14 @@ export interface AuthModuleConfig {
14
15
  /** Default role ID to assign to new users (default: none). Must NOT be "admin". */
15
16
  defaultRole?: string;
16
17
  /** Optional array of OAuth providers */
17
- oauthProviders?: OAuthProvider[];
18
+ oauthProviders?: OAuthProvider<any>[];
18
19
  /** When true, blocks all self-registration regardless of `allowRegistration`. */
19
20
  disableSelfRegistration?: boolean;
21
+ /**
22
+ * Auth overrides for customizing password hashing, credential
23
+ * verification, lifecycle hooks, etc.
24
+ */
25
+ overrides?: AuthOverrides;
20
26
  /**
21
27
  * Callback that checks if bootstrap has already been completed.
22
28
  * Used by GET /auth/config to report `needsSetup` status.
@@ -0,0 +1,131 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The full set of environment variables recognized by a Rebase backend.
4
+ */
5
+ declare const rebaseEnvSchema: z.ZodObject<{
6
+ NODE_ENV: z.ZodDefault<z.ZodEnum<["development", "production", "test"]>>;
7
+ PORT: z.ZodEffects<z.ZodDefault<z.ZodString>, number, string | undefined>;
8
+ DATABASE_URL: z.ZodString;
9
+ ADMIN_CONNECTION_STRING: z.ZodOptional<z.ZodString>;
10
+ JWT_SECRET: z.ZodString;
11
+ JWT_ACCESS_EXPIRES_IN: z.ZodDefault<z.ZodString>;
12
+ JWT_REFRESH_EXPIRES_IN: z.ZodDefault<z.ZodString>;
13
+ GOOGLE_CLIENT_ID: z.ZodOptional<z.ZodString>;
14
+ GOOGLE_CLIENT_SECRET: z.ZodOptional<z.ZodString>;
15
+ REBASE_SERVICE_KEY: z.ZodOptional<z.ZodString>;
16
+ ALLOW_REGISTRATION: z.ZodEffects<z.ZodDefault<z.ZodEnum<["true", "false", ""]>>, boolean, "" | "true" | "false" | undefined>;
17
+ ALLOW_LOCALHOST_IN_PRODUCTION: z.ZodEffects<z.ZodOptional<z.ZodEnum<["true", "false", ""]>>, boolean, "" | "true" | "false" | undefined>;
18
+ CORS_ORIGINS: z.ZodOptional<z.ZodString>;
19
+ FRONTEND_URL: z.ZodOptional<z.ZodString>;
20
+ DB_POOL_MAX: z.ZodEffects<z.ZodDefault<z.ZodString>, number, string | undefined>;
21
+ DB_POOL_IDLE_TIMEOUT: z.ZodEffects<z.ZodDefault<z.ZodString>, number, string | undefined>;
22
+ DB_POOL_CONNECT_TIMEOUT: z.ZodEffects<z.ZodDefault<z.ZodString>, number, string | undefined>;
23
+ FORCE_LOCAL_STORAGE: z.ZodEffects<z.ZodOptional<z.ZodEnum<["true", "false", ""]>>, boolean, "" | "true" | "false" | undefined>;
24
+ STORAGE_TYPE: z.ZodDefault<z.ZodEnum<["local", "s3"]>>;
25
+ STORAGE_PATH: z.ZodOptional<z.ZodString>;
26
+ S3_BUCKET: z.ZodOptional<z.ZodString>;
27
+ S3_REGION: z.ZodOptional<z.ZodString>;
28
+ S3_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
29
+ S3_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
30
+ S3_ENDPOINT: z.ZodOptional<z.ZodString>;
31
+ S3_FORCE_PATH_STYLE: z.ZodEffects<z.ZodOptional<z.ZodEnum<["true", "false", ""]>>, boolean, "" | "true" | "false" | undefined>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ NODE_ENV: "development" | "production" | "test";
34
+ PORT: number;
35
+ DATABASE_URL: string;
36
+ JWT_SECRET: string;
37
+ JWT_ACCESS_EXPIRES_IN: string;
38
+ JWT_REFRESH_EXPIRES_IN: string;
39
+ ALLOW_REGISTRATION: boolean;
40
+ ALLOW_LOCALHOST_IN_PRODUCTION: boolean;
41
+ DB_POOL_MAX: number;
42
+ DB_POOL_IDLE_TIMEOUT: number;
43
+ DB_POOL_CONNECT_TIMEOUT: number;
44
+ FORCE_LOCAL_STORAGE: boolean;
45
+ STORAGE_TYPE: "local" | "s3";
46
+ S3_FORCE_PATH_STYLE: boolean;
47
+ ADMIN_CONNECTION_STRING?: string | undefined;
48
+ GOOGLE_CLIENT_ID?: string | undefined;
49
+ GOOGLE_CLIENT_SECRET?: string | undefined;
50
+ REBASE_SERVICE_KEY?: string | undefined;
51
+ CORS_ORIGINS?: string | undefined;
52
+ FRONTEND_URL?: string | undefined;
53
+ STORAGE_PATH?: string | undefined;
54
+ S3_BUCKET?: string | undefined;
55
+ S3_REGION?: string | undefined;
56
+ S3_ACCESS_KEY_ID?: string | undefined;
57
+ S3_SECRET_ACCESS_KEY?: string | undefined;
58
+ S3_ENDPOINT?: string | undefined;
59
+ }, {
60
+ DATABASE_URL: string;
61
+ JWT_SECRET: string;
62
+ NODE_ENV?: "development" | "production" | "test" | undefined;
63
+ PORT?: string | undefined;
64
+ ADMIN_CONNECTION_STRING?: string | undefined;
65
+ JWT_ACCESS_EXPIRES_IN?: string | undefined;
66
+ JWT_REFRESH_EXPIRES_IN?: string | undefined;
67
+ GOOGLE_CLIENT_ID?: string | undefined;
68
+ GOOGLE_CLIENT_SECRET?: string | undefined;
69
+ REBASE_SERVICE_KEY?: string | undefined;
70
+ ALLOW_REGISTRATION?: "" | "true" | "false" | undefined;
71
+ ALLOW_LOCALHOST_IN_PRODUCTION?: "" | "true" | "false" | undefined;
72
+ CORS_ORIGINS?: string | undefined;
73
+ FRONTEND_URL?: string | undefined;
74
+ DB_POOL_MAX?: string | undefined;
75
+ DB_POOL_IDLE_TIMEOUT?: string | undefined;
76
+ DB_POOL_CONNECT_TIMEOUT?: string | undefined;
77
+ FORCE_LOCAL_STORAGE?: "" | "true" | "false" | undefined;
78
+ STORAGE_TYPE?: "local" | "s3" | undefined;
79
+ STORAGE_PATH?: string | undefined;
80
+ S3_BUCKET?: string | undefined;
81
+ S3_REGION?: string | undefined;
82
+ S3_ACCESS_KEY_ID?: string | undefined;
83
+ S3_SECRET_ACCESS_KEY?: string | undefined;
84
+ S3_ENDPOINT?: string | undefined;
85
+ S3_FORCE_PATH_STYLE?: "" | "true" | "false" | undefined;
86
+ }>;
87
+ /** Inferred type of the validated environment. */
88
+ export type RebaseEnv = z.infer<typeof rebaseEnvSchema>;
89
+ /**
90
+ * Load and validate the Rebase environment configuration from `process.env`.
91
+ *
92
+ * Call this **after** your `.env` file has been loaded (via `dotenv`, `--env-file`,
93
+ * container injection, etc.). This function does not load `.env` files itself —
94
+ * that is a deployment concern, not a framework concern.
95
+ *
96
+ * Behavior:
97
+ * - Auto-generates ephemeral `JWT_SECRET` and `REBASE_SERVICE_KEY` in
98
+ * non-production mode so developers can start without manual setup.
99
+ * - Blocks auto-generated secrets in production.
100
+ * - Returns a fully typed, validated env object.
101
+ *
102
+ * Use `extend` to add your own typed env variables on top of the base Rebase schema:
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * import dotenv from "dotenv";
107
+ * import { z } from "zod";
108
+ * import { loadEnv } from "@rebasepro/server-core";
109
+ *
110
+ * dotenv.config({ path: "../../.env" });
111
+ *
112
+ * // Basic — just Rebase env vars:
113
+ * export const env = loadEnv();
114
+ *
115
+ * // Extended — add your own typed vars:
116
+ * export const env = loadEnv({
117
+ * extend: z.object({
118
+ * SMTP_HOST: z.string().optional(),
119
+ * SMTP_PORT: z.string().default("587").transform(Number),
120
+ * STRIPE_SECRET_KEY: z.string(),
121
+ * })
122
+ * });
123
+ * // env.SMTP_HOST → string | undefined (fully typed)
124
+ * // env.STRIPE_SECRET_KEY → string (validated, required)
125
+ * ```
126
+ */
127
+ export declare function loadEnv(): RebaseEnv;
128
+ export declare function loadEnv<E extends z.AnyZodObject>(options: {
129
+ extend: E;
130
+ }): RebaseEnv & z.infer<E>;
131
+ export {};
@@ -27,3 +27,5 @@ export * from "./functions";
27
27
  export * from "./cron";
28
28
  export * from "./serve-spa";
29
29
  export * from "./utils/dev-port";
30
+ export { loadEnv } from "./env";
31
+ export type { RebaseEnv } from "./env";
@@ -1,4 +1,4 @@
1
- import { DataDriver, EntityCollection, BackendBootstrapper, BootstrappedAuth, RealtimeProvider, HealthCheckResult, BackendHooks } from "@rebasepro/types";
1
+ import { DataDriver, EntityCollection, BackendBootstrapper, BootstrappedAuth, RealtimeProvider, HealthCheckResult, BackendHooks, AuthAdapter, DatabaseAdapter } from "@rebasepro/types";
2
2
  import { BackendCollectionRegistry } from "./collections/BackendCollectionRegistry";
3
3
  import { DriverRegistry } from "./services/driver-registry";
4
4
  import { Server } from "http";
@@ -7,6 +7,7 @@ import { HonoEnv } from "./api/types";
7
7
  import { BackendStorageConfig, StorageController, StorageRegistry } from "./storage";
8
8
  import { EmailConfig } from "./email";
9
9
  import type { OAuthProvider } from "./auth/interfaces";
10
+ import type { AuthOverrides } from "./auth/auth-overrides";
10
11
  export interface RebaseAuthConfig {
11
12
  jwtSecret?: string;
12
13
  accessExpiresIn?: string;
@@ -81,6 +82,24 @@ export interface RebaseAuthConfig {
81
82
  };
82
83
  defaultRole?: string;
83
84
  providers?: OAuthProvider<any>[];
85
+ /**
86
+ * Override specific parts of the built-in auth implementation.
87
+ *
88
+ * Each override replaces one piece of the default behavior while
89
+ * keeping everything else intact. Unset overrides fall through
90
+ * to the built-in defaults (scrypt passwords, standard validation, etc.).
91
+ *
92
+ * @example bcrypt passwords with a custom hash
93
+ * ```ts
94
+ * import bcrypt from "bcrypt";
95
+ *
96
+ * overrides: {
97
+ * hashPassword: (pw) => bcrypt.hash(pw, 12),
98
+ * verifyPassword: (pw, hash) => bcrypt.compare(pw, hash),
99
+ * }
100
+ * ```
101
+ */
102
+ overrides?: AuthOverrides;
84
103
  [key: string]: unknown;
85
104
  }
86
105
  export interface RebaseBackendConfig {
@@ -89,11 +108,36 @@ export interface RebaseBackendConfig {
89
108
  server: Server;
90
109
  app: Hono<HonoEnv>;
91
110
  basePath?: string;
92
- bootstrappers: BackendBootstrapper[];
111
+ /**
112
+ * Database bootstrappers.
113
+ */
114
+ bootstrappers?: BackendBootstrapper[];
115
+ /**
116
+ * Database adapter.
117
+ *
118
+ * When set, this takes precedence over `bootstrappers`.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import { createPostgresAdapter } from "@rebasepro/server-postgresql";
123
+ * database: createPostgresAdapter({ connection: db, schema }),
124
+ * ```
125
+ */
126
+ database?: DatabaseAdapter;
93
127
  logging?: {
94
128
  level?: "error" | "warn" | "info" | "debug";
95
129
  };
96
- auth?: RebaseAuthConfig;
130
+ /**
131
+ * Authentication configuration.
132
+ *
133
+ * Accepts **either**:
134
+ * - `RebaseAuthConfig` — built-in configuration
135
+ * - `AuthAdapter` — pluggable adapter for external auth (Clerk, Auth0, etc.)
136
+ *
137
+ * When a plain config object is provided, the built-in adapter is created
138
+ * automatically from the bootstrapper's `initializeAuth()` result.
139
+ */
140
+ auth?: RebaseAuthConfig | AuthAdapter;
97
141
  /**
98
142
  * Storage configuration. Accepts:
99
143
  *
@@ -106,6 +150,12 @@ export interface RebaseBackendConfig {
106
150
  enableSwagger?: boolean;
107
151
  functionsDir?: string;
108
152
  cronsDir?: string;
153
+ /**
154
+ * Enable/disable database persistence for cron job execution logs.
155
+ * When set to false, cron jobs will run but logs will not be persisted to the database.
156
+ * Default: true.
157
+ */
158
+ cronPersistence?: boolean;
109
159
  /**
110
160
  * Maximum request body size in bytes for API routes (default: 10MB).
111
161
  * Set to 0 to disable the global limit entirely.
@@ -140,6 +190,15 @@ export interface RebaseBackendConfig {
140
190
  */
141
191
  hooks?: BackendHooks;
142
192
  }
193
+ /**
194
+ * Type guard to detect whether the `auth` config is an `AuthAdapter`
195
+ * (has a `verifyRequest` method) vs a plain `RebaseAuthConfig` (plain object).
196
+ */
197
+ export declare function isAuthAdapter(auth: RebaseAuthConfig | AuthAdapter): auth is AuthAdapter;
198
+ /**
199
+ * Type guard to detect whether `database` is a `DatabaseAdapter`.
200
+ */
201
+ export declare function isDatabaseAdapter(db: unknown): db is DatabaseAdapter;
143
202
  export interface RebaseBackendInstance {
144
203
  driverRegistry: DriverRegistry;
145
204
  driver: DataDriver;