@rebasepro/server 0.0.1-canary.4829d6e

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 (127) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +77 -0
  3. package/dist/api/ast-schema-editor.d.ts +21 -0
  4. package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
  5. package/dist/api/errors.d.ts +53 -0
  6. package/dist/api/index.d.ts +7 -0
  7. package/dist/api/logs-routes.d.ts +37 -0
  8. package/dist/api/openapi-generator.d.ts +16 -0
  9. package/dist/api/rest/api-generator.d.ts +68 -0
  10. package/dist/api/rest/index.d.ts +1 -0
  11. package/dist/api/rest/query-parser.d.ts +6 -0
  12. package/dist/api/schema-editor-routes.d.ts +3 -0
  13. package/dist/api/types.d.ts +56 -0
  14. package/dist/auth/adapter-middleware.d.ts +43 -0
  15. package/dist/auth/admin-roles-route.d.ts +18 -0
  16. package/dist/auth/admin-user-ops.d.ts +80 -0
  17. package/dist/auth/admin-users-route.d.ts +28 -0
  18. package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
  19. package/dist/auth/api-keys/api-key-permission-guard.d.ts +32 -0
  20. package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
  21. package/dist/auth/api-keys/api-key-store.d.ts +35 -0
  22. package/dist/auth/api-keys/api-key-types.d.ts +96 -0
  23. package/dist/auth/api-keys/index.d.ts +17 -0
  24. package/dist/auth/apple-oauth.d.ts +30 -0
  25. package/dist/auth/auth-hooks.d.ts +242 -0
  26. package/dist/auth/bitbucket-oauth.d.ts +11 -0
  27. package/dist/auth/builtin-auth-adapter.d.ts +55 -0
  28. package/dist/auth/cookie-utils.d.ts +32 -0
  29. package/dist/auth/crypto-utils.d.ts +16 -0
  30. package/dist/auth/custom-auth-adapter.d.ts +39 -0
  31. package/dist/auth/discord-oauth.d.ts +14 -0
  32. package/dist/auth/facebook-oauth.d.ts +14 -0
  33. package/dist/auth/github-oauth.d.ts +15 -0
  34. package/dist/auth/gitlab-oauth.d.ts +13 -0
  35. package/dist/auth/google-oauth.d.ts +47 -0
  36. package/dist/auth/index.d.ts +38 -0
  37. package/dist/auth/interfaces.d.ts +431 -0
  38. package/dist/auth/jwt.d.ts +67 -0
  39. package/dist/auth/linkedin-oauth.d.ts +18 -0
  40. package/dist/auth/magic-link-routes.d.ts +32 -0
  41. package/dist/auth/mfa-crypto.d.ts +23 -0
  42. package/dist/auth/mfa-routes.d.ts +7 -0
  43. package/dist/auth/mfa.d.ts +49 -0
  44. package/dist/auth/microsoft-oauth.d.ts +16 -0
  45. package/dist/auth/middleware.d.ts +158 -0
  46. package/dist/auth/password.d.ts +22 -0
  47. package/dist/auth/rate-limiter.d.ts +50 -0
  48. package/dist/auth/reset-password-admin.d.ts +29 -0
  49. package/dist/auth/rls-scope.d.ts +31 -0
  50. package/dist/auth/routes.d.ts +66 -0
  51. package/dist/auth/session-routes.d.ts +29 -0
  52. package/dist/auth/slack-oauth.d.ts +12 -0
  53. package/dist/auth/spotify-oauth.d.ts +12 -0
  54. package/dist/auth/twitter-oauth.d.ts +18 -0
  55. package/dist/backup/backup-common.d.ts +31 -0
  56. package/dist/backup/backup-routes.d.ts +22 -0
  57. package/dist/backup/index.d.ts +7 -0
  58. package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
  59. package/dist/collections/loader.d.ts +39 -0
  60. package/dist/cron/cron-loader.d.ts +18 -0
  61. package/dist/cron/cron-routes.d.ts +14 -0
  62. package/dist/cron/cron-scheduler.d.ts +106 -0
  63. package/dist/cron/cron-store.d.ts +32 -0
  64. package/dist/cron/define-cron.d.ts +28 -0
  65. package/dist/cron/index.d.ts +7 -0
  66. package/dist/db/interfaces.d.ts +18 -0
  67. package/dist/dynamic-import-Dvh-K5fl.js +22 -0
  68. package/dist/dynamic-import-Dvh-K5fl.js.map +1 -0
  69. package/dist/email/index.d.ts +6 -0
  70. package/dist/email/smtp-email-service.d.ts +30 -0
  71. package/dist/email/templates.d.ts +50 -0
  72. package/dist/email/types.d.ts +126 -0
  73. package/dist/env.d.ts +102 -0
  74. package/dist/functions/define-function.d.ts +55 -0
  75. package/dist/functions/function-loader.d.ts +18 -0
  76. package/dist/functions/function-routes.d.ts +10 -0
  77. package/dist/functions/index.d.ts +5 -0
  78. package/dist/history/history-routes.d.ts +23 -0
  79. package/dist/history/index.d.ts +1 -0
  80. package/dist/index.d.ts +38 -0
  81. package/dist/index.es.js +18458 -0
  82. package/dist/index.es.js.map +1 -0
  83. package/dist/index.umd.js +48745 -0
  84. package/dist/index.umd.js.map +1 -0
  85. package/dist/init/docs.d.ts +4 -0
  86. package/dist/init/health.d.ts +2 -0
  87. package/dist/init/middlewares.d.ts +10 -0
  88. package/dist/init/shutdown.d.ts +65 -0
  89. package/dist/init/storage.d.ts +5 -0
  90. package/dist/init.d.ts +375 -0
  91. package/dist/jwt-BwIn8xmk.js +4200 -0
  92. package/dist/jwt-BwIn8xmk.js.map +1 -0
  93. package/dist/logger-BYU66ENZ.js +94 -0
  94. package/dist/logger-BYU66ENZ.js.map +1 -0
  95. package/dist/ms-DnYXB-Wd.js +162 -0
  96. package/dist/ms-DnYXB-Wd.js.map +1 -0
  97. package/dist/openapi-generator-Z9oYWLf_.js +586 -0
  98. package/dist/openapi-generator-Z9oYWLf_.js.map +1 -0
  99. package/dist/schema-editor-routes-D3ef8zu1.js +219 -0
  100. package/dist/schema-editor-routes-D3ef8zu1.js.map +1 -0
  101. package/dist/serve-spa.d.ts +36 -0
  102. package/dist/services/driver-registry.d.ts +78 -0
  103. package/dist/services/routed-realtime-service.d.ts +43 -0
  104. package/dist/services/webhook-service.d.ts +29 -0
  105. package/dist/singleton.d.ts +51 -0
  106. package/dist/src-B4OLmNVa.js +437 -0
  107. package/dist/src-B4OLmNVa.js.map +1 -0
  108. package/dist/src-B9tjYmqP.js +24598 -0
  109. package/dist/src-B9tjYmqP.js.map +1 -0
  110. package/dist/storage/GCSStorageController.d.ts +43 -0
  111. package/dist/storage/LocalStorageController.d.ts +46 -0
  112. package/dist/storage/S3StorageController.d.ts +40 -0
  113. package/dist/storage/image-transform.d.ts +50 -0
  114. package/dist/storage/index.d.ts +31 -0
  115. package/dist/storage/routes.d.ts +70 -0
  116. package/dist/storage/storage-registry.d.ts +78 -0
  117. package/dist/storage/tus-handler.d.ts +53 -0
  118. package/dist/storage/types.d.ts +128 -0
  119. package/dist/types/index.d.ts +11 -0
  120. package/dist/utils/dev-port.d.ts +45 -0
  121. package/dist/utils/dynamic-import.d.ts +25 -0
  122. package/dist/utils/logger.d.ts +31 -0
  123. package/dist/utils/logging.d.ts +9 -0
  124. package/dist/utils/request-id.d.ts +4 -0
  125. package/dist/utils/request-logger.d.ts +19 -0
  126. package/dist/utils/sql.d.ts +27 -0
  127. package/package.json +115 -0
