@rebasepro/server-core 0.7.0 → 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.
Files changed (155) hide show
  1. package/build-errors.txt +52 -0
  2. package/dist/api/ast-schema-editor.d.ts +21 -0
  3. package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
  4. package/dist/api/errors.d.ts +53 -0
  5. package/dist/api/graphql/graphql-schema-generator.d.ts +44 -0
  6. package/dist/api/graphql/index.d.ts +1 -0
  7. package/dist/api/index.d.ts +9 -0
  8. package/dist/api/logs-routes.d.ts +37 -0
  9. package/dist/api/openapi-generator.d.ts +16 -0
  10. package/dist/api/rest/api-generator.d.ts +76 -0
  11. package/dist/api/rest/index.d.ts +1 -0
  12. package/dist/api/rest/query-parser.d.ts +8 -0
  13. package/dist/api/schema-editor-routes.d.ts +3 -0
  14. package/dist/api/server.d.ts +40 -0
  15. package/dist/api/types.d.ts +104 -0
  16. package/dist/auth/adapter-middleware.d.ts +43 -0
  17. package/dist/auth/admin-roles-route.d.ts +18 -0
  18. package/dist/auth/admin-user-ops.d.ts +79 -0
  19. package/dist/auth/admin-users-route.d.ts +28 -0
  20. package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
  21. package/dist/auth/api-keys/api-key-permission-guard.d.ts +32 -0
  22. package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
  23. package/dist/auth/api-keys/api-key-store.d.ts +35 -0
  24. package/dist/auth/api-keys/api-key-types.d.ts +96 -0
  25. package/dist/auth/api-keys/index.d.ts +17 -0
  26. package/dist/auth/apple-oauth.d.ts +30 -0
  27. package/dist/auth/auth-hooks.d.ts +242 -0
  28. package/dist/auth/bitbucket-oauth.d.ts +11 -0
  29. package/dist/auth/builtin-auth-adapter.d.ts +51 -0
  30. package/dist/auth/crypto-utils.d.ts +16 -0
  31. package/dist/auth/custom-auth-adapter.d.ts +39 -0
  32. package/dist/auth/discord-oauth.d.ts +14 -0
  33. package/dist/auth/facebook-oauth.d.ts +14 -0
  34. package/dist/auth/github-oauth.d.ts +15 -0
  35. package/dist/auth/gitlab-oauth.d.ts +13 -0
  36. package/dist/auth/google-oauth.d.ts +47 -0
  37. package/dist/auth/index.d.ts +37 -0
  38. package/dist/auth/interfaces.d.ts +431 -0
  39. package/dist/auth/jwt.d.ts +55 -0
  40. package/dist/auth/linkedin-oauth.d.ts +18 -0
  41. package/dist/auth/magic-link-routes.d.ts +30 -0
  42. package/dist/auth/mfa-crypto.d.ts +23 -0
  43. package/dist/auth/mfa-routes.d.ts +7 -0
  44. package/dist/auth/mfa.d.ts +49 -0
  45. package/dist/auth/microsoft-oauth.d.ts +16 -0
  46. package/dist/auth/middleware.d.ts +113 -0
  47. package/dist/auth/password.d.ts +22 -0
  48. package/dist/auth/rate-limiter.d.ts +50 -0
  49. package/dist/auth/reset-password-admin.d.ts +29 -0
  50. package/dist/auth/rls-scope.d.ts +31 -0
  51. package/dist/auth/routes.d.ts +35 -0
  52. package/dist/auth/session-routes.d.ts +27 -0
  53. package/dist/auth/slack-oauth.d.ts +12 -0
  54. package/dist/auth/spotify-oauth.d.ts +12 -0
  55. package/dist/auth/twitter-oauth.d.ts +18 -0
  56. package/dist/backend-CIxN4FVm.js +15 -0
  57. package/dist/backend-CIxN4FVm.js.map +1 -0
  58. package/dist/base64-js-C_frYBkI.js +1687 -0
  59. package/dist/base64-js-C_frYBkI.js.map +1 -0
  60. package/dist/chunk-Dze3rakg.js +42 -0
  61. package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
  62. package/dist/collections/loader.d.ts +5 -0
  63. package/dist/cron/cron-loader.d.ts +17 -0
  64. package/dist/cron/cron-routes.d.ts +14 -0
  65. package/dist/cron/cron-scheduler.d.ts +106 -0
  66. package/dist/cron/cron-store.d.ts +32 -0
  67. package/dist/cron/index.d.ts +6 -0
  68. package/dist/db/interfaces.d.ts +18 -0
  69. package/dist/dist-DMO-zF6D.js +832 -0
  70. package/dist/dist-DMO-zF6D.js.map +1 -0
  71. package/dist/email/index.d.ts +6 -0
  72. package/dist/email/smtp-email-service.d.ts +25 -0
  73. package/dist/email/templates.d.ts +50 -0
  74. package/dist/email/types.d.ts +126 -0
  75. package/dist/env.d.ts +102 -0
  76. package/dist/from-VbwD7xRf.js +3849 -0
  77. package/dist/from-VbwD7xRf.js.map +1 -0
  78. package/dist/functions/define-function.d.ts +50 -0
  79. package/dist/functions/function-loader.d.ts +17 -0
  80. package/dist/functions/function-routes.d.ts +10 -0
  81. package/dist/functions/index.d.ts +5 -0
  82. package/dist/history/history-routes.d.ts +23 -0
  83. package/dist/history/index.d.ts +1 -0
  84. package/dist/index.d.ts +31 -0
  85. package/dist/index.es.js +61322 -0
  86. package/dist/index.es.js.map +1 -0
  87. package/dist/index.umd.js +103633 -0
  88. package/dist/index.umd.js.map +1 -0
  89. package/dist/init/docs.d.ts +4 -0
  90. package/dist/init/health.d.ts +2 -0
  91. package/dist/init/middlewares.d.ts +10 -0
  92. package/dist/init/shutdown.d.ts +11 -0
  93. package/dist/init/storage.d.ts +5 -0
  94. package/dist/init.d.ts +314 -0
  95. package/dist/jws-BqRRaK11.js +616 -0
  96. package/dist/jws-BqRRaK11.js.map +1 -0
  97. package/dist/jwt-BETC8a1J.js +3560 -0
  98. package/dist/jwt-BETC8a1J.js.map +1 -0
  99. package/dist/logger-BYU66ENZ.js +94 -0
  100. package/dist/logger-BYU66ENZ.js.map +1 -0
  101. package/dist/ms-BeBSuOXN.js +125 -0
  102. package/dist/ms-BeBSuOXN.js.map +1 -0
  103. package/dist/multipart-parser-CedBDOeC.js +299 -0
  104. package/dist/multipart-parser-CedBDOeC.js.map +1 -0
  105. package/dist/serve-spa.d.ts +30 -0
  106. package/dist/services/driver-registry.d.ts +78 -0
  107. package/dist/services/routed-realtime-service.d.ts +43 -0
  108. package/dist/services/webhook-service.d.ts +29 -0
  109. package/dist/singleton.d.ts +35 -0
  110. package/dist/src-CB2PIpBe.js +1182 -0
  111. package/dist/src-CB2PIpBe.js.map +1 -0
  112. package/dist/src-DjzOT1kG.js +29746 -0
  113. package/dist/src-DjzOT1kG.js.map +1 -0
  114. package/dist/storage/GCSStorageController.d.ts +43 -0
  115. package/dist/storage/LocalStorageController.d.ts +46 -0
  116. package/dist/storage/S3StorageController.d.ts +36 -0
  117. package/dist/storage/image-transform.d.ts +50 -0
  118. package/dist/storage/index.d.ts +31 -0
  119. package/dist/storage/routes.d.ts +70 -0
  120. package/dist/storage/storage-registry.d.ts +78 -0
  121. package/dist/storage/tus-handler.d.ts +53 -0
  122. package/dist/storage/types.d.ts +128 -0
  123. package/dist/types/index.d.ts +11 -0
  124. package/dist/utils/dev-port.d.ts +37 -0
  125. package/dist/utils/logger.d.ts +31 -0
  126. package/dist/utils/logging.d.ts +9 -0
  127. package/dist/utils/request-id.d.ts +4 -0
  128. package/dist/utils/request-logger.d.ts +19 -0
  129. package/dist/utils/sql.d.ts +27 -0
  130. package/package.json +22 -14
  131. package/src/api/graphql/graphql-schema-generator.ts +5 -5
  132. package/src/api/openapi-generator.ts +2 -2
  133. package/src/api/rest/api-generator.ts +44 -123
  134. package/src/api/rest/query-parser.ts +6 -23
  135. package/src/api/types.ts +2 -2
  136. package/src/auth/auth-hooks.ts +1 -1
  137. package/src/auth/builtin-auth-adapter.ts +1 -2
  138. package/src/auth/interfaces.ts +11 -2
  139. package/src/auth/middleware.ts +4 -4
  140. package/src/auth/routes.ts +1 -2
  141. package/src/functions/define-function.ts +59 -0
  142. package/src/functions/function-loader.ts +16 -0
  143. package/src/functions/index.ts +2 -0
  144. package/src/index.ts +70 -37
  145. package/src/init.ts +78 -21
  146. package/src/storage/GCSStorageController.ts +334 -0
  147. package/src/storage/index.ts +9 -3
  148. package/src/storage/routes.ts +191 -23
  149. package/src/storage/tus-handler.ts +16 -4
  150. package/src/storage/types.ts +25 -3
  151. package/test/api-generator.test.ts +1 -1
  152. package/test/auth-config-types.test.ts +40 -0
  153. package/test/define-function.test.ts +45 -0
  154. package/test/storage-routes.test.ts +160 -0
  155. package/test/backend-hooks-data.test.ts +0 -477
@@ -74,8 +74,17 @@ export interface OAuthProvider<T = unknown> {
74
74
  /** Zod schema validating the expected request payload (e.g. { code: string }) */
75
75
  schema: z.ZodSchema<T>;
76
76
 
77
- /** Verify external tokens/codes and return a standardized user profile */
78
- verify: (payload: T) => Promise<OAuthProviderProfile | null>;
77
+ /**
78
+ * Verify external tokens/codes and return a standardized user profile.
79
+ *
80
+ * NOTE: Declared as method syntax (not arrow property) intentionally.
81
+ * This makes `OAuthProvider` bivariant in `T`, which is correct because
82
+ * each provider is self-contained — `schema` validates the request body
83
+ * and `verify` consumes the same `T`. Bivariance lets heterogeneous
84
+ * providers (`OAuthProvider<SpecificPayload>`) coexist in a single
85
+ * `OAuthProvider<unknown>[]` array without resorting to `any`.
86
+ */
87
+ verify(payload: T): Promise<OAuthProviderProfile | null>;
79
88
  }
80
89
 
81
90
  /**
@@ -37,7 +37,7 @@ export interface AuthMiddlewareOptions {
37
37
  * to Postgres Row-Level Security policies.
38
38
  */
39
39
  requireAuth?: boolean;