@@ -0,0 +1,106 @@
1
+ import type { CronJobStatus, CronJobLogEntry } from "@rebasepro/types";
2
+ import type { RebaseClient } from "@rebasepro/types";
3
+ import type { LoadedCronJob } from "./cron-loader";
4
+ import type { CronStore } from "./cron-store";
5
+ /**
6
+ * Validates a standard 5-field cron expression structurally and semantically.
7
+ * Returns `{ valid: true }` or `{ valid: false, reason: string }`.
8
+ */
9
+ export declare function validateCronExpression(schedule: string): {
10
+ valid: true;
11
+ } | {
12
+ valid: false;
13
+ reason: string;
14
+ };
15
+ export declare class CronScheduler {
16
+ private jobs;
17
+ private started;
18
+ private store?;
19
+ private client?;
20
+ /**
21
+ * Set the RebaseClient instance to make it available to cron job handlers.
22
+ */
23
+ setClient(client: RebaseClient): void;
24
+ /**
25
+ * Attach a persistence store for cron logs.
26
+ * When set, execution logs are written to the database after each run,
27
+ * and counters are seeded from the database on start.
28
+ */
29
+ setStore(store: CronStore): void;
30
+ /**
31
+ * Register a batch of loaded cron jobs.
32
+ *
33
+ * If the scheduler is already started, newly registered jobs are
34
+ * automatically scheduled (so late-registered jobs don't sit idle).
35
+ *
36
+ * Validates the cron schedule on registration — invalid schedules
37
+ * are rejected with a warning and the job is NOT registered.
38
+ */
39
+ registerJobs(loadedJobs: LoadedCronJob[]): void;
40
+ /**
41
+ * Start the scheduler — begins ticking all enabled jobs.
42
+ */
43
+ start(): void;
44
+ /**
45
+ * Stop the scheduler and clear all timers.
46
+ *
47
+ * Currently-executing handlers run to completion (they are async),
48
+ * but no further scheduling occurs after stop.
49
+ */
50
+ stop(): void;
51
+ /**
52
+ * List all registered jobs with their current status.
53
+ */
54
+ listJobs(): CronJobStatus[];
55
+ /**
56
+ * Get a single job status by ID.
57
+ */
58
+ getJob(id: string): CronJobStatus | undefined;
59
+ /**
60
+ * Get log entries for a job.
61
+ */
62
+ getJobLogs(id: string, limit?: number): CronJobLogEntry[];
63
+ /**
64
+ * Get log entries for a job from the database (if store is available).
65
+ * Falls back to in-memory logs if no store is configured.
66
+ */
67
+ getJobLogsFromDb(id: string, limit?: number): Promise<CronJobLogEntry[]>;
68
+ /**
69
+ * Enable or disable a job at runtime.
70
+ */
71
+ setJobEnabled(id: string, enabled: boolean): CronJobStatus | undefined;
72
+ /**
73
+ * Manually trigger a job execution immediately.
74
+ *
75
+ * Returns `undefined` if the job doesn't exist.
76
+ * If the job is currently executing, returns the log entry with
77
+ * a `skipped: true` result rather than running concurrently.
78
+ */
79
+ triggerJob(id: string): Promise<CronJobLogEntry | undefined>;
80
+ /**
81
+ * Schedule the next execution for a job.
82
+ *
83
+ * Safety guarantees:
84
+ * 1. Clears any existing timer first (prevents leaked/duplicate timers)
85
+ * 2. Enforces a minimum delay to prevent tight loops from jitter
86
+ * 3. Unref's the timer so it doesn't prevent process exit
87
+ * 4. Re-checks enabled & started state before executing
88
+ * 5. Concurrency guard prevents overlapping handler executions
89
+ */
90
+ private scheduleNext;
91
+ /**
92
+ * Stop a single job's timer and clear its next run state.
93
+ */
94
+ private stopJob;
95
+ /**
96
+ * Execute a job's handler with full isolation and safety.
97
+ *
98
+ * - Sets a concurrency flag to prevent overlapping runs
99
+ * - Wraps handler in a timeout race
100
+ * - Captures all logs, errors, and results
101
+ * - Persists to store (non-blocking) if available
102
+ * - Always restores state even on catastrophic errors
103
+ */
104
+ private executeJob;
105
+ private toStatus;
106
+ }
@@ -0,0 +1,32 @@
1
+ import type { CronJobLogEntry } from "@rebasepro/types";
2
+ import type { DataDriver } from "@rebasepro/types";
3
+ /**
4
+ * Persistence layer for cron job execution logs.
5
+ *
6
+ * Uses the DataDriver's `admin.executeSql` capability to store logs in a
7
+ * `rebase.cron_logs` table. Falls back gracefully if the driver doesn't
8
+ * support SQL (e.g. MongoDB) — in that case, no persistence occurs.
9
+ */
10
+ export interface CronStore {
11
+ /** Ensure the backing table exists. Called once on startup. */
12
+ ensureTable(): Promise<void>;
13
+ /** Persist a single log entry after execution. */
14
+ insertLog(entry: CronJobLogEntry): Promise<void>;
15
+ /**
16
+ * Fetch the most recent logs for a job.
17
+ * @param jobId The job identifier
18
+ * @param limit Max entries to return (default 50)
19
+ * @returns Logs sorted newest-first
20
+ */
21
+ fetchLogs(jobId: string, limit?: number): Promise<CronJobLogEntry[]>;
22
+ /**
23
+ * Fetch aggregate stats for all jobs (totalRuns, totalFailures, lastRunAt).
24
+ * Used to seed in-memory counters on startup.
25
+ */
26
+ fetchJobStats(): Promise<Map<string, {
27
+ totalRuns: number;
28
+ totalFailures: number;
29
+ lastRunAt?: string;
30
+ }>>;
31
+ }
32
+ export declare function createCronStore(driver: DataDriver): CronStore | undefined;
@@ -0,0 +1,28 @@
1
+ import type { CronJobDefinition } from "@rebasepro/types";
2
+ /**
3
+ * Typed authoring helper for a cron job file. Identity at runtime —
4
+ * a plain default-exported {@link CronJobDefinition} works identically;
5
+ * this adds type inference and autocomplete.
6
+ *
7
+ * @see {@link defineFunction} for the equivalent custom-functions helper.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { defineCron } from "@rebasepro/server";
12
+ *
13
+ * export default defineCron({
14
+ * name: "Nightly cleanup",
15
+ * schedule: "0 3 * * *",
16
+ * async handler({ client, log }) {
17
+ * const { data: expired } = await client.data.sessions.find({
18
+ * where: { expired: ["==", true] },
19
+ * });
20
+ * for (const session of expired) {
21
+ * await client.data.sessions.delete(session.id);
22
+ * }
23
+ * log(`Deleted ${expired.length} expired sessions`);
24
+ * },
25
+ * });
26
+ * ```
27
+ */
28
+ export declare function defineCron(definition: CronJobDefinition): CronJobDefinition;
@@ -0,0 +1,7 @@
1
+ export { loadCronJobsFromDirectory } from "./cron-loader";
2
+ export type { LoadedCronJob } from "./cron-loader";
3
+ export { defineCron } from "./define-cron";
4
+ export { CronScheduler, validateCronExpression } from "./cron-scheduler";
5
+ export { createCronRoutes } from "./cron-routes";
6
+ export { createCronStore } from "./cron-store";
7
+ export type { CronStore } from "./cron-store";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Database Abstraction Interfaces
3
+ *
4
+ * These interfaces define the contracts that any database backend must implement
5
+ * to be used with Rebase. This allows for pluggable database backends like
6
+ * PostgreSQL, MongoDB, MySQL, etc.
7
+ */
8
+ import { DatabaseConnection, QueryFilter, FetchCollectionOptions, SearchOptions, CountOptions, ConditionBuilder, ConditionBuilderStatic, DataRepository, CollectionSubscriptionConfig, SingleSubscriptionConfig, RealtimeProvider, CollectionRegistryInterface, DataTransformer, BackendConfig, BackendInstance, BackendFactory } from "@rebasepro/types";
9
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
10
+ import { PgTransaction } from "drizzle-orm/pg-core";
11
+ /**
12
+ * Type representing either a direct database connection or a transaction.
13
+ * Used to allow services to operate within a transaction context.
14
+ * Note: `any` is intentional here — it represents a Drizzle client with
15
+ * a dynamic schema, enabling `db.query[tableName]` access without casts.
16
+ */
17
+ export type DrizzleClient = NodePgDatabase<Record<string, unknown>> | PgTransaction<any, any, any>;
18
+ export type { DatabaseConnection, QueryFilter, FetchCollectionOptions, SearchOptions, CountOptions, ConditionBuilder, ConditionBuilderStatic, DataRepository, CollectionSubscriptionConfig, SingleSubscriptionConfig, RealtimeProvider, CollectionRegistryInterface, DataTransformer, BackendConfig, BackendInstance, BackendFactory };
@@ -0,0 +1,22 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ import "process";
3
+ __createRequire(import.meta.url);
4
+ //#region src/utils/dynamic-import.ts
5
+ /**
6
+ * Native ESM dynamic import.
7
+ *
8
+ * Wrapped in `new Function` so TypeScript / bundlers don't down-compile the
9
+ * `import()` to `require()` — the loaders must import user-authored `.ts`/`.js`
10
+ * files at runtime using the host's real ESM loader. This is the production
11
+ * default for {@link loadFunctionsFromDirectory} / {@link loadCronJobsFromDirectory}.
12
+ *
13
+ * @internal
14
+ */
15
+ var nativeDynamicImport = (() => {
16
+ const doImport = new Function("url", "return import(url)");
17
+ return (url) => doImport(url);
18
+ })();
19
+ //#endregion
20
+ export { nativeDynamicImport as t };
21
+
22
+ //# sourceMappingURL=dynamic-import-Dvh-K5fl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-import-Dvh-K5fl.js","names":[],"sources":["../src/utils/dynamic-import.ts"],"sourcesContent":["/**\n * Shape returned by a dynamic module import: an object whose `default`\n * property holds the module's default export (CJS `module.exports` is exposed\n * as `default` by Node's ESM interop).\n */\nexport interface ImportedModule {\n default?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Imports a module by file URL. Injected into the directory loaders so tests\n * can supply a deterministic importer.\n */\nexport type ModuleImporter = (url: string) => Promise<ImportedModule>;\n\n/**\n * Native ESM dynamic import.\n *\n * Wrapped in `new Function` so TypeScript / bundlers don't down-compile the\n * `import()` to `require()` — the loaders must import user-authored `.ts`/`.js`\n * files at runtime using the host's real ESM loader. This is the production\n * default for {@link loadFunctionsFromDirectory} / {@link loadCronJobsFromDirectory}.\n *\n * @internal\n */\nexport const nativeDynamicImport: ModuleImporter = (() => {\n const doImport = new Function(\"url\", \"return import(url)\") as (url: string) => Promise<ImportedModule>;\n return (url: string) => doImport(url);\n})();\n"],"mappings":";;;;;;;;;;;;;;AA0BA,IAAa,6BAA6C;CACtD,MAAM,WAAW,IAAI,SAAS,OAAO,oBAAoB;CACzD,QAAQ,QAAgB,SAAS,GAAG;AACxC,GAAG"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Email module exports
3
+ */
4
+ export type { EmailService, EmailSendOptions, SMTPConfig, EmailConfig, PasswordResetTemplateFunction, EmailVerificationTemplateFunction, UserInvitationTemplateFunction, WelcomeEmailTemplateFunction, MagicLinkTemplateFunction } from "./types";
5
+ export { SMTPEmailService, createEmailService } from "./smtp-email-service";
6
+ export { getPasswordResetTemplate, getEmailVerificationTemplate, getUserInvitationTemplate, getWelcomeEmailTemplate, getMagicLinkTemplate } from "./templates";
@@ -0,0 +1,30 @@
1
+ import { EmailConfig, EmailSendOptions, EmailService } from "./types";
2
+ /**
3
+ * SMTP Email Service implementation using Nodemailer
4
+ */
5
+ export declare class SMTPEmailService implements EmailService {
6
+ private transporter;
7
+ private config;
8
+ private _initialized;
9
+ constructor(config: EmailConfig);
10
+ /**
11
+ * Lazily initialize the SMTP transporter on first use
12
+ */
13
+ private ensureTransporter;
14
+ /**
15
+ * Check if the email service is properly configured
16
+ */
17
+ isConfigured(): boolean;
18
+ /**
19
+ * Send an email using SMTP or custom send function
20
+ */
21
+ send(options: EmailSendOptions): Promise<void>;
22
+ /**
23
+ * Verify SMTP connection (useful for startup checks)
24
+ */
25
+ verifyConnection(): Promise<boolean>;
26
+ }
27
+ /**
28
+ * Create an email service from configuration
29
+ */
30
+ export declare function createEmailService(config: EmailConfig): EmailService;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Default email templates for authentication emails
3
+ */
4
+ interface TemplateUser {
5
+ email: string;
6
+ displayName?: string | null;
7
+ }
8
+ /**
9
+ * Default password reset email template
10
+ */
11
+ export declare function getPasswordResetTemplate(resetUrl: string, user: TemplateUser, appName?: string): {
12
+ subject: string;
13
+ html: string;
14
+ text: string;
15
+ };
16
+ /**
17
+ * Default email verification template
18
+ */
19
+ export declare function getEmailVerificationTemplate(verifyUrl: string, user: TemplateUser, appName?: string): {
20
+ subject: string;
21
+ html: string;
22
+ text: string;
23
+ };
24
+ /**
25
+ * Default user invitation email template
26
+ * Sent when an admin creates a new user account
27
+ */
28
+ export declare function getUserInvitationTemplate(setPasswordUrl: string, user: TemplateUser, appName?: string): {
29
+ subject: string;
30
+ html: string;
31
+ text: string;
32
+ };
33
+ /**
34
+ * Default welcome email template
35
+ * Sent automatically when a new user registers
36
+ */
37
+ export declare function getWelcomeEmailTemplate(user: TemplateUser, appName?: string, loginUrl?: string): {
38
+ subject: string;
39
+ html: string;
40
+ text: string;
41
+ };
42
+ /**
43
+ * Default magic link email template
44
+ */
45
+ export declare function getMagicLinkTemplate(magicLinkUrl: string, user: TemplateUser, appName?: string): {
46
+ subject: string;
47
+ html: string;
48
+ text: string;
49
+ };
50
+ export {};
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Email service types and interfaces.
3
+ *
4
+ * The canonical `EmailService` and `EmailSendOptions` live in `@rebasepro/types`
5
+ * so they can be used on the `RebaseClient` interface without pulling in nodemailer.
6
+ * This file re-exports them for backward compatibility and adds server-specific
7
+ * config types (SMTP, template functions, etc.).
8
+ */
9
+ import type { EmailService, EmailSendOptions } from "@rebasepro/types";
10
+ export type { EmailService, EmailSendOptions };
11
+ /**
12
+ * SMTP server configuration
13
+ */
14
+ export interface SMTPConfig {
15
+ host: string;
16
+ port: number;
17
+ secure?: boolean;
18
+ auth?: {
19
+ user: string;
20
+ pass: string;
21
+ };
22
+ name?: string;
23
+ }
24
+ /**
25
+ * Template function for password reset emails
26
+ */
27
+ export type PasswordResetTemplateFunction = (resetUrl: string, user: {
28
+ email: string;
29
+ displayName?: string | null;
30
+ }) => {
31
+ subject: string;
32
+ html: string;
33
+ text?: string;
34
+ };
35
+ /**
36
+ * Template function for email verification emails
37
+ */
38
+ export type EmailVerificationTemplateFunction = (verifyUrl: string, user: {
39
+ email: string;
40
+ displayName?: string | null;
41
+ }) => {
42
+ subject: string;
43
+ html: string;
44
+ text?: string;
45
+ };
46
+ /**
47
+ * Template function for user invitation emails
48
+ */
49
+ export type UserInvitationTemplateFunction = (setPasswordUrl: string, user: {
50
+ email: string;
51
+ displayName?: string | null;
52
+ }) => {
53
+ subject: string;
54
+ html: string;
55
+ text?: string;
56
+ };
57
+ /**
58
+ * Template function for welcome emails sent after registration
59
+ */
60
+ export type WelcomeEmailTemplateFunction = (user: {
61
+ email: string;
62
+ displayName?: string | null;
63
+ }, appName: string) => {
64
+ subject: string;
65
+ html: string;
66
+ text?: string;
67
+ };
68
+ /**
69
+ * Template function for magic link emails
70
+ */
71
+ export type MagicLinkTemplateFunction = (magicLinkUrl: string, user: {
72
+ email: string;
73
+ displayName?: string | null;
74
+ }) => {
75
+ subject: string;
76
+ html: string;
77
+ text?: string;
78
+ };
79
+ /**
80
+ * Complete email configuration
81
+ */
82
+ export interface EmailConfig {
83
+ /**
84
+ * From address for all emails (e.g., "noreply@example.com" or "MyApp <noreply@example.com>")
85
+ */
86
+ from: string;
87
+ /**
88
+ * SMTP configuration for sending emails via SMTP server
89
+ */
90
+ smtp?: SMTPConfig;
91
+ /**
92
+ * Alternative: custom function to send emails
93
+ * Use this for custom email providers (AWS SES SDK, Resend, etc.)
94
+ */
95
+ sendEmail?: (options: EmailSendOptions) => Promise<void>;
96
+ /**
97
+ * Base URL for password reset links (e.g., "https://myapp.com")
98
+ * The reset link will be: {baseUrl}/reset-password?token={token}
99
+ */
100
+ resetPasswordUrl?: string;
101
+ /**
102
+ * Base URL for email verification links (e.g., "https://myapp.com")
103
+ * The verification link will be: {baseUrl}/verify-email?token={token}
104
+ */
105
+ verifyEmailUrl?: string;
106
+ /**
107
+ * Base URL for magic link login (e.g., "https://myapp.com")
108
+ * The magic link will be: {baseUrl}/auth/magic-link?token={token}
109
+ * Falls back to `resetPasswordUrl` if not set.
110
+ */
111
+ magicLinkUrl?: string;
112
+ /**
113
+ * Application name to use in email templates
114
+ */
115
+ appName?: string;
116
+ /**
117
+ * Custom email templates (optional - defaults are provided)
118
+ */
119
+ templates?: {
120
+ passwordReset?: PasswordResetTemplateFunction;
121
+ emailVerification?: EmailVerificationTemplateFunction;
122
+ userInvitation?: UserInvitationTemplateFunction;
123
+ welcomeEmail?: WelcomeEmailTemplateFunction;
124
+ magicLink?: MagicLinkTemplateFunction;
125
+ };
126
+ }
package/dist/env.d.ts ADDED
@@ -0,0 +1,102 @@
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<{
7
+ production: "production";
8
+ development: "development";
9
+ test: "test";
10
+ }>>;
11
+ PORT: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<number, string>>;
12
+ DATABASE_URL: z.ZodString;
13
+ ADMIN_CONNECTION_STRING: z.ZodOptional<z.ZodString>;
14
+ JWT_SECRET: z.ZodString;
15
+ JWT_ACCESS_EXPIRES_IN: z.ZodDefault<z.ZodString>;
16
+ JWT_REFRESH_EXPIRES_IN: z.ZodDefault<z.ZodString>;
17
+ GOOGLE_CLIENT_ID: z.ZodOptional<z.ZodString>;
18
+ GOOGLE_CLIENT_SECRET: z.ZodOptional<z.ZodString>;
19
+ REBASE_SERVICE_KEY: z.ZodOptional<z.ZodString>;
20
+ ALLOW_REGISTRATION: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
21
+ "": "";
22
+ true: "true";
23
+ false: "false";
24
+ }>>, z.ZodTransform<boolean, "" | "true" | "false">>;
25
+ ALLOW_LOCALHOST_IN_PRODUCTION: z.ZodPipe<z.ZodOptional<z.ZodEnum<{
26
+ "": "";
27
+ true: "true";
28
+ false: "false";
29
+ }>>, z.ZodTransform<boolean, "" | "true" | "false" | undefined>>;
30
+ CORS_ORIGINS: z.ZodOptional<z.ZodString>;
31
+ FRONTEND_URL: z.ZodOptional<z.ZodString>;
32
+ DB_POOL_MAX: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<number, string>>;
33
+ DB_POOL_IDLE_TIMEOUT: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<number, string>>;
34
+ DB_POOL_CONNECT_TIMEOUT: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<number, string>>;
35
+ DATABASE_DIRECT_URL: z.ZodOptional<z.ZodString>;
36
+ DATABASE_READ_URL: z.ZodOptional<z.ZodString>;
37
+ FORCE_LOCAL_STORAGE: z.ZodPipe<z.ZodOptional<z.ZodEnum<{
38
+ "": "";
39
+ true: "true";
40
+ false: "false";
41
+ }>>, z.ZodTransform<boolean, "" | "true" | "false" | undefined>>;
42
+ STORAGE_TYPE: z.ZodDefault<z.ZodEnum<{
43
+ local: "local";
44
+ s3: "s3";
45
+ }>>;
46
+ STORAGE_PATH: z.ZodOptional<z.ZodString>;
47
+ S3_BUCKET: z.ZodOptional<z.ZodString>;
48
+ S3_REGION: z.ZodOptional<z.ZodString>;
49
+ S3_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
50
+ S3_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
51
+ S3_ENDPOINT: z.ZodOptional<z.ZodString>;
52
+ S3_FORCE_PATH_STYLE: z.ZodPipe<z.ZodOptional<z.ZodEnum<{
53
+ "": "";
54
+ true: "true";
55
+ false: "false";
56
+ }>>, z.ZodTransform<boolean, "" | "true" | "false" | undefined>>;
57
+ }, z.core.$strip>;
58
+ /** Inferred type of the validated environment. */
59
+ export type RebaseEnv = z.infer<typeof rebaseEnvSchema>;
60
+ /**
61
+ * Load and validate the Rebase environment configuration from `process.env`.
62
+ *
63
+ * Call this **after** your `.env` file has been loaded (via `dotenv`, `--env-file`,
64
+ * container injection, etc.). This function does not load `.env` files itself —
65
+ * that is a deployment concern, not a framework concern.
66
+ *
67
+ * Behavior:
68
+ * - Auto-generates ephemeral `JWT_SECRET` and `REBASE_SERVICE_KEY` in
69
+ * non-production mode so developers can start without manual setup.
70
+ * - Blocks auto-generated secrets in production.
71
+ * - Returns a fully typed, validated env object.
72
+ *
73
+ * Use `extend` to add your own typed env variables on top of the base Rebase schema:
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * import dotenv from "dotenv";
78
+ * import { z } from "zod";
79
+ * import { loadEnv } from "@rebasepro/server";
80
+ *
81
+ * dotenv.config({ path: "../../.env" });
82
+ *
83
+ * // Basic — just Rebase env vars:
84
+ * export const env = loadEnv();
85
+ *
86
+ * // Extended — add your own typed vars:
87
+ * export const env = loadEnv({
88
+ * extend: z.object({
89
+ * SMTP_HOST: z.string().optional(),
90
+ * SMTP_PORT: z.string().default("587").transform(Number),
91
+ * STRIPE_SECRET_KEY: z.string(),
92
+ * })
93
+ * });
94
+ * // env.SMTP_HOST → string | undefined (fully typed)
95
+ * // env.STRIPE_SECRET_KEY → string (validated, required)
96
+ * ```
97
+ */
98
+ export declare function loadEnv(): RebaseEnv;
99
+ export declare function loadEnv<E extends z.ZodObject<z.ZodRawShape>>(options: {
100
+ extend: E;
101
+ }): RebaseEnv & z.infer<E>;
102
+ export {};
@@ -0,0 +1,55 @@
1
+ import { Hono } from "hono";
2
+ import type { RebaseServerClient } from "@rebasepro/types";
3
+ import type { HonoEnv } from "../api/types";
4
+ /**
5
+ * Typed context injected into a function authored with {@link defineFunction}.
6
+ *
7
+ * Surfaces the app-scoped Rebase singleton so handlers don't need to reach
8
+ * for the global `rebase` import. Request-scoped values (the authenticated
9
+ * `user`, the RLS-scoped `driver`, the `apiKey`, the `requestId`) are typed
10
+ * on the Hono context via {@link HonoEnv} — read them with `c.get("user")`
11
+ * / `c.var.driver` inside a handler.
12
+ */
13
+ export interface RebaseFunctionContext {
14
+ /**
15
+ * The server-side Rebase singleton (`dataAsAdmin`, `auth`, `storage`,
16
+ * `email`, `sql`).
17
+ *
18
+ * `rebase.dataAsAdmin` runs with **admin privileges and bypasses RLS** — use
19
+ * it only for trusted admin work. For user-scoped queries inside a handler,
20
+ * use the request `driver` (`c.var.driver`), which carries the caller's
21
+ * identity so RLS applies. (`rebase.data` is a deprecated alias for
22
+ * `rebase.dataAsAdmin`.)
23
+ */
24
+ rebase: RebaseServerClient;
25
+ }
26
+ /**
27
+ * Typed authoring contract for a custom backend function.
28
+ *
29
+ * A custom function is a file in the `functionsDir` that default-exports a
30
+ * Hono app; the loader mounts it at `/<filename>`. `defineFunction` is the
31
+ * typed opt-in for that contract: it hands you a pre-typed `Hono<HonoEnv>`
32
+ * app (so `c.var.user` / `c.var.driver` are typed) plus a
33
+ * {@link RebaseFunctionContext}, and returns exactly the Hono app the loader
34
+ * already accepts — so it is fully interchangeable with a plain
35
+ * `export default new Hono()`.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { defineFunction, requireAuth } from "@rebasepro/server";
40
+ *
41
+ * export default defineFunction((app, { rebase }) => {
42
+ * app.use("/*", requireAuth);
43
+ * app.get("/home", async (c) => {
44
+ * const [stats] = await rebase.sql!(`SELECT count(*) AS n FROM orders`);
45
+ * return c.json({ orders: Number(stats.n) });
46
+ * });
47
+ * });
48
+ * ```
49
+ *
50
+ * @param definition Receives the function's Hono app and the typed context.
51
+ * Register routes on the provided `app` and return nothing, or return your
52
+ * own `Hono<HonoEnv>` app to use instead.
53
+ * @returns The Hono app to default-export from the function file.
54
+ */
55
+ export declare function defineFunction(definition: (app: Hono<HonoEnv>, ctx: RebaseFunctionContext) => void | Hono<HonoEnv>): Hono<HonoEnv>;
@@ -0,0 +1,18 @@
1
+ import { Hono } from "hono";
2
+ import { type ModuleImporter } from "../utils/dynamic-import.js";
3
+ export interface LoadedFunction {
4
+ /** Endpoint name derived from filename (e.g., "send-invoice") */
5
+ name: string;
6
+ /** The Hono sub-app to mount */
7
+ app: Hono<import("hono").Env>;
8
+ }
9
+ /**
10
+ * Auto-discover Hono route files from a directory.
11
+ *
12
+ * Each file should default-export a Hono app (or router).
13
+ * The filename (without extension) becomes the mount path:
14
+ * `functions/send-invoice.ts` → mounted at `/send-invoice`
15
+ *
16
+ * This mirrors how `loadCollectionsFromDirectory` works for collections.
17
+ */
18
+ export declare function loadFunctionsFromDirectory(directory: string, importModule?: ModuleImporter): Promise<LoadedFunction[]>;
@@ -0,0 +1,10 @@
1
+ import { Hono } from "hono";
2
+ import { HonoEnv } from "../api/types";
3
+ import { LoadedFunction } from "./function-loader";
4
+ /**
5
+ * Mount all loaded function routes under a single Hono router.
6
+ *
7
+ * Each function is mounted at `/<function-name>`, preserving
8
+ * whatever HTTP methods and middleware the Hono sub-app defines.
9
+ */
10
+ export declare function createFunctionRoutes(functions: LoadedFunction[]): Hono<HonoEnv>;
@@ -0,0 +1,5 @@
1
+ export { loadFunctionsFromDirectory } from "./function-loader";
2
+ export type { LoadedFunction } from "./function-loader";
3
+ export { createFunctionRoutes } from "./function-routes";
4
+ export { defineFunction } from "./define-function";
5
+ export type { RebaseFunctionContext } from "./define-function";
@@ -0,0 +1,23 @@
1
+ import { Hono } from "hono";
2
+ import { HonoEnv } from "../api/types";
3
+ import { BackendCollectionRegistry } from "../collections/BackendCollectionRegistry";
4
+ import { DataDriver } from "@rebasepro/types";
5
+ /**
6
+ * Create Hono routes for entity history.
7
+ * Mounted at `{basePath}/data/:slug/:id/history`.
8
+ */
9
+ export interface HistoryService {
10
+ fetchHistory(tableName: string, id: string, options: {
11
+ limit: number;
12
+ offset: number;
13
+ }): Promise<{
14
+ data: Record<string, unknown>[];
15
+ total: number;
16
+ }>;
17
+ fetchHistoryEntry(historyId: string): Promise<Record<string, unknown> | null>;
18
+ }
19
+ export declare function createHistoryRoutes(params: {
20
+ historyService: HistoryService;
21
+ registry: BackendCollectionRegistry;
22
+ driver: DataDriver;
23
+ }): Hono<HonoEnv>;
@@ -0,0 +1 @@
1
+ export { createHistoryRoutes } from "./history-routes";