40
- /** Optional custom validator (for non-JWT auth, e.g. Firebase Auth) */
40
+ /** Optional custom validator (for non-JWT auth, e.g. external auth providers) */
41
41
  validator?: (c: Context<HonoEnv>) => Promise<AuthResult>;
42
42
  /**
43
43
  * A static secret key for server-to-server / script authentication.
@@ -47,7 +47,7 @@ export interface AuthMiddlewareOptions {
47
47
  * roles: `["admin"]`) **without** JWT verification. The driver is scoped
48
48
  * via `withAuth()` with the service identity.
49
49
  *
50
- * This is the Rebase equivalent of a Firebase Service Account key.
50
+ * This is the Rebase equivalent of a Service Account key.
51
51
  * Set via `REBASE_SERVICE_KEY` in `.env` and pass through the backend config.
52
52
  *
53
53
  * **Security:** The comparison uses constant-time equality to prevent
@@ -245,7 +245,7 @@ export function createAuthMiddleware(options: AuthMiddlewareOptions): Middleware
245
245
  // Pick the per-request delegate (multi-data-source) before scoping.
246
246
  const driver = resolveDriver ? resolveDriver(c) : baseDriver;
247
247
  if (validator) {
248
- // Custom validator path (e.g., Firebase Auth, API keys)
248
+ // Custom validator path (e.g., API keys, external auth)
249
249
  try {
250
250
  const authResult = await validator(c);
251
251
  if (authResult && typeof authResult === "object") {
@@ -291,7 +291,7 @@ code: "UNAUTHORIZED" } }, 401);
291
291
 
292
292
  // ── Service Key check ──────────────────────────────────
293
293
  // Check BEFORE JWT verification. Service keys are static
294
- // secrets (like Firebase SA keys) that grant admin access
294
+ // secrets (like Service Account keys) that grant admin access
295
295
  // for scripts, cron jobs, and server-to-server calls.
296
296
  if (serviceKey && safeCompare(token, serviceKey)) {
297
297
  const serviceUser: AccessTokenPayload = {
@@ -30,8 +30,7 @@ export interface AuthModuleConfig {
30
30
  /** Default role ID to assign to new users (default: none). Must NOT be "admin". */
31
31
  defaultRole?: string;
32
32
  /** Optional array of OAuth providers */
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- oauthProviders?: OAuthProvider<any>[];
33
+ oauthProviders?: OAuthProvider<unknown>[];
35
34
  /** When true, blocks all self-registration regardless of `allowRegistration`. */
36
35
  disableSelfRegistration?: boolean;
37
36
  /**
@@ -0,0 +1,59 @@
1
+ import { Hono } from "hono";
2
+ import type { RebaseClient } from "@rebasepro/types";
3
+ import type { HonoEnv } from "../api/types";
4
+ import { rebase } from "../singleton";
5
+
6
+ /**
7
+ * Typed context injected into a function authored with {@link defineFunction}.
8
+ *
9
+ * Surfaces the app-scoped Rebase singleton so handlers don't need to reach
10
+ * for the global `rebase` import. Request-scoped values (the authenticated
11
+ * `user`, the RLS-scoped `driver`, the `apiKey`, the `requestId`) are typed
12
+ * on the Hono context via {@link HonoEnv} — read them with `c.get("user")`
13
+ * / `c.var.driver` inside a handler.
14
+ */
15
+ export interface RebaseFunctionContext {
16
+ /**
17
+ * The server-side Rebase singleton (`data`, `auth`, `storage`, `email`,
18
+ * `sql`). `rebase.data` runs with **admin privileges** (no RLS) — use the
19
+ * request `driver` (`c.var.driver`) for user-scoped queries.
20
+ */
21
+ rebase: RebaseClient;
22
+ }
23
+
24
+ /**
25
+ * Typed authoring contract for a custom backend function.
26
+ *
27
+ * A custom function is a file in the `functionsDir` that default-exports a
28
+ * Hono app; the loader mounts it at `/<filename>`. `defineFunction` is the
29
+ * typed opt-in for that contract: it hands you a pre-typed `Hono<HonoEnv>`
30
+ * app (so `c.var.user` / `c.var.driver` are typed) plus a
31
+ * {@link RebaseFunctionContext}, and returns exactly the Hono app the loader
32
+ * already accepts — so it is fully interchangeable with a plain
33
+ * `export default new Hono()`.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import { defineFunction, requireAuth } from "@rebasepro/server-core";
38
+ *
39
+ * export default defineFunction((app, { rebase }) => {
40
+ * app.use("/*", requireAuth);
41
+ * app.get("/home", async (c) => {
42
+ * const [stats] = await rebase.sql!(`SELECT count(*) AS n FROM orders`);
43
+ * return c.json({ orders: Number(stats.n) });
44
+ * });
45
+ * });
46
+ * ```
47
+ *
48
+ * @param definition Receives the function's Hono app and the typed context.
49
+ * Register routes on the provided `app` and return nothing, or return your
50
+ * own `Hono<HonoEnv>` app to use instead.
51
+ * @returns The Hono app to default-export from the function file.
52
+ */
53
+ export function defineFunction(
54
+ definition: (app: Hono<HonoEnv>, ctx: RebaseFunctionContext) => void | Hono<HonoEnv>
55
+ ): Hono<HonoEnv> {
56
+ const app = new Hono<HonoEnv>();
57
+ const returned = definition(app, { rebase });
58
+ return returned instanceof Hono ? returned : app;
59
+ }
@@ -24,6 +24,10 @@ export async function loadFunctionsFromDirectory(
24
24
  directory: string
25
25
  ): Promise<LoadedFunction[]> {
26
26
  const functions: LoadedFunction[] = [];
27
+ // Aggregate problem files so a broken function surfaces as one loud
28
+ // summary line, not just a warning buried per-file. We still don't throw:
29
+ // a single malformed function must not crash server boot.
30
+ const problems: string[] = [];
27
31
 
28
32
  if (!fs.existsSync(directory)) {
29
33
  return functions;
@@ -51,6 +55,7 @@ export async function loadFunctionsFromDirectory(
51
55
 
52
56
  if (!exported) {
53
57
  logger.warn(`[functions] ${file}: no default export. Skipping.`);
58
+ problems.push(`${file} (no default export)`);
54
59
  continue;
55
60
  }
56
61
 
@@ -86,16 +91,27 @@ app: result as Hono });
86
91
  ` export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}\n` +
87
92
  ` prototype methods: ${keys}\n` +
88
93
  " Hint: ensure the function exports a Hono app created with the same hono version as the server.\n" +
94
+ " Author with `defineFunction(...)` from @rebasepro/server-core for a typed, checked contract.\n" +
89
95
  " The loader checks for .fetch() and .routes — any Hono-compatible app will work."
90
96
  );
97
+ problems.push(`${file} (not a Hono app or factory)`);
91
98
  } catch (err: unknown) {
92
99
  const message =
93
100
  err instanceof Error ? err.message : String(err);
94
101
  logger.error(`[functions] Failed to load ${file}: ${message}`);
102
+ problems.push(`${file} (threw: ${message})`);
95
103
  }
96
104
  }
97
105
  }
98
106
 
107
+ if (problems.length > 0) {
108
+ logger.warn(
109
+ `[functions] ${problems.length} function file(s) were skipped and will NOT be served:\n` +
110
+ problems.map((p) => ` - ${p}`).join("\n") + "\n" +
111
+ " Fix these or author them with `defineFunction(...)` for a typed, compile-checked contract."
112
+ );
113
+ }
114
+
99
115
  return functions;
100
116
  }
101
117
 
@@ -1,3 +1,5 @@
1
1
  export { loadFunctionsFromDirectory } from "./function-loader";
2
2
  export type { LoadedFunction } from "./function-loader";
3
3
  export { createFunctionRoutes } from "./function-routes";
4
+ export { defineFunction } from "./define-function";
5
+ export type { RebaseFunctionContext } from "./define-function";
package/src/index.ts CHANGED
@@ -8,63 +8,96 @@
8
8
  */
9
9
 
10
10
  // =============================================================================
11
- // Abstract Interfaces (for database abstraction)
11
+ // Core Initialization
12
12
  // =============================================================================
13
- export * from "./db/interfaces";
14
- export * from "./auth/interfaces";
15
-
16
- // Core functionality
17
- export * from "./init";
13
+ export {
14
+ initializeRebaseBackend,
15
+ isAuthAdapter,
16
+ isDatabaseAdapter
17
+ } from "./init";
18
+ export type {
19
+ RebaseBackendConfig,
20
+ RebaseBackendInstance,
21
+ RebaseAuthConfig
22
+ } from "./init";
18
23
 
24
+ // =============================================================================
19
25
  // Server-side singleton (import { rebase } from "@rebasepro/server-core")
26
+ // =============================================================================
20
27
  export { rebase, _setRebaseMock, _resetRebaseMock } from "./singleton";
21
28
 
22
- // Services
23
- export * from "./services/driver-registry";
29
+ // =============================================================================
30
+ // DB Abstractions (for database driver implementations)
31
+ // =============================================================================
32
+ export * from "./db/interfaces";
24
33
 
25
- // API types (HonoEnv, ApiConfig, etc.)
26
- export * from "./api/types";
34
+ // =============================================================================
35
+ // Auth
36
+ // =============================================================================
37
+ export * from "./auth";
27
38
 
28
- // API Generation
39
+ // =============================================================================
40
+ // API Layer
41
+ // =============================================================================
29
42
  export * from "./api";
30
43
 
31
- // Types
32
- export * from "./types";
33
- export * from "./types/index";
34
-
35
- // Auth module
36
- export * from "./auth";
37
-
38
- // Email module
44
+ // =============================================================================
45
+ // Email
46
+ // =============================================================================
39
47
  export * from "./email";
40
48
 
41
- // Storage module
49
+ // =============================================================================
50
+ // Storage
51
+ // =============================================================================
42
52
  export * from "./storage";
43
53
 
44
- export * from "./utils/logging";
45
- export * from "./utils/logger";
46
- export * from "./utils/request-logger";
47
- export * from "./utils/request-id";
48
- export * from "./utils/sql";
49
-
50
- // Entity history
51
- export * from "./history";
54
+ // =============================================================================
55
+ // Entity History
56
+ // =============================================================================
57
+ export { createHistoryRoutes } from "./history";
52
58
 
59
+ // =============================================================================
53
60
  // Custom Functions (auto-discovered Hono routes)
54
- export * from "./functions";
61
+ // =============================================================================
62
+ export { loadFunctionsFromDirectory, createFunctionRoutes, defineFunction } from "./functions";
63
+ export type { LoadedFunction, RebaseFunctionContext } from "./functions";
55
64
 
65
+ // =============================================================================
56
66
  // Cron Jobs (auto-discovered scheduled tasks)
57
- export * from "./cron";
58
-
67
+ // =============================================================================
68
+ export { loadCronJobsFromDirectory, CronScheduler, validateCronExpression, createCronRoutes, createCronStore } from "./cron";
69
+ export type { LoadedCronJob, CronStore } from "./cron";
59
70
 
60
- // SPA serving helper
61
- export * from "./serve-spa";
71
+ // =============================================================================
72
+ // SQL Helpers (for RLS policies)
73
+ // =============================================================================
74
+ export { authUid, authRoles, authJwt } from "./utils/sql";
62
75
 
63
- // Dev-mode port resolution (retry on EADDRINUSE)
64
- export * from "./utils/dev-port";
76
+ // =============================================================================
77
+ // Logger
78
+ // =============================================================================
79
+ export { logger } from "./utils/logger";
80
+ export type { Logger } from "./utils/logger";
65
81
 
66
- // Environment validation
82
+ // =============================================================================
83
+ // Environment Validation
84
+ // =============================================================================
67
85
  export { loadEnv } from "./env";
68
86
  export type { RebaseEnv } from "./env";
69
- export { z } from "zod";
70
87
 
88
+ // =============================================================================
89
+ // Server-core specific types (subscription types)
90
+ // =============================================================================
91
+ export * from "./types";
92
+
93
+ // =============================================================================
94
+ // Driver Registry
95
+ // =============================================================================
96
+ export * from "./services/driver-registry";
97
+
98
+ // =============================================================================
99
+ // Internal plumbing exported for the official app/backend
100
+ // =============================================================================
101
+ export { cleanupDevPortFile, listenWithPortRetry } from "./utils/dev-port";
102
+ export { serveSPA } from "./serve-spa";
103
+ export { z } from "zod";
package/src/init.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  AuthAdapter,
3
3
  BackendBootstrapper,
4
- BackendHooks,
5
4
  BootstrappedAuth,
6
5
  DatabaseAdapter,
7
6
  DataDriver,
8
7
  DataSourceDefinition,
8
+ EntityCallbacks,
9
9
  EntityCollection,
10
10
  HealthCheckResult,
11
11
  InitializedDriver,
@@ -83,7 +83,7 @@ export interface RebaseAuthConfig {
83
83
  *
84
84
  * When a request includes `Authorization: Bearer <serviceKey>`, it is
85
85
  * granted admin-level access without JWT verification. This is the
86
- * Rebase equivalent of a Firebase Service Account key.
86
+ * Rebase equivalent of a Service Account key.
87
87
  *
88
88
  * Generate with: `node -e "logger.info(require('crypto').randomBytes(48).toString('base64'))"`
89
89
  *
@@ -92,6 +92,14 @@ export interface RebaseAuthConfig {
92
92
  */
93
93
  serviceKey?: string;
94
94
  email?: EmailConfig;
95
+ // ── Convenience shortcuts ─────────────────────────────────────────
96
+ // Each named field below is syntactic sugar that internally resolves
97
+ // to an `OAuthProvider` via the corresponding `create*Provider`
98
+ // factory at startup. They are equivalent to constructing the
99
+ // provider manually and passing it in the `providers` array.
100
+ //
101
+ // For providers not listed here, or for full control over the
102
+ // provider configuration, use the `providers` array directly.
95
103
  google?: { clientId: string; clientSecret?: string };
96
104
  linkedin?: { clientId: string; clientSecret: string };
97
105
  github?: { clientId: string; clientSecret: string };
@@ -105,8 +113,31 @@ export interface RebaseAuthConfig {
105
113
  slack?: { clientId: string; clientSecret: string };
106
114
  spotify?: { clientId: string; clientSecret: string };
107
115
  defaultRole?: string;
108
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
- providers?: OAuthProvider<any>[];
116
+ /**
117
+ * Canonical array of OAuth providers.
118
+ *
119
+ * This is the primary extension point for **all** OAuth integrations.
120
+ * Each entry is an `OAuthProvider<unknown>` constructed via one of
121
+ * the `create*Provider` factories exported from `@rebasepro/server-core`
122
+ * (e.g. `createGoogleProvider`, `createGitHubProvider`).
123
+ *
124
+ * The named convenience fields above (`google`, `github`, etc.) are
125
+ * automatically resolved into this array at startup. You can mix both
126
+ * approaches; named fields and explicit entries are merged (named
127
+ * fields are appended after explicit entries).
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * import { createGoogleProvider } from "@rebasepro/server-core";
132
+ *
133
+ * auth: {
134
+ * providers: [
135
+ * createGoogleProvider({ clientId: "…", clientSecret: "…" }),
136
+ * ],
137
+ * }
138
+ * ```
139
+ */
140
+ providers?: OAuthProvider<unknown>[];
110
141
  /**
111
142
  * Override specific parts of the built-in auth implementation.
112
143
  *
@@ -131,8 +162,6 @@ export interface RebaseAuthConfig {
131
162
  * Requires email to be configured.
132
163
  */
133
164
  magicLink?: boolean;
134
-
135
- [key: string]: unknown;
136
165
  }
137
166
 
138
167
  export interface RebaseBackendConfig {
@@ -188,11 +217,22 @@ export interface RebaseBackendConfig {
188
217
  /**
189
218
  * Storage configuration. Accepts:
190
219
  *
191
- * - A `BackendStorageConfig` object (`{ type: 'local' | 's3', ... }`)
192
- * - A `StorageController` instance (for custom providers like GCS, Azure, etc.)
220
+ * - A `BackendStorageConfig` object (`{ type: 'local' | 's3' | 'gcs', ... }`)
221
+ * - A `StorageController` instance (for custom providers like Azure, etc.)
193
222
  * - A `Record<string, ...>` of either, for multi-backend setups
194
223
  */
195
224
  storage?: BackendStorageConfig | StorageController | Record<string, BackendStorageConfig | StorageController>;
225
+
226
+ /**
227
+ * Declared storage sources. Drives the client-side StorageSourceRegistry
228
+ * and the transport distinction (server vs direct).
229
+ *
230
+ * Server-backed sources are auto-derived from the `storage` map — you
231
+ * only need explicit entries for "direct" transport sources (e.g.
232
+ * external storage) that the backend does not proxy.
233
+ */
234
+ storageSources?: import("@rebasepro/types").StorageSourceDefinition[];
235
+
196
236
  history?: unknown;
197
237
  /**
198
238
  * Default security rules applied to any collection that does not define
@@ -242,14 +282,24 @@ export interface RebaseBackendConfig {
242
282
  origin: string | string[] | ((origin: string) => boolean);
243
283
  };
244
284
  /**
245
- * Backend-level hooks for intercepting collection entity data
246
- * at the REST API boundary. These run server-side after database
247
- * operations and before API responses are sent.
285
+ * Global lifecycle callbacks applied to every collection.
248
286
  *
249
- * Complement the per-collection `EntityCallbacks` system which
250
- * handles collection CRUD operations.
287
+ * Same type as per-collection `callbacks` fires on **every** data path
288
+ * (REST API, WebSocket / realtime, server-side `rebase.data`).
289
+ *
290
+ * Execution order: global callbacks → collection callbacks → property callbacks.
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * callbacks: {
295
+ * afterRead({ entity, collection }) {
296
+ * console.log(`Read ${collection.slug}/${entity.id}`);
297
+ * return entity;
298
+ * }
299
+ * }
300
+ * ```
251
301
  */
252
- hooks?: BackendHooks;
302
+ callbacks?: EntityCallbacks;
253
303
  }
254
304
 
255
305
  /**
@@ -337,6 +387,11 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
337
387
  // so normalization can resolve each collection's engine.
338
388
  const dataSourceRegistry = createDataSourceRegistry(config.dataSources);
339
389
  collectionRegistry.setDataSources(dataSourceRegistry);
390
+
391
+ // Global lifecycle callbacks — applied to every collection, on all data paths.
392
+ if (config.callbacks) {
393
+ collectionRegistry.setGlobalCallbacks(config.callbacks);
394
+ }
340
395
  let activeCollections = config.collections || [];
341
396
  if (config.collectionsDir && activeCollections.length === 0) {
342
397
  activeCollections = await loadCollectionsFromDirectory(config.collectionsDir);
@@ -607,8 +662,7 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
607
662
 
608
663
  if (!isAuthAdapter(config.auth)) {
609
664
  const safeAuthConfig = config.auth as RebaseAuthConfig;
610
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
611
- const oauthProviders: OAuthProvider<any>[] = [...(safeAuthConfig.providers || [])];
665
+ const oauthProviders: OAuthProvider<unknown>[] = [...(safeAuthConfig.providers || [])];
612
666
 
613
667
  // Resolve configured OAuth providers via data-driven registration.
614
668
  // Each entry maps a config key to its factory function name and required fields.
@@ -723,7 +777,10 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
723
777
 
724
778
  const storageRoutes = createStorageRoutes({
725
779
  controller: storageController,
726
- requireAuth: resolveRequireAuth(config.auth)
780
+ registry: storageRegistry,
781
+ sources: config.storageSources,
782
+ requireAuth: resolveRequireAuth(config.auth),
783
+ authAdapter
727
784
  });
728
785
 
729
786
  // Apply a permissive body limit specifically for the upload endpoint
@@ -825,7 +882,6 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
825
882
  const restGenerator = new RestApiGenerator(
826
883
  serverCollections,
827
884
  defaultDriver,
828
- config.hooks?.data,
829
885
  authAdapter
830
886
  );
831
887
  dataRouter.route("/", restGenerator.generateRoutes());
@@ -913,9 +969,10 @@ async function _initializeRebaseBackend(config: RebaseBackendConfig): Promise<Re
913
969
  const functionsRouter = new Hono<HonoEnv>();
914
970
  functionsRouter.onError(errorHandler);
915
971
 
916
- // Custom functions follow the same auth policy as data routes.
917
- // Per-route auth can be further refined inside individual functions.
918
- const functionsRequireAuth = resolveRequireAuth(config.auth);
972
+ // Custom functions do NOT require authentication at the global level by default.
973
+ // This allows custom functions to define public endpoints (like webhooks).
974
+ // Per-route auth can be further refined inside individual functions using `requireAuth`.
975
+ const functionsRequireAuth = false;
919
976
 
920
977
  // Use adapter middleware when available, fallback to built-in
921
978
  if (authAdapter) {